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