summaryrefslogtreecommitdiff
path: root/packages/libmicrohttpd/examples
diff options
context:
space:
mode:
authormichael <michael@3ad0048d-3df7-0310-abae-a5850022a9f2>2015-10-17 08:13:58 +0000
committermichael <michael@3ad0048d-3df7-0310-abae-a5850022a9f2>2015-10-17 08:13:58 +0000
commit749005d0e07af70a86963fb167df1aae44ae33b8 (patch)
treecb80fbfbb0d701b9ebe63884b06e4c04dd8c0600 /packages/libmicrohttpd/examples
parentfd48fbb220429d54fce7b147099444b4def5ca20 (diff)
downloadfpc-749005d0e07af70a86963fb167df1aae44ae33b8.tar.gz
* Added libmicrohttpd, from Silvio Clecio.
git-svn-id: http://svn.freepascal.org/svn/fpc/trunk@32067 3ad0048d-3df7-0310-abae-a5850022a9f2
Diffstat (limited to 'packages/libmicrohttpd/examples')
-rw-r--r--packages/libmicrohttpd/examples/basicauthentication.pp70
-rw-r--r--packages/libmicrohttpd/examples/benchmark.pp131
-rw-r--r--packages/libmicrohttpd/examples/benchmark_https.pp182
-rw-r--r--packages/libmicrohttpd/examples/chunked_example.pp81
-rw-r--r--packages/libmicrohttpd/examples/cutils.pas225
-rw-r--r--packages/libmicrohttpd/examples/demo.pp811
-rw-r--r--packages/libmicrohttpd/examples/demo_https.pp865
-rw-r--r--packages/libmicrohttpd/examples/digest_auth_example.pp127
-rw-r--r--packages/libmicrohttpd/examples/dual_stack_example.pp78
-rw-r--r--packages/libmicrohttpd/examples/fileserver_example.pp115
-rw-r--r--packages/libmicrohttpd/examples/fileserver_example_dirs.pp167
-rw-r--r--packages/libmicrohttpd/examples/fileserver_example_external_select.pp146
-rw-r--r--packages/libmicrohttpd/examples/hellobrowser.pp42
-rw-r--r--packages/libmicrohttpd/examples/https_fileserver_example.pp194
-rw-r--r--packages/libmicrohttpd/examples/largepost.pp187
-rw-r--r--packages/libmicrohttpd/examples/logging.pp43
-rw-r--r--packages/libmicrohttpd/examples/magic.inc15
-rw-r--r--packages/libmicrohttpd/examples/minimal_example.pp82
-rw-r--r--packages/libmicrohttpd/examples/minimal_example_comet.pp81
-rw-r--r--packages/libmicrohttpd/examples/post_example.pp640
-rw-r--r--packages/libmicrohttpd/examples/querystring_example.pp89
-rw-r--r--packages/libmicrohttpd/examples/refuse_post_example.pp94
-rw-r--r--packages/libmicrohttpd/examples/responseheaders.pp66
-rw-r--r--packages/libmicrohttpd/examples/sessions.pp623
-rw-r--r--packages/libmicrohttpd/examples/simplepost.pp155
-rw-r--r--packages/libmicrohttpd/examples/tlsauthentication.pp234
26 files changed, 5543 insertions, 0 deletions
diff --git a/packages/libmicrohttpd/examples/basicauthentication.pp b/packages/libmicrohttpd/examples/basicauthentication.pp
new file mode 100644
index 0000000000..2ab0b7446f
--- /dev/null
+++ b/packages/libmicrohttpd/examples/basicauthentication.pp
@@ -0,0 +1,70 @@
+(* Feel free to use this example code in any way
+ you see fit (Public Domain) *)
+
+// Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/basicauthentication.c
+
+program basicauthentication;
+
+{$mode objfpc}{$H+}
+
+uses
+ libmicrohttpd, SysUtils;
+
+const
+ PORT = 8888;
+
+ function AnswerToConnection(ACls: Pointer; AConnection: PMHD_Connection;
+ AUrl: Pcchar; AMethod: Pcchar; AVersion: Pcchar; AUploadData: Pcchar;
+ AUploadDataSize: Psize_t; AConCls: PPointer): cint; cdecl;
+ var
+ VPage: Pcchar;
+ VUser: Pcchar;
+ VPass: Pcchar;
+ VReturn: cint;
+ VFail: Boolean;
+ VResponse: PMHD_Response;
+ begin
+ if StrComp(AMethod, 'GET') <> 0 then
+ Exit(MHD_NO);
+ if not Assigned(AConCls^) then
+ begin
+ AConCls^ := AConnection;
+ Exit(MHD_YES);
+ end;
+ VPass := nil;
+ VUser := MHD_basic_auth_get_username_password(AConnection, @VPass);
+ VFail := (VUser = nil) or (StrComp(VUser, 'root') <> 0) or
+ (StrComp(VPass, 'pa$$w0rd') <> 0);
+ if VUser <> nil then
+ VUser := nil;
+ if VPass <> nil then
+ VPass := nil;
+ if VFail then
+ begin
+ VPage := '<html><body>Go away.</body></html>';
+ VResponse := MHD_create_response_from_buffer(Length(VPage),
+ Pointer(VPage), MHD_RESPMEM_PERSISTENT);
+ VReturn := MHD_queue_basic_auth_fail_response(AConnection,
+ 'my realm', VResponse);
+ end
+ else
+ begin
+ VPage := '<html><body>A secret.</body></html>';
+ VResponse := MHD_create_response_from_buffer(Length(VPage),
+ Pointer(VPage), MHD_RESPMEM_PERSISTENT);
+ VReturn := MHD_queue_response(AConnection, MHD_HTTP_OK, VResponse);
+ end;
+ MHD_destroy_response(VResponse);
+ Result := VReturn;
+ end;
+
+var
+ VDaemon: PMHD_Daemon;
+begin
+ VDaemon := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, nil,
+ nil, @AnswerToConnection, nil, MHD_OPTION_END);
+ if not Assigned(VDaemon) then
+ Halt(1);
+ ReadLn;
+ MHD_stop_daemon(VDaemon);
+end.
diff --git a/packages/libmicrohttpd/examples/benchmark.pp b/packages/libmicrohttpd/examples/benchmark.pp
new file mode 100644
index 0000000000..1cbe6e8928
--- /dev/null
+++ b/packages/libmicrohttpd/examples/benchmark.pp
@@ -0,0 +1,131 @@
+(*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007, 2013 Christian Grothoff (and other contributing authors)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*)
+(**
+ * @file benchmark.pp (Original: benchmark.c)
+ * @brief minimal code to benchmark MHD GET performance
+ * @author Christian Grothoff / Silvio Clécio
+ *)
+
+program benchmark;
+
+{$mode objfpc}{$H+}
+{$MACRO ON}
+{$IF DEFINED(CPU_COUNT) and (CPU_COUNT + 0) < 2}
+ {$UNDEF CPU_COUNT}
+{$ENDIF}
+{$IF NOT DEFINED(CPU_COUNT)}
+ {$DEFINE CPU_COUNT := 2}
+{$ENDIF}
+
+uses
+{$IFDEF MSWINDOWS}
+ WinSock2,
+{$ELSE}
+ BaseUnix, Unix,
+{$ENDIF}
+ cmem, sysutils, cutils, libmicrohttpd;
+
+const
+ PAGE: Pcchar = '<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>';
+ SMALL = 1024 * 128;
+ NUMBER_OF_THREADS = CPU_COUNT;
+
+var
+ small_deltas: array[0..SMALL] of cuint;
+ response: PMHD_Response;
+
+ procedure completed_callback(cls: Pointer; connection: PMHD_Connection;
+ con_cls: PPointer; toe: MHD_RequestTerminationCode); cdecl;
+ var
+ tv: ptimeval;
+ tve: timeval;
+ delta: cuint64;
+ begin
+ tv := con_cls^;
+ if nil = tv then
+ Exit;
+ fpgettimeofday(@tve, nil);
+ delta := 0;
+ if tve.tv_usec >= tv^.tv_usec then
+ delta += (tve.tv_sec - tv^.tv_sec) * 1000000 +
+ (tve.tv_usec - tv^.tv_usec)
+ else
+ delta += (tve.tv_sec - tv^.tv_sec) * 1000000 -
+ tv^.tv_usec + tve.tv_usec;
+ if delta < SMALL then
+ Inc(small_deltas[delta])
+ else
+ WriteLn(stdout, Format('D: %u 1', [delta]));
+ Free(tv);
+ end;
+
+ function uri_logger_cb(cls: Pointer; uri: Pcchar): Pointer; cdecl;
+ var
+ tv: ptimeval;
+ begin
+ tv := Malloc(SizeOf(timeval));
+ if nil <> tv then
+ fpgettimeofday(tv, nil);
+ Result := tv;
+ end;
+
+ function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar;
+ method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; ptr: PPointer): cint; cdecl;
+ begin
+ if 0 <> strcomp(method, 'GET') then
+ Exit(MHD_NO);
+ Result := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ end;
+
+var
+ d: PMHD_Daemon;
+ i: cuint;
+begin
+ if argc <> 2 then
+ begin
+ WriteLn(argv[0] + ' PORT');
+ Halt(1);
+ end;
+ response := MHD_create_response_from_buffer(Length(PAGE), Pointer(PAGE),
+ MHD_RESPMEM_PERSISTENT);
+{$IF 0}
+ MHD_add_response_header (response, MHD_HTTP_HEADER_CONNECTION, 'close');
+{$ENDIF}
+ d := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY or MHD_SUPPRESS_DATE_NO_CLOCK
+{$IFDEF EPOLL_SUPPORT}
+ or MHD_USE_EPOLL_LINUX_ONLY or MHD_USE_EPOLL_TURBO
+{$ENDIF},
+ StrToInt(argv[1]), nil, nil, @ahc_echo, nil,
+ MHD_OPTION_CONNECTION_TIMEOUT, 120,
+ MHD_OPTION_THREAD_POOL_SIZE, NUMBER_OF_THREADS,
+ MHD_OPTION_URI_LOG_CALLBACK, @uri_logger_cb, nil,
+ MHD_OPTION_NOTIFY_COMPLETED, @completed_callback, nil,
+ MHD_OPTION_CONNECTION_LIMIT, 1000,
+ MHD_OPTION_END);
+ if d = nil then
+ Halt(1);
+ ReadLn;
+ MHD_stop_daemon(d);
+ MHD_destroy_response(response);
+ for i := 0 to SMALL do
+ if 0 <> small_deltas[i] then
+ WriteLn(stdout, Format('D: %d %u', [i, small_deltas[i]]));
+end.
+
diff --git a/packages/libmicrohttpd/examples/benchmark_https.pp b/packages/libmicrohttpd/examples/benchmark_https.pp
new file mode 100644
index 0000000000..32496255c0
--- /dev/null
+++ b/packages/libmicrohttpd/examples/benchmark_https.pp
@@ -0,0 +1,182 @@
+(*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007, 2013 Christian Grothoff (and other contributing authors)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*)
+(**
+ * @file benchmark_https.pp (Original: benchmark_https.c)
+ * @brief minimal code to benchmark MHD GET performance with HTTPS
+ * @author Christian Grothoff / Silvio Clécio
+ *)
+
+program benchmark_https;
+
+{$mode objfpc}{$H+}
+{$MACRO ON}
+{$IF DEFINED(CPU_COUNT) and (CPU_COUNT + 0) < 2}
+ {$UNDEF CPU_COUNT}
+{$ENDIF}
+{$IF NOT DEFINED(CPU_COUNT)}
+ {$DEFINE CPU_COUNT := 2}
+{$ENDIF}
+
+uses
+{$IFDEF MSWINDOWS}
+ WinSock2,
+{$ELSE}
+ BaseUnix, Unix,
+{$ENDIF}
+ cmem, sysutils, cutils, libmicrohttpd;
+
+const
+ PAGE: Pcchar = '<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>';
+ SMALL = 1024 * 128;
+ NUMBER_OF_THREADS = CPU_COUNT;
+
+var
+ small_deltas: array[0..SMALL] of cuint;
+ response: PMHD_Response;
+
+ procedure completed_callback(cls: Pointer; connection: PMHD_Connection;
+ con_cls: PPointer; toe: MHD_RequestTerminationCode); cdecl;
+ var
+ tv: ptimeval;
+ tve: timeval;
+ delta: cuint64;
+ begin
+ tv := con_cls^;
+ if nil = tv then
+ Exit;
+ fpgettimeofday(@tve, nil);
+ delta := 0;
+ if tve.tv_usec >= tv^.tv_usec then
+ delta += (tve.tv_sec - tv^.tv_sec) * 1000000 +
+ (tve.tv_usec - tv^.tv_usec)
+ else
+ delta += (tve.tv_sec - tv^.tv_sec) * 1000000 -
+ tv^.tv_usec + tve.tv_usec;
+ if delta < SMALL then
+ Inc(small_deltas[delta])
+ else
+ WriteLn(stdout, Format('D: %u 1', [delta]));
+ Free(tv);
+ end;
+
+ function uri_logger_cb(cls: Pointer; uri: Pcchar): Pointer; cdecl;
+ var
+ tv: ptimeval;
+ begin
+ tv := Malloc(SizeOf(timeval));
+ if nil <> tv then
+ fpgettimeofday(tv, nil);
+ Result := tv;
+ end;
+
+ function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar;
+ method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; ptr: PPointer): cint; cdecl;
+ begin
+ if 0 <> strcomp(method, 'GET') then
+ Exit(MHD_NO);
+ Result := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ end;
+
+const
+ srv_signed_key_pem: array[0..1674] of AnsiChar =
+ '-----BEGIN RSA PRIVATE KEY-----'#10+
+ 'MIIEowIBAAKCAQEAvfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW'#10+
+ '+K03KwEku55QvnUndwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8IL'#10+
+ 'q4sw32vo0fbMu5BZF49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ0'#10+
+ '20Q5EAAEseD1YtWCIpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6'#10+
+ 'QYGGh1QmHRPAy3CBII6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6x'#10+
+ 'yoOl204xuekZOaG9RUPId74Rtmwfi1TLbBzo2wIDAQABAoIBADu09WSICNq5cMe4'#10+
+ '+NKCLlgAT1NiQpLls1gKRbDhKiHU9j8QWNvWWkJWrCya4QdUfLCfeddCMeiQmv3K'#10+
+ 'lJMvDs+5OjJSHFoOsGiuW2Ias7IjnIojaJalfBml6frhJ84G27IXmdz6gzOiTIer'#10+
+ 'DjeAgcwBaKH5WwIay2TxIaScl7AwHBauQkrLcyb4hTmZuQh6ArVIN6+pzoVuORXM'#10+
+ 'bpeNWl2l/HSN3VtUN6aCAKbN/X3o0GavCCMn5Fa85uJFsab4ss/uP+2PusU71+zP'#10+
+ 'sBm6p/2IbGvF5k3VPDA7X5YX61sukRjRBihY8xSnNYx1UcoOsX6AiPnbhifD8+xQ'#10+
+ 'Tlf8oJUCgYEA0BTfzqNpr9Wxw5/QXaSdw7S/0eP5a0C/nwURvmfSzuTD4equzbEN'#10+
+ 'd+dI/s2JMxrdj/I4uoAfUXRGaabevQIjFzC9uyE3LaOyR2zhuvAzX+vVcs6bSXeU'#10+
+ 'pKpCAcN+3Z3evMaX2f+z/nfSUAl2i4J2R+/LQAWJW4KwRky/m+cxpfUCgYEA6bN1'#10+
+ 'b73bMgM8wpNt6+fcmS+5n0iZihygQ2U2DEud8nZJL4Nrm1dwTnfZfJBnkGj6+0Q0'#10+
+ 'cOwj2KS0/wcEdJBP0jucU4v60VMhp75AQeHqidIde0bTViSRo3HWKXHBIFGYoU3T'#10+
+ 'LyPyKndbqsOObnsFXHn56Nwhr2HLf6nw4taGQY8CgYBoSW36FLCNbd6QGvLFXBGt'#10+
+ '2lMhEM8az/K58kJ4WXSwOLtr6MD/WjNT2tkcy0puEJLm6BFCd6A6pLn9jaKou/92'#10+
+ 'SfltZjJPb3GUlp9zn5tAAeSSi7YMViBrfuFiHObij5LorefBXISLjuYbMwL03MgH'#10+
+ 'Ocl2JtA2ywMp2KFXs8GQWQKBgFyIVv5ogQrbZ0pvj31xr9HjqK6d01VxIi+tOmpB'#10+
+ '4ocnOLEcaxX12BzprW55ytfOCVpF1jHD/imAhb3YrHXu0fwe6DXYXfZV4SSG2vB7'#10+
+ 'IB9z14KBN5qLHjNGFpMQXHSMek+b/ftTU0ZnPh9uEM5D3YqRLVd7GcdUhHvG8P8Q'#10+
+ 'C9aXAoGBAJtID6h8wOGMP0XYX5YYnhlC7dOLfk8UYrzlp3xhqVkzKthTQTj6wx9R'#10+
+ 'GtC4k7U1ki8oJsfcIlBNXd768fqDVWjYju5rzShMpo8OCTS6ipAblKjCxPPVhIpv'#10+
+ 'tWPlbSn1qj6wylstJ5/3Z+ZW5H4wIKp5jmLiioDhcP0L/Ex3Zx8O'#10+
+ '-----END RSA PRIVATE KEY-----'#10;
+
+ srv_signed_cert_pem: array[0..1138] of AnsiChar =
+ '-----BEGIN CERTIFICATE-----'#10+
+ 'MIIDGzCCAgWgAwIBAgIES0KCvTALBgkqhkiG9w0BAQUwFzEVMBMGA1UEAxMMdGVz'#10+
+ 'dF9jYV9jZXJ0MB4XDTEwMDEwNTAwMDcyNVoXDTQ1MDMxMjAwMDcyNVowFzEVMBMG'#10+
+ 'A1UEAxMMdGVzdF9jYV9jZXJ0MIIBHzALBgkqhkiG9w0BAQEDggEOADCCAQkCggEA'#10+
+ 'vfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW+K03KwEku55QvnUn'#10+
+ 'dwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8ILq4sw32vo0fbMu5BZ'#10+
+ 'F49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ020Q5EAAEseD1YtWC'#10+
+ 'IpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6QYGGh1QmHRPAy3CB'#10+
+ 'II6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6xyoOl204xuekZOaG9'#10+
+ 'RUPId74Rtmwfi1TLbBzo2wIDAQABo3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQM'#10+
+ 'MAoGCCsGAQUFBwMBMA8GA1UdDwEB/wQFAwMHIAAwHQYDVR0OBBYEFOFi4ilKOP1d'#10+
+ 'XHlWCMwmVKr7mgy8MB8GA1UdIwQYMBaAFP2olB4s2T/xuoQ5pT2RKojFwZo2MAsG'#10+
+ 'CSqGSIb3DQEBBQOCAQEAHVWPxazupbOkG7Did+dY9z2z6RjTzYvurTtEKQgzM2Vz'#10+
+ 'GQBA+3pZ3c5mS97fPIs9hZXfnQeelMeZ2XP1a+9vp35bJjZBBhVH+pqxjCgiUflg'#10+
+ 'A3Zqy0XwwVCgQLE2HyaU3DLUD/aeIFK5gJaOSdNTXZLv43K8kl4cqDbMeRpVTbkt'#10+
+ 'YmG4AyEOYRNKGTqMEJXJoxD5E3rBUNrVI/XyTjYrulxbNPcMWEHKNeeqWpKDYTFo'#10+
+ 'Bb01PCthGXiq/4A2RLAFosadzRa8SBpoSjPPfZ0b2w4MJpReHqKbR5+T2t6hzml6'#10+
+ '4ToyOKPDmamiTuN5KzLN3cw7DQlvWMvqSOChPLnA3Q=='#10+
+ '-----END CERTIFICATE-----'#10;
+
+
+var
+ d: PMHD_Daemon;
+ i: cuint;
+begin
+ if argc <> 2 then
+ begin
+ WriteLn(argv[0] + ' PORT');
+ Halt(1);
+ end;
+ response := MHD_create_response_from_buffer(Length(PAGE), Pointer(PAGE),
+ MHD_RESPMEM_PERSISTENT);
+ d := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY or MHD_USE_SSL
+{$IFDEF EPOLL_SUPPORT}
+ or MHD_USE_EPOLL_LINUX_ONLY or MHD_USE_EPOLL_TURBO
+{$ENDIF},
+ StrToInt(argv[1]), nil, nil, @ahc_echo, nil,
+ MHD_OPTION_CONNECTION_TIMEOUT, 120,
+ MHD_OPTION_THREAD_POOL_SIZE, NUMBER_OF_THREADS,
+ MHD_OPTION_URI_LOG_CALLBACK, @uri_logger_cb, nil,
+ MHD_OPTION_NOTIFY_COMPLETED, @completed_callback, nil,
+ MHD_OPTION_CONNECTION_LIMIT, 1000,
+ MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem,
+ MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem,
+ MHD_OPTION_END);
+ if d = nil then
+ Halt(1);
+ ReadLn;
+ MHD_stop_daemon(d);
+ MHD_destroy_response(response);
+ for i := 0 to SMALL do
+ if 0 <> small_deltas[i] then
+ WriteLn(stdout, Format('D: %d %u', [i, small_deltas[i]]));
+end.
+
diff --git a/packages/libmicrohttpd/examples/chunked_example.pp b/packages/libmicrohttpd/examples/chunked_example.pp
new file mode 100644
index 0000000000..338c5272f1
--- /dev/null
+++ b/packages/libmicrohttpd/examples/chunked_example.pp
@@ -0,0 +1,81 @@
+(*
+ This file is part of libmicrohttpd
+ Copyright (C) 2015 Christian Grothoff (and other contributing authors)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*)
+(**
+ * @file chunked_example.pp (original: chunked_example.c)
+ * @brief example for generating chunked encoding with libmicrohttpd
+ * @author Christian Grothoff / Silvio Clécio / Gilson Nunes
+ *)
+
+program chunked_example;
+
+{$mode objfpc}{$H+}
+
+uses
+ sysutils, libmicrohttpd;
+
+ function callback(cls: Pointer; pos: cuint64; buf: Pcchar;
+ max: size_t): ssize_t; cdecl;
+ begin
+ Result := MHD_CONTENT_READER_END_OF_STREAM;
+ end;
+
+ function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar;
+ method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; ptr: PPointer): cint; cdecl;
+ const
+ aptr: cint = 0;
+ var
+ response: PMHD_Response;
+ ret: cint;
+ begin
+ if 0 <> strcomp(method, 'GET') then
+ Exit(MHD_NO);
+ if @aptr <> ptr^ then
+ begin
+ ptr^ := @aptr;
+ Exit(MHD_YES);
+ end;
+ ptr^ := nil;
+ response := MHD_create_response_from_callback(UInt64(MHD_SIZE_UNKNOWN), 1024,
+ @callback, nil, nil);
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+var
+ d: PMHD_Daemon;
+begin
+ if argc <> 2 then
+ begin
+ WriteLn(argv[0], ' PORT');
+ Halt(1);
+ end;
+ d := MHD_start_daemon(// MHD_USE_SELECT_INTERNALLY or MHD_USE_DEBUG or MHD_USE_POLL,
+ MHD_USE_SELECT_INTERNALLY or MHD_USE_DEBUG,
+ // MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG or MHD_USE_POLL,
+ // MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG,
+ StrToInt(argv[1]), nil, nil, @ahc_echo, nil,
+ MHD_OPTION_CONNECTION_TIMEOUT, 120, MHD_OPTION_END);
+ if d = nil then
+ Halt(1);
+ ReadLn;
+ MHD_stop_daemon(d);
+end.
+
diff --git a/packages/libmicrohttpd/examples/cutils.pas b/packages/libmicrohttpd/examples/cutils.pas
new file mode 100644
index 0000000000..ab80880afc
--- /dev/null
+++ b/packages/libmicrohttpd/examples/cutils.pas
@@ -0,0 +1,225 @@
+unit cutils;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+{$IFDEF MSWINDOWS}
+ Windows, WinSock2,
+{$ELSE}
+ BaseUnix,
+{$ENDIF}
+ ctypes;
+
+const
+ LIB_NAME = {$IFDEF MSWINDOWS}'msvcrt'{$ELSE}'c'{$ENDIF};
+{$IFDEF UNIX}
+ UINT16_MAX = 65535;
+{$ENDIF}
+ SEEK_SET = 0;
+ SEEK_CUR = 1;
+ SEEK_END = 2;
+{$IFDEF MSWINDOWS}
+ DELTA_EPOCH_IN_MICROSECS: culonglong = 11644473600000000;
+{$ENDIF}
+
+type
+{$IFDEF UNIX}
+ __off_t = longint;
+{$ENDIF}
+ Pcchar = PAnsiChar;
+ Ppcchar = ^Pcchar;
+ FILEptr = ^File;
+ seek_mode = longint;
+ open_mode = (fopenread, fopenwrite, fappendwrite);
+
+{$IFDEF MSWINDOWS}
+function fpgettimeofday(tv: PTimeVal; tz: PTimeZone): cint;
+procedure _tzset; cdecl; external LIB_NAME name '_tzset';
+function _timezone: cint; cdecl; external LIB_NAME name '_timezone';
+function _daylight: clong; cdecl; external LIB_NAME name '__daylight';
+{$ENDIF}
+
+{$IFDEF UNIX}
+function sscanf(s: Pcchar; format: Pcchar): cint; cdecl; varargs; external LIB_NAME name 'sscanf';
+function lseek(fd: cint; offset: __off_t; whence: cint): __off_t; cdecl; external LIB_NAME name 'lseek';
+function isprint(p: Char): cint; cdecl; external LIB_NAME name 'isprint';
+function strdup(para1: Pcchar): Pcchar; cdecl; external LIB_NAME name 'strdup';
+function strchr(para1: Pcchar; para2: cint): Pcchar; cdecl; external LIB_NAME name 'strchr';
+function strstr(haystack: Pcchar; needle: Pcchar): Pcchar; cdecl; external LIB_NAME name 'strstr';
+function sprintf(s: Pcchar; format: Pcchar): cint; cdecl; varargs; external LIB_NAME name 'sprintf';
+function asprintf(resultp: Ppcchar; format: Pcchar): cint; cdecl; varargs; external LIB_NAME name 'asprintf';
+function errno: PInteger; cdecl; external LIB_NAME name '__errno_location';
+{$ENDIF}
+function memset(s: pointer; c: longint; n: size_t): pointer; cdecl; external LIB_NAME name 'memset';
+function snprintf(str: Pcchar; size: size_t; format: Pcchar): cint; cdecl; varargs; external LIB_NAME Name {$IFDEF MSWINDOWS}'_snprintf'{$ELSE}'snprintf'{$ENDIF};
+function rand: cint; cdecl; external LIB_NAME name 'rand';
+function strerror(errnum: cint): Pchar; cdecl; external LIB_NAME name 'strerror';
+function strncat(a, b: Pcchar; sz: size_t): Pchar; cdecl; external LIB_NAME name 'strncat';
+function strcpy(a, b: Pcchar): Pchar; cdecl; external LIB_NAME name 'strcpy';
+function strncmp(a, b: Pcchar; sz: size_t): cint; cdecl; external LIB_NAME name 'strncmp';
+
+function fopen(filename: PAnsiChar; mode: open_mode): FILEptr;
+procedure fclose(fp: FILEptr);
+function fseek(fp: FILEptr; recPos: longint; mode: seek_mode): longint;
+function fread(buf: pointer; recSize: longint; recCount: longint; fp: FILEptr): longint;
+function fwrite(buf: pointer; recSize: longint; recCount: longint; fp: FILEptr): longint;
+function ftell(fp: FILEptr): LongInt;
+function feof(fp: FILEptr): LongInt;
+
+implementation
+
+{$IFDEF MSWINDOWS}
+function fpgettimeofday(tv: PTimeVal; tz: PTimeZone): cint;
+const
+ tzflag: cint = 0;
+var
+ ft: FILETIME;
+ tmpres: QWord = 0;
+begin
+ if nil <> tv then
+ begin
+ GetSystemTimeAsFileTime(@ft);
+ tmpres := tmpres or ft.dwHighDateTime;
+ tmpres := tmpres shl 32;
+ tmpres := tmpres or ft.dwLowDateTime;
+ tmpres := tmpres div 10;
+ tmpres -= DELTA_EPOCH_IN_MICROSECS;
+ tv^.tv_sec := clong(tmpres div culong(1000000));
+ tv^.tv_usec := clong(tmpres mod culong(1000000));
+ end;
+ if nil <> tz then
+ begin
+ if tzflag <> 1 then
+ begin
+ _tzset;
+ Inc(tzflag);
+ end;
+ tz^.tz_minuteswest := _timezone div 60;
+ tz^.tz_dsttime := _daylight;
+ end;
+ Result := 0;
+end;
+{$ENDIF}
+
+function fopen(filename: PAnsiChar; mode: open_mode): FILEptr;
+var
+ fp: FILEptr;
+ OldFileMode: Byte;
+begin
+ fp := nil;
+ OldFileMode := FileMode;
+ GetMem(fp, SizeOf(File));
+ Assign(fp^, StrPas(filename));
+{$PUSH}{$I-}
+ case mode of
+ fopenread:
+ begin
+ FileMode := 0;
+ Reset(fp^, 1);
+ end;
+ fopenwrite:
+ begin
+ FileMode := 1;
+ ReWrite(fp^, 1);
+ end;
+ fappendwrite:
+ begin
+ FileMode := 2;
+ Reset(fp^, 1);
+ if IOResult = 2 then
+ ReWrite(fp^, 1);
+ Seek(fp^, FileSize(fp^));
+ end;
+ end;
+ FileMode := OldFileMode;
+{$POP}
+ if IOResult <> 0 then
+ begin
+ FreeMem(fp, SizeOf(File));
+ fp := nil;
+ end;
+ fopen := fp;
+end;
+
+procedure fclose(fp : FILEptr);
+begin
+ if Assigned(fp) then
+ begin
+{$PUSH}{$I-}
+ Close(fp^);
+{$POP}
+ if IOresult = 0 then
+ FreeMem(fp, SizeOf(File));
+ end;
+end;
+
+function fread(buf: Pointer; recSize: LongInt; recCount: LongInt;
+ fp : FILEptr): LongInt;
+var
+ totalSize, readcount : LongInt;
+begin
+ if Assigned(buf) then
+ begin
+ totalSize := recCount * LongInt(recSize);
+{$PUSH}{$I-}{$HINTS OFF}
+ BlockRead(fp^, buf^, totalSize, readcount);
+ if readcount <> totalSize then
+ fread := readcount div recSize
+ else
+ fread := recCount;
+{$POP}
+ end
+ else
+ fread := 0;
+end;
+
+function fwrite(buf: Pointer; recSize: LongInt; recCount: LongInt;
+ fp: FILEptr) : LongInt;
+var
+ totalSize, written: LongInt;
+begin
+ if Assigned(buf) then
+ begin
+ totalSize := recCount * LongInt(recSize);
+{$PUSH}{$I-}{$HINTS OFF}
+ BlockWrite(fp^, buf^, totalSize, written);
+ if written <> totalSize then
+ fwrite := written div recSize
+ else
+ fwrite := recCount;
+{$POP}
+ end
+ else
+ fwrite := 0;
+end;
+
+function fseek(fp: FILEptr; recPos: LongInt; mode: seek_mode): LongInt;
+begin
+{$PUSH}{$I-}
+ case mode of
+ SEEK_SET: Seek(fp^, recPos);
+ SEEK_CUR: Seek(fp^, FilePos(fp^) + recPos);
+ SEEK_END: Seek(fp^, FileSize(fp^) - 1 - recPos);
+ end;
+{$POP}
+ fseek := IOResult;
+end;
+
+function ftell(fp: FILEptr): LongInt;
+begin
+ ftell := FilePos(fp^);
+end;
+
+function feof(fp: FILEptr): LongInt;
+begin
+ feof := 0;
+ if Assigned(fp) then
+ if eof(fp^) then
+ feof := 1
+ else
+ feof := 0;
+end;
+
+end.
diff --git a/packages/libmicrohttpd/examples/demo.pp b/packages/libmicrohttpd/examples/demo.pp
new file mode 100644
index 0000000000..f327473297
--- /dev/null
+++ b/packages/libmicrohttpd/examples/demo.pp
@@ -0,0 +1,811 @@
+(*
+ This file is part of libmicrohttpd
+ Copyright (C) 2013 Christian Grothoff (and other contributing authors)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*)
+
+(**
+ * @file demo.pp (Original: demo.c)
+ * @brief complex demonstration site: create directory index, offer
+ * upload via form and HTTP POST, download with mime type detection
+ * and error reporting (403, etc.) --- and all of this with
+ * high-performance settings (large buffers, thread pool).
+ * If you want to benchmark MHD, this code should be used to
+ * run tests against. Note that the number of threads may need
+ * to be adjusted depending on the number of available cores.
+ * @author Christian Grothoff
+ *)
+
+program demo;
+
+{$mode objfpc}{$H+}
+{$MACRO ON}
+{$IF DEFINED(CPU_COUNT) AND (CPU_COUNT + 0) < 2}
+ {$UNDEF CPU_COUNT}
+{$ENDIF}
+{$IF NOT DEFINED(CPU_COUNT)}
+ {$DEFINE CPU_COUNT := 2}
+{$ENDIF}
+
+uses
+ sysutils, pthreads, ctypes, BaseUnix, cmem, cutils, libmicrohttpd;
+
+type
+{$i magic.inc}
+
+const
+
+ (**
+ * Number of threads to run in the thread pool. Should (roughly) match
+ * the number of cores on your system.
+ *)
+ NUMBER_OF_THREADS = CPU_COUNT;
+
+ (**
+ * How many bytes of a file do we give to libmagic to determine the mime type?
+ * 16k might be a bit excessive, but ought not hurt performance much anyway,
+ * and should definitively be on the safe side.
+ *)
+ MAGIC_HEADER_SIZE = 16 * 1024;
+
+ (**
+ * Page returned for file-not-found.
+ *)
+ FILE_NOT_FOUND_PAGE: Pcchar = '<html><head><title>File not found</title></head><body>File not found</body></html>';
+
+ (**
+ * Page returned for internal errors.
+ *)
+ INTERNAL_ERROR_PAGE: Pcchar = '<html><head><title>Internal error</title></head><body>Internal error</body></html>';
+
+
+ (**
+ * Page returned for refused requests.
+ *)
+ REQUEST_REFUSED_PAGE: Pcchar = '<html><head><title>Request refused</title></head><body>Request refused (file exists?)</body></html>';
+
+ (**
+ * Head of index page.
+ *)
+ INDEX_PAGE_HEADER = '<html>'#10'<head><title>Welcome</title></head>'#10'<body>'#10+
+ '<h1>Upload</h1>'#10+
+ '<form method="POST" enctype="multipart/form-data" action="/">'#10+
+ '<dl><dt>Content type:</dt><dd>'+
+ '<input type="radio" name="category" value="books">Book</input>'+
+ '<input type="radio" name="category" value="images">Image</input>'+
+ '<input type="radio" name="category" value="music">Music</input>'+
+ '<input type="radio" name="category" value="software">Software</input>'+
+ '<input type="radio" name="category" value="videos">Videos</input>'#10+
+ '<input type="radio" name="category" value="other" checked>Other</input></dd>'+
+ '<dt>Language:</dt><dd>'+
+ '<input type="radio" name="language" value="no-lang" checked>none</input>'+
+ '<input type="radio" name="language" value="en">English</input>'+
+ '<input type="radio" name="language" value="de">German</input>'+
+ '<input type="radio" name="language" value="fr">French</input>'+
+ '<input type="radio" name="language" value="es">Spanish</input></dd>'#10+
+ '<dt>File:</dt><dd>'+
+ '<input type="file" name="upload"/></dd></dl>'+
+ '<input type="submit" value="Send!"/>'#10+
+ '</form>'#10+
+ '<h1>Download</h1>'#10+
+ '<ol>'#10;
+
+ (**
+ * Footer of index page.
+ *)
+ INDEX_PAGE_FOOTER = '</ol>'#10'</body>'#10'</html>';
+
+ (**
+ * NULL-terminated array of supported upload categories. Should match HTML
+ * in the form.
+ *)
+ categories: array[0..6] of Pcchar = (
+ 'books',
+ 'images',
+ 'music',
+ 'software',
+ 'videos',
+ 'other',
+ nil
+ );
+
+type
+
+ (**
+ * Specification of a supported language.
+ *)
+ Language = packed record
+ (**
+ * Directory name for the language.
+ *)
+ dirname: Pcchar;
+
+ (**
+ * Long name for humans.
+ *)
+ longname: Pcchar;
+ end;
+ PLanguage = ^Language;
+
+const
+ (**
+ * NULL-terminated array of supported upload categories. Should match HTML
+ * in the form.
+ *)
+ languages: array[0..5] of Language = (
+ (dirname: 'no-lang'; longname: 'No language specified'),
+ (dirname: 'en'; longname: 'English'),
+ (dirname: 'de'; longname: 'German'),
+ (dirname: 'fr'; longname: 'French'),
+ (dirname: 'es'; longname: 'Spanish'),
+ (dirname: nil; longname: nil)
+ );
+
+var
+ (**
+ * Response returned if the requested file does not exist (or is not accessible).
+ *)
+ file_not_found_response: PMHD_Response;
+
+ (**
+ * Response returned for internal errors.
+ *)
+ internal_error_response: PMHD_Response;
+
+ (**
+ * Response returned for '/' (GET) to list the contents of the directory and allow upload.
+ *)
+ cached_directory_response: PMHD_Response;
+
+ (**
+ * Response returned for refused uploads.
+ *)
+ request_refused_response: PMHD_Response;
+
+ (**
+ * Mutex used when we update the cached directory response object.
+ *)
+ mutex: pthread_mutex_t;
+
+ (**
+ * Global handle to MAGIC data.
+ *)
+ magic: magic_t;
+
+ (**
+ * Mark the given response as HTML for the brower.
+ *
+ * @param response response to mark
+ *)
+ procedure mark_as_html(response: PMHD_Response);
+ begin
+ MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, 'text/html');
+ end;
+
+ (**
+ * Replace the existing 'cached_directory_response' with the
+ * given response.
+ *
+ * @param response new directory response
+ *)
+ procedure update_cached_response(response: PMHD_Response);
+ begin
+ pthread_mutex_lock(@mutex);
+ if nil <> cached_directory_response then
+ MHD_destroy_response(cached_directory_response);
+ cached_directory_response := response;
+ pthread_mutex_unlock(@mutex);
+ end;
+
+type
+ (**
+ * Context keeping the data for the response we're building.
+ *)
+ ResponseDataContext = packed record
+ (**
+ * Response data string.
+ *)
+ buf: Pcchar;
+
+ (**
+ * Number of bytes allocated for 'buf'.
+ *)
+ buf_len: size_t;
+
+ (**
+ * Current position where we append to 'buf'. Must be smaller or equal to 'buf_len'.
+ *)
+ off: size_t;
+ end;
+ PResponseDataContext = ^ResponseDataContext;
+
+ (**
+ * Create a listing of the files in 'dirname' in HTML.
+ *
+ * @param rdc where to store the list of files
+ * @param dirname name of the directory to list
+ * @return MHD_YES on success, MHD_NO on error
+ *)
+ function list_directory(rdc: PResponseDataContext; dirname: Pcchar): cint; cdecl;
+ var
+ fullname: array[0..PATH_MAX] of AnsiChar;
+ sbuf: stat;
+ dir: pDir;
+ de: pDirent;
+ r: Pointer;
+ begin
+ dir := FpOpendir(dirname);
+ if nil = dir then
+ Exit(MHD_NO);
+ while True do
+ begin
+ de := FpReaddir(dir^);
+ if de = nil then
+ Break;
+ if '.' = de^.d_name[0] then
+ Continue;
+ if SizeOf(fullname) <= size_t(
+ snprintf(fullname, SizeOf(fullname), '%s/%s', dirname, de^.d_name)) then
+ Continue; (* ugh, file too long? how can this be!? *)
+ if 0 <> FpStat(PAnsiChar(fullname), sbuf) then
+ Continue; (* ugh, failed to 'stat' *)
+ if not fpS_ISREG(sbuf.st_mode) then
+ Continue; (* not a regular file, skip *)
+ if rdc^.off + 1024 > rdc^.buf_len then
+ begin
+ if (2 * rdc^.buf_len + 1024) < rdc^.buf_len then
+ Break; (* more than SIZE_T _index_ size? Too big for us *)
+ rdc^.buf_len := 2 * rdc^.buf_len + 1024;
+ r := ReAlloc(rdc^.buf, rdc^.buf_len);
+ if nil = r then
+ Break; (* out of memory *)
+ rdc^.buf := r;
+ end;
+ rdc^.off += snprintf(@rdc^.buf[rdc^.off], rdc^.buf_len - rdc^.off,
+ '<li><a href="/%s">%s</a></li>'#10, fullname, de^.d_name);
+ end;
+ FpClosedir(dir^);
+ Result := MHD_YES;
+ end;
+
+ (**
+ * Re-scan our local directory and re-build the index.
+ *)
+ procedure update_directory;
+ const
+ initial_allocation: size_t = 32 * 1024; (* initial size for response buffer *)
+ var
+ response: PMHD_Response;
+ rdc: ResponseDataContext;
+ language_idx: cuint;
+ category_idx: cuint;
+ language: PLanguage;
+ category: Pcchar;
+ dir_name: array[0..128] of AnsiChar;
+ sbuf: stat;
+ begin
+ rdc.buf_len := initial_allocation;
+ rdc.buf := Malloc(rdc.buf_len);
+ if nil = rdc.buf then
+ begin
+ update_cached_response(nil);
+ Exit;
+ end;
+ rdc.off := snprintf(rdc.buf, rdc.buf_len, '%s', INDEX_PAGE_HEADER);
+ language_idx := 0;
+ while True do
+ begin
+ try
+ if languages[language_idx].dirname = nil then
+ Break;
+ language := @languages[language_idx];
+ if 0 <> FpStat(language^.dirname, sbuf) then
+ Continue; (* empty *)
+ (* we ensured always +1k room, filenames are ~256 bytes,
+ so there is always still enough space for the header
+ without need for an additional reallocation check. *)
+ rdc.off += snprintf(@rdc.buf[rdc.off], rdc.buf_len - rdc.off,
+ '<h2>%s</h2>'#10, language^.longname);
+ category_idx := 0;
+ while True do
+ begin
+ try
+ if categories[category_idx] = nil then
+ Break;
+ category := categories[category_idx];
+ snprintf(dir_name, sizeof(dir_name), '%s/%s', language^.dirname, category);
+ if 0 <> FpStat(PAnsiChar(dir_name), sbuf) then
+ Continue; (* empty *)
+ (* we ensured always +1k room, filenames are ~256 bytes,
+ so there is always still enough space for the header
+ without need for an additional reallocation check. *)
+ rdc.off += snprintf(@rdc.buf[rdc.off], rdc.buf_len - rdc.off,
+ '<h3>%s</h3>'#10, category);
+ if MHD_NO = list_directory(@rdc, dir_name) then
+ begin
+ Free(rdc.buf);
+ update_cached_response(nil);
+ Exit;
+ end;
+ finally
+ Inc(category_idx);
+ end;
+ end;
+ finally
+ Inc(language_idx);
+ end;
+ end;
+ (* we ensured always +1k room, filenames are ~256 bytes,
+ so there is always still enough space for the footer
+ without need for a final reallocation check. *)
+ rdc.off += snprintf(@rdc.buf[rdc.off], rdc.buf_len - rdc.off, '%s',
+ INDEX_PAGE_FOOTER);
+ initial_allocation := rdc.buf_len; (* remember for next time *)
+ response := MHD_create_response_from_buffer(rdc.off, rdc.buf,
+ MHD_RESPMEM_MUST_FREE);
+ mark_as_html(response);
+{$IFDEF FORCE_CLOSE}
+ MHD_add_response_header (response, MHD_HTTP_HEADER_CONNECTION, 'close');
+{$ENDIF}
+ update_cached_response(response);
+ end;
+
+type
+ (**
+ * Context we keep for an upload.
+ *)
+ UploadContext = packed record
+ (**
+ * Handle where we write the uploaded file to.
+ *)
+ fd: cint;
+
+ (**
+ * Name of the file on disk (used to remove on errors).
+ *)
+ filename: Pcchar;
+
+ (**
+ * Language for the upload.
+ *)
+ language: Pcchar;
+
+ (**
+ * Category for the upload.
+ *)
+ category: Pcchar;
+
+ (**
+ * Post processor we're using to process the upload.
+ *)
+ pp: PMHD_PostProcessor;
+
+ (**
+ * Handle to connection that we're processing the upload for.
+ *)
+ connection: PMHD_Connection;
+
+ (**
+ * Response to generate, NULL to use directory.
+ *)
+ response: PMHD_Response;
+ end;
+ PUploadContext = ^UploadContext;
+
+ (**
+ * Append the 'size' bytes from 'data' to '*ret', adding
+ * 0-termination. If '*ret' is NULL, allocate an empty string first.
+ *
+ * @param ret string to update, NULL or 0-terminated
+ * @param data data to append
+ * @param size number of bytes in 'data'
+ * @return MHD_NO on allocation failure, MHD_YES on success
+ *)
+ function do_append(ret: Ppcchar; data: Pcchar; size: size_t): cint; cdecl;
+ var
+ buf: Pcchar;
+ old_len: size_t;
+ begin
+ if nil = ret^ then
+ old_len := 0
+ else
+ old_len := strlen(ret^);
+ buf := Malloc(old_len + size + 1);
+ if nil = buf then
+ Exit(MHD_NO);
+ Move(ret^^, buf, old_len);
+ if nil <> ret^ then
+ Free(ret^);
+ Move(data^, buf[old_len], size);
+ buf[old_len + size] := #0;
+ ret^ := buf;
+ Result := MHD_YES;
+ end;
+
+ (**
+ * Iterator over key-value pairs where the value
+ * maybe made available in increments and/or may
+ * not be zero-terminated. Used for processing
+ * POST data.
+ *
+ * @param cls user-specified closure
+ * @param kind type of the value, always MHD_POSTDATA_KIND when called from MHD
+ * @param key 0-terminated key for the value
+ * @param filename name of the uploaded file, NULL if not known
+ * @param content_type mime-type of the data, NULL if not known
+ * @param transfer_encoding encoding of the data, NULL if not known
+ * @param data pointer to size bytes of data at the
+ * specified offset
+ * @param off offset of data in the overall value
+ * @param size number of bytes in data available
+ * @return MHD_YES to continue iterating,
+ * MHD_NO to abort the iteration
+ *)
+ function process_upload_data(cls: Pointer; kind: MHD_ValueKind; key: Pcchar;
+ filename: Pcchar; content_type: Pcchar; transfer_encoding: Pcchar;
+ data: Pcchar; off: cuint64; size: size_t): cint; cdecl;
+ var
+ uc: PUploadContext;
+ i: cint;
+ fn: array[0..PATH_MAX] of AnsiChar;
+ begin
+ uc := cls;
+ if 0 = strcomp(key, 'category') then
+ Exit(do_append(@uc^.category, data, size));
+ if 0 = strcomp(key, 'language') then
+ Exit(do_append(@uc^.language, data, size));
+ if 0 <> strcomp(key, 'upload') then
+ begin
+ WriteLn(stderr, Format('Ignoring unexpected form value `%s''', [key]));
+ Exit(MHD_YES); (* ignore *)
+ end;
+ if nil = filename then
+ begin
+ WriteLn(stderr, 'No filename, aborting upload');
+ Exit(MHD_NO); (* no filename, error *)
+ end;
+ if (nil = uc^.category) or (nil = uc^.language) then
+ begin
+ WriteLn(stderr, Format('Missing form data for upload `%s''', [filename]));
+ uc^.response := request_refused_response;
+ Exit(MHD_NO);
+ end;
+ if -1 = uc^.fd then
+ begin
+ if (nil <> strstr(filename, '..')) or (nil <> strchr(filename, Ord('/'))) or
+ (nil <> strchr(filename, Ord('\'))) then
+ begin
+ uc^.response := request_refused_response;
+ Exit(MHD_NO);
+ end;
+ (* create directories -- if they don't exist already *)
+{$IFDEF MSWINDOWS}
+ FpMkdir(uc^.language);
+{$ELSE}
+ FpMkdir(uc^.language, S_IRWXU);
+{$ENDIF}
+ snprintf(fn, SizeOf(fn), '%s/%s', uc^.language, uc^.category);
+{$IFDEF MSWINDOWS}
+ FpMkdir(fn);
+{$ELSE}
+ FpMkdir(PAnsiChar(fn), S_IRWXU);
+{$ENDIF}
+ (* open file *)
+ snprintf(fn, sizeof(fn), '%s/%s/%s', uc^.language, uc^.category, filename);
+ for i := strlen(fn) - 1 downto 0 do
+ if isprint(fn[i]) = 1 then
+ fn[i] := '_';
+ uc^.fd := FpOpen(PAnsiChar(fn), O_CREAT or O_EXCL
+{$IFDEF O_LARGEFILE}
+ or O_LARGEFILE
+{$ENDIF}
+ or O_WRONLY, S_IRUSR or S_IWUSR);
+ if -1 = uc^.fd then
+ begin
+ WriteLn(stderr, Format('Error opening file `%s'' for upload: %s',
+ [fn, strerror(errno^)]));
+ uc^.response := request_refused_response;
+ Exit(MHD_NO);
+ end;
+ uc^.filename := strdup(fn);
+ end;
+ if (0 <> size) and (size <> size_t(FpWrite(uc^.fd, data, size))) then
+ begin
+ (* write failed; likely: disk full *)
+ WriteLn(stderr, Format('Error writing to file `%s'': %s', [uc^.filename,
+ strerror(errno^)]));
+ uc^.response := internal_error_response;
+ FpClose(uc^.fd);
+ uc^.fd := -1;
+ if nil <> uc^.filename then
+ begin
+ FpUnlink(uc^.filename);
+ Free(uc^.filename);
+ uc^.filename := nil;
+ end;
+ Exit(MHD_NO);
+ end;
+ Exit(MHD_YES);
+ end;
+
+ (**
+ * Function called whenever a request was completed.
+ * Used to clean up 'struct UploadContext' objects.
+ *
+ * @param cls client-defined closure, NULL
+ * @param connection connection handle
+ * @param con_cls value as set by the last call to
+ * the MHD_AccessHandlerCallback, points to NULL if this was
+ * not an upload
+ * @param toe reason for request termination
+ *)
+ procedure response_completed_callback(cls: Pointer; connection: PMHD_Connection;
+ con_cls: PPointer; toe: MHD_RequestTerminationCode); cdecl;
+ var
+ uc: PUploadContext;
+ begin
+ uc := con_cls^;
+ if nil = uc then
+ Exit; (* this request wasn't an upload request *)
+ if nil <> uc^.pp then
+ begin
+ MHD_destroy_post_processor(uc^.pp);
+ uc^.pp := nil;
+ end;
+ if -1 <> uc^.fd then
+ begin
+ FpClose(uc^.fd);
+ if nil <> uc^.filename then
+ begin
+ WriteLn(stderr, Format(
+ 'Upload of file `%s'' failed (incomplete or aborted), removing file.',
+ [uc^.filename]));
+ FpUnlink(uc^.filename);
+ end;
+ end;
+ if nil <> uc^.filename then
+ Free(uc^.filename);
+ Free(uc);
+ end;
+
+ (**
+ * Return the current directory listing.
+ *
+ * @param connection connection to return the directory for
+ * @return MHD_YES on success, MHD_NO on error
+ *)
+ function return_directory_response(connection: PMHD_Connection): cint;
+ var
+ ret: cint;
+ begin
+ pthread_mutex_lock(@mutex);
+ if nil = cached_directory_response then
+ ret := MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
+ internal_error_response)
+ else
+ ret := MHD_queue_response(connection, MHD_HTTP_OK,
+ cached_directory_response);
+ pthread_mutex_unlock(@mutex);
+ Result := ret;
+ end;
+
+ (**
+ * Main callback from MHD, used to generate the page.
+ *
+ * @param cls NULL
+ * @param connection connection handle
+ * @param url requested URL
+ * @param method GET, PUT, POST, etc.
+ * @param version HTTP version
+ * @param upload_data data from upload (PUT/POST)
+ * @param upload_data_size number of bytes in "upload_data"
+ * @param ptr our context
+ * @return MHD_YES on success, MHD_NO to drop connection
+ *)
+ function generate_page(cls: Pointer; connection: PMHD_Connection; url: Pcchar;
+ method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; ptr: PPointer): cint; cdecl;
+ var
+ response: PMHD_Response;
+ ret: cint;
+ fd: cint;
+ buf: stat;
+ (* should be file download *)
+ file_data: array[0..MAGIC_HEADER_SIZE] of AnsiChar;
+ got: ssize_t ;
+ mime: Pcchar;
+ uc: PUploadContext;
+ begin
+ if 0 <> strcomp(url, '/') then
+ begin
+ if (0 <> strcomp(method, MHD_HTTP_METHOD_GET)) and
+ (0 <> strcomp(method, MHD_HTTP_METHOD_HEAD)) then
+ Exit(MHD_NO); (* unexpected method (we're not polite...) *)
+ if (0 = FpStat(@url[1], buf)) and (nil = strstr(@url[1], '..')) and
+ ('/' <> url[1]) then
+ fd := FpOpen(@url[1], O_RDONLY)
+ else
+ fd := -1;
+ if -1 = fd then
+ Exit(MHD_queue_response(connection, MHD_HTTP_NOT_FOUND,
+ file_not_found_response));
+ (* read beginning of the file to determine mime type *)
+ got := FpRead(fd, file_data, SizeOf(file_data));
+ if -1 <> got then
+ mime := magic_buffer(magic, Pcchar(file_data), got)
+ else
+ mime := nil;
+ lseek(fd, 0, SEEK_SET);
+ response := MHD_create_response_from_fd(buf.st_size, fd);
+ if nil = response then
+ begin
+ (* internal error (i.e. out of memory) *)
+ FpClose(fd);
+ Exit(MHD_NO);
+ end;
+ (* add mime type if we had one *)
+ if nil <> mime then
+ MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, mime);
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ Exit(ret);
+ end;
+ if 0 = strcomp(method, MHD_HTTP_METHOD_POST) then
+ begin
+ (* upload! *)
+ uc := ptr^;
+ if nil = uc then
+ begin
+ uc := Malloc(SizeOf(UploadContext));
+ if nil = uc then
+ Exit(MHD_NO); (* out of memory, close connection *)
+ memset(uc, 0, SizeOf(UploadContext));
+ uc^.fd := -1;
+ uc^.connection := connection;
+ uc^.pp := MHD_create_post_processor(connection, 64 * 1024 (* buffer size *),
+ @process_upload_data, uc);
+ if nil = uc^.pp then
+ begin
+ (* out of memory, close connection *)
+ Free(uc);
+ Exit(MHD_NO);
+ end;
+ ptr^ := uc;
+ Exit(MHD_YES);
+ end;
+ if 0 <> upload_data_size^ then
+ begin
+ if nil = uc^.response then
+ MHD_post_process(uc^.pp, upload_data, upload_data_size^);
+ upload_data_size^ := 0;
+ Exit(MHD_YES);
+ end;
+ (* end of upload, finish it! *)
+ MHD_destroy_post_processor(uc^.pp);
+ uc^.pp := nil;
+ if -1 <> uc^.fd then
+ begin
+ FpClose(uc^.fd);
+ uc^.fd := -1;
+ end;
+ if nil <> uc^.response then
+ Exit(MHD_queue_response(connection, MHD_HTTP_FORBIDDEN, uc^.response))
+ else
+ begin
+ update_directory;
+ Exit(return_directory_response(connection));
+ end;
+ end;
+ if (0 = strcomp(method, MHD_HTTP_METHOD_GET)) or
+ (0 = strcomp(method, MHD_HTTP_METHOD_HEAD)) then
+ Exit(return_directory_response(connection));
+ (* unexpected request, refuse *)
+ Result := MHD_queue_response(connection, MHD_HTTP_FORBIDDEN,
+ request_refused_response);
+ end;
+
+ (**
+ * Function called if we get a SIGPIPE. Does nothing.
+ *
+ * @param sig will be SIGPIPE (ignored)
+ *)
+ procedure catcher(signal: longint; info: psiginfo; context: psigcontext); cdecl;
+ begin
+ (* do nothing *)
+ end;
+
+ (**
+ * setup handlers to ignore SIGPIPE.
+ *)
+ procedure ignore_sigpipe;
+ var
+ oldsig: sigactionrec;
+ sig: sigactionrec;
+ begin
+ sig.sa_handler := @catcher;
+ FpsigEmptySet(sig.sa_mask);
+ {$IFDEF SA_INTERRUPT}
+ sig.sa_flags := SA_INTERRUPT; (* SunOS *)
+ {$ELSE}
+ sig.sa_flags := SA_RESTART;
+ {$ENDIF}
+ if 0 <> FPSigaction(SIGPIPE, @sig, @oldsig) then
+ WriteLn(stderr, Format('Failed to install SIGPIPE handler: %s',
+ [strerror(errno^)]));
+ end;
+
+ (**
+ * Entry point to demo. Note: this HTTP server will make all
+ * files in the current directory and its subdirectories available
+ * to anyone. Press ENTER to stop the server once it has started.
+ *
+ * @param argc number of arguments in argv
+ * @param argv first and only argument should be the port number
+ * @return 0 on success
+ *)
+var
+ d: PMHD_Daemon;
+ port: cuint;
+begin
+ if (argc <> 2) or (1 <> sscanf(argv[1], '%u', @port)) or
+ (UINT16_MAX < port) then
+ begin
+ WriteLn(stderr, argv[0], ' PORT');
+ Halt(1);
+ end;
+ ignore_sigpipe;
+ magic := magic_open(MAGIC_MIME_TYPE);
+ magic_load(magic, nil);
+ pthread_mutex_init(@mutex, nil);
+ file_not_found_response := MHD_create_response_from_buffer(
+ strlen(FILE_NOT_FOUND_PAGE), FILE_NOT_FOUND_PAGE,
+ MHD_RESPMEM_PERSISTENT);
+ mark_as_html(file_not_found_response);
+ request_refused_response := MHD_create_response_from_buffer(
+ strlen(REQUEST_REFUSED_PAGE), REQUEST_REFUSED_PAGE,
+ MHD_RESPMEM_PERSISTENT);
+ mark_as_html(request_refused_response);
+ internal_error_response := MHD_create_response_from_buffer(
+ strlen(INTERNAL_ERROR_PAGE), INTERNAL_ERROR_PAGE,
+ MHD_RESPMEM_PERSISTENT);
+ mark_as_html(internal_error_response);
+ update_directory;
+ d := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY or MHD_USE_DEBUG
+{$IFDEF EPOLL_SUPPORT}
+ or MHD_USE_EPOLL_LINUX_ONLY
+{$ENDIF},
+ port, nil, nil, @generate_page, nil,
+ MHD_OPTION_CONNECTION_MEMORY_LIMIT, size_t(256 * 1024),
+{$IFDEF PRODUCTION}
+ MHD_OPTION_PER_IP_CONNECTION_LIMIT, cuint(64),
+{$ENDIF}
+ MHD_OPTION_CONNECTION_TIMEOUT, cuint(120 (* seconds *)),
+ MHD_OPTION_THREAD_POOL_SIZE, cuint(NUMBER_OF_THREADS),
+ MHD_OPTION_NOTIFY_COMPLETED, @response_completed_callback, nil,
+ MHD_OPTION_END);
+ if nil = d then
+ Halt(1);
+ WriteLn(stderr, 'HTTP server running. Press ENTER to stop the server');
+ ReadLn;
+ MHD_stop_daemon(d);
+ MHD_destroy_response(file_not_found_response);
+ MHD_destroy_response(request_refused_response);
+ MHD_destroy_response(internal_error_response);
+ update_cached_response(nil);
+ pthread_mutex_destroy(@mutex);
+ magic_close(magic);
+end.
+
diff --git a/packages/libmicrohttpd/examples/demo_https.pp b/packages/libmicrohttpd/examples/demo_https.pp
new file mode 100644
index 0000000000..c4fd089903
--- /dev/null
+++ b/packages/libmicrohttpd/examples/demo_https.pp
@@ -0,0 +1,865 @@
+(*
+ This file is part of libmicrohttpd
+ Copyright (C) 2013 Christian Grothoff (and other contributing authors)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*)
+
+(**
+ * @file demo_https.pp (Original: demo_https.c)
+ * @brief complex demonstration site: create directory index, offer
+ * upload via form and HTTP POST, download with mime type detection
+ * and error reporting (403, etc.) --- and all of this with
+ * high-performance settings (large buffers, thread pool).
+ * If you want to benchmark MHD, this code should be used to
+ * run tests against. Note that the number of threads may need
+ * to be adjusted depending on the number of available cores.
+ * Logic is identical to demo.pp, just adds HTTPS support.
+ * @author Christian Grothoff
+ *)
+
+program demo_https;
+
+{$mode objfpc}{$H+}
+{$MACRO ON}
+{$IF DEFINED(CPU_COUNT) AND (CPU_COUNT + 0) < 2}
+ {$UNDEF CPU_COUNT}
+{$ENDIF}
+{$IF NOT DEFINED(CPU_COUNT)}
+ {$DEFINE CPU_COUNT := 2}
+{$ENDIF}
+
+uses
+ sysutils, pthreads, ctypes, BaseUnix, cmem, cutils, libmicrohttpd;
+
+type
+{$i magic.inc}
+
+const
+
+ (**
+ * Number of threads to run in the thread pool. Should (roughly) match
+ * the number of cores on your system.
+ *)
+ NUMBER_OF_THREADS = CPU_COUNT;
+
+ (**
+ * How many bytes of a file do we give to libmagic to determine the mime type?
+ * 16k might be a bit excessive, but ought not hurt performance much anyway,
+ * and should definitively be on the safe side.
+ *)
+ MAGIC_HEADER_SIZE = 16 * 1024;
+
+ (**
+ * Page returned for file-not-found.
+ *)
+ FILE_NOT_FOUND_PAGE: Pcchar = '<html><head><title>File not found</title></head><body>File not found</body></html>';
+
+ (**
+ * Page returned for internal errors.
+ *)
+ INTERNAL_ERROR_PAGE: Pcchar = '<html><head><title>Internal error</title></head><body>Internal error</body></html>';
+
+
+ (**
+ * Page returned for refused requests.
+ *)
+ REQUEST_REFUSED_PAGE: Pcchar = '<html><head><title>Request refused</title></head><body>Request refused (file exists?)</body></html>';
+
+ (**
+ * Head of index page.
+ *)
+ INDEX_PAGE_HEADER = '<html>'#10'<head><title>Welcome</title></head>'#10'<body>'#10+
+ '<h1>Upload</h1>'#10+
+ '<form method="POST" enctype="multipart/form-data" action="/">'#10+
+ '<dl><dt>Content type:</dt><dd>'+
+ '<input type="radio" name="category" value="books">Book</input>'+
+ '<input type="radio" name="category" value="images">Image</input>'+
+ '<input type="radio" name="category" value="music">Music</input>'+
+ '<input type="radio" name="category" value="software">Software</input>'+
+ '<input type="radio" name="category" value="videos">Videos</input>'#10+
+ '<input type="radio" name="category" value="other" checked>Other</input></dd>'+
+ '<dt>Language:</dt><dd>'+
+ '<input type="radio" name="language" value="no-lang" checked>none</input>'+
+ '<input type="radio" name="language" value="en">English</input>'+
+ '<input type="radio" name="language" value="de">German</input>'+
+ '<input type="radio" name="language" value="fr">French</input>'+
+ '<input type="radio" name="language" value="es">Spanish</input></dd>'#10+
+ '<dt>File:</dt><dd>'+
+ '<input type="file" name="upload"/></dd></dl>'+
+ '<input type="submit" value="Send!"/>'#10+
+ '</form>'#10+
+ '<h1>Download</h1>'#10+
+ '<ol>'#10;
+
+ (**
+ * Footer of index page.
+ *)
+ INDEX_PAGE_FOOTER = '</ol>'#10'</body>'#10'</html>';
+
+ (**
+ * NULL-terminated array of supported upload categories. Should match HTML
+ * in the form.
+ *)
+ categories: array[0..6] of Pcchar = (
+ 'books',
+ 'images',
+ 'music',
+ 'software',
+ 'videos',
+ 'other',
+ nil
+ );
+
+type
+
+ (**
+ * Specification of a supported language.
+ *)
+ Language = packed record
+ (**
+ * Directory name for the language.
+ *)
+ dirname: Pcchar;
+
+ (**
+ * Long name for humans.
+ *)
+ longname: Pcchar;
+ end;
+ PLanguage = ^Language;
+
+const
+ (**
+ * NULL-terminated array of supported upload categories. Should match HTML
+ * in the form.
+ *)
+ languages: array[0..5] of Language = (
+ (dirname: 'no-lang'; longname: 'No language specified'),
+ (dirname: 'en'; longname: 'English'),
+ (dirname: 'de'; longname: 'German'),
+ (dirname: 'fr'; longname: 'French'),
+ (dirname: 'es'; longname: 'Spanish'),
+ (dirname: nil; longname: nil)
+ );
+
+var
+ (**
+ * Response returned if the requested file does not exist (or is not accessible).
+ *)
+ file_not_found_response: PMHD_Response;
+
+ (**
+ * Response returned for internal errors.
+ *)
+ internal_error_response: PMHD_Response;
+
+ (**
+ * Response returned for '/' (GET) to list the contents of the directory and allow upload.
+ *)
+ cached_directory_response: PMHD_Response;
+
+ (**
+ * Response returned for refused uploads.
+ *)
+ request_refused_response: PMHD_Response;
+
+ (**
+ * Mutex used when we update the cached directory response object.
+ *)
+ mutex: pthread_mutex_t;
+
+ (**
+ * Global handle to MAGIC data.
+ *)
+ magic: magic_t;
+
+ (**
+ * Mark the given response as HTML for the brower.
+ *
+ * @param response response to mark
+ *)
+ procedure mark_as_html(response: PMHD_Response);
+ begin
+ MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, 'text/html');
+ end;
+
+ (**
+ * Replace the existing 'cached_directory_response' with the
+ * given response.
+ *
+ * @param response new directory response
+ *)
+ procedure update_cached_response(response: PMHD_Response);
+ begin
+ pthread_mutex_lock(@mutex);
+ if nil <> cached_directory_response then
+ MHD_destroy_response(cached_directory_response);
+ cached_directory_response := response;
+ pthread_mutex_unlock(@mutex);
+ end;
+
+type
+ (**
+ * Context keeping the data for the response we're building.
+ *)
+ ResponseDataContext = packed record
+ (**
+ * Response data string.
+ *)
+ buf: Pcchar;
+
+ (**
+ * Number of bytes allocated for 'buf'.
+ *)
+ buf_len: size_t;
+
+ (**
+ * Current position where we append to 'buf'. Must be smaller or equal to 'buf_len'.
+ *)
+ off: size_t;
+ end;
+ PResponseDataContext = ^ResponseDataContext;
+
+ (**
+ * Create a listing of the files in 'dirname' in HTML.
+ *
+ * @param rdc where to store the list of files
+ * @param dirname name of the directory to list
+ * @return MHD_YES on success, MHD_NO on error
+ *)
+ function list_directory(rdc: PResponseDataContext; dirname: Pcchar): cint; cdecl;
+ var
+ fullname: array[0..PATH_MAX] of AnsiChar;
+ sbuf: stat;
+ dir: pDir;
+ de: pDirent;
+ r: Pointer;
+ begin
+ dir := FpOpendir(dirname);
+ if nil = dir then
+ Exit(MHD_NO);
+ while True do
+ begin
+ de := FpReaddir(dir^);
+ if de = nil then
+ Break;
+ if '.' = de^.d_name[0] then
+ Continue;
+ if SizeOf(fullname) <= size_t(
+ snprintf(fullname, SizeOf(fullname), '%s/%s', dirname, de^.d_name)) then
+ Continue; (* ugh, file too long? how can this be!? *)
+ if 0 <> FpStat(PAnsiChar(fullname), sbuf) then
+ Continue; (* ugh, failed to 'stat' *)
+ if not fpS_ISREG(sbuf.st_mode) then
+ Continue; (* not a regular file, skip *)
+ if rdc^.off + 1024 > rdc^.buf_len then
+ begin
+ if (2 * rdc^.buf_len + 1024) < rdc^.buf_len then
+ Break; (* more than SIZE_T _index_ size? Too big for us *)
+ rdc^.buf_len := 2 * rdc^.buf_len + 1024;
+ r := ReAlloc(rdc^.buf, rdc^.buf_len);
+ if nil = r then
+ Break; (* out of memory *)
+ rdc^.buf := r;
+ end;
+ rdc^.off += snprintf(@rdc^.buf[rdc^.off], rdc^.buf_len - rdc^.off,
+ '<li><a href="/%s">%s</a></li>'#10, fullname, de^.d_name);
+ end;
+ FpClosedir(dir^);
+ Result := MHD_YES;
+ end;
+
+ (**
+ * Re-scan our local directory and re-build the index.
+ *)
+ procedure update_directory;
+ const
+ initial_allocation: size_t = 32 * 1024; (* initial size for response buffer *)
+ var
+ response: PMHD_Response;
+ rdc: ResponseDataContext;
+ language_idx: cuint;
+ category_idx: cuint;
+ language: PLanguage;
+ category: Pcchar;
+ dir_name: array[0..128] of AnsiChar;
+ sbuf: stat;
+ begin
+ rdc.buf_len := initial_allocation;
+ rdc.buf := Malloc(rdc.buf_len);
+ if nil = rdc.buf then
+ begin
+ update_cached_response(nil);
+ Exit;
+ end;
+ rdc.off := snprintf(rdc.buf, rdc.buf_len, '%s', INDEX_PAGE_HEADER);
+ language_idx := 0;
+ while True do
+ begin
+ try
+ if languages[language_idx].dirname = nil then
+ Break;
+ language := @languages[language_idx];
+ if 0 <> FpStat(language^.dirname, sbuf) then
+ Continue; (* empty *)
+ (* we ensured always +1k room, filenames are ~256 bytes,
+ so there is always still enough space for the header
+ without need for an additional reallocation check. *)
+ rdc.off += snprintf(@rdc.buf[rdc.off], rdc.buf_len - rdc.off,
+ '<h2>%s</h2>'#10, language^.longname);
+ category_idx := 0;
+ while True do
+ begin
+ try
+ if categories[category_idx] = nil then
+ Break;
+ category := categories[category_idx];
+ snprintf(dir_name, sizeof(dir_name), '%s/%s', language^.dirname, category);
+ if 0 <> FpStat(PAnsiChar(dir_name), sbuf) then
+ Continue; (* empty *)
+ (* we ensured always +1k room, filenames are ~256 bytes,
+ so there is always still enough space for the header
+ without need for an additional reallocation check. *)
+ rdc.off += snprintf(@rdc.buf[rdc.off], rdc.buf_len - rdc.off,
+ '<h3>%s</h3>'#10, category);
+ if MHD_NO = list_directory(@rdc, dir_name) then
+ begin
+ Free(rdc.buf);
+ update_cached_response(nil);
+ Exit;
+ end;
+ finally
+ Inc(category_idx);
+ end;
+ end;
+ finally
+ Inc(language_idx);
+ end;
+ end;
+ (* we ensured always +1k room, filenames are ~256 bytes,
+ so there is always still enough space for the footer
+ without need for a final reallocation check. *)
+ rdc.off += snprintf(@rdc.buf[rdc.off], rdc.buf_len - rdc.off, '%s',
+ INDEX_PAGE_FOOTER);
+ initial_allocation := rdc.buf_len; (* remember for next time *)
+ response := MHD_create_response_from_buffer(rdc.off, rdc.buf,
+ MHD_RESPMEM_MUST_FREE);
+ mark_as_html(response);
+{$IFDEF FORCE_CLOSE}
+ MHD_add_response_header (response, MHD_HTTP_HEADER_CONNECTION, 'close');
+{$ENDIF}
+ update_cached_response(response);
+ end;
+
+type
+ (**
+ * Context we keep for an upload.
+ *)
+ UploadContext = packed record
+ (**
+ * Handle where we write the uploaded file to.
+ *)
+ fd: cint;
+
+ (**
+ * Name of the file on disk (used to remove on errors).
+ *)
+ filename: Pcchar;
+
+ (**
+ * Language for the upload.
+ *)
+ language: Pcchar;
+
+ (**
+ * Category for the upload.
+ *)
+ category: Pcchar;
+
+ (**
+ * Post processor we're using to process the upload.
+ *)
+ pp: PMHD_PostProcessor;
+
+ (**
+ * Handle to connection that we're processing the upload for.
+ *)
+ connection: PMHD_Connection;
+
+ (**
+ * Response to generate, NULL to use directory.
+ *)
+ response: PMHD_Response;
+ end;
+ PUploadContext = ^UploadContext;
+
+ (**
+ * Append the 'size' bytes from 'data' to '*ret', adding
+ * 0-termination. If '*ret' is NULL, allocate an empty string first.
+ *
+ * @param ret string to update, NULL or 0-terminated
+ * @param data data to append
+ * @param size number of bytes in 'data'
+ * @return MHD_NO on allocation failure, MHD_YES on success
+ *)
+ function do_append(ret: Ppcchar; data: Pcchar; size: size_t): cint; cdecl;
+ var
+ buf: Pcchar;
+ old_len: size_t;
+ begin
+ if nil = ret^ then
+ old_len := 0
+ else
+ old_len := strlen(ret^);
+ buf := Malloc(old_len + size + 1);
+ if nil = buf then
+ Exit(MHD_NO);
+ Move(ret^^, buf, old_len);
+ if nil <> ret^ then
+ Free(ret^);
+ Move(data^, buf[old_len], size);
+ buf[old_len + size] := #0;
+ ret^ := buf;
+ Result := MHD_YES;
+ end;
+
+ (**
+ * Iterator over key-value pairs where the value
+ * maybe made available in increments and/or may
+ * not be zero-terminated. Used for processing
+ * POST data.
+ *
+ * @param cls user-specified closure
+ * @param kind type of the value, always MHD_POSTDATA_KIND when called from MHD
+ * @param key 0-terminated key for the value
+ * @param filename name of the uploaded file, NULL if not known
+ * @param content_type mime-type of the data, NULL if not known
+ * @param transfer_encoding encoding of the data, NULL if not known
+ * @param data pointer to size bytes of data at the
+ * specified offset
+ * @param off offset of data in the overall value
+ * @param size number of bytes in data available
+ * @return MHD_YES to continue iterating,
+ * MHD_NO to abort the iteration
+ *)
+ function process_upload_data(cls: Pointer; kind: MHD_ValueKind; key: Pcchar;
+ filename: Pcchar; content_type: Pcchar; transfer_encoding: Pcchar;
+ data: Pcchar; off: cuint64; size: size_t): cint; cdecl;
+ var
+ uc: PUploadContext;
+ i: cint;
+ fn: array[0..PATH_MAX] of AnsiChar;
+ begin
+ uc := cls;
+ if 0 = strcomp(key, 'category') then
+ Exit(do_append(@uc^.category, data, size));
+ if 0 = strcomp(key, 'language') then
+ Exit(do_append(@uc^.language, data, size));
+ if 0 <> strcomp(key, 'upload') then
+ begin
+ WriteLn(stderr, Format('Ignoring unexpected form value `%s''', [key]));
+ Exit(MHD_YES); (* ignore *)
+ end;
+ if nil = filename then
+ begin
+ WriteLn(stderr, 'No filename, aborting upload');
+ Exit(MHD_NO); (* no filename, error *)
+ end;
+ if (nil = uc^.category) or (nil = uc^.language) then
+ begin
+ WriteLn(stderr, Format('Missing form data for upload `%s''', [filename]));
+ uc^.response := request_refused_response;
+ Exit(MHD_NO);
+ end;
+ if -1 = uc^.fd then
+ begin
+ if (nil <> strstr(filename, '..')) or (nil <> strchr(filename, Ord('/'))) or
+ (nil <> strchr(filename, Ord('\'))) then
+ begin
+ uc^.response := request_refused_response;
+ Exit(MHD_NO);
+ end;
+ (* create directories -- if they don't exist already *)
+{$IFDEF MSWINDOWS}
+ FpMkdir(uc^.language);
+{$ELSE}
+ FpMkdir(uc^.language, S_IRWXU);
+{$ENDIF}
+ snprintf(fn, SizeOf(fn), '%s/%s', uc^.language, uc^.category);
+{$IFDEF MSWINDOWS}
+ FpMkdir(fn);
+{$ELSE}
+ FpMkdir(PAnsiChar(fn), S_IRWXU);
+{$ENDIF}
+ (* open file *)
+ snprintf(fn, sizeof(fn), '%s/%s/%s', uc^.language, uc^.category, filename);
+ for i := strlen(fn) - 1 downto 0 do
+ if isprint(fn[i]) = 1 then
+ fn[i] := '_';
+ uc^.fd := FpOpen(PAnsiChar(fn), O_CREAT or O_EXCL
+{$IFDEF O_LARGEFILE}
+ or O_LARGEFILE
+{$ENDIF}
+ or O_WRONLY, S_IRUSR or S_IWUSR);
+ if -1 = uc^.fd then
+ begin
+ WriteLn(stderr, Format('Error opening file `%s'' for upload: %s',
+ [fn, strerror(errno^)]));
+ uc^.response := request_refused_response;
+ Exit(MHD_NO);
+ end;
+ uc^.filename := strdup(fn);
+ end;
+ if (0 <> size) and (size <> size_t(FpWrite(uc^.fd, data, size))) then
+ begin
+ (* write failed; likely: disk full *)
+ WriteLn(stderr, Format('Error writing to file `%s'': %s', [uc^.filename,
+ strerror(errno^)]));
+ uc^.response := internal_error_response;
+ FpClose(uc^.fd);
+ uc^.fd := -1;
+ if nil <> uc^.filename then
+ begin
+ FpUnlink(uc^.filename);
+ Free(uc^.filename);
+ uc^.filename := nil;
+ end;
+ Exit(MHD_NO);
+ end;
+ Exit(MHD_YES);
+ end;
+
+ (**
+ * Function called whenever a request was completed.
+ * Used to clean up 'struct UploadContext' objects.
+ *
+ * @param cls client-defined closure, NULL
+ * @param connection connection handle
+ * @param con_cls value as set by the last call to
+ * the MHD_AccessHandlerCallback, points to NULL if this was
+ * not an upload
+ * @param toe reason for request termination
+ *)
+ procedure response_completed_callback(cls: Pointer; connection: PMHD_Connection;
+ con_cls: PPointer; toe: MHD_RequestTerminationCode); cdecl;
+ var
+ uc: PUploadContext;
+ begin
+ uc := con_cls^;
+ if nil = uc then
+ Exit; (* this request wasn't an upload request *)
+ if nil <> uc^.pp then
+ begin
+ MHD_destroy_post_processor(uc^.pp);
+ uc^.pp := nil;
+ end;
+ if -1 <> uc^.fd then
+ begin
+ FpClose(uc^.fd);
+ if nil <> uc^.filename then
+ begin
+ WriteLn(stderr, Format(
+ 'Upload of file `%s'' failed (incomplete or aborted), removing file.',
+ [uc^.filename]));
+ FpUnlink(uc^.filename);
+ end;
+ end;
+ if nil <> uc^.filename then
+ Free(uc^.filename);
+ Free(uc);
+ end;
+
+ (**
+ * Return the current directory listing.
+ *
+ * @param connection connection to return the directory for
+ * @return MHD_YES on success, MHD_NO on error
+ *)
+ function return_directory_response(connection: PMHD_Connection): cint;
+ var
+ ret: cint;
+ begin
+ pthread_mutex_lock(@mutex);
+ if nil = cached_directory_response then
+ ret := MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
+ internal_error_response)
+ else
+ ret := MHD_queue_response(connection, MHD_HTTP_OK,
+ cached_directory_response);
+ pthread_mutex_unlock(@mutex);
+ Result := ret;
+ end;
+
+ (**
+ * Main callback from MHD, used to generate the page.
+ *
+ * @param cls NULL
+ * @param connection connection handle
+ * @param url requested URL
+ * @param method GET, PUT, POST, etc.
+ * @param version HTTP version
+ * @param upload_data data from upload (PUT/POST)
+ * @param upload_data_size number of bytes in "upload_data"
+ * @param ptr our context
+ * @return MHD_YES on success, MHD_NO to drop connection
+ *)
+ function generate_page(cls: Pointer; connection: PMHD_Connection; url: Pcchar;
+ method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; ptr: PPointer): cint; cdecl;
+ var
+ response: PMHD_Response;
+ ret: cint;
+ fd: cint;
+ buf: stat;
+ (* should be file download *)
+ file_data: array[0..MAGIC_HEADER_SIZE] of AnsiChar;
+ got: ssize_t ;
+ mime: Pcchar;
+ uc: PUploadContext;
+ begin
+ if 0 <> strcomp(url, '/') then
+ begin
+ if 0 <> strcomp(method, MHD_HTTP_METHOD_GET) then
+ Exit(MHD_NO); (* unexpected method (we're not polite...) *)
+ if (0 = FpStat(@url[1], buf)) and (nil = strstr(@url[1], '..')) and
+ ('/' <> url[1]) then
+ fd := FpOpen(@url[1], O_RDONLY)
+ else
+ fd := -1;
+ if -1 = fd then
+ Exit(MHD_queue_response(connection, MHD_HTTP_NOT_FOUND,
+ file_not_found_response));
+ (* read beginning of the file to determine mime type *)
+ got := FpRead(fd, file_data, SizeOf(file_data));
+ if -1 <> got then
+ mime := magic_buffer(magic, Pcchar(file_data), got)
+ else
+ mime := nil;
+ lseek(fd, 0, SEEK_SET);
+ response := MHD_create_response_from_fd(buf.st_size, fd);
+ if nil = response then
+ begin
+ (* internal error (i.e. out of memory) *)
+ FpClose(fd);
+ Exit(MHD_NO);
+ end;
+ (* add mime type if we had one *)
+ if nil <> mime then
+ MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, mime);
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ Exit(ret);
+ end;
+ if 0 = strcomp(method, MHD_HTTP_METHOD_POST) then
+ begin
+ (* upload! *)
+ uc := ptr^;
+ if nil = uc then
+ begin
+ uc := Malloc(SizeOf(UploadContext));
+ if nil = uc then
+ Exit(MHD_NO); (* out of memory, close connection *)
+ memset(uc, 0, SizeOf(UploadContext));
+ uc^.fd := -1;
+ uc^.connection := connection;
+ uc^.pp := MHD_create_post_processor(connection, 64 * 1024 (* buffer size *),
+ @process_upload_data, uc);
+ if nil = uc^.pp then
+ begin
+ (* out of memory, close connection *)
+ Free(uc);
+ Exit(MHD_NO);
+ end;
+ ptr^ := uc;
+ Exit(MHD_YES);
+ end;
+ if 0 <> upload_data_size^ then
+ begin
+ if nil = uc^.response then
+ MHD_post_process(uc^.pp, upload_data, upload_data_size^);
+ upload_data_size^ := 0;
+ Exit(MHD_YES);
+ end;
+ (* end of upload, finish it! *)
+ MHD_destroy_post_processor(uc^.pp);
+ uc^.pp := nil;
+ if -1 <> uc^.fd then
+ begin
+ FpClose(uc^.fd);
+ uc^.fd := -1;
+ end;
+ if nil <> uc^.response then
+ Exit(MHD_queue_response(connection, MHD_HTTP_FORBIDDEN, uc^.response))
+ else
+ begin
+ update_directory;
+ Exit(return_directory_response(connection));
+ end;
+ end;
+ if 0 = strcomp(method, MHD_HTTP_METHOD_GET) then
+ Exit(return_directory_response(connection));
+ (* unexpected request, refuse *)
+ Result := MHD_queue_response(connection, MHD_HTTP_FORBIDDEN,
+ request_refused_response);
+ end;
+
+ (**
+ * Function called if we get a SIGPIPE. Does nothing.
+ *
+ * @param sig will be SIGPIPE (ignored)
+ *)
+ procedure catcher(signal: longint; info: psiginfo; context: psigcontext); cdecl;
+ begin
+ (* do nothing *)
+ end;
+
+ (**
+ * setup handlers to ignore SIGPIPE.
+ *)
+ procedure ignore_sigpipe;
+ var
+ oldsig: sigactionrec;
+ sig: sigactionrec;
+ begin
+ sig.sa_handler := @catcher;
+ FpsigEmptySet(sig.sa_mask);
+ {$IFDEF SA_INTERRUPT}
+ sig.sa_flags := SA_INTERRUPT; (* SunOS *)
+ {$ELSE}
+ sig.sa_flags := SA_RESTART;
+ {$ENDIF}
+ if 0 <> FPSigaction(SIGPIPE, @sig, @oldsig) then
+ WriteLn(stderr, Format('Failed to install SIGPIPE handler: %s',
+ [strerror(errno^)]));
+ end;
+
+const
+ (* test server key *)
+ srv_signed_key_pem: array[0..1674] of AnsiChar =
+ '-----BEGIN RSA PRIVATE KEY-----'#10+
+ 'MIIEowIBAAKCAQEAvfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW'#10+
+ '+K03KwEku55QvnUndwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8IL'#10+
+ 'q4sw32vo0fbMu5BZF49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ0'#10+
+ '20Q5EAAEseD1YtWCIpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6'#10+
+ 'QYGGh1QmHRPAy3CBII6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6x'#10+
+ 'yoOl204xuekZOaG9RUPId74Rtmwfi1TLbBzo2wIDAQABAoIBADu09WSICNq5cMe4'#10+
+ '+NKCLlgAT1NiQpLls1gKRbDhKiHU9j8QWNvWWkJWrCya4QdUfLCfeddCMeiQmv3K'#10+
+ 'lJMvDs+5OjJSHFoOsGiuW2Ias7IjnIojaJalfBml6frhJ84G27IXmdz6gzOiTIer'#10+
+ 'DjeAgcwBaKH5WwIay2TxIaScl7AwHBauQkrLcyb4hTmZuQh6ArVIN6+pzoVuORXM'#10+
+ 'bpeNWl2l/HSN3VtUN6aCAKbN/X3o0GavCCMn5Fa85uJFsab4ss/uP+2PusU71+zP'#10+
+ 'sBm6p/2IbGvF5k3VPDA7X5YX61sukRjRBihY8xSnNYx1UcoOsX6AiPnbhifD8+xQ'#10+
+ 'Tlf8oJUCgYEA0BTfzqNpr9Wxw5/QXaSdw7S/0eP5a0C/nwURvmfSzuTD4equzbEN'#10+
+ 'd+dI/s2JMxrdj/I4uoAfUXRGaabevQIjFzC9uyE3LaOyR2zhuvAzX+vVcs6bSXeU'#10+
+ 'pKpCAcN+3Z3evMaX2f+z/nfSUAl2i4J2R+/LQAWJW4KwRky/m+cxpfUCgYEA6bN1'#10+
+ 'b73bMgM8wpNt6+fcmS+5n0iZihygQ2U2DEud8nZJL4Nrm1dwTnfZfJBnkGj6+0Q0'#10+
+ 'cOwj2KS0/wcEdJBP0jucU4v60VMhp75AQeHqidIde0bTViSRo3HWKXHBIFGYoU3T'#10+
+ 'LyPyKndbqsOObnsFXHn56Nwhr2HLf6nw4taGQY8CgYBoSW36FLCNbd6QGvLFXBGt'#10+
+ '2lMhEM8az/K58kJ4WXSwOLtr6MD/WjNT2tkcy0puEJLm6BFCd6A6pLn9jaKou/92'#10+
+ 'SfltZjJPb3GUlp9zn5tAAeSSi7YMViBrfuFiHObij5LorefBXISLjuYbMwL03MgH'#10+
+ 'Ocl2JtA2ywMp2KFXs8GQWQKBgFyIVv5ogQrbZ0pvj31xr9HjqK6d01VxIi+tOmpB'#10+
+ '4ocnOLEcaxX12BzprW55ytfOCVpF1jHD/imAhb3YrHXu0fwe6DXYXfZV4SSG2vB7'#10+
+ 'IB9z14KBN5qLHjNGFpMQXHSMek+b/ftTU0ZnPh9uEM5D3YqRLVd7GcdUhHvG8P8Q'#10+
+ 'C9aXAoGBAJtID6h8wOGMP0XYX5YYnhlC7dOLfk8UYrzlp3xhqVkzKthTQTj6wx9R'#10+
+ 'GtC4k7U1ki8oJsfcIlBNXd768fqDVWjYju5rzShMpo8OCTS6ipAblKjCxPPVhIpv'#10+
+ 'tWPlbSn1qj6wylstJ5/3Z+ZW5H4wIKp5jmLiioDhcP0L/Ex3Zx8O'#10+
+ '-----END RSA PRIVATE KEY-----'#10;
+
+ (* test server CA signed certificates *)
+ srv_signed_cert_pem: array[0..1138] of AnsiChar =
+ '-----BEGIN CERTIFICATE-----'#10+
+ 'MIIDGzCCAgWgAwIBAgIES0KCvTALBgkqhkiG9w0BAQUwFzEVMBMGA1UEAxMMdGVz'#10+
+ 'dF9jYV9jZXJ0MB4XDTEwMDEwNTAwMDcyNVoXDTQ1MDMxMjAwMDcyNVowFzEVMBMG'#10+
+ 'A1UEAxMMdGVzdF9jYV9jZXJ0MIIBHzALBgkqhkiG9w0BAQEDggEOADCCAQkCggEA'#10+
+ 'vfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW+K03KwEku55QvnUn'#10+
+ 'dwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8ILq4sw32vo0fbMu5BZ'#10+
+ 'F49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ020Q5EAAEseD1YtWC'#10+
+ 'IpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6QYGGh1QmHRPAy3CB'#10+
+ 'II6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6xyoOl204xuekZOaG9'#10+
+ 'RUPId74Rtmwfi1TLbBzo2wIDAQABo3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQM'#10+
+ 'MAoGCCsGAQUFBwMBMA8GA1UdDwEB/wQFAwMHIAAwHQYDVR0OBBYEFOFi4ilKOP1d'#10+
+ 'XHlWCMwmVKr7mgy8MB8GA1UdIwQYMBaAFP2olB4s2T/xuoQ5pT2RKojFwZo2MAsG'#10+
+ 'CSqGSIb3DQEBBQOCAQEAHVWPxazupbOkG7Did+dY9z2z6RjTzYvurTtEKQgzM2Vz'#10+
+ 'GQBA+3pZ3c5mS97fPIs9hZXfnQeelMeZ2XP1a+9vp35bJjZBBhVH+pqxjCgiUflg'#10+
+ 'A3Zqy0XwwVCgQLE2HyaU3DLUD/aeIFK5gJaOSdNTXZLv43K8kl4cqDbMeRpVTbkt'#10+
+ 'YmG4AyEOYRNKGTqMEJXJoxD5E3rBUNrVI/XyTjYrulxbNPcMWEHKNeeqWpKDYTFo'#10+
+ 'Bb01PCthGXiq/4A2RLAFosadzRa8SBpoSjPPfZ0b2w4MJpReHqKbR5+T2t6hzml6'#10+
+ '4ToyOKPDmamiTuN5KzLN3cw7DQlvWMvqSOChPLnA3Q=='#10+
+ '-----END CERTIFICATE-----'#10;
+
+ (**
+ * Entry point to demo. Note: this HTTP server will make all
+ * files in the current directory and its subdirectories available
+ * to anyone. Press ENTER to stop the server once it has started.
+ *
+ * @param argc number of arguments in argv
+ * @param argv first and only argument should be the port number
+ * @return 0 on success
+ *)
+var
+ d: PMHD_Daemon;
+ port: cuint;
+begin
+ if (argc <> 2) or (1 <> sscanf(argv[1], '%u', @port)) or
+ (UINT16_MAX < port) then
+ begin
+ WriteLn(stderr, argv[0], ' PORT');
+ Halt(1);
+ end;
+ ignore_sigpipe;
+ magic := magic_open(MAGIC_MIME_TYPE);
+ magic_load(magic, nil);
+ pthread_mutex_init(@mutex, nil);
+ file_not_found_response := MHD_create_response_from_buffer(
+ strlen(FILE_NOT_FOUND_PAGE), FILE_NOT_FOUND_PAGE,
+ MHD_RESPMEM_PERSISTENT);
+ mark_as_html(file_not_found_response);
+ request_refused_response := MHD_create_response_from_buffer(
+ strlen(REQUEST_REFUSED_PAGE), REQUEST_REFUSED_PAGE,
+ MHD_RESPMEM_PERSISTENT);
+ mark_as_html(request_refused_response);
+ internal_error_response := MHD_create_response_from_buffer(
+ strlen(INTERNAL_ERROR_PAGE), INTERNAL_ERROR_PAGE,
+ MHD_RESPMEM_PERSISTENT);
+ mark_as_html(internal_error_response);
+ update_directory;
+ d := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY or MHD_USE_DEBUG or MHD_USE_SSL
+{$IFDEF EPOLL_SUPPORT}
+ or MHD_USE_EPOLL_LINUX_ONLY
+{$ENDIF},
+ port, nil, nil, @generate_page, nil,
+ MHD_OPTION_CONNECTION_MEMORY_LIMIT, size_t(256 * 1024),
+{$IFDEF PRODUCTION}
+ MHD_OPTION_PER_IP_CONNECTION_LIMIT, cuint(64),
+{$ENDIF}
+ MHD_OPTION_CONNECTION_TIMEOUT, cuint(120 (* seconds *)),
+ MHD_OPTION_THREAD_POOL_SIZE, cuint(NUMBER_OF_THREADS),
+ MHD_OPTION_NOTIFY_COMPLETED, @response_completed_callback, nil,
+ MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem,
+ MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem,
+ MHD_OPTION_END);
+ if nil = d then
+ Halt(1);
+ WriteLn(stderr, 'HTTP server running. Press ENTER to stop the server');
+ ReadLn;
+ MHD_stop_daemon(d);
+ MHD_destroy_response(file_not_found_response);
+ MHD_destroy_response(request_refused_response);
+ MHD_destroy_response(internal_error_response);
+ update_cached_response(nil);
+ pthread_mutex_destroy(@mutex);
+ magic_close(magic);
+end.
+
diff --git a/packages/libmicrohttpd/examples/digest_auth_example.pp b/packages/libmicrohttpd/examples/digest_auth_example.pp
new file mode 100644
index 0000000000..cb92d36852
--- /dev/null
+++ b/packages/libmicrohttpd/examples/digest_auth_example.pp
@@ -0,0 +1,127 @@
+(*
+ This file is part of libmicrohttpd
+ Copyright (C) 2010 Christian Grothoff (and other contributing authors)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*)
+(**
+ * @file digest_auth_example.pp (Original: digest_auth_example.c)
+ * @brief minimal example for how to use digest auth with libmicrohttpd
+ * @author Amr Ali / Silvio Clécio
+ *)
+
+program digest_auth_example;
+
+{$mode objfpc}{$H+}
+
+uses
+ sysutils, BaseUnix, cmem, cutils, libmicrohttpd;
+
+const
+ PAGE: Pcchar = '<html><head><title>libmicrohttpd demo</title></head><body>Access granted</body></html>';
+ DENIED: Pcchar = '<html><head><title>libmicrohttpd demo</title></head><body>Access denied</body></html>';
+ MY_OPAQUE_STR = '11733b200778ce33060f31c9af70a870ba96ddd4';
+
+ function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar;
+ method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; ptr: PPointer): cint; cdecl;
+ const
+ password: Pcchar = 'testpass';
+ realm: Pcchar = 'test@example.com';
+ var
+ response: PMHD_Response;
+ username: Pcchar;
+ ret: cint;
+ signal_stale: cint;
+ begin
+ username := MHD_digest_auth_get_username(connection);
+ if username = nil then
+ begin
+ response := MHD_create_response_from_buffer(strlen(DENIED), DENIED,
+ MHD_RESPMEM_PERSISTENT);
+ ret := MHD_queue_auth_fail_response(connection, realm, MY_OPAQUE_STR,
+ response, MHD_NO);
+ MHD_destroy_response(response);
+ Exit(ret);
+ end;
+ ret := MHD_digest_auth_check(connection, realm, username, password, 300);
+ Free(username);
+ if (ret = MHD_INVALID_NONCE) or (ret = MHD_NO) then
+ begin
+ response := MHD_create_response_from_buffer(strlen(DENIED), DENIED,
+ MHD_RESPMEM_PERSISTENT);
+ if nil = response then
+ Exit(MHD_NO);
+ if ret = MHD_INVALID_NONCE then
+ signal_stale := MHD_YES
+ else
+ signal_stale := MHD_NO;
+ ret := MHD_queue_auth_fail_response(connection, realm, MY_OPAQUE_STR,
+ response, signal_stale);
+ MHD_destroy_response(response);
+ Exit(ret);
+ end;
+ response := MHD_create_response_from_buffer(strlen(PAGE), PAGE,
+ MHD_RESPMEM_PERSISTENT);
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+var
+ fd: cint;
+ rnd: array[0..7] of AnsiChar;
+ len: ssize_t;
+ off: size_t;
+ d: PMHD_Daemon;
+begin
+ if argc <> 2 then
+ begin
+ WriteLn(argv[0], ' PORT');
+ Halt(1);
+ end;
+ fd := FpOpen('/dev/urandom', O_RDONLY);
+ if -1 = fd then
+ begin
+ WriteLn(stderr, Format('Failed to open `%s'': %s', [
+ '/dev/urandom', strerror(errno^)]));
+ Halt(1);
+ end;
+ off := 0;
+ while off < 8 do
+ begin
+ len := FpRead(fd, rnd, 8);
+ if len = -1 then
+ begin
+ WriteLn(stderr, Format('Failed to read `%s'': %s', [
+ '/dev/urandom', strerror(errno^)]));
+ FpClose(fd);
+ Halt(1);
+ end;
+ off += len;
+ end;
+ FpClose(fd);
+ d := MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG,
+ StrToInt(argv[1]), nil, nil, @ahc_echo, PAGE,
+ MHD_OPTION_DIGEST_AUTH_RANDOM, SizeOf(rnd), rnd,
+ MHD_OPTION_NONCE_NC_SIZE, 300,
+ MHD_OPTION_CONNECTION_TIMEOUT, cuint(120),
+ MHD_OPTION_END);
+ if d = nil then
+ Halt(1);
+ ReadLn;
+ MHD_stop_daemon (d);
+end.
+
diff --git a/packages/libmicrohttpd/examples/dual_stack_example.pp b/packages/libmicrohttpd/examples/dual_stack_example.pp
new file mode 100644
index 0000000000..a55635c6e0
--- /dev/null
+++ b/packages/libmicrohttpd/examples/dual_stack_example.pp
@@ -0,0 +1,78 @@
+(*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007, 2012 Christian Grothoff (and other contributing authors)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*)
+(**
+ * @file dual_stack_example.pp (Original: dual_stack_example.c)
+ * @brief how to use MHD with both IPv4 and IPv6 support (dual-stack)
+ * @author Christian Grothoff / Silvio Clécio
+ *)
+
+// To test it, just execute: $ curl -g -6 "http://[::1]:8888/"
+
+program dual_stack_example;
+
+{$mode objfpc}{$H+}
+
+uses
+ sysutils, libmicrohttpd;
+
+const
+ PAGE: Pcchar = '<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>';
+
+ function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar;
+ method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; ptr: PPointer): cint; cdecl;
+ const
+ aptr: cint = 0;
+ var
+ me: Pcchar;
+ response: PMHD_Response;
+ ret: cint;
+ begin
+ me := cls;
+ if 0 <> strcomp(method, 'GET') then
+ Exit(MHD_NO); (* unexpected method *)
+ if @aptr <> ptr^ then
+ begin
+ (* do never respond on first call *)
+ ptr^ := @aptr;
+ Exit(MHD_YES);
+ end;
+ ptr^ := nil; (* reset when done *)
+ response := MHD_create_response_from_buffer(strlen(me), Pointer(me),
+ MHD_RESPMEM_PERSISTENT);
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+var
+ d: PMHD_Daemon;
+begin
+ if argc <> 2 then
+ begin
+ WriteLn(argv[0], ' PORT');
+ Halt(1);
+ end;
+ d := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY or MHD_USE_DEBUG or
+ MHD_USE_DUAL_STACK, StrToInt(argv[1]), nil, nil, @ahc_echo, PAGE,
+ MHD_OPTION_CONNECTION_TIMEOUT, cuint(120), MHD_OPTION_END);
+ ReadLn;
+ MHD_stop_daemon(d);
+end.
+
diff --git a/packages/libmicrohttpd/examples/fileserver_example.pp b/packages/libmicrohttpd/examples/fileserver_example.pp
new file mode 100644
index 0000000000..b4a08f8cf1
--- /dev/null
+++ b/packages/libmicrohttpd/examples/fileserver_example.pp
@@ -0,0 +1,115 @@
+(*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007 Christian Grothoff (and other contributing authors)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*)
+(**
+ * @file fileserver_example.pp (Original: fileserver_example.c)
+ * @brief minimal example for how to use libmicrohttpd to serve files
+ * @author Christian Grothoff / Silvio Clécio
+ *)
+
+program fileserver_example;
+
+{$mode objfpc}{$H+}
+
+uses
+ sysutils, BaseUnix, cutils, libmicrohttpd;
+
+const
+ PAGE: Pcchar = '<html><head><title>File not found</title></head><body>File not found</body></html>';
+
+ function file_reader(cls: Pointer; pos: cuint64; buf: Pcchar;
+ max: size_t): ssize_t; cdecl;
+ var
+ &file: FILEptr;
+ begin
+ &file := cls;
+ fseek(&file, pos, SEEK_SET);
+ Result := fread(buf, 1, max, &file);
+ end;
+
+ procedure free_callback(cls: Pointer); cdecl;
+ var
+ &file: FILEptr;
+ begin
+ &file := cls;
+ fclose(&file);
+ end;
+
+ function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar;
+ method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; ptr: PPointer): cint; cdecl;
+ const
+ aptr: cint = 0;
+ var
+ response: PMHD_Response;
+ ret: cint;
+ &file: FILEptr;
+ buf: stat;
+ begin
+ if (0 <> strcomp(method, MHD_HTTP_METHOD_GET)) and
+ (0 <> strcomp(method, MHD_HTTP_METHOD_HEAD)) then
+ Exit(MHD_NO); (* unexpected method *)
+ if @aptr <> ptr^ then
+ begin
+ (* do never respond on first call *)
+ ptr^ := @aptr;
+ Exit(MHD_YES);
+ end;
+ ptr^ := nil; (* reset when done *)
+ if 0 = FpStat(@url[1], buf) then
+ &file := fopen(@url[1], fopenread)
+ else
+ &file := nil;
+ if nil = &file then
+ begin
+ response := MHD_create_response_from_buffer(strlen(PAGE), Pointer(PAGE),
+ MHD_RESPMEM_PERSISTENT);
+ ret := MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
+ MHD_destroy_response(response);
+ end
+ else
+ begin
+ response := MHD_create_response_from_callback(buf.st_size, 32 * 1024, (* 32k page size *)
+ @file_reader, &file, @free_callback);
+ if nil = response then
+ begin
+ fclose(&file);
+ Exit(MHD_NO);
+ end;
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ end;
+ Result := ret;
+ end;
+
+var
+ d: PMHD_Daemon;
+begin
+ if argc <> 2 then
+ begin
+ WriteLn(argv[0], ' PORT');
+ Halt(1);
+ end;
+ d := MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG,
+ StrToInt(argv[1]), nil, nil, @ahc_echo, PAGE, MHD_OPTION_END);
+ if d = nil then
+ Halt(1);
+ ReadLn;
+ MHD_stop_daemon(d);
+end.
+
diff --git a/packages/libmicrohttpd/examples/fileserver_example_dirs.pp b/packages/libmicrohttpd/examples/fileserver_example_dirs.pp
new file mode 100644
index 0000000000..bc06b38c22
--- /dev/null
+++ b/packages/libmicrohttpd/examples/fileserver_example_dirs.pp
@@ -0,0 +1,167 @@
+(*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007 Christian Grothoff (and other contributing authors)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*)
+
+(**
+ * @file fileserver_example_dirs.pp (Original: fileserver_example_dirs.c)
+ * @brief example for how to use libmicrohttpd to serve files (with directory support)
+ * @author Christian Grothoff / Silvio Clécio
+ *)
+
+program fileserver_example_dirs;
+
+{$mode objfpc}{$H+}
+
+uses
+ sysutils, BaseUnix, cutils, libmicrohttpd;
+
+const
+ PAGE: Pcchar = '<html><head><title>File not found</title></head><body>File not found</body></html>';
+
+ function file_reader(cls: Pointer; pos: cuint64; buf: Pcchar;
+ max: size_t): ssize_t; cdecl;
+ var
+ &file: FILEptr;
+ begin
+ &file := cls;
+ fseek(&file, pos, SEEK_SET);
+ Result := fread(buf, 1, max, &file);
+ end;
+
+ procedure file_free_callback(cls: Pointer); cdecl;
+ var
+ &file: FILEptr;
+ begin
+ &file := cls;
+ fclose(&file);
+ end;
+
+ procedure dir_free_callback(cls: Pointer); cdecl;
+ var
+ dir: pDir;
+ begin
+ dir := cls;
+ if dir <> nil then
+ FpClosedir(dir^);
+ end;
+
+ function dir_reader(cls: Pointer; pos: cuint64; buf: Pcchar;
+ max: size_t): ssize_t; cdecl;
+ var
+ dir: pDir;
+ e: pDirent;
+ begin
+ dir := cls;
+ if max < 512 then
+ Exit(0);
+ repeat
+ e := FpReaddir(dir^);
+ if e = nil then
+ Exit(MHD_CONTENT_READER_END_OF_STREAM);
+ until not (e^.d_name[0] = '.');
+ Result := snprintf(buf, max, '<a href="/%s">%s</a><br>', e^.d_name,
+ e^.d_name);
+ end;
+
+ function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar;
+ method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; ptr: PPointer): cint; cdecl;
+ const
+ aptr: cint = 0;
+ var
+ response: PMHD_Response;
+ ret: cint;
+ &file: FILEptr;
+ dir: pDir;
+ buf: stat;
+ emsg: array[0..1023] of AnsiChar;
+ begin
+ if 0 <> strcomp(method, MHD_HTTP_METHOD_GET) then
+ Exit(MHD_NO); (* unexpected method *)
+ if @aptr <> ptr^ then
+ begin
+ (* do never respond on first call *)
+ ptr^ := @aptr;
+ Exit(MHD_YES);
+ end;
+ ptr^ := nil; (* reset when done *)
+ if (0 = FpStat(@url[1], buf)) and fpS_ISREG(buf.st_mode) then
+ &file := fopen(@url[1], fopenread)
+ else
+ &file := nil;
+ if &file = nil then
+ begin
+ dir := FpOpendir(PChar('.'));
+ if dir = nil then
+ begin
+ (* most likely cause: more concurrent requests than
+ available file descriptors / 2 *)
+ snprintf(emsg, SizeOf(emsg), 'Failed to open directory `.'': %s'#10,
+ strerror(errno^));
+ response := MHD_create_response_from_buffer(strlen(emsg), @emsg,
+ MHD_RESPMEM_MUST_COPY);
+ if response = nil then
+ Exit(MHD_NO);
+ ret := MHD_queue_response(connection, MHD_HTTP_SERVICE_UNAVAILABLE,
+ response);
+ MHD_destroy_response(response);
+ end
+ else
+ begin
+ response := MHD_create_response_from_callback(cuint64(MHD_SIZE_UNKNOWN),
+ 32 * 1024, @dir_reader, dir, @dir_free_callback);
+ if response = nil then
+ begin
+ FpClosedir(dir^);
+ Exit(MHD_NO);
+ end;
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ end;
+ end
+ else
+ begin
+ response := MHD_create_response_from_callback(buf.st_size, 32 * 1024, (* 32k page size *)
+ @file_reader, &file, @file_free_callback);
+ if response = nil then
+ begin
+ fclose(&file);
+ Exit(MHD_NO);
+ end;
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ end;
+ Result := ret;
+ end;
+
+var
+ d: PMHD_Daemon;
+begin
+ if argc <> 2 then
+ begin
+ WriteLn(argv[0], ' PORT');
+ Halt(1);
+ end;
+ d := MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG,
+ StrToInt(argv[1]), nil, nil, @ahc_echo, PAGE, MHD_OPTION_END);
+ if d = nil then
+ Halt(1);
+ ReadLn;
+ MHD_stop_daemon(d);
+end.
+
diff --git a/packages/libmicrohttpd/examples/fileserver_example_external_select.pp b/packages/libmicrohttpd/examples/fileserver_example_external_select.pp
new file mode 100644
index 0000000000..e19ce9f80b
--- /dev/null
+++ b/packages/libmicrohttpd/examples/fileserver_example_external_select.pp
@@ -0,0 +1,146 @@
+(*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007, 2008 Christian Grothoff (and other contributing authors)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*)
+(**
+ * @file fileserver_example_external_select.pp (Original: fileserver_example_external_select.c)
+ * @brief minimal example for how to use libmicrohttpd to server files
+ * @author Christian Grothoff / Silvio Clécio
+ *)
+
+program fileserver_example_external_select;
+
+{$mode objfpc}{$H+}
+
+uses
+ sysutils, BaseUnix, cutils, libmicrohttpd;
+
+const
+ PAGE: Pcchar = '<html><head><title>File not found</title></head><body>File not found</body></html>';
+
+ function file_reader(cls: Pointer; pos: cuint64; buf: Pcchar;
+ max: size_t): ssize_t; cdecl;
+ var
+ &file: FILEptr;
+ begin
+ &file := cls;
+ fseek(&file, pos, SEEK_SET);
+ Result := fread(buf, 1, max, &file);
+ end;
+
+ procedure free_callback(cls: Pointer); cdecl;
+ var
+ &file: FILEptr;
+ begin
+ &file := cls;
+ fclose(&file);
+ end;
+
+ function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar;
+ method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; ptr: PPointer): cint; cdecl;
+ const
+ aptr: cint = 0;
+ var
+ response: PMHD_Response;
+ ret: cint;
+ &file: FILEptr;
+ buf: stat;
+ begin
+ if 0 <> strcomp(method, MHD_HTTP_METHOD_GET) then
+ Exit(MHD_NO); (* unexpected method *)
+ if @aptr <> ptr^ then
+ begin
+ (* do never respond on first call *)
+ ptr^ := @aptr;
+ Exit(MHD_YES);
+ end;
+ ptr^ := nil; (* reset when done *)
+ if (0 = FpStat(@url[1], buf)) and fpS_ISREG(buf.st_mode) then
+ &file := fopen(@url[1], fopenread)
+ else
+ &file := nil;
+ if &file = nil then
+ begin
+ response := MHD_create_response_from_buffer(strlen(PAGE), Pointer(PAGE),
+ MHD_RESPMEM_PERSISTENT);
+ ret := MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
+ MHD_destroy_response(response);
+ end
+ else
+ begin
+ response := MHD_create_response_from_callback(buf.st_size, 32 * 1024, (* 32k page size *)
+ @file_reader, &file, @free_callback);
+ if response = nil then
+ begin
+ fclose(&file);
+ Exit(MHD_NO);
+ end;
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ end;
+ Result := ret;
+ end;
+
+var
+ d: PMHD_Daemon;
+ &end: time_t;
+ t: time_t;
+ tv: timeval;
+ rs: TFDSet;
+ ws: TFDSet;
+ es: TFDSet;
+ max: MHD_socket;
+ mhd_timeout: MHD_UNSIGNED_LONG_LONG;
+begin
+ if argc <> 3 then
+ begin
+ WriteLn(argv[0], ' PORT SECONDS-TO-RUN');
+ Halt(1);
+ end;
+ d := MHD_start_daemon(MHD_USE_DEBUG, StrToInt(argv[1]), nil, nil, @ahc_echo,
+ PAGE, MHD_OPTION_END);
+ if d = nil then
+ Halt(1);
+ &end := fptime + StrToInt(argv[2]);
+ while True do
+ begin
+ t := fptime;
+ if not (t < &end) then
+ Break;
+ tv.tv_sec := &end - t;
+ tv.tv_usec := 0;
+ max := 0;
+ fpFD_ZERO(rs);
+ fpFD_ZERO(ws);
+ fpFD_ZERO(es);
+ if MHD_YES <> MHD_get_fdset (d, @rs, @ws, @es, @max) then
+ Break; (* fatal internal error *)
+ if MHD_get_timeout(d, @mhd_timeout) = MHD_YES then
+ begin
+ if MHD_UNSIGNED_LONG_LONG(tv.tv_sec) < mhd_timeout div clonglong(1000) then
+ begin
+ tv.tv_sec := mhd_timeout div clonglong(1000);
+ tv.tv_usec := (mhd_timeout - (tv.tv_sec * clonglong(1000))) * clonglong(1000);
+ end;
+ end;
+ fpSelect(max + 1, @rs, @ws, @es, @tv);
+ MHD_run(d);
+ end;
+ MHD_stop_daemon(d);
+end.
+
diff --git a/packages/libmicrohttpd/examples/hellobrowser.pp b/packages/libmicrohttpd/examples/hellobrowser.pp
new file mode 100644
index 0000000000..fa7d05cc54
--- /dev/null
+++ b/packages/libmicrohttpd/examples/hellobrowser.pp
@@ -0,0 +1,42 @@
+(* Feel free to use this example code in any way
+ you see fit (Public Domain) *)
+
+// Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/hellobrowser.c
+
+program hellobrowser;
+
+{$mode objfpc}{$H+}
+
+uses
+ libmicrohttpd;
+
+const
+ PORT = 8888;
+
+ function AnswerToConnection(ACls: Pointer; AConnection: PMHD_Connection;
+ AUrl: Pcchar; AMethod: Pcchar; AVersion: Pcchar; AUploadData: Pcchar;
+ AUploadDataSize: Psize_t; AConCls: PPointer): cint; cdecl;
+ const
+ PAGE: Pcchar = 'Hello world';
+ var
+ VReturn: cint;
+ VResponse: PMHD_Response;
+ begin
+ VResponse := MHD_create_response_from_buffer(Length(PAGE), Pointer(PAGE),
+ MHD_RESPMEM_PERSISTENT);
+ VReturn := MHD_queue_response(AConnection, MHD_HTTP_OK, VResponse);
+ MHD_destroy_response(VResponse);
+ Result := VReturn;
+ end;
+
+var
+ VDaemon: PMHD_Daemon;
+begin
+ VDaemon := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, nil, nil,
+ @AnswerToConnection, nil, MHD_OPTION_END);
+ if not Assigned(VDaemon) then
+ Halt(1);
+ ReadLn;
+ MHD_stop_daemon(VDaemon)
+end.
+
diff --git a/packages/libmicrohttpd/examples/https_fileserver_example.pp b/packages/libmicrohttpd/examples/https_fileserver_example.pp
new file mode 100644
index 0000000000..502428ae74
--- /dev/null
+++ b/packages/libmicrohttpd/examples/https_fileserver_example.pp
@@ -0,0 +1,194 @@
+(*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007, 2008 Christian Grothoff (and other contributing authors)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*)
+(**
+ * @file https_fileserver_example.pp (Original: https_fileserver_example.c)
+ * @brief a simple HTTPS file server using TLS.
+ *
+ * Usage :
+ *
+ * 'https_fileserver_example HTTP-PORT'
+ *
+ * The certificate & key are required by the server to operate, Omitting the
+ * path arguments will cause the server to use the hard coded example certificate & key.
+ *
+ * 'certtool' may be used to generate these if required.
+ *
+ * @author Sagie Amir / Silvio Clécio
+ *)
+
+program https_fileserver_example;
+
+{$mode objfpc}{$H+}
+
+uses
+ sysutils, BaseUnix, cutils, libmicrohttpd;
+
+const
+ BUF_SIZE = 1024;
+ MAX_URL_LEN = 255;
+
+ // TODO remove if unused
+ CAFILE: Pcchar = 'ca.pem';
+ CRLFILE: Pcchar = 'crl.pem';
+
+ EMPTY_PAGE: Pcchar = '<html><head><title>File not found</title></head><body>File not found</body></html>';
+
+ (* Test Certificate *)
+ cert_pem: array[0..980] of AnsiChar =
+ '-----BEGIN CERTIFICATE-----'#10+
+ 'MIICpjCCAZCgAwIBAgIESEPtjjALBgkqhkiG9w0BAQUwADAeFw0wODA2MDIxMjU0'#10+
+ 'MzhaFw0wOTA2MDIxMjU0NDZaMAAwggEfMAsGCSqGSIb3DQEBAQOCAQ4AMIIBCQKC'#10+
+ 'AQC03TyUvK5HmUAirRp067taIEO4bibh5nqolUoUdo/LeblMQV+qnrv/RNAMTx5X'#10+
+ 'fNLZ45/kbM9geF8qY0vsPyQvP4jumzK0LOJYuIwmHaUm9vbXnYieILiwCuTgjaud'#10+
+ '3VkZDoQ9fteIo+6we9UTpVqZpxpbLulBMh/VsvX0cPJ1VFC7rT59o9hAUlFf9jX/'#10+
+ 'GmKdYI79MtgVx0OPBjmmSD6kicBBfmfgkO7bIGwlRtsIyMznxbHu6VuoX/eVxrTv'#10+
+ 'rmCwgEXLWRZ6ru8MQl5YfqeGXXRVwMeXU961KefbuvmEPccgCxm8FZ1C1cnDHFXh'#10+
+ 'siSgAzMBjC/b6KVhNQ4KnUdZAgMBAAGjLzAtMAwGA1UdEwEB/wQCMAAwHQYDVR0O'#10+
+ 'BBYEFJcUvpjvE5fF/yzUshkWDpdYiQh/MAsGCSqGSIb3DQEBBQOCAQEARP7eKSB2'#10+
+ 'RNd6XjEjK0SrxtoTnxS3nw9sfcS7/qD1+XHdObtDFqGNSjGYFB3Gpx8fpQhCXdoN'#10+
+ '8QUs3/5ZVa5yjZMQewWBgz8kNbnbH40F2y81MHITxxCe1Y+qqHWwVaYLsiOTqj2/'#10+
+ '0S3QjEJ9tvklmg7JX09HC4m5QRYfWBeQLD1u8ZjA1Sf1xJriomFVyRLI2VPO2bNe'#10+
+ 'JDMXWuP+8kMC7gEvUnJ7A92Y2yrhu3QI3bjPk8uSpHea19Q77tul1UVBJ5g+zpH3'#10+
+ 'OsF5p0MyaVf09GTzcLds5nE/osTdXGUyHJapWReVmPm3Zn6gqYlnzD99z+DPIgIV'#10+
+ 'RhZvQx74NQnS6g=='#10+
+ '-----END CERTIFICATE-----'#10;
+
+ key_pem: array[0..1674] of AnsiChar =
+ '-----BEGIN RSA PRIVATE KEY-----'#10+
+ 'MIIEowIBAAKCAQEAtN08lLyuR5lAIq0adOu7WiBDuG4m4eZ6qJVKFHaPy3m5TEFf'#10+
+ 'qp67/0TQDE8eV3zS2eOf5GzPYHhfKmNL7D8kLz+I7psytCziWLiMJh2lJvb2152I'#10+
+ 'niC4sArk4I2rnd1ZGQ6EPX7XiKPusHvVE6VamacaWy7pQTIf1bL19HDydVRQu60+'#10+
+ 'faPYQFJRX/Y1/xpinWCO/TLYFcdDjwY5pkg+pInAQX5n4JDu2yBsJUbbCMjM58Wx'#10+
+ '7ulbqF/3lca0765gsIBFy1kWeq7vDEJeWH6nhl10VcDHl1PetSnn27r5hD3HIAsZ'#10+
+ 'vBWdQtXJwxxV4bIkoAMzAYwv2+ilYTUOCp1HWQIDAQABAoIBAArOQv3R7gmqDspj'#10+
+ 'lDaTFOz0C4e70QfjGMX0sWnakYnDGn6DU19iv3GnX1S072ejtgc9kcJ4e8VUO79R'#10+
+ 'EmqpdRR7k8dJr3RTUCyjzf/C+qiCzcmhCFYGN3KRHA6MeEnkvRuBogX4i5EG1k5l'#10+
+ '/5t+YBTZBnqXKWlzQLKoUAiMLPg0eRWh+6q7H4N7kdWWBmTpako7TEqpIwuEnPGx'#10+
+ 'u3EPuTR+LN6lF55WBePbCHccUHUQaXuav18NuDkcJmCiMArK9SKb+h0RqLD6oMI/'#10+
+ 'dKD6n8cZXeMBkK+C8U/K0sN2hFHACsu30b9XfdnljgP9v+BP8GhnB0nCB6tNBCPo'#10+
+ '32srOwECgYEAxWh3iBT4lWqL6bZavVbnhmvtif4nHv2t2/hOs/CAq8iLAw0oWGZc'#10+
+ '+JEZTUDMvFRlulr0kcaWra+4fN3OmJnjeuFXZq52lfMgXBIKBmoSaZpIh2aDY1Rd'#10+
+ 'RbEse7nQl9hTEPmYspiXLGtnAXW7HuWqVfFFP3ya8rUS3t4d07Hig8ECgYEA6ou6'#10+
+ 'OHiBRTbtDqLIv8NghARc/AqwNWgEc9PelCPe5bdCOLBEyFjqKiT2MttnSSUc2Zob'#10+
+ 'XhYkHC6zN1Mlq30N0e3Q61YK9LxMdU1vsluXxNq2rfK1Scb1oOlOOtlbV3zA3VRF'#10+
+ 'hV3t1nOA9tFmUrwZi0CUMWJE/zbPAyhwWotKyZkCgYEAh0kFicPdbABdrCglXVae'#10+
+ 'SnfSjVwYkVuGd5Ze0WADvjYsVkYBHTvhgRNnRJMg+/vWz3Sf4Ps4rgUbqK8Vc20b'#10+
+ 'AU5G6H6tlCvPRGm0ZxrwTWDHTcuKRVs+pJE8C/qWoklE/AAhjluWVoGwUMbPGuiH'#10+
+ '6Gf1bgHF6oj/Sq7rv/VLZ8ECgYBeq7ml05YyLuJutuwa4yzQ/MXfghzv4aVyb0F3'#10+
+ 'QCdXR6o2IYgR6jnSewrZKlA9aPqFJrwHNR6sNXlnSmt5Fcf/RWO/qgJQGLUv3+rG'#10+
+ '7kuLTNDR05azSdiZc7J89ID3Bkb+z2YkV+6JUiPq/Ei1+nDBEXb/m+/HqALU/nyj'#10+
+ 'P3gXeQKBgBusb8Rbd+KgxSA0hwY6aoRTPRt8LNvXdsB9vRcKKHUFQvxUWiUSS+L9'#10+
+ '/Qu1sJbrUquKOHqksV5wCnWnAKyJNJlhHuBToqQTgKXjuNmVdYSe631saiI7PHyC'#10+
+ 'eRJ6DxULPxABytJrYCRrNqmXi5TCiqR2mtfalEMOPxz8rUU8dYyx'#10+
+ '-----END RSA PRIVATE KEY-----'#10;
+
+ function file_reader(cls: Pointer; pos: cuint64; buf: Pcchar;
+ max: size_t): ssize_t; cdecl;
+ var
+ &file: FILEptr;
+ begin
+ &file := cls;
+ fseek(&file, pos, SEEK_SET);
+ Result := fread(buf, 1, max, &file);
+ end;
+
+ procedure file_free_callback(cls: Pointer); cdecl;
+ var
+ &file: FILEptr;
+ begin
+ &file := cls;
+ fclose(&file);
+ end;
+
+ function http_ahc(cls: Pointer; connection: PMHD_Connection; url: Pcchar;
+ method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; ptr: PPointer): cint; cdecl;
+ const
+ aptr: cint = 0;
+ var
+ response: PMHD_Response;
+ ret: cint;
+ &file: FILEptr;
+ buf: stat;
+ begin
+ if 0 <> strcomp(method, MHD_HTTP_METHOD_GET) then
+ Exit(MHD_NO); (* unexpected method *)
+ if @aptr <> ptr^ then
+ begin
+ (* do never respond on first call *)
+ ptr^ := @aptr;
+ Exit(MHD_YES);
+ end;
+ ptr^ := nil; (* reset when done *)
+ if (0 = FpStat(@url[1], buf)) and fpS_ISREG(buf.st_mode) then
+ &file := fopen(@url[1], fopenread)
+ else
+ &file := nil;
+ if &file = nil then
+ begin
+ response := MHD_create_response_from_buffer(strlen(EMPTY_PAGE),
+ Pointer(EMPTY_PAGE), MHD_RESPMEM_PERSISTENT);
+ ret := MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
+ MHD_destroy_response(response);
+ end
+ else
+ begin
+ response := MHD_create_response_from_callback(buf.st_size, 32 * 1024, (* 32k page size *)
+ @file_reader, &file, @file_free_callback);
+ if response = nil then
+ begin
+ fclose(&file);
+ Exit(MHD_NO);
+ end;
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ end;
+ Result := ret;
+ end;
+
+var
+ TLS_daemon: PMHD_Daemon;
+begin
+ if argc = 2 then
+ begin
+ (* TODO check if this is truly necessary - disallow usage of the blocking /dev/random *)
+ (* gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM, 0); *)
+ TLS_daemon := MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION or
+ MHD_USE_DEBUG or MHD_USE_SSL, StrToInt(argv[1]), nil, nil,
+ @http_ahc, nil, MHD_OPTION_CONNECTION_TIMEOUT, 256,
+ MHD_OPTION_HTTPS_MEM_KEY, key_pem,
+ MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
+ MHD_OPTION_END);
+ end
+ else
+ begin
+ WriteLn(' Usage: ', argv[0], ' HTTP-PORT');
+ Halt(1);
+ end;
+ if TLS_daemon = nil then
+ begin
+ WriteLn(stderr, 'Error: failed to start TLS_daemon');
+ Halt(1);
+ end
+ else
+ WriteLn('MHD daemon listening on port ', argv[1]);
+ ReadLn;
+ MHD_stop_daemon(TLS_daemon);
+end.
+
diff --git a/packages/libmicrohttpd/examples/largepost.pp b/packages/libmicrohttpd/examples/largepost.pp
new file mode 100644
index 0000000000..6293899ed5
--- /dev/null
+++ b/packages/libmicrohttpd/examples/largepost.pp
@@ -0,0 +1,187 @@
+(* Feel free to use this example code in any way
+ you see fit (Public Domain) *)
+
+// Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/largepost.c
+
+program largepost;
+
+{$mode objfpc}{$H+}
+
+uses
+ libmicrohttpd, SysUtils, cutils;
+
+type
+ TConnectionInfoStruct = record
+ ConnectionType: cint;
+ PostProcessor: PMHD_PostProcessor;
+ Fp: FILEptr;
+ AnswerString: Pcchar;
+ AnswerCode: cint;
+ end;
+ PConnectionInfoStruct = ^TConnectionInfoStruct;
+
+const
+ PORT = 8888;
+ POSTBUFFERSIZE = 512;
+ MAXCLIENTS = 2;
+ GET = 0;
+ POST = 1;
+
+var
+ NrOfUploadingClients: Cardinal;
+ AskPage: Pcchar =
+ '<html><body>'+#10+
+ 'Upload a file, please!<br>'+#10+
+ 'There are %d clients uploading at the moment.<br>'+#10+
+ '<form action="/filepost" method="post" enctype="multipart/form-data">'+#10+
+ '<input name="file" type="file">'+#10+
+ '<input type="submit" value="Send"></form>'+#10+
+ '</body></html>';
+ BusyPage: Pcchar = '<html><body>This server is busy, please try again later.</body></html>';
+ CompletePage: Pcchar = '<html><body>The upload has been completed.</body></html>';
+ ErrorPage: Pcchar = '<html><body>This doesn''t seem to be right.</body></html>';
+ ServerErrorPage: Pcchar = '<html><body>An internal server error has occured.</body></html>';
+ FileExistsPage: Pcchar = '<html><body>This file already exists.</body></html>';
+
+ function SendPage(AConnection: PMHD_Connection; APage: Pcchar; AStatusCode: cint): cint;
+ var
+ VRet: cint;
+ VResponse: PMHD_Response;
+ begin
+ VResponse := MHD_create_response_from_buffer(Length(APage),
+ Pointer(APage), MHD_RESPMEM_MUST_COPY);
+ if not Assigned(VResponse) then
+ Exit(MHD_NO);
+ MHD_add_response_header(VResponse, MHD_HTTP_HEADER_CONTENT_TYPE, 'text/html');
+ VRet := MHD_queue_response(AConnection, AStatusCode, VResponse);
+ MHD_destroy_response(VResponse);
+ Result := VRet;
+ end;
+
+ function IteratePost(AConInfoCls: Pointer; AKind: MHD_ValueKind; AKey: Pcchar;
+ AFileName: Pcchar; AContentType: Pcchar; ATransferEncoding: Pcchar;
+ AData: Pcchar; AOff: cuint64; ASize: size_t): cint; cdecl;
+ var
+ VConInfo: PConnectionInfoStruct;
+ begin
+ VConInfo := AConInfoCls;
+ VConInfo^.AnswerString := ServerErrorPage;
+ VConInfo^.AnswerCode := MHD_HTTP_INTERNAL_SERVER_ERROR;
+ if StrComp(AKey, 'file') <> 0 then
+ Exit(MHD_NO);
+ if not Assigned(VConInfo^.Fp) then
+ begin
+ if FileExists(AFileName) then
+ begin
+ VConInfo^.AnswerString := FileExistsPage;
+ VConInfo^.AnswerCode := MHD_HTTP_FORBIDDEN;
+ Exit(MHD_NO);
+ end;
+ VConInfo^.Fp := fopen(AFileName, fappendwrite);
+ if not Assigned(VConInfo^.Fp) then
+ Exit(MHD_NO);
+ end;
+ if ASize > 0 then
+ if fwrite(AData, ASize, SizeOf(AnsiChar), VConInfo^.Fp) = 0 then
+ Exit(MHD_NO);
+ VConInfo^.AnswerString := CompletePage;
+ VConInfo^.AnswerCode := MHD_HTTP_OK;
+ Result := MHD_YES;
+ end;
+
+ procedure RequestCompleted(ACls: Pointer; AConnection: PMHD_Connection;
+ AConCls: PPointer; AToe: MHD_RequestTerminationCode); cdecl;
+ var
+ VConInfo: PConnectionInfoStruct;
+ begin
+ VConInfo := AConCls^;
+ if not Assigned(VConInfo) then
+ Exit;
+ if VConInfo^.ConnectionType = POST then
+ begin
+ if Assigned(VConInfo^.PostProcessor) then
+ begin
+ MHD_destroy_post_processor(VConInfo^.PostProcessor);
+ Dec(NrOfUploadingClients);
+ end;
+ if Assigned(VConInfo^.Fp) then
+ fclose(VConInfo^.Fp);
+ end;
+ FreeMem(VConInfo);
+ AConCls^ := nil;
+ end;
+
+ function AnswerToConnection(ACls: Pointer; AConnection: PMHD_Connection;
+ AUrl: Pcchar; AMethod: Pcchar; AVersion: Pcchar; AUploadData: Pcchar;
+ AUploadDataSize: Psize_t; AConCls: PPointer): cint; cdecl;
+ var
+ VBuffer: array[0..1024] of AnsiChar;
+ VConInfo: PConnectionInfoStruct;
+ begin
+ if not Assigned(AConCls^) then
+ begin
+ if NrOfUploadingClients >= MAXCLIENTS then
+ Exit(SendPage(AConnection, BusyPage, MHD_HTTP_SERVICE_UNAVAILABLE));
+ VConInfo := AllocMem(SizeOf(TConnectionInfoStruct));
+ if not Assigned(VConInfo) then
+ Exit(MHD_NO);
+ VConInfo^.Fp := nil;
+ if StrComp(AMethod, 'POST') = 0 then
+ begin
+ VConInfo^.PostProcessor := MHD_create_post_processor(AConnection,
+ POSTBUFFERSIZE, @IteratePost, VConInfo);
+ if not Assigned(VConInfo^.PostProcessor) then
+ begin
+ FreeMem(VConInfo);
+ Exit(MHD_NO);
+ end;
+ Inc(NrOfUploadingClients);
+ VConInfo^.ConnectionType := POST;
+ VConInfo^.AnswerCode := MHD_HTTP_OK;
+ VConInfo^.AnswerString := CompletePage;
+ end
+ else
+ VConInfo^.ConnectionType := GET;
+ AConCls^ := VConInfo;
+ Exit(MHD_YES);
+ end;
+ if StrComp(AMethod, 'GET') = 0 then
+ begin
+ StrLFmt(VBuffer, SizeOf(VBuffer), AskPage, [NrOfUploadingClients]);
+ Exit(SendPage(AConnection, VBuffer, MHD_HTTP_OK));
+ end;
+ if StrComp(AMethod, 'POST') = 0 then
+ begin
+ VConInfo := AConCls^;
+ if AUploadDataSize^ <> 0 then
+ begin
+ MHD_post_process(VConInfo^.PostProcessor, AUploadData, AUploadDataSize^);
+ AUploadDataSize^ := 0;
+ Exit(MHD_YES);
+ end
+ else
+ begin
+ if Assigned(VConInfo^.Fp) then
+ begin
+ fclose(VConInfo^.Fp);
+ VConInfo^.Fp := nil;
+ end;
+ (* Now it is safe to open and inspect the file before calling send_page with a response *)
+ Exit(SendPage(AConnection, VConInfo^.AnswerString, VConInfo^.AnswerCode));
+ end;
+ end;
+ Result := SendPage(AConnection, ErrorPage, MHD_HTTP_BAD_REQUEST);
+ end;
+
+var
+ VDaemon: PMHD_Daemon;
+begin
+ VDaemon := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, nil, nil,
+ @AnswerToConnection, nil, MHD_OPTION_NOTIFY_COMPLETED, @RequestCompleted,
+ nil, MHD_OPTION_END);
+ if not Assigned(VDaemon) then
+ Halt(1);
+ ReadLn;
+ MHD_stop_daemon(VDaemon);
+end.
+
diff --git a/packages/libmicrohttpd/examples/logging.pp b/packages/libmicrohttpd/examples/logging.pp
new file mode 100644
index 0000000000..cb8e47a838
--- /dev/null
+++ b/packages/libmicrohttpd/examples/logging.pp
@@ -0,0 +1,43 @@
+(* Feel free to use this example code in any way
+ you see fit (Public Domain) *)
+
+// Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/logging.c
+
+program logging;
+
+{$mode objfpc}{$H+}
+
+uses
+ libmicrohttpd, sysutils;
+
+const
+ PORT = 8888;
+
+ function PrintOutKey(ACls: Pointer; AKind: MHD_ValueKind; AKey: Pcchar;
+ AValue: Pcchar): cint; cdecl;
+ begin
+ WriteLn(Format('%s: %s', [AKey, AValue]));
+ Result := MHD_YES;
+ end;
+
+ function AnswerToConnection(ACls: Pointer; AConnection: PMHD_Connection;
+ AUrl: Pcchar; AMethod: Pcchar; AVersion: Pcchar; AUploadData: Pcchar;
+ AUploadDataSize: Psize_t; AConCls: PPointer): cint; cdecl;
+ begin
+ WriteLn(Format('New %s request for %s using version %s',
+ [AMethod, AUrl, AVersion]));
+ MHD_get_connection_values(AConnection, MHD_HEADER_KIND, @PrintOutKey, nil);
+ Result := MHD_NO;
+ end;
+
+var
+ VDaemon: PMHD_Daemon;
+begin
+ VDaemon := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, nil, nil,
+ @AnswerToConnection, nil, MHD_OPTION_END);
+ if not Assigned(VDaemon) then
+ Halt(1);
+ ReadLn;
+ MHD_stop_daemon(VDaemon)
+end.
+
diff --git a/packages/libmicrohttpd/examples/magic.inc b/packages/libmicrohttpd/examples/magic.inc
new file mode 100644
index 0000000000..b4a5f4e9f1
--- /dev/null
+++ b/packages/libmicrohttpd/examples/magic.inc
@@ -0,0 +1,15 @@
+{$PACKRECORDS C}
+
+ magic_set = record
+ end;
+
+ magic_t = ^magic_set;
+
+const
+ LIB_NAME = 'magic';
+ MAGIC_MIME_TYPE = $000010;
+
+function magic_open(flags: cint): magic_t; cdecl; external LIB_NAME name 'magic_open';
+procedure magic_close(cookie: magic_t); cdecl; external LIB_NAME name 'magic_close';
+function magic_load(cookie: magic_t; filename: Pcchar): cint; cdecl; external LIB_NAME name 'magic_load';
+function magic_buffer(cookie: magic_t; buffer: Pointer; length: size_t): Pcchar; cdecl; external LIB_NAME name 'magic_buffer';
diff --git a/packages/libmicrohttpd/examples/minimal_example.pp b/packages/libmicrohttpd/examples/minimal_example.pp
new file mode 100644
index 0000000000..ec78f0ab77
--- /dev/null
+++ b/packages/libmicrohttpd/examples/minimal_example.pp
@@ -0,0 +1,82 @@
+(*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007 Christian Grothoff (and other contributing authors)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*)
+(**
+ * @file minimal_example.pp (Original: minimal_example.c)
+ * @brief minimal example for how to use libmicrohttpd
+ * @author Christian Grothoff / Silvio Clécio
+ *)
+
+program minimal_example;
+
+{$mode objfpc}{$H+}
+
+uses
+ sysutils, cutils, libmicrohttpd;
+
+const
+ PAGE: Pcchar = '<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>';
+
+ function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar;
+ method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; ptr: PPointer): cint; cdecl;
+ const
+ aptr: cint = 0;
+ var
+ me: Pcchar;
+ response: PMHD_Response;
+ ret: cint;
+ begin
+ me := cls;
+ if 0 <> strcomp(method, 'GET') then
+ Exit(MHD_NO);
+ if @aptr <> ptr^ then
+ begin
+ ptr^ := @aptr;
+ Exit(MHD_YES);
+ end;
+ ptr^ := nil;
+ response := MHD_create_response_from_buffer(strlen(me), Pointer(me),
+ MHD_RESPMEM_PERSISTENT);
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+var
+ d: PMHD_Daemon;
+begin
+ if argc <> 2 then
+ begin
+ WriteLn(argv[0], ' PORT');
+ Halt(1);
+ end;
+ d := MHD_start_daemon(// MHD_USE_SELECT_INTERNALLY or MHD_USE_DEBUG or MHD_USE_POLL,
+ MHD_USE_SELECT_INTERNALLY or MHD_USE_DEBUG,
+ // MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG or MHD_USE_POLL,
+ // MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG,
+ StrToInt(argv[1]),
+ nil, nil, @ahc_echo, PAGE,
+ MHD_OPTION_CONNECTION_TIMEOUT, cuint(120),
+ MHD_OPTION_END);
+ if d = nil then
+ Halt(1);
+ ReadLn;
+ MHD_stop_daemon(d);
+end.
+
diff --git a/packages/libmicrohttpd/examples/minimal_example_comet.pp b/packages/libmicrohttpd/examples/minimal_example_comet.pp
new file mode 100644
index 0000000000..292ca09a02
--- /dev/null
+++ b/packages/libmicrohttpd/examples/minimal_example_comet.pp
@@ -0,0 +1,81 @@
+(*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007, 2008 Christian Grothoff (and other contributing authors)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*)
+(**
+ * @file minimal_example.pp (original: minimal_example.c)
+ * @brief minimal example for how to generate an infinite stream with libmicrohttpd
+ * @author Christian Grothoff / Silvio Clécio / Gilson Nunes
+ *)
+
+program minimal_example_comet;
+
+{$mode objfpc}{$H+}
+
+uses
+ sysutils, cutils, libmicrohttpd;
+
+ function data_generator(cls: Pointer; pos: cuint64; buf: Pcchar;
+ max: size_t): ssize_t; cdecl;
+ begin
+ if max < 80 then
+ Exit(0);
+ memset(buf, Ord('A'), max - 1);
+ buf[79] := #10;
+ Exit(80);
+ end;
+
+ function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar;
+ method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; ptr: PPointer): cint; cdecl;
+ const
+ aptr: cint = 0;
+ var
+ response: PMHD_Response;
+ ret: cint;
+ begin
+ if 0 <> strcomp(method, 'GET') then
+ Exit(MHD_NO);
+ if @aptr <> ptr^ then
+ begin
+ ptr^ := @aptr;
+ Exit(MHD_YES);
+ end;
+ ptr^ := nil;
+ response := MHD_create_response_from_callback(UInt64(MHD_SIZE_UNKNOWN), 80,
+ @data_generator, nil, nil);
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+var
+ d: PMHD_Daemon;
+begin
+ if argc <> 2 then
+ begin
+ WriteLn(argv[0], ' PORT');
+ Halt(1);
+ end;
+ d := MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG,
+ StrToInt(argv[1]), nil, nil, @ahc_echo, nil, MHD_OPTION_END);
+ if d = nil then
+ Halt(1);
+ ReadLn;
+ MHD_stop_daemon(d);
+end.
+
diff --git a/packages/libmicrohttpd/examples/post_example.pp b/packages/libmicrohttpd/examples/post_example.pp
new file mode 100644
index 0000000000..1bb88b6e61
--- /dev/null
+++ b/packages/libmicrohttpd/examples/post_example.pp
@@ -0,0 +1,640 @@
+(*
+ This file is part of libmicrohttpd
+ Copyright (C) 2011 Christian Grothoff (and other contributing authors)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*)
+(**
+ * @file post_example.pp (Original: post_example.c)
+ * @brief example for processing POST requests using libmicrohttpd
+ * @author Christian Grothoff / Silvio Clécio
+ *)
+
+program post_example;
+
+{$mode objfpc}{$H+}
+
+uses
+ SysUtils, BaseUnix, cmem, cutils, libmicrohttpd;
+
+const
+ (**
+ * Invalid method page.
+ *)
+ METHOD_ERROR = '<html><head><title>Illegal request</title></head><body>Go away.</body></html>';
+
+ (**
+ * Invalid URL page.
+ *)
+ NOT_FOUND_ERROR = '<html><head><title>Not found</title></head><body>Go away.</body></html>';
+
+ (**
+ * Front page. (/)
+ *)
+ MAIN_PAGE = '<html><head><title>Welcome</title></head><body><form action="/2" method="post">What is your name? <input type="text" name="v1" value="%s" /><input type="submit" value="Next" /></body></html>';
+
+ (**
+ * Second page. (/2)
+ *)
+ SECOND_PAGE = '<html><head><title>Tell me more</title></head><body><a href="/">previous</a> <form action="/S" method="post">%s, what is your job? <input type="text" name="v2" value="%s" /><input type="submit" value="Next" /></body></html>';
+
+ (**
+ * Second page (/S)
+ *)
+ SUBMIT_PAGE = '<html><head><title>Ready to submit?</title></head><body><form action="/F" method="post"><a href="/2">previous </a> <input type="hidden" name="DONE" value="yes" /><input type="submit" value="Submit" /></body></html>';
+
+ (**
+ * Last page.
+ *)
+ LAST_PAGE = '<html><head><title>Thank you</title></head><body>Thank you.</body></html>';
+
+ (**
+ * Name of our cookie.
+ *)
+ COOKIE_NAME = 'session';
+
+type
+ (**
+ * State we keep for each user/session/browser.
+ *)
+ PSession = ^TSession;
+ TSession = packed record
+ (**
+ * We keep all sessions in a linked list.
+ *)
+ next: PSession;
+
+ (**
+ * Unique ID for this session.
+ *)
+ sid: array[0..33] of Char;
+
+ (**
+ * Reference counter giving the number of connections
+ * currently using this session.
+ *)
+ rc: cint;
+
+ (**
+ * Time when this session was last active.
+ *)
+ start: time_t;
+
+ (**
+ * String submitted via form.
+ *)
+ value_1: array[0..64] of Char;
+
+ (**
+ * Another value submitted via form.
+ *)
+ value_2: array[0..64] of Char;
+ end;
+
+ (**
+ * Data kept per request.
+ *)
+ TRequest = packed record
+
+ (**
+ * Associated session.
+ *)
+ session: PSession;
+
+ (**
+ * Post processor handling form data (IF this is
+ * a POST request).
+ *)
+ pp: PMHD_PostProcessor;
+
+ (**
+ * URL to serve in response to this POST (if this request
+ * was a 'POST')
+ *)
+ post_url: pcchar;
+
+ end;
+ PRequest = ^TRequest;
+
+var
+ (**
+ * Linked list of all active sessions. Yes, O(n) but a
+ * hash table would be overkill for a simple example...
+ *)
+ _sessions: PSession;
+
+ (**
+ * Return the session handle for this connection, or
+ * create one if this is a new user.
+ *)
+ function get_session(connection: PMHD_Connection): PSession;
+ var
+ ret: PSession;
+ cookie: pcchar;
+ begin
+ cookie := MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, COOKIE_NAME);
+ if cookie <> nil then
+ begin
+ (* find existing session *)
+ ret := _sessions;
+ while nil <> ret do
+ begin
+ if StrComp(cookie, ret^.sid) = 0 then
+ Break;
+ ret := ret^.next;
+ end;
+ if nil <> ret then
+ begin
+ Inc(ret^.rc);
+ Exit(ret);
+ end;
+ end;
+ (* create fresh session *)
+ ret := CAlloc(1, SizeOf(TSession));
+ if nil = ret then
+ begin
+ WriteLn(stderr, 'calloc error: ', strerror(errno^));
+ Exit(nil);
+ end;
+ (* not a super-secure way to generate a random session ID,
+ but should do for a simple example... *)
+ snprintf(ret^.sid, SizeOf(ret^.sid), '%X%X%X%X', Cardinal(rand),
+ Cardinal(rand), Cardinal(rand), Cardinal(rand));
+ Inc(ret^.rc);
+ ret^.start := FpTime;
+ ret^.next := _sessions;
+ _sessions := ret;
+ Result := ret;
+ end;
+
+(**
+ * Type of handler that generates a reply.
+ *
+ * @param cls content for the page (handler-specific)
+ * @param mime mime type to use
+ * @param session session information
+ * @param connection connection to process
+ * @param MHD_YES on success, MHD_NO on failure
+ *)
+type
+ TPageHandler = function(cls: Pointer; mime: Pcchar; session: PSession;
+ connection: PMHD_Connection): LongInt; cdecl;
+
+ (**
+ * Entry we generate for each page served.
+ *)
+ TPage = packed record
+ (**
+ * Acceptable URL for this page.
+ *)
+ url: Pcchar;
+
+ (**
+ * Mime type to set for the page.
+ *)
+ mime: Pcchar;
+
+ (**
+ * Handler to call to generate response.
+ *)
+ handler: TPageHandler;
+
+ (**
+ * Extra argument to handler.
+ *)
+ handler_cls: Pcchar;
+ end;
+
+ (**
+ * Add header to response to set a session cookie.
+ *
+ * @param session session to use
+ * @param response response to modify
+ *)
+ procedure add_session_cookie(session: PSession; response: PMHD_Response);
+ var
+ cstr: array[0..256] of Char;
+ begin
+ snprintf(cstr, SizeOf(cstr), '%s=%s', COOKIE_NAME, session^.sid);
+ if MHD_NO =
+ MHD_add_response_header(response, MHD_HTTP_HEADER_SET_COOKIE, cstr) then
+ WriteLn(stderr, 'Failed to set session cookie header!');
+ end;
+
+ (**
+ * Handler that returns a simple static HTTP page that
+ * is passed in via 'cls'.
+ *
+ * @param cls a 'const char *' with the HTML webpage to return
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ *)
+ function serve_simple_form(cls: Pointer; mime: Pcchar; session: PSession;
+ connection: PMHD_Connection): cint; cdecl;
+ var
+ ret: cint;
+ form: Pcchar;
+ response: PMHD_Response;
+ begin
+ form := cls;
+ (* return static form *)
+ response := MHD_create_response_from_buffer(Length(form), Pointer(form),
+ MHD_RESPMEM_PERSISTENT);
+ add_session_cookie(session, response);
+ MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, mime);
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+ (**
+ * Handler that adds the 'v1' value to the given HTML code.
+ *
+ * @param cls unused
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ *)
+ function fill_v1_form(cls: Pointer; mime: Pcchar; session: PSession;
+ connection: PMHD_Connection): cint; cdecl;
+ var
+ ret: cint;
+ reply: Pcchar;
+ response: PMHD_Response;
+ begin
+ reply := Malloc(strlen(MAIN_PAGE) + strlen(session^.value_1) + 1);
+ if nil = reply then
+ Exit(MHD_NO);
+ snprintf (reply, strlen(MAIN_PAGE) + strlen(session^.value_1) + 1,
+ MAIN_PAGE, session^.value_1);
+ (* return static form *)
+ response := MHD_create_response_from_buffer (strlen(reply), Pointer(reply),
+ MHD_RESPMEM_MUST_FREE);
+ if nil = response then
+ Exit(MHD_NO);
+ add_session_cookie(session, response);
+ MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, mime);
+ ret := MHD_queue_response (connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+ (**
+ * Handler that adds the 'v1' and 'v2' values to the given HTML code.
+ *
+ * @param cls unused
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ *)
+ function fill_v1_v2_form(cls: Pointer; mime: Pcchar; session: PSession;
+ connection: PMHD_Connection): cint; cdecl;
+ var
+ ret: cint;
+ reply: Pcchar;
+ response: PMHD_Response;
+ begin
+ reply := Malloc(strlen(SECOND_PAGE) + strlen(session^.value_1) +
+ strlen(session^.value_2) + 1);
+ if nil = reply then
+ Exit(MHD_NO);
+ snprintf(reply, strlen(SECOND_PAGE) + strlen(session^.value_1) +
+ strlen(session^.value_2) + 1, SECOND_PAGE, session^.value_1,
+ session^.value_2);
+ (* return static form *)
+ response := MHD_create_response_from_buffer(strlen(reply), Pointer(reply),
+ MHD_RESPMEM_MUST_FREE);
+ if nil = response then
+ Exit(MHD_NO);
+ add_session_cookie(session, response);
+ MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, mime);
+ ret := MHD_queue_response (connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+ (**
+ * Handler used to generate a 404 reply.
+ *
+ * @param cls a 'const char *' with the HTML webpage to return
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ *)
+ function not_found_page(cls: Pointer; mime: Pcchar; session: PSession;
+ connection: PMHD_Connection): cint; cdecl;
+ var
+ ret: cint;
+ response: PMHD_Response;
+ begin
+ (* unsupported HTTP method *)
+ response := MHD_create_response_from_buffer(Length(NOT_FOUND_ERROR),
+ Pcchar(NOT_FOUND_ERROR), MHD_RESPMEM_PERSISTENT);
+ ret := MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
+ MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, mime);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+const
+ (**
+ * List of all pages served by this HTTP server.
+ *)
+ pages: array[0..4] of TPage = (
+ (url: '/'; mime: 'text/html'; handler: @fill_v1_form; handler_cls: nil),
+ (url: '/2'; mime: 'text/html'; handler: @fill_v1_v2_form; handler_cls: nil),
+ (url: '/S'; mime: 'text/html'; handler: @serve_simple_form; handler_cls: SUBMIT_PAGE),
+ (url: '/F'; mime: 'text/html'; handler: @serve_simple_form; handler_cls: LAST_PAGE),
+ (url: nil; mime: nil; handler: @not_found_page; handler_cls: nil) (* 404 *)
+ );
+
+ (**
+ * Iterator over key-value pairs where the value
+ * maybe made available in increments and/or may
+ * not be zero-terminated. Used for processing
+ * POST data.
+ *
+ * @param cls user-specified closure
+ * @param kind type of the value
+ * @param key 0-terminated key for the value
+ * @param filename name of the uploaded file, NULL if not known
+ * @param content_type mime-type of the data, NULL if not known
+ * @param transfer_encoding encoding of the data, NULL if not known
+ * @param data pointer to size bytes of data at the
+ * specified offset
+ * @param off offset of data in the overall value
+ * @param size number of bytes in data available
+ * @return MHD_YES to continue iterating,
+ * MHD_NO to abort the iteration
+ *)
+ function post_iterator(cls: Pointer; kind: MHD_ValueKind; key: Pcchar;
+ filename: Pcchar; content_type: Pcchar; transfer_encoding: Pcchar;
+ data: Pcchar; off: cuint64; size: size_t): cint; cdecl;
+ var
+ request: PRequest;
+ session: PSession;
+ begin
+ request := cls;
+ session := request^.session;
+ if StrComp('DONE', key) = 0 then
+ begin
+ WriteLn(stdout, Format('Session `%s'' submitted `%s'', `%s''', [
+ session^.sid, session^.value_1, session^.value_2]));
+ Exit(MHD_YES);
+ end;
+ if StrComp('v1', key) = 0 then
+ begin
+ if (size + off) > SizeOf(session^.value_1) then
+ size := SizeOf(session^.value_1) - off - 1;
+ Move(data^, session^.value_1[off], size);
+ if (size + off) < SizeOf(session^.value_1) then
+ session^.value_1[size + off] := #0;
+ Exit(MHD_YES);
+ end;
+ if StrComp('v2', key) = 0 then
+ begin
+ if (size + off) > SizeOf(session^.value_2) then
+ size := SizeOf(session^.value_2) - off - 1;
+ Move(data^, session^.value_2[off], size);
+ if (size + off) < SizeOf(session^.value_2) then
+ session^.value_2[size + off] := #0;
+ Exit(MHD_YES);
+ end;
+ WriteLn(stderr, Format('Unsupported form value `%s''', [key]));
+ Result := MHD_YES;
+ end;
+
+ (**
+ * Main MHD callback for handling requests.
+ *
+ *
+ * @param cls argument given together with the function
+ * pointer when the handler was registered with MHD
+ * @param connection handle to connection which is being processed
+ * @param url the requested url
+ * @param method the HTTP method used ("GET", "PUT", etc.)
+ * @param version the HTTP version string (i.e. "HTTP/1.1")
+ * @param upload_data the data being uploaded (excluding HEADERS,
+ * for a POST that fits into memory and that is encoded
+ * with a supported encoding, the POST data will NOT be
+ * given in upload_data and is instead available as
+ * part of MHD_get_connection_values; very large POST
+ * data *will* be made available incrementally in
+ * upload_data)
+ * @param upload_data_size set initially to the size of the
+ * upload_data provided; the method must update this
+ * value to the number of bytes NOT processed;
+ * @param ptr pointer that the callback can set to some
+ * address and that will be preserved by MHD for future
+ * calls for this request; since the access handler may
+ * be called many times (i.e., for a PUT/POST operation
+ * with plenty of upload data) this allows the application
+ * to easily associate some request-specific state.
+ * If necessary, this state can be cleaned up in the
+ * global "MHD_RequestCompleted" callback (which
+ * can be set with the MHD_OPTION_NOTIFY_COMPLETED).
+ * Initially, <tt>*con_cls</tt> will be NULL.
+ * @return MHS_YES if the connection was handled successfully,
+ * MHS_NO if the socket must be closed due to a serios
+ * error while handling the request
+ *)
+ function create_response(cls: Pointer; connection: PMHD_Connection;
+ url: Pcchar; method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; ptr: PPointer): cint; cdecl;
+ var
+ response: PMHD_Response;
+ request: PRequest;
+ session: PSession;
+ ret: cint;
+ i: Cardinal;
+ begin
+ request := ptr^;
+ if nil = request then
+ begin
+ request := CAlloc(1, SizeOf(TRequest));
+ if nil = request then
+ begin
+ WriteLn(stderr, 'calloc error: ', strerror(errno^));
+ Exit(MHD_NO);
+ end;
+ ptr^ := request;
+ if StrComp(method, MHD_HTTP_METHOD_POST) = 0 then
+ begin
+ request^.pp := MHD_create_post_processor(connection, 1024,
+ @post_iterator, request);
+ if nil = request^.pp then
+ begin
+ WriteLn(stderr, Format('Failed to setup post processor for `%s''',
+ [url]));
+ Exit(MHD_NO); (* internal error *)
+ end;
+ end;
+ Exit(MHD_YES);
+ end;
+ if nil = request^.session then
+ begin
+ request^.session := get_session(connection);
+ if nil = request^.session then
+ begin
+ WriteLn(stderr, Format('Failed to setup session for `%s''', [url]));
+ Exit(MHD_NO); (* internal error *)
+ end;
+ end;
+ session := request^.session;
+ session^.start := FpTime;
+ if StrComp(method, MHD_HTTP_METHOD_POST) = 0 then
+ begin
+ (* evaluate POST data *)
+ MHD_post_process(request^.pp, upload_data, upload_data_size^);
+ if upload_data_size^ <> 0 then
+ begin
+ upload_data_size^ := 0;
+ Exit(MHD_YES);
+ end;
+ (* done with POST data, serve response *)
+ MHD_destroy_post_processor(request^.pp);
+ request^.pp := nil;
+ method := MHD_HTTP_METHOD_GET; (* fake 'GET' *)
+ if nil <> request^.post_url then
+ url := request^.post_url;
+ end;
+ if (StrComp(method, MHD_HTTP_METHOD_GET) = 0) or
+ (StrComp(method, MHD_HTTP_METHOD_HEAD) = 0) then
+ begin
+ (* find out which page to serve *)
+ i := 0;
+ while (pages[i].url <> nil) and (StrComp(pages[i].url, url) <> 0) do
+ Inc(i);
+ ret := pages[i].handler(pages[i].handler_cls, pages[i].mime, session,
+ connection);
+ if ret <> MHD_YES then
+ WriteLn(stderr, Format('Failed to create page for `%s''', [url]));
+ Exit(ret);
+ end;
+ (* unsupported HTTP method *)
+ response := MHD_create_response_from_buffer(Length(METHOD_ERROR),
+ Pcchar(METHOD_ERROR), MHD_RESPMEM_PERSISTENT);
+ ret := MHD_queue_response(connection, MHD_HTTP_NOT_ACCEPTABLE, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+ (**
+ * Callback called upon completion of a request.
+ * Decrements session reference counter.
+ *
+ * @param cls not used
+ * @param connection connection that completed
+ * @param con_cls session handle
+ * @param toe status code
+ *)
+ procedure request_completed_callback(cls: Pointer; connection: PMHD_Connection;
+ con_cls: PPointer; toe: MHD_RequestTerminationCode);
+ var
+ request: PRequest;
+ begin
+ request := con_cls^;
+ if nil = request then
+ Exit;
+ if nil <> request^.session then
+ Dec(request^.session^.rc);
+ if nil <> request^.pp then
+ MHD_destroy_post_processor(request^.pp);
+ Free(request);
+ end;
+
+ (**
+ * Clean up handles of sessions that have been idle for
+ * too long.
+ *)
+ procedure expire_sessions;
+ var
+ pos: PSession;
+ prev: PSession;
+ next: PSession;
+ now: time_t;
+ begin
+ now := FpTime;
+ prev := nil;
+ pos := _sessions;
+ while nil <> pos do
+ begin
+ next := pos^.next;
+ if (now - pos^.start) > (60 * 60) then
+ begin
+ (* expire sessions after 1h *)
+ if nil = prev then
+ _sessions := pos^.next
+ else
+ prev^.next := next;
+ Free(pos);
+ end
+ else
+ prev := pos;
+ pos := next;
+ end;
+ end;
+
+(**
+ * Call with the port number as the only argument.
+ * Never terminates (other than by signals, such as CTRL-C).
+ *)
+var
+ d: PMHD_Daemon;
+ tv: timeval;
+ tvp: ptimeval;
+ rs: TFDSet;
+ ws: TFDSet;
+ es: TFDSet;
+ max: cint;
+ mhd_timeout: MHD_UNSIGNED_LONG_LONG;
+begin
+ if argc <> 2 then
+ begin
+ WriteLn(argv[0], ' PORT');
+ Halt(1);
+ end;
+ (* initialize PRNG *)
+ Randomize;
+
+ d := MHD_start_daemon(MHD_USE_DEBUG, StrToInt(argv[1]), nil, nil,
+ @create_response, nil, MHD_OPTION_CONNECTION_TIMEOUT, cuint(15),
+ MHD_OPTION_NOTIFY_COMPLETED, @request_completed_callback, nil, MHD_OPTION_END);
+ if nil = d then
+ Halt(1);
+
+ while True do
+ begin
+ expire_sessions;
+ max := 0;
+ fpFD_ZERO(rs);
+ fpFD_ZERO(ws);
+ fpFD_ZERO(es);
+ if MHD_YES <> MHD_get_fdset(d, @rs, @ws, @es, @max) then
+ Break; (* fatal internal error *)
+ if MHD_get_timeout(d, @mhd_timeout) = MHD_YES then
+ begin
+ tv.tv_sec := mhd_timeout div 1000;
+ tv.tv_usec := (mhd_timeout - (tv.tv_sec * 1000)) * 1000;
+ tvp := @tv;
+ end
+ else
+ tvp := nil;
+ fpSelect(max + 1, @rs, @ws, @es, tvp);
+ MHD_run(d);
+ end;
+ MHD_stop_daemon(d);
+end.
diff --git a/packages/libmicrohttpd/examples/querystring_example.pp b/packages/libmicrohttpd/examples/querystring_example.pp
new file mode 100644
index 0000000000..e95f4e96d9
--- /dev/null
+++ b/packages/libmicrohttpd/examples/querystring_example.pp
@@ -0,0 +1,89 @@
+(*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007, 2008 Christian Grothoff (and other contributing authors)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*)
+(**
+ * @file querystring_example.pp (Original: querystring_example.c)
+ * @brief example for how to get the query string from libmicrohttpd
+ * Call with an URI ending with something like "?q=QUERY"
+ * @author Christian Grothoff / Silvio Clécio
+ *)
+
+program querystring_example;
+
+{$mode objfpc}{$H+}
+
+uses
+ sysutils, cmem, ctypes, cutils, libmicrohttpd;
+
+const
+ PAGE: Pcchar = '<html><head><title>libmicrohttpd demo</title></head><body>Query string for &quot;%s&quot; was &quot;%s&quot;</body></html>';
+
+ function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar;
+ method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; ptr: PPointer): cint; cdecl;
+ const
+ aptr: cint = 0;
+ var
+ fmt: Pcchar;
+ val: Pcchar;
+ me: Pcchar;
+ response: PMHD_Response;
+ ret: cint;
+ begin
+ fmt := cls;
+ if 0 <> strcomp(method, 'GET') then
+ Exit(MHD_NO);
+ if @aptr <> ptr^ then
+ begin
+ ptr^ := @aptr;
+ Exit(MHD_YES);
+ end;
+ ptr^ := nil;
+ val := MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, 'q');
+ me := Malloc(snprintf(nil, 0, fmt, Pcchar('q'), val) + 1);
+ if me = nil then
+ Exit(MHD_NO);
+ sprintf(me, fmt, Pcchar('q'), val);
+ response := MHD_create_response_from_buffer(strlen(me), Pointer(me),
+ MHD_RESPMEM_MUST_FREE);
+ if response = nil then
+ begin
+ Free(me);
+ Exit(MHD_NO);
+ end;
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+var
+ d: PMHD_Daemon;
+begin
+ if argc <> 2 then
+ begin
+ WriteLn(argv[0], ' PORT');
+ Halt(1);
+ end;
+ d := MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG,
+ StrToInt(argv[1]), nil, nil, @ahc_echo, PAGE, MHD_OPTION_END);
+ if d = nil then
+ Halt(1);
+ ReadLn;
+ MHD_stop_daemon(d);
+end.
+
diff --git a/packages/libmicrohttpd/examples/refuse_post_example.pp b/packages/libmicrohttpd/examples/refuse_post_example.pp
new file mode 100644
index 0000000000..1bad4f6f64
--- /dev/null
+++ b/packages/libmicrohttpd/examples/refuse_post_example.pp
@@ -0,0 +1,94 @@
+(*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007, 2008 Christian Grothoff (and other contributing authors)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*)
+(**
+ * @file refuse_post_example.pp (Original: refuse_post_example.c)
+ * @brief example for how to refuse a POST request properly
+ * @author Christian Grothoff, Sebastian Gerhardt and Silvio Clécio
+ *)
+
+program refuse_post_example;
+
+{$mode objfpc}{$H+}
+
+uses
+ sysutils, libmicrohttpd;
+
+const
+ askpage: Pcchar =
+ '<html><body>'#10+
+ 'Upload a file, please!<br>'#10+
+ '<form action="/filepost" method="post" enctype="multipart/form-data">'#10+
+ '<input name="file" type="file">'#10+
+ '<input type="submit" value=" Send "></form>'#10+
+ '</body></html>';
+
+ BUSYPAGE: Pcchar = '<html><head><title>Webserver busy</title></head><body>We are too busy to process POSTs right now.</body></html>';
+
+ function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar;
+ method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; ptr: PPointer): cint; cdecl;
+ const
+ aptr: cint = 0;
+ var
+ me: Pcchar;
+ response: PMHD_Response;
+ ret: cint;
+ begin
+ me := cls;
+ if (0 <> strcomp(method, 'GET')) and (0 <> strcomp(method, 'POST')) then
+ Exit(MHD_NO); (* unexpected method *)
+ if @aptr <> ptr^ then
+ begin
+ ptr^ := @aptr;
+ (* always to busy for POST requests *)
+ if 0 = strcomp(method, 'POST') then
+ begin
+ response := MHD_create_response_from_buffer(strlen(BUSYPAGE),
+ Pointer(BUSYPAGE), MHD_RESPMEM_PERSISTENT);
+ ret := MHD_queue_response (connection, MHD_HTTP_SERVICE_UNAVAILABLE,
+ response);
+ MHD_destroy_response (response);
+ Exit(ret);
+ end;
+ end;
+ ptr^ := nil; (* reset when done *)
+ response := MHD_create_response_from_buffer(strlen(me), Pointer(me),
+ MHD_RESPMEM_PERSISTENT);
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+var
+ d: PMHD_Daemon;
+begin
+ if argc <> 2 then
+ begin
+ WriteLn(argv[0], ' PORT');
+ Halt(1);
+ end;
+ d := MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG,
+ StrToInt(argv[1]), nil, nil, @ahc_echo, Pointer(askpage),
+ MHD_OPTION_END);
+ if d = nil then
+ Halt(1);
+ ReadLn;
+ MHD_stop_daemon(d);
+end.
+
diff --git a/packages/libmicrohttpd/examples/responseheaders.pp b/packages/libmicrohttpd/examples/responseheaders.pp
new file mode 100644
index 0000000000..e7fc0ca566
--- /dev/null
+++ b/packages/libmicrohttpd/examples/responseheaders.pp
@@ -0,0 +1,66 @@
+(* Feel free to use this example code in any way
+ you see fit (Public Domain) *)
+
+// Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/responseheaders.c
+
+program responseheaders;
+
+{$mode objfpc}{$H+}
+
+uses
+ BaseUnix, SysUtils, libmicrohttpd;
+
+const
+ PORT = 8888;
+ FILENAME = 'picture.png';
+ MIMETYPE = 'image/png';
+
+ function AnswerToConnection(ACls: Pointer; AConnection: PMHD_Connection;
+ AUrl: Pcchar; AMethod: Pcchar; AVersion: Pcchar; AUploadData: Pcchar;
+ AUploadDataSize: Psize_t; AConCls: PPointer): cint; cdecl;
+ const
+ errorstr: Pcchar = '<html><body>An internal server error has occured!</body></html>';
+ var
+ VFd: cint;
+ VReturn: cint;
+ VResponse: PMHD_Response;
+ VSbuf: TStat;
+ begin
+ if StrComp(AMethod, 'GET') <> 0 then
+ Exit(MHD_NO);
+ VFd := FpOpen(FILENAME, O_RDONLY);
+ VSbuf := Default(TStat);
+ if (VFd = -1) or (FpFStat(VFd, VSbuf) <> 0) then
+ begin
+ (* error accessing file *)
+ if VFd <> -1 then
+ FpClose(VFd);
+ VResponse := MHD_create_response_from_buffer(Length(errorstr),
+ Pointer(errorstr), MHD_RESPMEM_PERSISTENT);
+ if Assigned(VResponse) then
+ begin
+ VReturn := MHD_queue_response(AConnection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR, VResponse);
+ MHD_destroy_response(VResponse);
+ Exit(VReturn);
+ end
+ else
+ Exit(MHD_NO);
+ end;
+ VResponse := MHD_create_response_from_fd_at_offset64(VSbuf.st_size, VFd, 0);
+ MHD_add_response_header(VResponse, 'Content-Type', MIMETYPE);
+ VReturn := MHD_queue_response(AConnection, MHD_HTTP_OK, VResponse);
+ MHD_destroy_response(VResponse);
+ Result := VReturn;
+ end;
+
+var
+ VDaemon: PMHD_Daemon;
+begin
+ VDaemon := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, nil,
+ nil, @AnswerToConnection, nil, MHD_OPTION_END);
+ if not Assigned(VDaemon) then
+ Halt(1);
+ ReadLn;
+ MHD_stop_daemon(VDaemon);
+end.
diff --git a/packages/libmicrohttpd/examples/sessions.pp b/packages/libmicrohttpd/examples/sessions.pp
new file mode 100644
index 0000000000..0539b6703b
--- /dev/null
+++ b/packages/libmicrohttpd/examples/sessions.pp
@@ -0,0 +1,623 @@
+(* Feel free to use this example code in any way
+ you see fit (Public Domain) *)
+
+// Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/sessions.c
+
+program sessions;
+
+{$mode objfpc}{$H+}
+
+uses
+ SysUtils, BaseUnix, cmem, cutils, libmicrohttpd;
+
+const
+ (**
+ * Invalid method page.
+ *)
+ METHOD_ERROR = '<html><head><title>Illegal request</title></head><body>Go away.</body></html>';
+
+ (**
+ * Invalid URL page.
+ *)
+ NOT_FOUND_ERROR = '<html><head><title>Not found</title></head><body>Go away.</body></html>';
+
+ (**
+ * Front page. (/)
+ *)
+ MAIN_PAGE = '<html><head><title>Welcome</title></head><body><form action="/2" method="post">What is your name? <input type="text" name="v1" value="%s" /><input type="submit" value="Next" /></body></html>';
+
+ (**
+ * Second page. (/2)
+ *)
+ SECOND_PAGE = '<html><head><title>Tell me more</title></head><body><a href="/">previous</a> <form action="/S" method="post">%s, what is your job? <input type="text" name="v2" value="%s" /><input type="submit" value="Next" /></body></html>';
+
+ (**
+ * Second page (/S)
+ *)
+ SUBMIT_PAGE = '<html><head><title>Ready to submit?</title></head><body><form action="/F" method="post"><a href="/2">previous </a> <input type="hidden" name="DONE" value="yes" /><input type="submit" value="Submit" /></body></html>';
+
+ (**
+ * Last page.
+ *)
+ LAST_PAGE = '<html><head><title>Thank you</title></head><body>Thank you.</body></html>';
+
+ (**
+ * Name of our cookie.
+ *)
+ COOKIE_NAME = 'session';
+
+type
+ (**
+ * State we keep for each user/session/browser.
+ *)
+ PSession = ^TSession;
+ TSession = packed record
+ (**
+ * We keep all sessions in a linked list.
+ *)
+ next: PSession;
+
+ (**
+ * Unique ID for this session.
+ *)
+ sid: array[0..33] of Char;
+
+ (**
+ * Reference counter giving the number of connections
+ * currently using this session.
+ *)
+ rc: cint;
+
+ (**
+ * Time when this session was last active.
+ *)
+ start: time_t;
+
+ (**
+ * String submitted via form.
+ *)
+ value_1: array[0..64] of Char;
+
+ (**
+ * Another value submitted via form.
+ *)
+ value_2: array[0..64] of Char;
+ end;
+
+ (**
+ * Data kept per request.
+ *)
+ TRequest = packed record
+
+ (**
+ * Associated session.
+ *)
+ session: PSession;
+
+ (**
+ * Post processor handling form data (IF this is
+ * a POST request).
+ *)
+ pp: PMHD_PostProcessor;
+
+ (**
+ * URL to serve in response to this POST (if this request
+ * was a 'POST')
+ *)
+ post_url: pcchar;
+
+ end;
+ PRequest = ^TRequest;
+
+var
+ (**
+ * Linked list of all active sessions. Yes, O(n) but a
+ * hash table would be overkill for a simple example...
+ *)
+ _sessions: PSession;
+
+ (**
+ * Return the session handle for this connection, or
+ * create one if this is a new user.
+ *)
+ function get_session(connection: PMHD_Connection): PSession;
+ var
+ ret: PSession;
+ cookie: pcchar;
+ begin
+ cookie := MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, COOKIE_NAME);
+ if cookie <> nil then
+ begin
+ (* find existing session *)
+ ret := _sessions;
+ while nil <> ret do
+ begin
+ if StrComp(cookie, ret^.sid) = 0 then
+ Break;
+ ret := ret^.next;
+ end;
+ if nil <> ret then
+ begin
+ Inc(ret^.rc);
+ Exit(ret);
+ end;
+ end;
+ (* create fresh session *)
+ ret := CAlloc(1, SizeOf(TSession));
+ if nil = ret then
+ begin
+ WriteLn(stderr, 'calloc error: ', strerror(errno^));
+ Exit(nil);
+ end;
+ (* not a super-secure way to generate a random session ID,
+ but should do for a simple example... *)
+ snprintf(ret^.sid, SizeOf(ret^.sid), '%X%X%X%X', Cardinal(rand),
+ Cardinal(rand), Cardinal(rand), Cardinal(rand));
+ Inc(ret^.rc);
+ ret^.start := FpTime;
+ ret^.next := _sessions;
+ _sessions := ret;
+ Result := ret;
+ end;
+
+(**
+ * Type of handler that generates a reply.
+ *
+ * @param cls content for the page (handler-specific)
+ * @param mime mime type to use
+ * @param session session information
+ * @param connection connection to process
+ * @param MHD_YES on success, MHD_NO on failure
+ *)
+type
+ TPageHandler = function(cls: Pointer; mime: Pcchar; session: PSession;
+ connection: PMHD_Connection): LongInt; cdecl;
+
+ (**
+ * Entry we generate for each page served.
+ *)
+
+ { TPage }
+
+ TPage = packed record
+ (**
+ * Acceptable URL for this page.
+ *)
+ url: Pcchar;
+
+ (**
+ * Mime type to set for the page.
+ *)
+ mime: Pcchar;
+
+ (**
+ * Handler to call to generate response.
+ *)
+ handler: TPageHandler;
+
+ (**
+ * Extra argument to handler.
+ *)
+ handler_cls: Pcchar;
+ end;
+
+ (**
+ * Add header to response to set a session cookie.
+ *
+ * @param session session to use
+ * @param response response to modify
+ *)
+ procedure add_session_cookie(session: PSession; response: PMHD_Response);
+ var
+ cstr: array[0..256] of Char;
+ begin
+ snprintf(cstr, SizeOf(cstr), '%s=%s', COOKIE_NAME, session^.sid);
+ if MHD_NO =
+ MHD_add_response_header(response, MHD_HTTP_HEADER_SET_COOKIE, cstr) then
+ WriteLn(stderr, 'Failed to set session cookie header!');
+ end;
+
+ (**
+ * Handler that returns a simple static HTTP page that
+ * is passed in via 'cls'.
+ *
+ * @param cls a 'const char *' with the HTML webpage to return
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ *)
+ function serve_simple_form(cls: Pointer; mime: Pcchar; session: PSession;
+ connection: PMHD_Connection): cint; cdecl;
+ var
+ ret: cint;
+ form: Pcchar;
+ response: PMHD_Response;
+ begin
+ form := cls;
+ (* return static form *)
+ response := MHD_create_response_from_buffer(Length(form), Pointer(form),
+ MHD_RESPMEM_PERSISTENT);
+ add_session_cookie(session, response);
+ MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, mime);
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+ (**
+ * Handler that adds the 'v1' value to the given HTML code.
+ *
+ * @param cls a 'const char *' with the HTML webpage to return
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ *)
+ function fill_v1_form(cls: Pointer; mime: Pcchar; session: PSession;
+ connection: PMHD_Connection): cint; cdecl;
+ var
+ ret: cint;
+ form: Pcchar;
+ reply: Pcchar;
+ response: PMHD_Response;
+ begin
+ form := cls;
+ if asprintf(@reply, form, session^.value_1) = -1 then
+ (* oops *)
+ Exit(MHD_NO);
+ (* return static form *)
+ response := MHD_create_response_from_buffer(Length(reply), Pointer(reply),
+ MHD_RESPMEM_MUST_FREE);
+ add_session_cookie(session, response);
+ MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, mime);
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+ (**
+ * Handler that adds the 'v1' and 'v2' values to the given HTML code.
+ *
+ * @param cls a 'const char *' with the HTML webpage to return
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ *)
+ function fill_v1_v2_form(cls: Pointer; mime: Pcchar; session: PSession;
+ connection: PMHD_Connection): cint; cdecl;
+ var
+ ret: cint;
+ form: Pcchar;
+ reply: Pcchar;
+ response: PMHD_Response;
+ begin
+ form := cls;
+ if asprintf(@reply, form, session^.value_1, session^.value_2) = -1 then
+ (* oops *)
+ Exit(MHD_NO);
+ (* return static form *)
+ response := MHD_create_response_from_buffer(Length(reply), Pointer(reply),
+ MHD_RESPMEM_MUST_FREE);
+ add_session_cookie(session, response);
+ MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, mime);
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+ (**
+ * Handler used to generate a 404 reply.
+ *
+ * @param cls a 'const char *' with the HTML webpage to return
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ *)
+ function not_found_page(cls: Pointer; mime: Pcchar; session: PSession;
+ connection: PMHD_Connection): cint; cdecl;
+ var
+ ret: cint;
+ response: PMHD_Response;
+ begin
+ (* unsupported HTTP method *)
+ response := MHD_create_response_from_buffer(Length(NOT_FOUND_ERROR),
+ Pcchar(NOT_FOUND_ERROR), MHD_RESPMEM_PERSISTENT);
+ ret := MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
+ MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, mime);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+const
+ (**
+ * List of all pages served by this HTTP server.
+ *)
+ pages: array[0..4] of TPage = (
+ (url: '/'; mime: 'text/html'; handler: @fill_v1_form; handler_cls: MAIN_PAGE),
+ (url: '/2'; mime: 'text/html'; handler: @fill_v1_v2_form; handler_cls: SECOND_PAGE),
+ (url: '/S'; mime: 'text/html'; handler: @serve_simple_form; handler_cls: SUBMIT_PAGE),
+ (url: '/F'; mime: 'text/html'; handler: @serve_simple_form; handler_cls: LAST_PAGE),
+ (url: nil; mime: nil; handler: @not_found_page; handler_cls: nil) (* 404 *)
+ );
+
+ (**
+ * Iterator over key-value pairs where the value
+ * maybe made available in increments and/or may
+ * not be zero-terminated. Used for processing
+ * POST data.
+ *
+ * @param cls user-specified closure
+ * @param kind type of the value
+ * @param key 0-terminated key for the value
+ * @param filename name of the uploaded file, NULL if not known
+ * @param content_type mime-type of the data, NULL if not known
+ * @param transfer_encoding encoding of the data, NULL if not known
+ * @param data pointer to size bytes of data at the
+ * specified offset
+ * @param off offset of data in the overall value
+ * @param size number of bytes in data available
+ * @return MHD_YES to continue iterating,
+ * MHD_NO to abort the iteration
+ *)
+ function post_iterator(cls: Pointer; kind: MHD_ValueKind; key: Pcchar;
+ filename: Pcchar; content_type: Pcchar; transfer_encoding: Pcchar;
+ data: Pcchar; off: cuint64; size: size_t): cint; cdecl;
+ var
+ request: PRequest;
+ session: PSession;
+ begin
+ request := cls;
+ session := request^.session;
+ if StrComp('DONE', key) = 0 then
+ begin
+ WriteLn(stdout, Format('Session `%s'' submitted `%s'', `%s''', [
+ session^.sid, session^.value_1, session^.value_2]));
+ Exit(MHD_YES);
+ end;
+ if StrComp('v1', key) = 0 then
+ begin
+ if (size + off) > SizeOf(session^.value_1) then
+ size := SizeOf(session^.value_1) - off;
+ Move(data^, session^.value_1[off], size);
+ if (size + off) < SizeOf(session^.value_1) then
+ session^.value_1[size + off] := #0;
+ Exit(MHD_YES);
+ end;
+ if StrComp('v2', key) = 0 then
+ begin
+ if (size + off) > SizeOf(session^.value_2) then
+ size := SizeOf(session^.value_2) - off;
+ Move(data^, session^.value_2[off], size);
+ if (size + off) < SizeOf(session^.value_2) then
+ session^.value_2[size + off] := #0;
+ Exit(MHD_YES);
+ end;
+ WriteLn(stderr, Format('Unsupported form value `%s''', [key]));
+ Result := MHD_YES;
+ end;
+
+ (**
+ * Main MHD callback for handling requests.
+ *
+ *
+ * @param cls argument given together with the function
+ * pointer when the handler was registered with MHD
+ * @param connection handle to connection which is being processed
+ * @param url the requested url
+ * @param method the HTTP method used ("GET", "PUT", etc.)
+ * @param version the HTTP version string (i.e. "HTTP/1.1")
+ * @param upload_data the data being uploaded (excluding HEADERS,
+ * for a POST that fits into memory and that is encoded
+ * with a supported encoding, the POST data will NOT be
+ * given in upload_data and is instead available as
+ * part of MHD_get_connection_values; very large POST
+ * data *will* be made available incrementally in
+ * upload_data)
+ * @param upload_data_size set initially to the size of the
+ * upload_data provided; the method must update this
+ * value to the number of bytes NOT processed;
+ * @param ptr pointer that the callback can set to some
+ * address and that will be preserved by MHD for future
+ * calls for this request; since the access handler may
+ * be called many times (i.e., for a PUT/POST operation
+ * with plenty of upload data) this allows the application
+ * to easily associate some request-specific state.
+ * If necessary, this state can be cleaned up in the
+ * global "MHD_RequestCompleted" callback (which
+ * can be set with the MHD_OPTION_NOTIFY_COMPLETED).
+ * Initially, <tt>*con_cls</tt> will be NULL.
+ * @return MHS_YES if the connection was handled successfully,
+ * MHS_NO if the socket must be closed due to a serios
+ * error while handling the request
+ *)
+ function create_response(cls: Pointer; connection: PMHD_Connection;
+ url: Pcchar; method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; ptr: PPointer): cint; cdecl;
+ var
+ response: PMHD_Response;
+ request: PRequest;
+ session: PSession;
+ ret: cint;
+ i: Cardinal;
+ begin
+ request := ptr^;
+ if nil = request then
+ begin
+ request := CAlloc(1, SizeOf(TRequest));
+ if nil = request then
+ begin
+ WriteLn(stderr, 'calloc error: ', strerror(errno^));
+ Exit(MHD_NO);
+ end;
+ ptr^ := request;
+ if StrComp(method, MHD_HTTP_METHOD_POST) = 0 then
+ begin
+ request^.pp := MHD_create_post_processor(connection, 1024,
+ @post_iterator, request);
+ if nil = request^.pp then
+ begin
+ WriteLn(stderr, Format('Failed to setup post processor for `%s''',
+ [url]));
+ Exit(MHD_NO); (* internal error *)
+ end;
+ end;
+ Exit(MHD_YES);
+ end;
+ if nil = request^.session then
+ begin
+ request^.session := get_session(connection);
+ if nil = request^.session then
+ begin
+ WriteLn(stderr, Format('Failed to setup session for `%s''', [url]));
+ Exit(MHD_NO); (* internal error *)
+ end;
+ end;
+ session := request^.session;
+ session^.start := FpTime;
+ if StrComp(method, MHD_HTTP_METHOD_POST) = 0 then
+ begin
+ (* evaluate POST data *)
+ MHD_post_process(request^.pp, upload_data, upload_data_size^);
+ if upload_data_size^ <> 0 then
+ begin
+ upload_data_size^ := 0;
+ Exit(MHD_YES);
+ end;
+ (* done with POST data, serve response *)
+ MHD_destroy_post_processor(request^.pp);
+ request^.pp := nil;
+ method := MHD_HTTP_METHOD_GET; (* fake 'GET' *)
+ if nil <> request^.post_url then
+ url := request^.post_url;
+ end;
+ if (StrComp(method, MHD_HTTP_METHOD_GET) = 0) or
+ (StrComp(method, MHD_HTTP_METHOD_HEAD) = 0) then
+ begin
+ (* find out which page to serve *)
+ i := 0;
+ while (pages[i].url <> nil) and (StrComp(pages[i].url, url) <> 0) do
+ Inc(i);
+ ret := pages[i].handler(pages[i].handler_cls, pages[i].mime, session,
+ connection);
+ if ret <> MHD_YES then
+ WriteLn(stderr, Format('Failed to create page for `%s''', [url]));
+ Exit(ret);
+ end;
+ (* unsupported HTTP method *)
+ response := MHD_create_response_from_buffer(Length(METHOD_ERROR),
+ Pcchar(METHOD_ERROR), MHD_RESPMEM_PERSISTENT);
+ ret := MHD_queue_response(connection, MHD_HTTP_NOT_ACCEPTABLE, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+ (**
+ * Callback called upon completion of a request.
+ * Decrements session reference counter.
+ *
+ * @param cls not used
+ * @param connection connection that completed
+ * @param con_cls session handle
+ * @param toe status code
+ *)
+ procedure request_completed_callback(cls: Pointer; connection: PMHD_Connection;
+ con_cls: PPointer; toe: MHD_RequestTerminationCode);
+ var
+ request: PRequest;
+ begin
+ request := con_cls^;
+ if nil = request then
+ Exit;
+ if nil <> request^.session then
+ Dec(request^.session^.rc);
+ if nil <> request^.pp then
+ MHD_destroy_post_processor(request^.pp);
+ Free(request);
+ end;
+
+ (**
+ * Clean up handles of sessions that have been idle for
+ * too long.
+ *)
+ procedure expire_sessions;
+ var
+ pos: PSession;
+ prev: PSession;
+ next: PSession;
+ now: time_t;
+ begin
+ now := FpTime;
+ prev := nil;
+ pos := _sessions;
+ while nil <> pos do
+ begin
+ next := pos^.next;
+ if (now - pos^.start) > (60 * 60) then
+ begin
+ (* expire sessions after 1h *)
+ if nil = prev then
+ _sessions := pos^.next
+ else
+ prev^.next := next;
+ Free(pos);
+ end
+ else
+ prev := pos;
+ pos := next;
+ end;
+ end;
+
+(**
+ * Call with the port number as the only argument.
+ * Never terminates (other than by signals, such as CTRL-C).
+ *)
+var
+ d: PMHD_Daemon;
+ tv: timeval;
+ tvp: ptimeval;
+ rs: TFDSet;
+ ws: TFDSet;
+ es: TFDSet;
+ max: cint;
+ mhd_timeout: MHD_UNSIGNED_LONG_LONG;
+begin
+ if argc <> 2 then
+ begin
+ WriteLn(argv[0], ' PORT');
+ Halt(1);
+ end;
+ (* initialize PRNG *)
+ Randomize;
+
+ d := MHD_start_daemon(MHD_USE_DEBUG, StrToInt(argv[1]), nil, nil,
+ @create_response, nil, MHD_OPTION_CONNECTION_TIMEOUT, cuint(15),
+ MHD_OPTION_NOTIFY_COMPLETED, @request_completed_callback, nil, MHD_OPTION_END);
+ if nil = d then
+ Halt(1);
+
+ while True do
+ begin
+ expire_sessions;
+ max := 0;
+ fpFD_ZERO(rs);
+ fpFD_ZERO(ws);
+ fpFD_ZERO(es);
+ if MHD_YES <> MHD_get_fdset(d, @rs, @ws, @es, @max) then
+ Break; (* fatal internal error *)
+ if MHD_get_timeout(d, @mhd_timeout) = MHD_YES then
+ begin
+ tv.tv_sec := mhd_timeout div 1000;
+ tv.tv_usec := (mhd_timeout - (tv.tv_sec * 1000)) * 1000;
+ tvp := @tv;
+ end
+ else
+ tvp := nil;
+ if -1 = fpSelect(max + 1, @rs, @ws, @es, tvp) then
+ begin
+ if (ESysEINTR <> errno^) then
+ WriteLn(stderr, 'Aborting due to error during select: ', strerror(errno^));
+ Break;
+ end;
+ MHD_run(d);
+ end;
+ MHD_stop_daemon(d);
+end.
diff --git a/packages/libmicrohttpd/examples/simplepost.pp b/packages/libmicrohttpd/examples/simplepost.pp
new file mode 100644
index 0000000000..5693283e3e
--- /dev/null
+++ b/packages/libmicrohttpd/examples/simplepost.pp
@@ -0,0 +1,155 @@
+(* Feel free to use this example code in any way
+ you see fit (Public Domain) *)
+
+// Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/simplepost.c
+
+program simplepost;
+
+{$mode objfpc}{$H+}
+
+uses
+ SysUtils, cmem, cutils, libmicrohttpd;
+
+const
+ PORT = 8888;
+ POSTBUFFERSIZE = 512;
+ MAXNAMESIZE = 20;
+ MAXANSWERSIZE = 512;
+ GET = 0;
+ POST = 1;
+
+ askpage: Pcchar =
+ '<html><body>'+
+ 'What''s your name, Sir?<br>'+
+ '<form action="/namepost" method="post">'+
+ '<input name="name" type="text">'+
+ '<input type="submit" value=" Send "></form>'+
+ '</body></html>';
+
+ greetingpage: Pcchar = '<html><body><h1>Welcome, %s!</center></h1></body></html>';
+
+ errorpage: Pcchar = '<html><body>This doesn''t seem to be right.</body></html>';
+
+type
+ Tconnection_info_struct = packed record
+ connectiontype: cint;
+ answerstring: Pcchar;
+ postprocessor: PMHD_PostProcessor;
+ end;
+ Pconnection_info_struct = ^Tconnection_info_struct;
+
+ function send_page(connection: PMHD_Connection; page: Pcchar): cint; cdecl;
+ var
+ ret: cint;
+ response: PMHD_Response;
+ begin
+ response := MHD_create_response_from_buffer(Length(page),
+ Pointer(page), MHD_RESPMEM_PERSISTENT);
+ if not Assigned(response) then
+ Exit(MHD_NO);
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+ function iterate_post(coninfo_cls: Pointer; kind: MHD_ValueKind;
+ key: Pcchar; filename: Pcchar; content_type: Pcchar; transfer_encoding: Pcchar;
+ Data: Pcchar; off: cuint64; size: size_t): cint; cdecl;
+ var
+ con_info: Pconnection_info_struct;
+ answerstring: Pcchar;
+ begin
+ con_info := coninfo_cls;
+ if 0 = strcomp(key, 'name') then
+ begin
+ if (size > 0) and (size <= MAXNAMESIZE) then
+ begin
+ answerstring := Malloc(MAXANSWERSIZE);
+ if not Assigned(answerstring) then
+ Exit(MHD_NO);
+ snprintf(answerstring, MAXANSWERSIZE, greetingpage, Data);
+ con_info^.answerstring := answerstring;
+ end
+ else
+ con_info^.answerstring := nil;
+ Exit(MHD_NO);
+ end;
+ Result := MHD_YES;
+ end;
+
+ procedure request_completed(cls: Pointer; connection: PMHD_Connection;
+ con_cls: PPointer; toe: MHD_RequestTerminationCode); cdecl;
+ var
+ con_info: Pconnection_info_struct;
+ begin
+ con_info := con_cls^;
+ if nil = con_info then
+ Exit;
+ if con_info^.connectiontype = POST then
+ begin
+ MHD_destroy_post_processor(con_info^.postprocessor);
+ if Assigned(con_info^.answerstring) then
+ Free(con_info^.answerstring);
+ end;
+ Free(con_info);
+ con_cls^ := nil;
+ end;
+
+ function answer_to_connection(cls: Pointer; connection: PMHD_Connection;
+ url: Pcchar; method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; con_cls: PPointer): cint; cdecl;
+ var
+ con_info: Pconnection_info_struct;
+ begin
+ if nil = con_cls^ then
+ begin
+ con_info := Malloc(SizeOf(Tconnection_info_struct));
+ if nil = con_info then
+ Exit(MHD_NO);
+ con_info^.answerstring := nil;
+ if 0 = strcomp(method, 'POST') then
+ begin
+ con_info^.postprocessor :=
+ MHD_create_post_processor(connection, POSTBUFFERSIZE,
+ @iterate_post, Pointer(con_info));
+ if nil = con_info^.postprocessor then
+ begin
+ Free(con_info);
+ Exit(MHD_NO);
+ end;
+ con_info^.connectiontype := POST;
+ end
+ else
+ con_info^.connectiontype := GET;
+ con_cls^ := Pointer(con_info);
+ Exit(MHD_YES);
+ end;
+ if 0 = strcomp(method, 'GET') then
+ Exit(send_page(connection, askpage));
+ if 0 = strcomp(method, 'POST') then
+ begin
+ con_info := con_cls^;
+ if upload_data_size^ <> 0 then
+ begin
+ MHD_post_process(con_info^.postprocessor, upload_data, upload_data_size^);
+ upload_data_size^ := 0;
+ Exit(MHD_YES);
+ end
+ else
+ if nil <> con_info^.answerstring then
+ Exit(send_page(connection, con_info^.answerstring));
+ end;
+ Result := send_page(connection, errorpage);
+ end;
+
+var
+ daemon: PMHD_Daemon;
+begin
+ daemon := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, nil, nil,
+ @answer_to_connection, nil, MHD_OPTION_NOTIFY_COMPLETED, @request_completed,
+ nil, MHD_OPTION_END);
+ if nil = daemon then
+ Halt(1);
+ ReadLn;
+ MHD_stop_daemon(daemon);
+end.
diff --git a/packages/libmicrohttpd/examples/tlsauthentication.pp b/packages/libmicrohttpd/examples/tlsauthentication.pp
new file mode 100644
index 0000000000..8ecd0c133a
--- /dev/null
+++ b/packages/libmicrohttpd/examples/tlsauthentication.pp
@@ -0,0 +1,234 @@
+(* Feel free to use this example code in any way
+ you see fit (Public Domain) *)
+
+// Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/tlsauthentication.c
+
+(*
+ * Generate PEM files for test this example:
+ *
+ * openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem
+ *
+ * or
+ *
+ * openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout server.key -out server.pem
+ *)
+
+program tlsauthentication;
+
+{$mode objfpc}{$H+}
+
+uses
+ SysUtils, ctypes, cmem, cutils, libmicrohttpd;
+
+const
+ PORT = 8888;
+ REALM = '"Maintenance"';
+ USER = 'a legitimate user';
+ PASSWORD = 'and his password';
+
+ SERVERKEYFILE = 'server.key';
+ SERVERCERTFILE = 'server.pem';
+
+ function iif(c: cbool; t, f: culong): culong;
+ begin
+ if c then
+ Result := t
+ else
+ Result := f;
+ end;
+
+ function string_to_base64(message: Pcchar): Pcchar;
+ var
+ lookup: Pcchar = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+ l: culong;
+ i: cint;
+ tmp: Pcchar;
+ len: SizeInt;
+ begin
+ len := strlen(message);
+ tmp := Malloc(len * 2);
+ if nil = tmp then
+ Exit(tmp);
+ tmp[0] := #0;
+ i := 0;
+ while i < len do
+ begin
+ l := (culong(message[i]) shl 16)
+ or iif((i + 1) < len, culong(message[i + 1]) shl 8, 0)
+ or iif((i + 2) < len, culong(message[i + 2]), 0);
+ strncat(tmp, @lookup[(l shr 18) and $3F], 1);
+ strncat(tmp, @lookup[(l shr 12) and $3F], 1);
+ if i + 1 < len then
+ strncat(tmp, @lookup[(l shr 6) and $3F], 1);
+ if i + 2 < len then
+ strncat(tmp, @lookup[l and $3F], 1);
+ i += 3;
+ end;
+ if (len mod 3 = 1) then
+ strncat(tmp, '===', 3 - len mod 3);
+ Result := tmp;
+ end;
+
+ function get_file_size(filename: Pcchar): clong;
+ var
+ fp: FILEptr;
+ size: clong;
+ begin
+ fp := fopen(filename, fopenread);
+ if Assigned(fp) then
+ begin
+ if 0 <> fseek(fp, 0, SEEK_END) then
+ size := 0;
+ size := ftell(fp);
+ if -1 = size then
+ size := 0;
+ fclose(fp);
+ Result := size;
+ end
+ else
+ Result := 0;
+ end;
+
+ function load_file(filename: Pcchar): Pcchar;
+ var
+ fp: FILEptr;
+ buffer: Pcchar;
+ size: clong;
+ begin
+ size := get_file_size(filename);
+ if size = 0 then
+ Exit(nil);
+ fp := fopen(filename, fopenread);
+ if not Assigned(fp) then
+ Exit(nil);
+ buffer := Malloc(size);
+ if not Assigned(buffer) then
+ begin
+ fclose(fp);
+ Exit(nil);
+ end;
+ if size <> fread(buffer, 1, size, fp) then
+ begin
+ free(buffer);
+ buffer := nil;
+ end;
+ fclose(fp);
+ Result := buffer;
+ end;
+
+ function ask_for_authentication(connection: PMHD_Connection;
+ realm: Pcchar): cint; cdecl;
+ var
+ ret: cint;
+ response: PMHD_Response;
+ headervalue: Pcchar;
+ strbase: Pcchar = 'Basic realm=';
+ begin
+ response := MHD_create_response_from_buffer(0, nil, MHD_RESPMEM_PERSISTENT);
+ if not Assigned(response) then
+ Exit(MHD_NO);
+ headervalue := Malloc(strlen(strbase) + strlen(realm) + 1);
+ if not Assigned(headervalue) then
+ Exit(MHD_NO);
+ strcpy(headervalue, strbase);
+ strcat(headervalue, realm);
+ ret := MHD_add_response_header(response, 'WWW-Authenticate', headervalue);
+ Free(headervalue);
+ if ret <> 1 then
+ begin
+ MHD_destroy_response(response);
+ Exit(MHD_NO);
+ end;
+ ret := MHD_queue_response(connection, MHD_HTTP_UNAUTHORIZED, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+ function is_authenticated(connection: PMHD_Connection;
+ username, password: Pcchar): cint; cdecl;
+ var
+ headervalue: Pcchar;
+ expected_b64, expected: Pcchar;
+ strbase: Pcchar = 'Basic ';
+ authenticated: cint;
+ begin
+ headervalue := MHD_lookup_connection_value(connection, MHD_HEADER_KIND,
+ 'Authorization');
+ if nil = headervalue then
+ Exit(0);
+ if 0 <> strncmp(headervalue, strbase, strlen(strbase)) then
+ Exit(0);
+ expected := malloc(strlen(username) + 1 + strlen(password) + 1);
+ if nil = expected then
+ Exit(0);
+ strcpy(expected, username);
+ strcat(expected, ':');
+ strcat(expected, password);
+ expected_b64 := string_to_base64(expected);
+ free(expected);
+ if nil = expected_b64 then
+ Exit(0);
+ authenticated := cint(strcomp(headervalue + strlen(strbase), expected_b64) = 0);
+ Free(expected_b64);
+ Result := authenticated;
+ end;
+
+ function secret_page(connection: PMHD_Connection): cint; cdecl;
+ var
+ ret: cint;
+ response: PMHD_Response;
+ page: Pcchar = '<html><body>A secret.</body></html>';
+ begin
+ response := MHD_create_response_from_buffer(strlen(page), Pointer(page),
+ MHD_RESPMEM_PERSISTENT);
+ if not Assigned(response) then
+ Exit(MHD_NO);
+ ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
+ MHD_destroy_response(response);
+ Result := ret;
+ end;
+
+ function answer_to_connection(cls: Pointer; connection: PMHD_Connection;
+ url: Pcchar; method: Pcchar; version: Pcchar; upload_data: Pcchar;
+ upload_data_size: Psize_t; con_cls: PPointer): cint; cdecl;
+ begin
+ if 0 <> strcomp(method, 'GET') then
+ Exit(MHD_NO);
+ if nil = con_cls^ then
+ begin
+ con_cls^ := connection;
+ Exit(MHD_YES);
+ end;
+ if is_authenticated(connection, USER, PASSWORD) <> 1 then
+ Exit(ask_for_authentication(connection, REALM));
+ Result := secret_page(connection);
+ end;
+
+var
+ daemon: PMHD_Daemon;
+ key_pem: Pcchar;
+ cert_pem: Pcchar;
+begin
+ key_pem := load_file(SERVERKEYFILE);
+ cert_pem := load_file(SERVERCERTFILE);
+ if (key_pem = nil) or (cert_pem = nil) then
+ begin
+ WriteLn('The key/certificate files could not be read.');
+ Halt(1);
+ end;
+ daemon := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY or MHD_USE_SSL, PORT,
+ nil, nil, @answer_to_connection, nil, MHD_OPTION_HTTPS_MEM_KEY, key_pem,
+ MHD_OPTION_HTTPS_MEM_CERT, cert_pem, MHD_OPTION_END);
+ if nil = daemon then
+ begin
+ WriteLn(cert_pem);
+ Free(key_pem);
+ Free(cert_pem);
+ Halt(1);
+ end;
+ ReadLn;
+ MHD_stop_daemon(daemon);
+ Free(key_pem);
+ Free(cert_pem);
+end.
+