aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeffrey Armstrong <jeff@approximatrix.com>2021-03-27 16:50:20 -0400
committerJeffrey Armstrong <jeff@approximatrix.com>2021-03-27 16:50:20 -0400
commitfd9077056f7f33c60b218636ead0644d42e75a09 (patch)
tree9010b2d5ed8d22fa1e571cdec79f8a6f0c30d66c
parent0b8ec300ca4f2f2c3ce09d14ac1eed5478ea6420 (diff)
downloadlevitating-fd9077056f7f33c60b218636ead0644d42e75a09.tar.gz
levitating-fd9077056f7f33c60b218636ead0644d42e75a09.zip
Minor cleanup of the template code. Started on main program handling requests.
-rw-r--r--captain/captian.f9079
-rw-r--r--captain/config.f90106
-rw-r--r--captain/db.f902
-rw-r--r--captain/gemini.f90131
-rw-r--r--captain/levitating-captain.prj36
-rw-r--r--captain/log.f9034
-rw-r--r--captain/template.f9097
7 files changed, 451 insertions, 34 deletions
diff --git a/captain/captian.f90 b/captain/captian.f90
index e115280..432bb0a 100644
--- a/captain/captian.f90
+++ b/captain/captian.f90
@@ -1,12 +1,87 @@
program captain
use captain_db
+use config
+use logging, only: initialize_log => initialize
implicit none
+ integer::mode
+ integer, parameter::MODE_GEMINI = 1, &
+ MODE_CGI_HTML = 2
- call initialize_db("/tmp/test.db")
+ call random_seed() ! For possible crypto
+
+ call parse_options()
+
+ call initialize_db(database_filename)
+ call initialize_log(log_filename)
- call add_player_db("windows", "asdf")
+ select case(mode)
+ case(MODE_GEMINI)
+ call handle_gemini()
+ end select
call shutdown_db()
+ close(logunit)
+
+contains
+
+ subroutine usage()
+ implicit none
+
+ character(len=256)::pname
+
+ call get_command_argument(0, pname)
+
+ Print *, "Usage: "//trim(pname)//" <options>"
+ Print *, " "
+
+ Print *, "Options:"
+ Print *, " -h Display this help"
+ Print *, " -c <configfile> Use the specified config file"
+ Print *, " -g Operate in Gemini mode"
+ Print *, " -w Operate in CGI mode (default)"
+
+ end subroutine usage
+
+ subroutine parse_options
+ use config
+ implicit none
+
+ character(len=1024)::option
+ logical::config_loaded
+ integer::i
+
+ config_loaded = .false.
+ mode = MODE_CGI_HTML
+
+ i = 1
+ do while(i <= command_argument_count())
+ call get_command_argument(i, option)
+
+ if(trim(option) == "-h") then
+ call usage()
+ stop
+
+ else if(trim(option) == "-g") then
+ mode = MODE_GEMINI
+
+ else if(trim(option) == "-c") then
+ i = i + 1
+ call get_command_argument(i, option)
+ call load_configuration(trim(option))
+ config_loaded = .true.
+
+ end if
+
+ i = i + 1
+ end do
+
+ ! Assign working directory from command if not specified
+ if(.not. config_loaded) then
+ Print *, "No configuration file specified"
+ stop
+ end if
+
+ end subroutine parse_options
end program captain \ No newline at end of file
diff --git a/captain/config.f90 b/captain/config.f90
index 9daee6b..e66d42f 100644
--- a/captain/config.f90
+++ b/captain/config.f90
@@ -1,18 +1,120 @@
module config
implicit none
+ character(*), parameter::TEMPLATE_DIRECTORY_VARIABLE = "template-directory"
character(1024)::template_directory
+
+ character(*), parameter::DATABASE_VARIABLE = "database"
character(1024)::database_filename
- character(1024)::description_filename
+
+ character(*), parameter::LOGFILE_VARIABLE = "log-filename"
+ character(1024)::log_filename
+
+ character(*), parameter::PROJECT_NAME_VARIABLE = "project"
+ character(32)::project
+
+ character(*), parameter::DESCRIPTION_VARIABLE = "description"
+ character(1024)::description
+
+ character(*), parameter::PUBLIC_CERT_VARIABLE = "public-cert"
+ character(1024)::pubcert
+
+ character(*), parameter::PRIVATE_CERT_VARIABLE = "private-cert"
+ character(1024)::privcert
contains
+ subroutine get_variable(str, v)
+ implicit none
+
+ character(*), intent(in)::str
+ character(*), intent(out)::v
+
+ integer::i
+
+ v = " "
+
+ i = index(str, '=')
+ if(i > 0) then
+ v = adjustl(str(1:i))
+ end if
+
+ end subroutine get_variable
+
+ subroutine get_value(str, v)
+ implicit none
+
+ character(*), intent(in)::str
+ character(*), intent(out)::v
+
+ integer::i,n
+
+ v = " "
+
+ i = index(str, '=')
+ n = len_trim(str)
+ if(i > 0) then
+ v = adjustl(str(i+1:n))
+ end if
+
+ end subroutine get_value
+
+ subroutine assign_config(cvariable, cvalue)
+ implicit none
+
+ character(*), intent(in)::cvariable, cvalue
+
+ if(cvariable == TEMPLATE_DIRECTORY_VARIABLE) then
+ template_directory = cvalue
+
+ else if(cvariable == DATABASE_VARIABLE) then
+ database_filename = cvalue
+
+ else if(cvariable == PROJECT_NAME_VARIABLE) then
+ project = cvalue
+
+ else if(cvariable == DESCRIPTION_VARIABLE) then
+ description = cvalue
+
+ else if(cvariable == PUBLIC_CERT_VARIABLE) then
+ pubcert = cvalue
+
+ else if(cvariable == PRIVATE_CERT_VARIABLE) then
+ privcert = cvalue
+
+ else if(cvariable == LOGFILE_VARIABLE) then
+ log_filename = cvalue
+
+ end if
+
+ end subroutine assign_config
+
subroutine load_configuration(filename)
implicit none
character(*), intent(in)::filename
+ integer::unit_number, istatus
+ character(1024)::line, cvalue
+ character(64)::cvariable
-
+ open(newunit=unit_number, file=trim(filename), status='old', &
+ action="read", iostat=istatus)
+
+ read(unit_number, '(A)', iostat=istatus) line
+ do while(istatus == 0)
+
+ if(len_trim(line) > 0 .and. line(1:1) /= '#') then
+
+ call get_variable(line, cvariable)
+ call get_value(line, cvalue)
+ call assign_config(trim(cvariable), trim(cvalue))
+
+ end if
+
+ read(unit_number, '(A)', iostat=istatus) line
+ end do
+
+ close(unit_number)
end subroutine load_configuration
diff --git a/captain/db.f90 b/captain/db.f90
index 6624cc1..5fbd0b4 100644
--- a/captain/db.f90
+++ b/captain/db.f90
@@ -37,7 +37,7 @@ contains
type(sqlite3_stmt)::stmt
if(stmt%prepare(db, "INSERT INTO players(name, token) VALUES(?, ?)") == SQLITE_OK) then
- if(stmt%bind_text(1, "name!") == SQLITE_OK .and. stmt%bind_text(2, "token!") == SQLITE_OK) then
+ if(stmt%bind_text(1, name) == SQLITE_OK .and. stmt%bind_text(2, token) == SQLITE_OK) then
call stmt%step_now()
end if
end if
diff --git a/captain/gemini.f90 b/captain/gemini.f90
new file mode 100644
index 0000000..ce7b984
--- /dev/null
+++ b/captain/gemini.f90
@@ -0,0 +1,131 @@
+module gemini
+implicit none
+
+contains
+
+ 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
+
+ subroutine handle_request()
+ use jessl
+ use iso_c_binding
+ use config
+ use iso_fortran_env
+ 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
+ character(512)::mimetype
+
+ call library_init()
+
+ 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))
+
+ ! If it ends in a slash, let's manually and silently add "index.gmi"
+ if(request(len_trim(request):len_trim(request)) == "/") then
+ request = trim(request)//"index.gmi"
+ end if
+
+
+ end subroutine handle_request
+
+end module gemini \ No newline at end of file
diff --git a/captain/levitating-captain.prj b/captain/levitating-captain.prj
index 3a11a71..88c0910 100644
--- a/captain/levitating-captain.prj
+++ b/captain/levitating-captain.prj
@@ -4,41 +4,57 @@
"Folders":[],
"Name":"+common",
"Files":[{
- "filename":"../common/jessl.f90",
+ "filename":"..\\common\\jessl.f90",
"enabled":"1"
},{
- "filename":"../common/network.F90",
+ "filename":"..\\common\\network.F90",
"enabled":"1"
},{
- "filename":"../common/protocol.f90",
+ "filename":"..\\common\\protocol.f90",
"enabled":"1"
},{
- "filename":"../common/request.f90",
+ "filename":"..\\common\\request.f90",
"enabled":"1"
},{
- "filename":"../common/utilities.F90",
+ "filename":"..\\common\\utilities.F90",
"enabled":"1"
},{
- "filename":"../common/wsa.f90",
+ "filename":"..\\common\\wsa.f90",
"enabled":"0"
}]
},{
"Folders":[],
"Name":"+sql",
"Files":[{
- "filename":"sql/create.sql",
+ "filename":".\\sql\\create.sql",
+ "enabled":"1"
+ }]
+ },{
+ "Folders":[],
+ "Name":"+templates",
+ "Files":[{
+ "filename":".\\templates\\index.gmi",
"enabled":"1"
}]
}],
"Name":"+levitating-captain (levitating-captain)",
"Files":[{
- "filename":"captian.f90",
+ "filename":".\\captian.f90",
+ "enabled":"1"
+ },{
+ "filename":".\\config.f90",
+ "enabled":"1"
+ },{
+ "filename":".\\db.f90",
+ "enabled":"1"
+ },{
+ "filename":".\\external.f90",
"enabled":"1"
},{
- "filename":"db.f90",
+ "filename":".\\sqlite.f90",
"enabled":"1"
},{
- "filename":"sqlite.f90",
+ "filename":".\\template.f90",
"enabled":"1"
}]
},
diff --git a/captain/log.f90 b/captain/log.f90
new file mode 100644
index 0000000..3757faf
--- /dev/null
+++ b/captain/log.f90
@@ -0,0 +1,34 @@
+module logging
+implicit none
+
+ integer::logunit
+
+contains
+
+ subroutine initialize(filename)
+ implicit none
+
+ character(*), intent(in)::filename
+ open(newunit=logunit, file=trim(filename), action="write", status="unknown", position="append")
+
+ end subroutine initialize
+
+ subroutine shutdown()
+ implicit none
+
+ close(logunit)
+
+ end subroutine shutdown
+
+ subroutine write_log(string)
+ implicit none
+
+ character(*), intent(in)::string
+
+ ! GNU Extension... :(
+ write(logunit, *) fdate()//" :: "//string
+ call flush(logunit)
+
+ end subroutine write_log
+
+end module logging \ No newline at end of file
diff --git a/captain/template.f90 b/captain/template.f90
index 9543c42..6b5429d 100644
--- a/captain/template.f90
+++ b/captain/template.f90
@@ -3,7 +3,8 @@ implicit none
integer, parameter::VTYPE_NONE = 0, &
VTYPE_STRING = 1, &
- VTYPE_INTEGER = 2
+ VTYPE_INTEGER = 2, &
+ VTYPE_LOGICAL = 3
type :: variable
@@ -21,8 +22,9 @@ implicit none
procedure :: set_string_value => variable_set_string_value
procedure :: assign_integer => variable_assign_integer
procedure :: assign_string => variable_assign_string
+ procedure :: assign_logical => variable_assign_logical
- generic :: assign => assign_integer, assign_string
+ generic :: assign => assign_integer, assign_string, assign_logical
end type
type :: template
@@ -36,12 +38,13 @@ implicit none
procedure :: destroy => template_destroy
procedure :: assign_integer => template_assign_integer
procedure :: assign_string => template_assign_string
+ procedure :: assign_logical => template_assign_logical
procedure :: render_unit => template_render_unit
procedure :: render_filename => template_render_filename
procedure :: evaluate => template_evaluate
generic :: render => render_unit, render_filename
- generic :: assign => assign_integer, assign_string
+ generic :: assign => assign_integer, assign_string, assign_logical
end type
contains
@@ -147,6 +150,25 @@ contains
call self%set_string_value(str)
end subroutine variable_assign_string
+
+ subroutine variable_assign_logical(self, name, lg)
+ implicit none
+
+ class(variable)::self
+ character(*), intent(in)::name
+ logical, intent(in)::lg
+
+ call self%set_name(name)
+ self%vtype = VTYPE_LOGICAL
+ if(lg) then
+ self%vint = 1
+ call self%set_string_value("True")
+ else
+ self%vint = 0
+ call self%set_string_value("False")
+ end if
+
+ end subroutine variable_assign_logical
subroutine template_init(self, filename)
implicit none
@@ -228,6 +250,19 @@ contains
end subroutine template_assign_string
+ subroutine template_assign_logical(self, name, value)
+ implicit none
+
+ class(template)::self
+ character(*), intent(in)::name
+ logical, intent(in)::value
+ integer::i
+
+ i = template_available_variable_index(self)
+ call self%variables(i)%assign(name, value)
+
+ end subroutine template_assign_logical
+
subroutine template_render_filename(self, filename)
implicit none
@@ -250,50 +285,74 @@ contains
integer, intent(in)::unum
character::this_char, last_char
+ character(len=2)::running_pair, closing_pair
character(256)::brace_internals
character(len=:), pointer::replacement
+ integer(kind=8)::input_position
+
+ logical::writing_now
+
integer::input, istat, i
open(newunit=input, file=self%base_filename, status="old", form="formatted", action="read")
+ writing_now = .true.
+
last_char = ' '
read(input, '(A1)', iostat=istat) this_char
do while(istat == 0)
- if(this_char /= '{') then
- if(last_char == '{') then
- write(unum, '(A1)', advance='no') last_char
- end if
- write(unum, '(A1)', advance='no') this_char
-
- ! Two curly braces
- else if(last_char == '{') then
+
+ running_pair = last_char//this_char
+ if(running_pair == '{{') then
+
+ ! Future expansion
+ if(running_pair == '{{') then
+ closing_pair = '}}'
+ end if
+
brace_internals = ' '
read(input, '(A1)', iostat=istat) this_char
i = 1
- do while(istat == 0 .and. .not. (this_char == '}' .and. last_char == '}'))
+ do while(istat == 0 .and. .not. (running_pair /= closing_pair))
brace_internals(i:i) = this_char
i = i + 1
last_char = this_char
read(input, '(A1)', iostat=istat) this_char
+ running_pair = last_char//this_char
end do
brace_internals(i:i) = ' ' ! Remove trailing bracket
brace_internals = adjustl(brace_internals)
- replacement => self%evaluate(brace_internals)
+ ! Simple variable
+ if(closing_pair == '}}') then
+ replacement => self%evaluate(brace_internals)
+
+ if(associated(replacement)) then
+ do i=1, len_trim(replacement)
+ write(unum, '(A1)', advance='no') replacement(i:i)
+ end do
+ replacement => null() ! do not free - internal strings
+ end if
+
+ ! Expression
+ else
- if(associated(replacement)) then
- do i=1, len_trim(replacement)
- write(unum, '(A1)', advance='no') replacement(i:i)
- end do
- replacement => null() ! do not free - internal strings
+
end if
+ else if(last_char == '{') then
+ write(unum, '(A1)', advance='no') last_char
+ write(unum, '(A1)', advance='no') this_char
+
+ else
+ write(unum, '(A1)', advance='no') this_char
+
end if
-
+
last_char = this_char
read(input, '(A1)', iostat=istat) this_char
end do