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 <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <microhttpd.h>
15
16 #ifdef _MSC_VER
17 #ifndef strcasecmp
18 #define strcasecmp(a,b) _stricmp ((a),(b))
19 #endif /* !strcasecmp */
20 #endif /* _MSC_VER */
21
22 #if defined(_MSC_VER) && _MSC_VER + 0 <= 1800
23 /* Substitution is OK while return value is not used */
24 #define snprintf _snprintf
25 #endif
26
27 #define PORT 8888
28 #define POSTBUFFERSIZE 512
29 #define MAXCLIENTS 2
30
31 enum ConnectionType
32 {
33 GET = 0,
34 POST = 1
35 };
36
37 static unsigned int nr_of_uploading_clients = 0;
38
39
40 /**
41 * Information we keep per connection.
42 */
43 struct connection_info_struct
44 {
45 enum ConnectionType connectiontype;
46
47 /**
48 * Handle to the POST processing state.
49 */
50 struct MHD_PostProcessor *postprocessor;
51
52 /**
53 * File handle where we write uploaded data.
54 */
55 FILE *fp;
56
57 /**
58 * HTTP response body we will return, NULL if not yet known.
59 */
60 const char *answerstring;
61
62 /**
63 * HTTP status code we will return, 0 for undecided.
64 */
65 unsigned int answercode;
66 };
67
68
69 const char *askpage =
70 "<html><body>\n\
71 Upload a file, please!<br>\n\
72 There are %u clients uploading at the moment.<br>\n\
73 <form action=\"/filepost\" method=\"post\" enctype=\"multipart/form-data\">\n\
74 <input name=\"file\" type=\"file\">\n\
75 <input type=\"submit\" value=\" Send \"></form>\n\
76 </body></html>";
77 const char *busypage =
78 "<html><body>This server is busy, please try again later.</body></html>";
79 const char *completepage =
80 "<html><body>The upload has been completed.</body></html>";
81 const char *errorpage =
82 "<html><body>This doesn't seem to be right.</body></html>";
83 const char *servererrorpage =
84 "<html><body>Invalid request.</body></html>";
85 const char *fileexistspage =
86 "<html><body>This file already exists.</body></html>";
87 const char *fileioerror =
88 "<html><body>IO error writing to disk.</body></html>";
89 const char*const postprocerror =
90 "<html><head><title>Error</title></head><body>Error processing POST data</body></html>";
91
92
93 static enum MHD_Result
send_page(struct MHD_Connection * connection,const char * page,int status_code)94 send_page (struct MHD_Connection *connection,
95 const char *page,
96 int status_code)
97 {
98 enum MHD_Result ret;
99 struct MHD_Response *response;
100
101 response =
102 MHD_create_response_from_buffer (strlen (page),
103 (void *) page,
104 MHD_RESPMEM_MUST_COPY);
105 if (! response)
106 return MHD_NO;
107 MHD_add_response_header (response,
108 MHD_HTTP_HEADER_CONTENT_TYPE,
109 "text/html");
110 ret = MHD_queue_response (connection,
111 status_code,
112 response);
113 MHD_destroy_response (response);
114
115 return ret;
116 }
117
118
119 static enum MHD_Result
iterate_post(void * coninfo_cls,enum MHD_ValueKind kind,const char * key,const char * filename,const char * content_type,const char * transfer_encoding,const char * data,uint64_t off,size_t size)120 iterate_post (void *coninfo_cls,
121 enum MHD_ValueKind kind,
122 const char *key,
123 const char *filename,
124 const char *content_type,
125 const char *transfer_encoding,
126 const char *data,
127 uint64_t off,
128 size_t size)
129 {
130 struct connection_info_struct *con_info = coninfo_cls;
131 FILE *fp;
132 (void) kind; /* Unused. Silent compiler warning. */
133 (void) content_type; /* Unused. Silent compiler warning. */
134 (void) transfer_encoding; /* Unused. Silent compiler warning. */
135 (void) off; /* Unused. Silent compiler warning. */
136
137 if (0 != strcmp (key, "file"))
138 {
139 con_info->answerstring = servererrorpage;
140 con_info->answercode = MHD_HTTP_BAD_REQUEST;
141 return MHD_YES;
142 }
143
144 if (! con_info->fp)
145 {
146 if (0 != con_info->answercode) /* something went wrong */
147 return MHD_YES;
148 if (NULL != (fp = fopen (filename, "rb")))
149 {
150 fclose (fp);
151 con_info->answerstring = fileexistspage;
152 con_info->answercode = MHD_HTTP_FORBIDDEN;
153 return MHD_YES;
154 }
155 /* NOTE: This is technically a race with the 'fopen()' above,
156 but there is no easy fix, short of moving to open(O_EXCL)
157 instead of using fopen(). For the example, we do not care. */
158 con_info->fp = fopen (filename, "ab");
159 if (! con_info->fp)
160 {
161 con_info->answerstring = fileioerror;
162 con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR;
163 return MHD_YES;
164 }
165 }
166
167 if (size > 0)
168 {
169 if (! fwrite (data, sizeof (char), size, con_info->fp))
170 {
171 con_info->answerstring = fileioerror;
172 con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR;
173 return MHD_YES;
174 }
175 }
176
177 return MHD_YES;
178 }
179
180
181 static void
request_completed(void * cls,struct MHD_Connection * connection,void ** con_cls,enum MHD_RequestTerminationCode toe)182 request_completed (void *cls,
183 struct MHD_Connection *connection,
184 void **con_cls,
185 enum MHD_RequestTerminationCode toe)
186 {
187 struct connection_info_struct *con_info = *con_cls;
188 (void) cls; /* Unused. Silent compiler warning. */
189 (void) connection; /* Unused. Silent compiler warning. */
190 (void) toe; /* Unused. Silent compiler warning. */
191
192 if (NULL == con_info)
193 return;
194
195 if (con_info->connectiontype == POST)
196 {
197 if (NULL != con_info->postprocessor)
198 {
199 MHD_destroy_post_processor (con_info->postprocessor);
200 nr_of_uploading_clients--;
201 }
202
203 if (con_info->fp)
204 fclose (con_info->fp);
205 }
206
207 free (con_info);
208 *con_cls = NULL;
209 }
210
211
212 static enum MHD_Result
answer_to_connection(void * cls,struct MHD_Connection * connection,const char * url,const char * method,const char * version,const char * upload_data,size_t * upload_data_size,void ** con_cls)213 answer_to_connection (void *cls,
214 struct MHD_Connection *connection,
215 const char *url,
216 const char *method,
217 const char *version,
218 const char *upload_data,
219 size_t *upload_data_size,
220 void **con_cls)
221 {
222 (void) cls; /* Unused. Silent compiler warning. */
223 (void) url; /* Unused. Silent compiler warning. */
224 (void) version; /* Unused. Silent compiler warning. */
225
226 if (NULL == *con_cls)
227 {
228 /* First call, setup data structures */
229 struct connection_info_struct *con_info;
230
231 if (nr_of_uploading_clients >= MAXCLIENTS)
232 return send_page (connection,
233 busypage,
234 MHD_HTTP_SERVICE_UNAVAILABLE);
235
236 con_info = malloc (sizeof (struct connection_info_struct));
237 if (NULL == con_info)
238 return MHD_NO;
239 con_info->answercode = 0; /* none yet */
240 con_info->fp = NULL;
241
242 if (0 == strcasecmp (method, MHD_HTTP_METHOD_POST))
243 {
244 con_info->postprocessor =
245 MHD_create_post_processor (connection,
246 POSTBUFFERSIZE,
247 &iterate_post,
248 (void *) con_info);
249
250 if (NULL == con_info->postprocessor)
251 {
252 free (con_info);
253 return MHD_NO;
254 }
255
256 nr_of_uploading_clients++;
257
258 con_info->connectiontype = POST;
259 }
260 else
261 {
262 con_info->connectiontype = GET;
263 }
264
265 *con_cls = (void *) con_info;
266
267 return MHD_YES;
268 }
269
270 if (0 == strcasecmp (method, MHD_HTTP_METHOD_GET))
271 {
272 /* We just return the standard form for uploads on all GET requests */
273 char buffer[1024];
274
275 snprintf (buffer,
276 sizeof (buffer),
277 askpage,
278 nr_of_uploading_clients);
279 return send_page (connection,
280 buffer,
281 MHD_HTTP_OK);
282 }
283
284 if (0 == strcasecmp (method, MHD_HTTP_METHOD_POST))
285 {
286 struct connection_info_struct *con_info = *con_cls;
287
288 if (0 != *upload_data_size)
289 {
290 /* Upload not yet done */
291 if (0 != con_info->answercode)
292 {
293 /* we already know the answer, skip rest of upload */
294 *upload_data_size = 0;
295 return MHD_YES;
296 }
297 if (MHD_YES !=
298 MHD_post_process (con_info->postprocessor,
299 upload_data,
300 *upload_data_size))
301 {
302 con_info->answerstring = postprocerror;
303 con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR;
304 }
305 *upload_data_size = 0;
306
307 return MHD_YES;
308 }
309 /* Upload finished */
310 if (NULL != con_info->fp)
311 {
312 fclose (con_info->fp);
313 con_info->fp = NULL;
314 }
315 if (0 == con_info->answercode)
316 {
317 /* No errors encountered, declare success */
318 con_info->answerstring = completepage;
319 con_info->answercode = MHD_HTTP_OK;
320 }
321 return send_page (connection,
322 con_info->answerstring,
323 con_info->answercode);
324 }
325
326 /* Note a GET or a POST, generate error */
327 return send_page (connection,
328 errorpage,
329 MHD_HTTP_BAD_REQUEST);
330 }
331
332
333 int
main()334 main ()
335 {
336 struct MHD_Daemon *daemon;
337
338 daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD,
339 PORT, NULL, NULL,
340 &answer_to_connection, NULL,
341 MHD_OPTION_NOTIFY_COMPLETED, &request_completed,
342 NULL,
343 MHD_OPTION_END);
344 if (NULL == daemon)
345 {
346 fprintf (stderr,
347 "Failed to start daemon.\n");
348 return 1;
349 }
350 (void) getchar ();
351 MHD_stop_daemon (daemon);
352 return 0;
353 }
354