module gemini implicit none private public :: handle_request contains subroutine simplify_request(full_request, local_request) use logging, only: write_log implicit none character(*), intent(in)::full_request character(*), intent(out)::local_request integer::i, j ! Get the file of interest i = index(full_request, "://") if(i <= 0) then local_request = full_request else j = index(full_request(i+3:len_trim(full_request)), "/") local_request = full_request((i+j+2):len_trim(full_request)) end if call write_log("Simplified Local Request: "//trim(local_request)) end subroutine simplify_request subroutine read_request(ssl, req) use jessl, only: ssl_read use iso_c_binding implicit none type(c_ptr)::ssl character(*), intent(out)::req character, dimension(64)::buf integer::bufread integer::i, j req = " " i = 1 bufread = ssl_read(ssl, buf) do while(bufread > 0) do j = 1, bufread if(buf(j) == c_new_line) then exit end if if(buf(j) /= c_carriage_return) then req(i:i) = buf(j) i = i + 1 end if end do if(buf(j) == c_new_line) then exit end if bufread = ssl_read(ssl, buf) end do end subroutine read_request function read_into_buffer(unit_number, buffer) implicit none integer, intent(in)::unit_number character, dimension(*), intent(out)::buffer integer::read_into_buffer integer::i, ierr ierr = 0 i = 0 do while(ierr == 0 .and. i < len(buffer)) i = i + 1 read(unit_number, iostat=ierr) buffer(i) end do if(ierr /= 0) then i = i - 1 end if read_into_buffer = i end function read_into_buffer subroutine write_file(ssl, unit_number, mimetype) use iso_c_binding, only: c_ptr, c_carriage_return, c_new_line use jessl, only: ssl_write use logging implicit none type(c_ptr)::ssl integer, intent(in)::unit_number character(*), intent(in)::mimetype character, dimension(64)::buf integer::buflen, written call write_string(ssl, "20 "//trim(mimetype)//c_carriage_return//c_new_line) buflen = read_into_buffer(unit_number, buf) do while(buflen > 0) written = ssl_write(ssl, buf(1:buflen)) buflen = read_into_buffer(unit_number, buf) end do end subroutine write_file subroutine write_string(ssl, string) use jessl, only: ssl_write use iso_c_binding, only: c_ptr implicit none type(c_ptr)::ssl character(*)::string character, dimension(:), allocatable::buf integer::i allocate(buf(len(string))) do i = 1, len(string) buf(i) = string(i:i) end do i = ssl_write(ssl, buf) deallocate(buf) end subroutine write_string subroutine handle_request() use jessl use iso_c_binding use config use iso_fortran_env use external_handling, only: external_request_gemini use logging, only: write_log implicit none ! For our TLS connection type(c_ptr)::ctx type(c_ptr)::method type(c_ptr)::ssl integer(kind=c_long)::res ! Requested file character(1024)::request, local_request character(512)::mimetype character(len=:), pointer::filename_ptr integer::rendered_unit, ioerror call library_init() filename_ptr => null() method = tls_server_method() ctx = ctx_new(method) if(.not. C_ASSOCIATED(ctx)) then call write_log("Context failed") return end if ! Seems to be a dummy now... !res = ctx_set_ecdh_auto(ctx, 1) if(.not. ctx_use_certificate_file(ctx, trim(pubcert), SSL_FILETYPE_PEM)) then call write_log("Cert file failed") call write_log("Public: "//trim(pubcert)) !call print_error() return end if if(.not. ctx_use_private_key_file(ctx, trim(privcert), SSL_FILETYPE_PEM)) then call write_log("Cert file failed") call write_log("Private: "//trim(privcert)) !call print_error() return end if ssl = ssl_new(ctx) call write_log("Initiating connection") ! So this is a GNU Extension... res = set_read_fd(ssl, fnum(input_unit)) if(res /= 1) then call write_log("set rfd failed") !call print_error() return end if res = set_write_fd(ssl, fnum(output_unit)) if(res /= 1) then call write_log("set wfd failed") !call print_error() return end if res = ssl_accept(ssl) if(res <= 0) then call write_log("ssl_accept failed") !call print_error() return end if call write_log("Handling read_request") ! Do the actual protocol nonsense call read_request(ssl, request) call write_log("Request is "//trim(request)) call simplify_request(request, local_request) if(len_trim(local_request) .ge. 4) then if(local_request(1:4) == '/api') then !call handle_api_request(request) else filename_ptr => external_request_gemini(local_request) end if else filename_ptr => external_request_gemini(local_request) end if if(associated(filename_ptr)) then open(newunit=rendered_unit, file=trim(filename_ptr), status="old", & form="unformatted", iostat=ioerror, access="stream") call write_log("transferring "//trim(filename_ptr)) call write_file(ssl, rendered_unit, "text/gemini") close(rendered_unit) if(filename_ptr(1:5) == '/tmp/') then call unlink(filename_ptr) end if deallocate(filename_ptr) end if end subroutine handle_request end module gemini