initial commit
This commit is contained in:
parent
f967624953
commit
f129784a21
|
@ -0,0 +1,7 @@
|
||||||
|
.DS_Store
|
||||||
|
a.out
|
||||||
|
|
||||||
|
*.mod
|
||||||
|
*.log
|
||||||
|
|
||||||
|
build/
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* raylib [core] example - Basic window
|
||||||
|
*
|
||||||
|
* Welcome to raylib!
|
||||||
|
*
|
||||||
|
* To test examples, just press F6 and execute raylib_compile_execute script
|
||||||
|
* Note that compiled executable is placed in the same folder as .c file
|
||||||
|
*
|
||||||
|
* You can find all basic examples on C:\raylib\raylib\examples folder or
|
||||||
|
* raylib official webpage: www.raylib.com
|
||||||
|
*
|
||||||
|
* Enjoy using raylib. :)
|
||||||
|
*
|
||||||
|
* Example originally created with raylib 1.0, last time updated with raylib 1.0
|
||||||
|
*
|
||||||
|
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
|
||||||
|
* BSD-like license that allows static linking with closed source software
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5)
|
||||||
|
*
|
||||||
|
********************************************************************************************/
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------
|
||||||
|
// Program main entry point
|
||||||
|
//------------------------------------------------------------------------------------
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
// Initialization
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
const int screenWidth = 800;
|
||||||
|
const int screenHeight = 450;
|
||||||
|
|
||||||
|
InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window");
|
||||||
|
|
||||||
|
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Main game loop
|
||||||
|
while (!WindowShouldClose()) // Detect window close button or ESC key
|
||||||
|
{
|
||||||
|
// Update
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// TODO: Update your variables here
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Draw
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
BeginDrawing();
|
||||||
|
|
||||||
|
ClearBackground(RAYWHITE);
|
||||||
|
|
||||||
|
DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
|
||||||
|
|
||||||
|
EndDrawing();
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
}
|
||||||
|
|
||||||
|
// De-Initialization
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
CloseWindow(); // Close window and OpenGL context
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <err.h>
|
||||||
|
|
||||||
|
char response[] = "HTTP/1.1 200 OK\r\n"
|
||||||
|
"Content-Type: text/html; charset=UTF-8\r\n\r\n"
|
||||||
|
"<!DOCTYPE html>\r\n"
|
||||||
|
"<html lang='en'>\r\n"
|
||||||
|
"<head>\r\n"
|
||||||
|
"<style>body {background-color: black;color: #ddd;} </style>\r\n"
|
||||||
|
" <meta charset='UTF-8'>\r\n"
|
||||||
|
" <meta http-equiv='X-UA-Compatible' content='IE=edge'>\r\n"
|
||||||
|
" <meta name='viewport' content='width=device-width, initial-scale=1.0'>\r\n"
|
||||||
|
" <title>Document</title>\r\n"
|
||||||
|
"</head>\r\n"
|
||||||
|
"<body>\r\n"
|
||||||
|
" <h1>test</h1>\r\n"
|
||||||
|
"</body>\r\n"
|
||||||
|
"</html>";
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int one = 1, client_fd;
|
||||||
|
struct sockaddr_in svr_addr, cli_addr;
|
||||||
|
socklen_t sin_len = sizeof(cli_addr);
|
||||||
|
|
||||||
|
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if (sock < 0)
|
||||||
|
err(1, "can't open socket");
|
||||||
|
|
||||||
|
int port = 8080;
|
||||||
|
svr_addr.sin_family = AF_INET;
|
||||||
|
svr_addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
svr_addr.sin_port = htons(port);
|
||||||
|
|
||||||
|
if (bind(sock, (struct sockaddr *)&svr_addr, sizeof(svr_addr)) == -1)
|
||||||
|
{
|
||||||
|
close(sock);
|
||||||
|
err(1, "Can't bind");
|
||||||
|
}
|
||||||
|
|
||||||
|
listen(sock, 5);
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
client_fd = accept(sock, (struct sockaddr *)&cli_addr, &sin_len);
|
||||||
|
printf("got connection\n");
|
||||||
|
|
||||||
|
if (client_fd == -1)
|
||||||
|
{
|
||||||
|
perror("Can't accept");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
write(client_fd, response, sizeof(response) - 1); /*-1:'\0'*/
|
||||||
|
shutdown(client_fd, SHUT_WR);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>MMO Testing Game</title>
|
||||||
|
<style>
|
||||||
|
input:invalid {
|
||||||
|
border: 2px dashed black;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:valid {
|
||||||
|
border: 2px solid black;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>MMO Testing Game</h1>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<p>
|
||||||
|
I wanted to create this design document to better define the style and
|
||||||
|
what features will be in the testing game. The testing game is going to
|
||||||
|
be a extremely simplified 3D MMORPG in the spirit of Runescape 2. An
|
||||||
|
environment where players can move around the environment and chat with
|
||||||
|
each other.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form method="post" action="/">
|
||||||
|
<h5>Signup Form</h5>
|
||||||
|
<label for="username">Username: (max length 32 characters)</label><br />
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
id="username"
|
||||||
|
name="username"
|
||||||
|
maxlength="32"
|
||||||
|
value=""
|
||||||
|
/><br />
|
||||||
|
<label for="password"
|
||||||
|
>Password: (Must be between 8 and 24 characters and include 1
|
||||||
|
Lowercase Character, 1 Capital, 1 Number, and 1 Symbol)</label
|
||||||
|
><br />
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$"
|
||||||
|
minlength="8"
|
||||||
|
maxlength="24"
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
value=""
|
||||||
|
/><br />
|
||||||
|
<label for="appearance">Appearance:</label><br />
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
type="color"
|
||||||
|
value="#000000"
|
||||||
|
id="appearance"
|
||||||
|
name="appearance"
|
||||||
|
/>
|
||||||
|
<br /><br />
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
Binary file not shown.
|
@ -0,0 +1,7 @@
|
||||||
|
# Programming Language Project Fortran Implementation
|
||||||
|
|
||||||
|
[fortran-µhttpd (game website & webserver)](./www-f/README.md)
|
||||||
|
|
||||||
|
server (game backend)
|
||||||
|
|
||||||
|
client (game frontend / UI)
|
|
@ -0,0 +1 @@
|
||||||
|
# fortran mmo-project client
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/sh
|
||||||
|
gfortran -fno-range-check main.f90 raylib.f90 $(pkg-config --libs --cflags raylib) -o client
|
|
@ -0,0 +1,51 @@
|
||||||
|
program main
|
||||||
|
use iso_c_binding
|
||||||
|
use raylib
|
||||||
|
implicit none
|
||||||
|
|
||||||
|
integer(c_int) :: screenWidth = 800
|
||||||
|
integer(c_int) :: screenHeight = 450
|
||||||
|
|
||||||
|
type(camera3d), target :: camera
|
||||||
|
type(c_ptr) :: camera_p
|
||||||
|
|
||||||
|
call init_window(screenWidth, screenHeight, "fortran client : raylib"//c_null_char)
|
||||||
|
|
||||||
|
camera%position = vector3(0.0_c_float, 2.0_c_float, 4.0_c_float) !Camera position
|
||||||
|
camera%target = vector3(0.0_c_float, 2.0_c_float, 0.0_c_float) !Camera looking at point
|
||||||
|
camera%up = vector3(0.0_c_float, -1.0_c_float, 0.0_c_float) !Camera up vector(rotation towards target)
|
||||||
|
camera%fovy = 60.0_c_float !Camera field - of - view Y
|
||||||
|
camera%projection = CAMERA_PERSPECTIVE !Camera projection type
|
||||||
|
|
||||||
|
camera_p = c_loc(camera)
|
||||||
|
|
||||||
|
call disable_cursor()
|
||||||
|
call set_target_fps(60_c_int)
|
||||||
|
|
||||||
|
!Main game loop
|
||||||
|
do while (.not. window_should_close()) ! Detect window close button or ESC key
|
||||||
|
|
||||||
|
call update_camera(camera_p, CAMERA_THIRD_PERSON) ! Update camera
|
||||||
|
|
||||||
|
call begin_drawing()
|
||||||
|
call clear_background(RAYWHITE)
|
||||||
|
|
||||||
|
call begin_mode_3d(camera)
|
||||||
|
call draw_plane(vector3(0.0_c_float, 0.0_c_float, 0.0_c_float), vector2(32.0_c_float, 32.0_c_float), LIGHTGRAY) !Draw ground
|
||||||
|
call draw_cube(vector3(-16.0_c_float, 2.5_c_float, 0.0_c_float), 1.0_c_float, 5.0_c_float, 32.0_c_float, BLUE) !Draw a blue wall
|
||||||
|
call draw_cube(vector3(16.0_c_float, 2.5_c_float, 0.0_c_float), 1.0_c_float, 5.0_c_float, 32.0_c_float, LIME) !Draw a green wall
|
||||||
|
call draw_cube(vector3(0.0_c_float, 2.5_c_float, 16.0_c_float), 32.0_c_float, 5.0_c_float, 1.0_c_float, GOLD) !Draw a yellow wall
|
||||||
|
|
||||||
|
!Draw player cube
|
||||||
|
call draw_cube(camera%target, 0.5_c_float, 0.5_c_float, 0.5_c_float, PURPLE)
|
||||||
|
call draw_cube_wires(camera%target, 0.5_c_float, 0.5_c_float, 0.5_c_float, DARKPURPLE)
|
||||||
|
call end_mode_3d()
|
||||||
|
call end_drawing()
|
||||||
|
|
||||||
|
end do
|
||||||
|
|
||||||
|
call close_window() !Close window and OpenGL context
|
||||||
|
|
||||||
|
contains
|
||||||
|
|
||||||
|
end program main
|
|
@ -0,0 +1,171 @@
|
||||||
|
module raylib
|
||||||
|
use iso_c_binding
|
||||||
|
implicit none
|
||||||
|
|
||||||
|
type, bind(c) :: vector2
|
||||||
|
real(c_float) :: x
|
||||||
|
real(c_float) :: y
|
||||||
|
end type
|
||||||
|
|
||||||
|
type, bind(c) :: vector3
|
||||||
|
real(c_float) :: x
|
||||||
|
real(c_float) :: y
|
||||||
|
real(c_float) :: z
|
||||||
|
end type
|
||||||
|
|
||||||
|
type, bind(c) :: color
|
||||||
|
integer(c_int8_t) :: r
|
||||||
|
integer(c_int8_t) :: g
|
||||||
|
integer(c_int8_t) :: b
|
||||||
|
integer(c_int8_t) :: a
|
||||||
|
end type
|
||||||
|
|
||||||
|
type(color) :: LIGHTGRAY = color(200, 200, 200, 255)
|
||||||
|
type(color) :: GRAY = color(130, 130, 130, 255)
|
||||||
|
type(color) :: DARKGRAY = color(80, 80, 80, 255)
|
||||||
|
type(color) :: YELLOW = color(253, 249, 0, 255)
|
||||||
|
type(color) :: GOLD = color(255, 203, 0, 255)
|
||||||
|
type(color) :: ORANGE = color(255, 161, 0, 255)
|
||||||
|
type(color) :: PINK = color(255, 109, 194, 255)
|
||||||
|
type(color) :: RED = color(230, 41, 55, 255)
|
||||||
|
type(color) :: MAROON = color(190, 33, 55, 255)
|
||||||
|
type(color) :: GREEN = color(0, 228, 48, 255)
|
||||||
|
type(color) :: LIME = color(0, 158, 47, 255)
|
||||||
|
type(color) :: DARKGREEN = color(0, 117, 44, 255)
|
||||||
|
type(color) :: SKYBLUE = color(102, 191, 255, 255)
|
||||||
|
type(color) :: BLUE = color(0, 121, 241, 255)
|
||||||
|
type(color) :: DARKBLUE = color(0, 82, 172, 255)
|
||||||
|
type(color) :: PURPLE = color(200, 122, 255, 255)
|
||||||
|
type(color) :: VIOLET = color(135, 60, 190, 255)
|
||||||
|
type(color) :: DARKPURPLE = color(112, 31, 126, 255)
|
||||||
|
type(color) :: BEIGE = color(211, 176, 131, 255)
|
||||||
|
type(color) :: BROWN = color(127, 106, 79, 255)
|
||||||
|
type(color) :: DARKBROWN = color(76, 63, 47, 255)
|
||||||
|
type(color) :: WHITE = color(255, 255, 255, 255)
|
||||||
|
type(color) :: BLACK = color(0, 0, 0, 255)
|
||||||
|
type(color) :: BLANK = color(0, 0, 0, 0)
|
||||||
|
type(color) :: MAGENTA = color(255, 0, 255, 255)
|
||||||
|
type(color) :: RAYWHITE = color(245, 245, 245, 255)
|
||||||
|
|
||||||
|
type, bind(c) :: camera3d
|
||||||
|
type(vector3) :: position
|
||||||
|
type(vector3) :: target
|
||||||
|
type(vector3) :: up
|
||||||
|
real(c_float) :: fovy
|
||||||
|
integer(c_int) :: projection
|
||||||
|
end type
|
||||||
|
|
||||||
|
!Camera system modes
|
||||||
|
integer(c_int) :: CAMERA_CUSTOM = 0
|
||||||
|
integer(c_int) :: CAMERA_FREE = 1
|
||||||
|
integer(c_int) :: CAMERA_ORBITAL = 2
|
||||||
|
integer(c_int) :: CAMERA_FIRST_PERSON = 3
|
||||||
|
integer(c_int) :: CAMERA_THIRD_PERSON = 4
|
||||||
|
|
||||||
|
!Camera projection
|
||||||
|
integer(c_int) :: CAMERA_PERSPECTIVE = 0
|
||||||
|
integer(c_int) :: CAMERA_ORTHOGRAPHIC = 1
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
subroutine init_window(width, height, title) bind(c, name="InitWindow")
|
||||||
|
import :: c_char
|
||||||
|
import :: c_int
|
||||||
|
integer(c_int), intent(in), value :: width
|
||||||
|
integer(c_int), intent(in), value :: height
|
||||||
|
character(c_char), dimension(*), intent(in) :: title
|
||||||
|
end subroutine
|
||||||
|
|
||||||
|
function window_should_close() result(res) bind(c, name="WindowShouldClose")
|
||||||
|
import :: c_bool
|
||||||
|
logical(c_bool) :: res
|
||||||
|
end function
|
||||||
|
|
||||||
|
subroutine close_window() bind(c, name="CloseWindow")
|
||||||
|
end subroutine
|
||||||
|
|
||||||
|
subroutine clear_background(col) bind(c, name="ClearBackground")
|
||||||
|
import :: color
|
||||||
|
Type(color), intent(in), value :: col
|
||||||
|
end subroutine
|
||||||
|
|
||||||
|
subroutine begin_drawing() bind(c, name="BeginDrawing")
|
||||||
|
end subroutine
|
||||||
|
|
||||||
|
subroutine end_drawing() bind(c, name="EndDrawing")
|
||||||
|
end subroutine
|
||||||
|
|
||||||
|
subroutine begin_mode_3d(camera) bind(c, name="BeginMode3D")
|
||||||
|
import :: camera3d
|
||||||
|
type(camera3d), intent(in), value :: camera
|
||||||
|
end subroutine
|
||||||
|
|
||||||
|
subroutine end_mode_3d() bind(c, name="EndMode3D")
|
||||||
|
end subroutine
|
||||||
|
|
||||||
|
subroutine set_target_fps(fps) bind(c, name="SetTargetFPS")
|
||||||
|
import :: c_int
|
||||||
|
integer(c_int), intent(in), value :: fps
|
||||||
|
end subroutine
|
||||||
|
|
||||||
|
subroutine update_camera(camera_ptr, mode) bind(c, name="UpdateCamera")
|
||||||
|
import :: c_ptr, c_int
|
||||||
|
type(c_ptr) :: camera_ptr
|
||||||
|
integer(c_int):: mode
|
||||||
|
end subroutine update_camera
|
||||||
|
|
||||||
|
subroutine show_cursor() bind(c, name="ShowCursor")
|
||||||
|
end subroutine show_cursor
|
||||||
|
|
||||||
|
subroutine hide_cursor() bind(c, name="HideCursor")
|
||||||
|
end subroutine hide_cursor
|
||||||
|
|
||||||
|
subroutine EnableCursor() bind(c, name="EnableCursor")
|
||||||
|
end subroutine EnableCursor
|
||||||
|
|
||||||
|
subroutine disable_cursor() bind(c, name="DisableCursor")
|
||||||
|
end subroutine disable_cursor
|
||||||
|
|
||||||
|
subroutine draw_text(text, posX, posY, fontSize, col) bind(c, name="DrawText")
|
||||||
|
import :: c_int
|
||||||
|
import :: color
|
||||||
|
import :: c_char
|
||||||
|
character(c_char), dimension(*), intent(in) :: text
|
||||||
|
integer(c_int), intent(in), value :: posX
|
||||||
|
integer(c_int), intent(in), value :: posY
|
||||||
|
integer(c_int), intent(in), value :: fontSize
|
||||||
|
type(color), intent(in), value :: col
|
||||||
|
end subroutine draw_text
|
||||||
|
|
||||||
|
subroutine draw_cube(position, width, height, length, col) bind(c, name="DrawCube")
|
||||||
|
import :: vector3
|
||||||
|
import :: c_float
|
||||||
|
import :: color
|
||||||
|
type(vector3), intent(in), value :: position
|
||||||
|
real(c_float), intent(in), value :: width
|
||||||
|
real(c_float), intent(in), value :: height
|
||||||
|
real(c_float), intent(in), value :: length
|
||||||
|
type(color), intent(in), value :: col
|
||||||
|
end subroutine draw_cube
|
||||||
|
|
||||||
|
subroutine draw_cube_wires(position, width, height, length, col) bind(c, name="DrawCubeWires")
|
||||||
|
import :: vector3
|
||||||
|
import :: c_float
|
||||||
|
import :: color
|
||||||
|
type(vector3), intent(in), value :: position
|
||||||
|
real(c_float), intent(in), value :: width
|
||||||
|
real(c_float), intent(in), value :: height
|
||||||
|
real(c_float), intent(in), value :: length
|
||||||
|
type(color), intent(in), value :: col
|
||||||
|
end subroutine draw_cube_wires
|
||||||
|
|
||||||
|
subroutine draw_plane(centerPos, size, col) bind(c, name="DrawPlane")
|
||||||
|
import vector3
|
||||||
|
import vector2
|
||||||
|
import color
|
||||||
|
type(vector3), intent(in), value :: centerPos
|
||||||
|
type(vector2), intent(in), value :: size
|
||||||
|
type(color), intent(in), value :: col
|
||||||
|
end subroutine draw_plane
|
||||||
|
end interface
|
||||||
|
end module
|
|
@ -0,0 +1,38 @@
|
||||||
|
# fortran-µhttpd (game website & webserver)
|
||||||
|
|
||||||
|
Fortran does not have any native way to connect to the internet. It cannot even create sockets of any kind.
|
||||||
|
|
||||||
|
This makes it an absolute nightmare to create a web server since you need to import sockets and tcp libraries from C, using the using the [use iso_c_binding feature](https://fortranwiki.org/fortran/show/iso_c_binding).
|
||||||
|
|
||||||
|
However, in the spirit of the challenge, I wanted to not have to resort to importing functions from another programming language as much as possible. Later when I create the client I would *have* to import libraries from C, but for this I could try and use "Fortran Only".
|
||||||
|
|
||||||
|
While reading this [old Fortran google groups question from 2009](https://groups.google.com/g/comp.lang.fortran/c/wL1xdnB1plk) the group suggested that the user just use [netcat](https://en.wikipedia.org/wiki/Netcat) and serve the Fortran program as if it were a cgi script.
|
||||||
|
|
||||||
|
This was a great idea because instead of needing to mess around with networking and making sure the functions and types imported corretly from C, I could just read and write to standard in/out and to files.
|
||||||
|
|
||||||
|
The downside of using `netcat` is that it uses unidirectional sockets. You would have to create a named pipe and do a bunch of redrieting to get it to work.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkfifo ncpipe
|
||||||
|
nc -l 8080 0<ncpipe | ./fortran-µhttpd '../../common/html/index.html' 1>ncpipe
|
||||||
|
```
|
||||||
|
|
||||||
|
It just doesnt seem all that clean to me. Additionally, it refused to stay open after running one message from the web client, so I gave up on that approach.
|
||||||
|
|
||||||
|
Somthing that does work and be a lot cleaner is [listen1](https://9fans.github.io/plan9port/man/man8/listen1.html) from [plan9port](https://9fans.github.io/plan9port/). This allows for the web browser to connect to the programs stdin, stdout, & stderr, and the program can read and write as a normal command line program.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
listen1 'tcp!*!8080' ./fortran-µhttpd
|
||||||
|
```
|
||||||
|
I next implemented the post handling. In www.f90, it checks to see if the header returning from the
|
||||||
|
|
||||||
|
I found a library to interface with the sqlite library I will store the username, password, and other information about.
|
||||||
|
|
||||||
|
The issue is that fortran does not have a built in cryptographic hash library nor is it in the stdlib, I did find a implementation of the [SM3](https://github.com/zoziha/SM3-Fortran/tree/main) hash which is cryptographically sound, and have used that.
|
||||||
|
|
||||||
|
If this were to be used in the real world I would prefer to use [bcrypt](https://en.wikipedia.org/wiki/Bcrypt) as it is the most common password hashing algorithm.
|
||||||
|
|
||||||
|
The implementation does not work well with existing systems since you have to convert from fortran strings to c strings and it returns an integer array of c strings which has to be converted back into fortran strings.
|
||||||
|
|
||||||
|
This is a horrible implementation from a security POV, since there is no easy way to sanitize the input, fortran certainly doesn't do that out of the box.
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
program main
|
||||||
|
use www
|
||||||
|
use sqliteff
|
||||||
|
use SM3_module, only: SM3
|
||||||
|
use iso_varying_string
|
||||||
|
use, intrinsic :: iso_c_binding, only: c_signed_char, c_size_t
|
||||||
|
implicit none
|
||||||
|
|
||||||
|
character, dimension(:), allocatable :: form_data
|
||||||
|
character(len=32) :: index_path
|
||||||
|
character(len=128) :: db_path
|
||||||
|
integer :: err, i, length
|
||||||
|
logical :: exist
|
||||||
|
|
||||||
|
call getarg(1, index_path)
|
||||||
|
index_path = trim(index_path)
|
||||||
|
|
||||||
|
call getarg(2, db_path)
|
||||||
|
db_path = trim(db_path)
|
||||||
|
|
||||||
|
call request(form_data, length)
|
||||||
|
|
||||||
|
if (length .gt. 0) then
|
||||||
|
call createDB(db_path)
|
||||||
|
inquire(file="debug.log", exist=exist)
|
||||||
|
if (exist) then
|
||||||
|
open(12, file="debug.log", status="old", position="rewind", action="write")
|
||||||
|
else
|
||||||
|
open(12, file="debug.log", status="new", action="write")
|
||||||
|
end if
|
||||||
|
do i=1,length
|
||||||
|
write(12, '(A)', advance="NO") form_data(i)
|
||||||
|
end do
|
||||||
|
write(12, '(A)')
|
||||||
|
close(unit=12)
|
||||||
|
call write_index(index_path)
|
||||||
|
else
|
||||||
|
call write_index(index_path)
|
||||||
|
end if
|
||||||
|
|
||||||
|
if (allocated(form_data)) deallocate (form_data, stat=err)
|
||||||
|
if (err /= 0) print *, "index_html: Deallocation request denied"
|
||||||
|
|
||||||
|
contains
|
||||||
|
|
||||||
|
subroutine createDB(db_path)
|
||||||
|
character(len=128), intent(in) :: db_path
|
||||||
|
type(SqliteDatabase_t) :: connection
|
||||||
|
type(SqliteStatement_t) :: statement
|
||||||
|
type(VARYING_STRING) :: remaining
|
||||||
|
integer :: status
|
||||||
|
|
||||||
|
status = sqliteff_open(db_path, connection)
|
||||||
|
|
||||||
|
status = sqliteff_prepare( &
|
||||||
|
connection, &
|
||||||
|
var_str("CREATE TABLE users (identifier INTEGER PRIMARY KEY ASC, " &
|
||||||
|
// "username TEXT, password TEXT, apperance TEXT, x_pos INTEGER, " &
|
||||||
|
// "y_pos INTEGER, last_login INTEGER, created INTEGER);"), &
|
||||||
|
statement, &
|
||||||
|
remaining)
|
||||||
|
|
||||||
|
if (status .eq. SQLITE_OK) then
|
||||||
|
status = sqliteff_step(statement)
|
||||||
|
status = sqliteff_finalize(statement)
|
||||||
|
status = sqliteff_close(connection)
|
||||||
|
end if
|
||||||
|
|
||||||
|
end subroutine createDB
|
||||||
|
|
||||||
|
subroutine addUser(db_path, username, password, appearance)
|
||||||
|
character(len=128), intent(in) :: db_path
|
||||||
|
character(len=24), intent(in) :: username
|
||||||
|
character(len=24), intent(in) :: password
|
||||||
|
character(len=8), intent(in) :: appearance
|
||||||
|
character(len=32) :: encrypted_password
|
||||||
|
|
||||||
|
type(SqliteDatabase_t) :: connection
|
||||||
|
type(VARYING_STRING) :: errmsg
|
||||||
|
integer :: status
|
||||||
|
integer(8) :: created
|
||||||
|
character(len=32) :: created_str
|
||||||
|
|
||||||
|
integer(c_signed_char) :: dgst(32)
|
||||||
|
integer(c_signed_char) :: msg(25)
|
||||||
|
|
||||||
|
msg = transfer(password, msg)
|
||||||
|
call SM3(msg, 25_c_size_t, dgst)
|
||||||
|
|
||||||
|
write (encrypted_password, *) dgst
|
||||||
|
encrypted_password = adjustl(encrypted_password)
|
||||||
|
|
||||||
|
created = time()
|
||||||
|
write (created_str, *) created
|
||||||
|
created_str = adjustl(created_str)
|
||||||
|
|
||||||
|
status = sqliteff_open(db_path, connection)
|
||||||
|
|
||||||
|
status = sqliteff_exec( &
|
||||||
|
connection, &
|
||||||
|
'INSERT INTO users (username, password, apperance, x_pos, y_pos, last_login, created) &
|
||||||
|
VALUES ("'//username//'", "'//encrypted_password//'", "#'//appearance//'", &
|
||||||
|
0, 0, 0, '//created_str//'");', &
|
||||||
|
errmsg)
|
||||||
|
|
||||||
|
status = sqliteff_close(connection)
|
||||||
|
|
||||||
|
end subroutine addUser
|
||||||
|
|
||||||
|
end program main
|
|
@ -0,0 +1,21 @@
|
||||||
|
author = "zongor"
|
||||||
|
copyright = "Copyright 2023, zongor"
|
||||||
|
license = "license"
|
||||||
|
maintainer = "admin@alfrescocavern.com"
|
||||||
|
name = "fortran-micro-httpd"
|
||||||
|
version = "0.1.0"
|
||||||
|
[build]
|
||||||
|
auto-examples = true
|
||||||
|
auto-executables = true
|
||||||
|
auto-tests = false
|
||||||
|
module-naming = false
|
||||||
|
[install]
|
||||||
|
library = false
|
||||||
|
[fortran]
|
||||||
|
implicit-external = false
|
||||||
|
implicit-typing = false
|
||||||
|
source-form = "free"
|
||||||
|
[dependencies]
|
||||||
|
iso_varying_string.git = "https://gitlab.com/everythingfunctional/iso_varying_string"
|
||||||
|
sqliteff.git = "https://gitlab.com/everythingfunctional/sqliteff"
|
||||||
|
sm3-fortran.git = "https://github.com/zoziha/SM3-Fortran"
|
|
@ -0,0 +1,73 @@
|
||||||
|
module www
|
||||||
|
use iso_fortran_env
|
||||||
|
implicit none
|
||||||
|
private
|
||||||
|
character(len=1024) :: buffer
|
||||||
|
|
||||||
|
public :: response_header, request, write_index
|
||||||
|
contains
|
||||||
|
|
||||||
|
subroutine response_header(length)
|
||||||
|
integer, intent(in) :: length
|
||||||
|
|
||||||
|
write (output_unit, '(A)') "HTTP/1.1 200 OK"
|
||||||
|
write (output_unit, '(A)') "Server: fortran-µhttpd"
|
||||||
|
write (output_unit, '(A)') "Content-Type: text/html; charset=UTF-8"
|
||||||
|
write (output_unit, '(A)', advance='no') "Content-length: "
|
||||||
|
write (output_unit, *) length
|
||||||
|
write (output_unit, *)
|
||||||
|
end subroutine response_header
|
||||||
|
|
||||||
|
subroutine request(post_content, post_content_length)
|
||||||
|
character, dimension(:), allocatable, intent(out) :: post_content
|
||||||
|
integer, intent(out) :: post_content_length
|
||||||
|
character(len=:), allocatable :: client_request
|
||||||
|
integer :: content_length, err, isize, cr_index, i
|
||||||
|
|
||||||
|
client_request = ''
|
||||||
|
post_content_length = 0
|
||||||
|
read_client_request: do
|
||||||
|
read (input_unit, iostat=err, fmt='(a)', advance='no', size=isize) buffer
|
||||||
|
if (isize .eq. 0) exit read_client_request
|
||||||
|
|
||||||
|
cr_index = index(buffer(:isize), "Content-Length: ", back=.true.)
|
||||||
|
if (cr_index .ne. 0) then
|
||||||
|
cr_index = index(buffer(:isize), ": ")
|
||||||
|
read (buffer(cr_index + 2:isize), *) content_length
|
||||||
|
end if
|
||||||
|
|
||||||
|
client_request = client_request//buffer(:isize)//new_line('(A)')
|
||||||
|
end do read_client_request
|
||||||
|
|
||||||
|
if (content_length .gt. 0) then
|
||||||
|
post_content_length = content_length
|
||||||
|
allocate (post_content(content_length))
|
||||||
|
do i = 1, content_length
|
||||||
|
read (input_unit, iostat=err, fmt='(a)', advance='no', size=isize) post_content(i)
|
||||||
|
end do
|
||||||
|
end if
|
||||||
|
end subroutine request
|
||||||
|
|
||||||
|
subroutine write_index(filename)
|
||||||
|
character(len=32), intent(in) :: filename
|
||||||
|
character, dimension(:), allocatable :: index_html
|
||||||
|
integer :: filesize, err, i
|
||||||
|
|
||||||
|
open (unit=11, file=filename, status='old', access='stream')
|
||||||
|
inquire (unit=11, size=filesize)
|
||||||
|
|
||||||
|
allocate (index_html(filesize))
|
||||||
|
|
||||||
|
do i = 1, filesize
|
||||||
|
read (11, pos=i) index_html(i)
|
||||||
|
end do
|
||||||
|
close (unit=11)
|
||||||
|
|
||||||
|
call response_header(filesize)
|
||||||
|
write (output_unit, *) index_html
|
||||||
|
|
||||||
|
if (allocated(index_html)) deallocate (index_html, stat=err)
|
||||||
|
if (err /= 0) print *, "index_html: Deallocation request denied"
|
||||||
|
end subroutine write_index
|
||||||
|
|
||||||
|
end module www
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/sh
|
||||||
|
listen1 'tcp!*!1234' ./build/gfortran_D153B38149EACCAC/app/fortran-micro-httpd ../../common/html/index.html ../../common/sql/test.db3
|
Loading…
Reference in New Issue