+:- module(serve, [serve/3]).
+
+:- use_module(library(dcgs)).
+:- use_module(config).
+:- use_module(library(files)).
+:- use_module(library(iso_ext)).
+:- use_module(library(lists)).
+:- use_module(library(pio)).
+:- use_module(library(sockets)).
+:- use_module(log).
+:- use_module(mime).
+:- use_module(response).
+
+%% serve(+Stream, +Path, +Query)
+%
+% Serve the file at Path to Stream
+serve(S, /, Q) :-
+ serve(S, '/index.gmi', Q).
+serve(S, Path, _) :-
+ atom_chars(Path, Chars),
+ ( is_absolute(Chars)
+ -> guess_mime(Chars, Mime),
+ content(Root),
+ append(Root, Chars, File),
+ ( file_exists(File)
+ -> log_msg("response", "File does exist~n", []),
+ ( serve_text(S, Mime, File)
+ ; serve_binary(S, Mime, File)
+ )
+ ; log_msg("error", "File not found~n", []),
+ phrase(response(not_found, "File not found!"), Response0),
+ format(S, "~s", [Response0])
+ )
+ ; log_msg("error", "Non-absolute path requested~n", []),
+ phrase(response(bad_request, "Non-absolute paths are forbidden!"), Response0),
+ format(S, "~s", [Response0])
+ ).
+
+is_absolute(Path) :-
+ path_segments(Path, Segs),
+ \+ member("..", Segs),
+ \+ member(".", Segs).
+
+serve_text(S, Mime, File) :-
+ append("text/", _, Mime),
+ phrase_from_file(seq(Body), File),
+ log_msg("response", "Sending text response~n", []),
+ phrase(response(success, Mime), Response0),
+ format(S, "~s", [Response0]),
+ format(S, "~s", [Body]),
+ log_msg("response", "Sent text response~n", []).
+
+serve_binary(S, Mime, File) :-
+ setup_call_cleanup(
+ open(File, read, FileStream, [type(binary)]),
+ (
+ log_msg("response", "Sending binary response~n", []),
+ phrase(response(success, Mime), Response0),
+ format(S, "~s", [Response0]),
+ open(stream(S), write, _, [type(binary)]),
+ catch(copy_stream(FileStream, S),
+ error(existence_error(stream, _), _),
+ log_msg("response", "Client disconnected mid-stream~n", [])),
+ log_msg("response", "Sent binary response~n", [])
+ ),
+ close(FileStream)
+ ).