1/* Feel free to use this example code in any way
2   you see fit (Public Domain) */
3
4#include <sys/types.h>
5#ifndef _WIN32
6#include <sys/select.h>
7#include <sys/socket.h>
8#else
9#include <winsock2.h>
10#endif
11#include <microhttpd.h>
12#include <string.h>
13#include <stdio.h>
14#include <stdlib.h>
15
16#define PORT 8888
17
18#define REALM     "\"Maintenance\""
19#define USER      "a legitimate user"
20#define PASSWORD  "and his password"
21
22#define SERVERKEYFILE "server.key"
23#define SERVERCERTFILE "server.pem"
24
25
26static char *
27string_to_base64 (const char *message)
28{
29  const char *lookup =
30    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
31  unsigned long l;
32  int i;
33  char *tmp;
34  size_t length = strlen (message);
35
36  tmp = malloc (length * 2);
37  if (NULL == tmp)
38    return tmp;
39
40  tmp[0] = 0;
41
42  for (i = 0; i < length; i += 3)
43    {
44      l = (((unsigned long) message[i]) << 16)
45        | (((i + 1) < length) ? (((unsigned long) message[i + 1]) << 8) : 0)
46        | (((i + 2) < length) ? ((unsigned long) message[i + 2]) : 0);
47
48
49      strncat (tmp, &lookup[(l >> 18) & 0x3F], 1);
50      strncat (tmp, &lookup[(l >> 12) & 0x3F], 1);
51
52      if (i + 1 < length)
53        strncat (tmp, &lookup[(l >> 6) & 0x3F], 1);
54      if (i + 2 < length)
55        strncat (tmp, &lookup[l & 0x3F], 1);
56    }
57
58  if (length % 3)
59    strncat (tmp, "===", 3 - length % 3);
60
61  return tmp;
62}
63
64
65static long
66get_file_size (const char *filename)
67{
68  FILE *fp;
69
70  fp = fopen (filename, "rb");
71  if (fp)
72    {
73      long size;
74
75      if ((0 != fseek (fp, 0, SEEK_END)) || (-1 == (size = ftell (fp))))
76        size = 0;
77
78      fclose (fp);
79
80      return size;
81    }
82  else
83    return 0;
84}
85
86static char *
87load_file (const char *filename)
88{
89  FILE *fp;
90  char *buffer;
91  long size;
92
93  size = get_file_size (filename);
94  if (size == 0)
95    return NULL;
96
97  fp = fopen (filename, "rb");
98  if (!fp)
99    return NULL;
100
101  buffer = malloc (size);
102  if (!buffer)
103    {
104      fclose (fp);
105      return NULL;
106    }
107
108  if (size != fread (buffer, 1, size, fp))
109    {
110      free (buffer);
111      buffer = NULL;
112    }
113
114  fclose (fp);
115  return buffer;
116}
117
118static int
119ask_for_authentication (struct MHD_Connection *connection, const char *realm)
120{
121  int ret;
122  struct MHD_Response *response;
123  char *headervalue;
124  const char *strbase = "Basic realm=";
125
126  response = MHD_create_response_from_buffer (0, NULL,
127					      MHD_RESPMEM_PERSISTENT);
128  if (!response)
129    return MHD_NO;
130
131  headervalue = malloc (strlen (strbase) + strlen (realm) + 1);
132  if (!headervalue)
133    return MHD_NO;
134
135  strcpy (headervalue, strbase);
136  strcat (headervalue, realm);
137
138  ret = MHD_add_response_header (response, "WWW-Authenticate", headervalue);
139  free (headervalue);
140  if (!ret)
141    {
142      MHD_destroy_response (response);
143      return MHD_NO;
144    }
145
146  ret = MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response);
147
148  MHD_destroy_response (response);
149
150  return ret;
151}
152
153static int
154is_authenticated (struct MHD_Connection *connection,
155                  const char *username, const char *password)
156{
157  const char *headervalue;
158  char *expected_b64, *expected;
159  const char *strbase = "Basic ";
160  int authenticated;
161
162  headervalue =
163    MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
164                                 "Authorization");
165  if (NULL == headervalue)
166    return 0;
167  if (0 != strncmp (headervalue, strbase, strlen (strbase)))
168    return 0;
169
170  expected = malloc (strlen (username) + 1 + strlen (password) + 1);
171  if (NULL == expected)
172    return 0;
173
174  strcpy (expected, username);
175  strcat (expected, ":");
176  strcat (expected, password);
177
178  expected_b64 = string_to_base64 (expected);
179  free (expected);
180  if (NULL == expected_b64)
181    return 0;
182
183  authenticated =
184    (strcmp (headervalue + strlen (strbase), expected_b64) == 0);
185
186  free (expected_b64);
187
188  return authenticated;
189}
190
191
192static int
193secret_page (struct MHD_Connection *connection)
194{
195  int ret;
196  struct MHD_Response *response;
197  const char *page = "<html><body>A secret.</body></html>";
198
199  response =
200    MHD_create_response_from_buffer (strlen (page), (void *) page,
201				     MHD_RESPMEM_PERSISTENT);
202  if (!response)
203    return MHD_NO;
204
205  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
206  MHD_destroy_response (response);
207
208  return ret;
209}
210
211
212static int
213answer_to_connection (void *cls, struct MHD_Connection *connection,
214                      const char *url, const char *method,
215                      const char *version, const char *upload_data,
216                      size_t *upload_data_size, void **con_cls)
217{
218  if (0 != strcmp (method, "GET"))
219    return MHD_NO;
220  if (NULL == *con_cls)
221    {
222      *con_cls = connection;
223      return MHD_YES;
224    }
225
226  if (!is_authenticated (connection, USER, PASSWORD))
227    return ask_for_authentication (connection, REALM);
228
229  return secret_page (connection);
230}
231
232
233int
234main ()
235{
236  struct MHD_Daemon *daemon;
237  char *key_pem;
238  char *cert_pem;
239
240  key_pem = load_file (SERVERKEYFILE);
241  cert_pem = load_file (SERVERCERTFILE);
242
243  if ((key_pem == NULL) || (cert_pem == NULL))
244    {
245      printf ("The key/certificate files could not be read.\n");
246      return 1;
247    }
248
249  daemon =
250    MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL, PORT, NULL,
251                      NULL, &answer_to_connection, NULL,
252                      MHD_OPTION_HTTPS_MEM_KEY, key_pem,
253                      MHD_OPTION_HTTPS_MEM_CERT, cert_pem, MHD_OPTION_END);
254  if (NULL == daemon)
255    {
256      printf ("%s\n", cert_pem);
257
258      free (key_pem);
259      free (cert_pem);
260
261      return 1;
262    }
263
264  (void) getchar ();
265
266  MHD_stop_daemon (daemon);
267  free (key_pem);
268  free (cert_pem);
269
270  return 0;
271}
272