-fix no-dot-in-path issue on FreeBSD bot
[oweals/gnunet.git] / src / gns / gnunet-gns-fcfsd.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file gnunet-gns-fcfsd.c
22  * @brief HTTP daemon that offers first-come-first-serve GNS domain registration
23  * @author Christian Grothoff
24  *
25  * TODO:
26  * - the code currently contains a 'race' between checking that the
27  *   domain name is available and allocating it to the new public key
28  *   (should this race be solved by namestore or by fcfsd?)
29  * - nicer error reporting to browser
30  * - figure out where this binary should go (is gns the right directory!?)
31  */
32 #include "platform.h"
33 #include <gnunet_util_lib.h>
34 #include <microhttpd.h>
35 #include <gnunet_namestore_service.h>
36
37 /**
38  * Invalid method page.
39  */
40 #define METHOD_ERROR "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>Illegal request</title></head><body>Go away.</body></html>"
41
42 /**
43  * Front page. (/)
44  */
45 #define MAIN_PAGE "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>GNUnet FCFS Authority Name Registration Service</title></head><body><form action=\"S\" method=\"post\">What is your desired domain name? (at most 63 lowercase characters, no dots allowed.) <input type=\"text\" name=\"domain\" /> <p> What is your public key? (Copy from gnunet-setup.) <input type=\"text\" name=\"pkey\" /> <input type=\"submit\" value=\"Next\" /><br/><a href=./Zoneinfo> List of all registered names </a></body></html>"
46
47 /**
48  * Second page (/S)
49  */
50 #define SUBMIT_PAGE "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>%s</title></head><body>%s</body></html>"
51
52 /**
53  * Fcfs zoneinfo page (/Zoneinfo)
54  */
55 #define ZONEINFO_PAGE "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>FCFS Zoneinfo</title></head><body><h1> FCFS Zoneinfo </h1><table border=\"1\"><th>name</th><th>PKEY</th>%s</table></body></html>"
56
57 #define FCFS_ZONEINFO_URL "/Zoneinfo"
58
59 /**
60  * Mime type for HTML pages.
61  */
62 #define MIME_HTML "text/html"
63
64 /**
65  * Name of our cookie.
66  */
67 #define COOKIE_NAME "gns-fcfs"
68
69 #define DEFAULT_ZONEINFO_BUFSIZE 2048
70
71 /**
72  * Phases a request goes through.
73  */
74 enum Phase
75   {
76     /**
77      * Start phase (parsing POST, checking).
78      */
79     RP_START = 0,
80
81     /**
82      * Lookup to see if the domain name is taken.
83      */
84     RP_LOOKUP,
85
86     /**
87      * Storing of the record.
88      */
89     RP_PUT,
90
91     /**
92      * We're done with success.
93      */
94     RP_SUCCESS,
95
96     /**
97      * Send failure message.
98      */
99     RP_FAIL
100   };
101
102
103 /**
104  * Data kept per request.
105  */
106 struct Request
107 {
108
109   /**
110    * Associated session.
111    */
112   struct Session *session;
113
114   /**
115    * Post processor handling form data (IF this is
116    * a POST request).
117    */
118   struct MHD_PostProcessor *pp;
119
120   /**
121    * URL to serve in response to this POST (if this request 
122    * was a 'POST')
123    */
124   const char *post_url;
125
126   /**
127    * Active request with the namestore.
128    */
129   struct GNUNET_NAMESTORE_QueueEntry *qe;
130   
131   /**
132    * Current processing phase.
133    */
134   enum Phase phase;
135
136   /**
137    * Domain name submitted via form.
138    */
139   char domain_name[64];
140
141   /**
142    * Public key submitted via form.
143    */
144   char public_key[64];
145
146 };
147
148 /**
149  * Zoneinfo request
150  */
151 struct ZoneinfoRequest
152 {
153   /**
154    * Connection
155    */
156   struct MHD_Connection *connection;
157
158   /**
159    * List iterator
160    */
161   struct GNUNET_NAMESTORE_ZoneIterator *list_it;
162
163   /**
164    * Buffer
165    */
166   char* zoneinfo;
167
168   /**
169    * Buffer length
170    */
171   size_t buf_len;
172   
173   /**
174    * Buffer write offset
175    */
176   size_t write_offset;
177 };
178
179 /**
180  * MHD deamon reference.
181  */
182 static struct MHD_Daemon *httpd;
183
184 /**
185  * Main HTTP task.
186  */
187 static GNUNET_SCHEDULER_TaskIdentifier httpd_task;
188
189 /**
190  * Handle to the namestore.
191  */
192 static struct GNUNET_NAMESTORE_Handle *ns;
193
194 /**
195  * Hash of the public key of the fcfsd zone.
196  */
197 static struct GNUNET_CRYPTO_ShortHashCode fcfsd_zone;
198
199 /**
200  * Private key for the fcfsd zone.
201  */
202 static struct GNUNET_CRYPTO_RsaPrivateKey *fcfs_zone_pkey;
203                         
204
205 /**
206  * Task run whenever HTTP server operations are pending.
207  *
208  * @param cls unused
209  * @param tc scheduler context
210  */
211 static void
212 do_httpd (void *cls,
213           const struct GNUNET_SCHEDULER_TaskContext *tc);
214
215
216 /**
217  * Schedule task to run MHD server now.
218  */
219 static void
220 run_httpd_now ()
221 {
222   if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
223   {
224     GNUNET_SCHEDULER_cancel (httpd_task);
225     httpd_task = GNUNET_SCHEDULER_NO_TASK;
226   }
227   httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL);
228 }
229
230 static void
231 iterate_cb (void *cls,
232                 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key,
233                 struct GNUNET_TIME_Absolute expire,
234                 const char *name,
235                 unsigned int rd_len,
236                 const struct GNUNET_NAMESTORE_RecordData *rd,
237                 const struct GNUNET_CRYPTO_RsaSignature *signature)
238 {
239   struct ZoneinfoRequest *zr = cls;
240   struct MHD_Response *response;
241   char* full_page;
242   size_t bytes_free;
243   char* pkey;
244   char* new_buf;
245
246
247   if (NULL == name)
248   {
249     zr->list_it = NULL;
250
251     /* return static form */
252     GNUNET_asprintf (&full_page,
253                      ZONEINFO_PAGE,
254                      zr->zoneinfo,
255                      zr->zoneinfo);
256     response = MHD_create_response_from_buffer (strlen (full_page),
257                                               (void *) full_page,
258                                               MHD_RESPMEM_MUST_FREE);
259     MHD_add_response_header (response,
260                            MHD_HTTP_HEADER_CONTENT_TYPE,
261                            MIME_HTML);
262     MHD_queue_response (zr->connection, 
263                             MHD_HTTP_OK, 
264                             response);
265     MHD_destroy_response (response);
266     GNUNET_free (zr->zoneinfo);
267     GNUNET_free (zr);
268     run_httpd_now ();
269     return;
270   }
271
272   if (1 != rd_len)
273   {
274     GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
275     return;
276   }
277
278   if (GNUNET_NAMESTORE_TYPE_PKEY != rd->record_type)
279   {
280     GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
281     return;
282   }
283
284   bytes_free = zr->buf_len - zr->write_offset;
285   pkey = GNUNET_NAMESTORE_value_to_string (rd->record_type,
286                                            rd->data,
287                                            rd->data_size);
288
289   if (bytes_free < (strlen (name) + strlen (pkey) + 10))
290   {
291     new_buf = GNUNET_malloc (zr->buf_len * 2);
292     memcpy (new_buf, zr->zoneinfo, zr->write_offset);
293     GNUNET_free (zr->zoneinfo);
294     zr->zoneinfo = new_buf;
295   }
296
297   sprintf (zr->zoneinfo+zr->write_offset, "<tr><td>%s</td><td>%s</td></tr>", name, pkey);
298
299   zr->write_offset = strlen (zr->zoneinfo);
300   GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
301   GNUNET_free (pkey);
302 }
303
304
305
306 /**
307  * Handler that returns FCFS zoneinfo page.
308  *
309  * @param connection connection to use
310  * @return MHD_YES on success
311  */
312 static int
313 serve_zoneinfo_page (struct MHD_Connection *connection)
314 {
315   struct ZoneinfoRequest *zr;
316
317   zr = GNUNET_malloc (sizeof (struct ZoneinfoRequest));
318
319   zr->zoneinfo = GNUNET_malloc (DEFAULT_ZONEINFO_BUFSIZE);
320   zr->buf_len = DEFAULT_ZONEINFO_BUFSIZE;
321   zr->connection = connection;
322   zr->write_offset = 0;
323
324   printf ("adsadad1!\n");
325   zr->list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
326                                                    &fcfsd_zone,
327                                                    GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION,
328                                                    GNUNET_NAMESTORE_RF_PRIVATE,
329                                                    &iterate_cb,
330                                                    zr);
331
332   return MHD_YES;
333 }
334
335
336 /**
337  * Handler that returns a simple static HTTP page.
338  *
339  * @param connection connection to use
340  * @return MHD_YES on success
341  */
342 static int
343 serve_main_page (struct MHD_Connection *connection)
344 {
345   int ret;
346   struct MHD_Response *response;
347
348   /* return static form */
349   response = MHD_create_response_from_buffer (strlen (MAIN_PAGE),
350                                               (void *) MAIN_PAGE,
351                                               MHD_RESPMEM_PERSISTENT);
352   MHD_add_response_header (response,
353                            MHD_HTTP_HEADER_CONTENT_TYPE,
354                            MIME_HTML);
355   ret = MHD_queue_response (connection, 
356                             MHD_HTTP_OK, 
357                             response);
358   MHD_destroy_response (response);
359   return ret;
360 }
361
362
363 /**
364  * Send the 'SUBMIT_PAGE'.
365  *
366  * @param info information string to send to the user
367  * @param request request information
368  * @param connection connection to use
369  */
370 static int
371 fill_s_reply (const char *info,
372               struct Request *request,
373               struct MHD_Connection *connection)
374 {
375   int ret;
376   char *reply;
377   struct MHD_Response *response;
378
379   GNUNET_asprintf (&reply,
380                    SUBMIT_PAGE,
381                    info,
382                    info);
383   /* return static form */
384   response = MHD_create_response_from_buffer (strlen (reply),
385                                               (void *) reply,
386                                               MHD_RESPMEM_MUST_FREE);
387   MHD_add_response_header (response,
388                            MHD_HTTP_HEADER_CONTENT_TYPE,
389                            MIME_HTML);
390   ret = MHD_queue_response (connection, 
391                             MHD_HTTP_OK, 
392                             response);
393   MHD_destroy_response (response);
394   return ret;
395 }
396
397
398 /**
399  * Iterator over key-value pairs where the value
400  * maybe made available in increments and/or may
401  * not be zero-terminated.  Used for processing
402  * POST data.
403  *
404  * @param cls user-specified closure
405  * @param kind type of the value
406  * @param key 0-terminated key for the value
407  * @param filename name of the uploaded file, NULL if not known
408  * @param content_type mime-type of the data, NULL if not known
409  * @param transfer_encoding encoding of the data, NULL if not known
410  * @param data pointer to size bytes of data at the
411  *              specified offset
412  * @param off offset of data in the overall value
413  * @param size number of bytes in data available
414  * @return MHD_YES to continue iterating,
415  *         MHD_NO to abort the iteration
416  */
417 static int
418 post_iterator (void *cls,
419                enum MHD_ValueKind kind,
420                const char *key,
421                const char *filename,
422                const char *content_type,
423                const char *transfer_encoding,
424                const char *data, uint64_t off, size_t size)
425 {
426   struct Request *request = cls;
427
428   if (0 == strcmp ("domain", key))
429     {
430       if (size + off >= sizeof(request->domain_name))
431         size = sizeof (request->domain_name) - off - 1;
432       memcpy (&request->domain_name[off],
433               data,
434               size);
435       request->domain_name[size+off] = '\0';
436       return MHD_YES;
437     }
438   if (0 == strcmp ("pkey", key))
439     {
440       if (size + off >= sizeof(request->public_key))
441         size = sizeof (request->public_key) - off - 1;
442       memcpy (&request->public_key[off],
443               data,
444               size);
445       request->public_key[size+off] = '\0';
446       return MHD_YES;
447     }
448   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
449               _("Unsupported form value `%s'\n"),
450               key);
451   return MHD_YES;
452 }
453
454
455
456
457 /**
458  * Continuation called to notify client about result of the
459  * operation.
460  *
461  * @param cls closure
462  * @param success GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
463  *                GNUNET_NO if content was already there
464  *                GNUNET_YES (or other positive value) on success
465  * @param emsg NULL on success, otherwise an error message
466  */
467 static void 
468 put_continuation (void *cls,
469                   int32_t success,
470                   const char *emsg)
471 {
472   struct Request *request = cls;
473
474   request->qe = NULL;
475   if (0 >= success)
476   {
477     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
478                 _("Failed to create record for domain `%s': %s\n"),
479                 request->domain_name,
480                 emsg);
481     request->phase = RP_FAIL;
482   }
483   else
484     request->phase = RP_SUCCESS;
485   run_httpd_now ();
486 }
487
488
489 /**
490  * Test if a name mapping was found, if so, refuse.  If not, initiate storing of the record.
491  *
492  * @param cls closure
493  * @param zone_key public key of the zone
494  * @param expire when does the corresponding block in the DHT expire (until
495  *               when should we never do a DHT lookup for the same name again)?; 
496  *               GNUNET_TIME_UNIT_ZERO_ABS if there are no records of any type in the namestore,
497  *               or the expiration time of the block in the namestore (even if there are zero
498  *               records matching the desired record type)
499  * @param name name that is being mapped (at most 255 characters long)
500  * @param rd_count number of entries in 'rd' array
501  * @param rd array of records with data to store
502  * @param signature signature of the record block, NULL if signature is unavailable (i.e. 
503  *        because the user queried for a particular record type only)
504  */
505 static void 
506 zone_to_name_cb (void *cls,
507                  const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key,
508                  struct GNUNET_TIME_Absolute expire,                        
509                  const char *name,
510                  unsigned int rd_count,
511                  const struct GNUNET_NAMESTORE_RecordData *rd,
512                  const struct GNUNET_CRYPTO_RsaSignature *signature)
513 {
514   struct Request *request = cls;
515   struct GNUNET_NAMESTORE_RecordData r;
516   struct GNUNET_CRYPTO_ShortHashCode pub;
517   
518   request->qe = NULL;
519   if (NULL != name)
520   {
521     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
522                 _("Found existing name `%s' for the given key\n"),
523                 name);
524     request->phase = RP_FAIL;
525     run_httpd_now ();
526     return;
527   }
528   GNUNET_assert (GNUNET_OK ==
529                  GNUNET_CRYPTO_short_hash_from_string2 (request->public_key,
530                                                         strlen (request->public_key),
531                                                         &pub));
532   r.data = &pub;
533   r.data_size = sizeof (pub);
534   r.expiration_time = UINT64_MAX;
535   r.record_type = GNUNET_NAMESTORE_TYPE_PKEY;
536   r.flags = GNUNET_NAMESTORE_RF_AUTHORITY;
537   request->qe = GNUNET_NAMESTORE_record_create (ns,
538                                                 fcfs_zone_pkey,
539                                                 request->domain_name,
540                                                 &r,
541                                                 &put_continuation,
542                                                 request);
543 }
544
545
546 /**
547  * Process a record that was stored in the namestore.  Used to check if
548  * the requested name already exists in the namestore.  If not,
549  * proceed to check if the requested key already exists.
550  *
551  * @param cls closure
552  * @param zone_key public key of the zone
553  * @param expire when does the corresponding block in the DHT expire (until
554  *               when should we never do a DHT lookup for the same name again)?; 
555  *               GNUNET_TIME_UNIT_ZERO_ABS if there are no records of any type in the namestore,
556  *               or the expiration time of the block in the namestore (even if there are zero
557  *               records matching the desired record type)
558  * @param name name that is being mapped (at most 255 characters long)
559  * @param rd_count number of entries in 'rd' array
560  * @param rd array of records with data to store
561  * @param signature signature of the record block, NULL if signature is unavailable (i.e. 
562  *        because the user queried for a particular record type only)
563  */
564 static void 
565 lookup_result_processor (void *cls,
566                          const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key,
567                          struct GNUNET_TIME_Absolute expire,                        
568                          const char *name,
569                          unsigned int rd_count,
570                          const struct GNUNET_NAMESTORE_RecordData *rd,
571                          const struct GNUNET_CRYPTO_RsaSignature *signature)
572 {
573   struct Request *request = cls;
574   struct GNUNET_CRYPTO_ShortHashCode pub;
575   
576   request->qe = NULL;
577   GNUNET_assert (GNUNET_OK ==
578                  GNUNET_CRYPTO_short_hash_from_string2 (request->public_key,
579                                                         strlen (request->public_key),
580                                                         &pub));
581   if (0 != rd_count)
582   {
583     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
584                 _("Found %u existing records for domain `%s'\n"),
585                 rd_count,
586                 request->domain_name);
587     request->phase = RP_FAIL;
588     run_httpd_now ();
589     return;
590   }
591   request->qe = GNUNET_NAMESTORE_zone_to_name (ns,
592                                                &fcfsd_zone,
593                                                &pub,
594                                                &zone_to_name_cb,
595                                                request);
596 }
597
598
599 /**
600  * Main MHD callback for handling requests.
601  *
602  * @param cls unused
603  * @param connection MHD connection handle
604  * @param url the requested url
605  * @param method the HTTP method used ("GET", "PUT", etc.)
606  * @param version the HTTP version string (i.e. "HTTP/1.1")
607  * @param upload_data the data being uploaded (excluding HEADERS,
608  *        for a POST that fits into memory and that is encoded
609  *        with a supported encoding, the POST data will NOT be
610  *        given in upload_data and is instead available as
611  *        part of MHD_get_connection_values; very large POST
612  *        data *will* be made available incrementally in
613  *        upload_data)
614  * @param upload_data_size set initially to the size of the
615  *        upload_data provided; the method must update this
616  *        value to the number of bytes NOT processed;
617  * @param ptr pointer to location where we store the 'struct Request'
618  * @return MHD_YES if the connection was handled successfully,
619  *         MHD_NO if the socket must be closed due to a serious
620  *         error while handling the request
621  */
622 static int
623 create_response (void *cls,
624                  struct MHD_Connection *connection,
625                  const char *url,
626                  const char *method,
627                  const char *version,
628                  const char *upload_data, 
629                  size_t *upload_data_size,
630                  void **ptr)
631 {
632   struct MHD_Response *response;
633   struct Request *request;
634   int ret;
635   struct GNUNET_CRYPTO_ShortHashCode pub;
636
637   if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
638        (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) )
639     {
640       if (0 == strcmp (url, FCFS_ZONEINFO_URL))
641         ret = serve_zoneinfo_page (connection);
642       else
643         ret = serve_main_page (connection);
644       if (ret != MHD_YES)
645         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
646                     _("Failed to create page for `%s'\n"),
647                     url);
648       return ret;
649     }
650   if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
651     {   
652       request = *ptr;
653       if (NULL == request)
654       {
655         request = GNUNET_malloc (sizeof (struct Request));
656         *ptr = request;
657         request->pp = MHD_create_post_processor (connection, 1024,
658                                                  &post_iterator, request);
659         if (NULL == request->pp)
660           {
661             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
662                         _("Failed to setup post processor for `%s'\n"),
663                         url);
664             return MHD_NO; /* internal error */
665           }    
666         return MHD_YES;
667       }
668       if (NULL != request->pp)
669       {
670         /* evaluate POST data */
671         MHD_post_process (request->pp,
672                           upload_data,
673                           *upload_data_size);
674         if (0 != *upload_data_size)
675           {
676             *upload_data_size = 0;
677             return MHD_YES;
678           }
679         /* done with POST data, serve response */
680         MHD_destroy_post_processor (request->pp);
681         request->pp = NULL;
682       }
683       if (GNUNET_OK !=
684           GNUNET_CRYPTO_short_hash_from_string2 (request->public_key,
685                                                  strlen (request->public_key),
686                                                  &pub))
687       {
688         /* parse error */
689         return fill_s_reply ("Failed to parse given public key",
690                              request, connection);
691       }
692       switch (request->phase)
693         {
694         case RP_START:
695           if (NULL != strchr (request->domain_name, (int) '.'))
696           {
697             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
698                         _("Domain name must not contain `.'\n"));
699             request->phase = RP_FAIL;
700             return fill_s_reply ("Domain name must not contain `.', sorry.",
701                                  request, connection);
702           }
703           if (NULL != strchr (request->domain_name, (int) '+'))
704           {
705             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
706                         _("Domain name must not contain `+'\n"));
707             request->phase = RP_FAIL;
708             return fill_s_reply ("Domain name must not contain `+', sorry.",
709                                  request, connection);
710           }
711           request->phase = RP_LOOKUP;
712           request->qe = GNUNET_NAMESTORE_lookup_record (ns,
713                                                         &fcfsd_zone,
714                                                         request->domain_name,
715                                                         GNUNET_NAMESTORE_TYPE_PKEY,
716                                                         &lookup_result_processor,
717                                                         request);
718           break;
719         case RP_LOOKUP:
720           break;
721         case RP_PUT:
722           break;
723         case RP_FAIL:
724           return fill_s_reply ("Request failed, sorry.",
725                                request, connection);
726         case RP_SUCCESS:
727           return fill_s_reply ("Success.",
728                                request, connection);
729         default:
730           GNUNET_break (0);
731           return MHD_NO;
732         }
733         return MHD_YES; /* will have a reply later... */    
734     }
735   /* unsupported HTTP method */
736   response = MHD_create_response_from_buffer (strlen (METHOD_ERROR),
737                                               (void *) METHOD_ERROR,
738                                               MHD_RESPMEM_PERSISTENT);
739   ret = MHD_queue_response (connection, 
740                             MHD_HTTP_METHOD_NOT_ACCEPTABLE, 
741                             response);
742   MHD_destroy_response (response);
743   return ret;
744 }
745
746
747 /**
748  * Callback called upon completion of a request.
749  * Decrements session reference counter.
750  *
751  * @param cls not used
752  * @param connection connection that completed
753  * @param con_cls session handle
754  * @param toe status code
755  */
756 static void
757 request_completed_callback (void *cls,
758                             struct MHD_Connection *connection,
759                             void **con_cls,
760                             enum MHD_RequestTerminationCode toe)
761 {
762   struct Request *request = *con_cls;
763
764   if (NULL == request)
765     return;
766   if (NULL != request->pp)
767     MHD_destroy_post_processor (request->pp);
768   if (NULL != request->qe)
769     GNUNET_NAMESTORE_cancel (request->qe);
770   GNUNET_free (request);
771 }
772
773
774 #define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG
775
776
777 /**
778  * Schedule tasks to run MHD server.
779  */
780 static void
781 run_httpd ()
782 {
783   fd_set rs;
784   fd_set ws;
785   fd_set es;
786   struct GNUNET_NETWORK_FDSet *wrs;
787   struct GNUNET_NETWORK_FDSet *wws;
788   struct GNUNET_NETWORK_FDSet *wes;
789   int max;
790   int haveto;
791   UNSIGNED_MHD_LONG_LONG timeout;
792   struct GNUNET_TIME_Relative tv;
793
794   FD_ZERO (&rs);
795   FD_ZERO (&ws);
796   FD_ZERO (&es);
797   wrs = GNUNET_NETWORK_fdset_create ();
798   wes = GNUNET_NETWORK_fdset_create ();
799   wws = GNUNET_NETWORK_fdset_create ();
800   max = -1;
801   GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
802   haveto = MHD_get_timeout (httpd, &timeout);
803   if (haveto == MHD_YES)
804     tv.rel_value = (uint64_t) timeout;
805   else
806     tv = GNUNET_TIME_UNIT_FOREVER_REL;
807   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
808   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
809   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
810   httpd_task =
811       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
812                                    tv, wrs, wws,
813                                    &do_httpd, NULL);
814   GNUNET_NETWORK_fdset_destroy (wrs);
815   GNUNET_NETWORK_fdset_destroy (wws);
816   GNUNET_NETWORK_fdset_destroy (wes);
817 }
818
819
820 /**
821  * Task run whenever HTTP server operations are pending.
822  *
823  * @param cls unused
824  * @param tc scheduler context
825  */
826 static void
827 do_httpd (void *cls,
828           const struct GNUNET_SCHEDULER_TaskContext *tc)
829 {
830   httpd_task = GNUNET_SCHEDULER_NO_TASK;
831   MHD_run (httpd);
832   run_httpd ();
833 }
834
835
836 /**
837  * Task run on shutdown.  Cleans up everything.
838  *
839  * @param cls unused
840  * @param tc scheduler context
841  */
842 static void
843 do_shutdown (void *cls,
844              const struct GNUNET_SCHEDULER_TaskContext *tc)
845 {
846   if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
847   {
848     GNUNET_SCHEDULER_cancel (httpd_task);
849     httpd_task = GNUNET_SCHEDULER_NO_TASK;
850   }
851   if (NULL != ns)
852   {
853     GNUNET_NAMESTORE_disconnect (ns);
854     ns = NULL;
855   }
856   if (NULL != httpd)
857   {
858     MHD_stop_daemon (httpd);
859     httpd = NULL;
860   }
861   if (NULL != fcfs_zone_pkey)
862   {
863     GNUNET_CRYPTO_rsa_key_free (fcfs_zone_pkey);
864     fcfs_zone_pkey = NULL;
865   }
866 }
867
868
869 /**
870  * Main function that will be run.
871  *
872  * @param cls closure
873  * @param args remaining command-line arguments
874  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
875  * @param cfg configuration
876  */
877 static void
878 run (void *cls, char *const *args, const char *cfgfile,
879      const struct GNUNET_CONFIGURATION_Handle *cfg)
880 {
881   char *keyfile;
882   unsigned long long port;
883   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;
884
885   if (GNUNET_OK !=
886       GNUNET_CONFIGURATION_get_value_number (cfg,
887                                              "fcfsd",
888                                              "HTTPPORT",
889                                              &port))
890   {
891     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
892                                "fcfsd", "HTTPPORT");
893     return;
894   }
895   if (GNUNET_OK !=
896       GNUNET_CONFIGURATION_get_value_filename (cfg,
897                                                "fcfsd",
898                                                "ZONEKEY",
899                                                &keyfile))
900   {
901     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
902                                "fcfsd", "ZONEKEY");
903     return;
904   }
905   fcfs_zone_pkey = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
906   GNUNET_free (keyfile);
907   if (NULL == fcfs_zone_pkey)
908   {
909     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
910                 _("Failed to read or create private zone key\n"));
911     return;
912   }
913   GNUNET_CRYPTO_rsa_key_get_public (fcfs_zone_pkey,
914                                     &pub);
915   GNUNET_CRYPTO_short_hash (&pub, sizeof (pub), &fcfsd_zone);
916   ns = GNUNET_NAMESTORE_connect (cfg);
917   if (NULL == ns)
918     {
919       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
920                   _("Failed to connect to namestore\n"));
921       return;
922     }
923   httpd = MHD_start_daemon (MHD_USE_DEBUG,
924                             (uint16_t) port,
925                             NULL, NULL, 
926                             &create_response, NULL, 
927                             MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
928                             MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
929                             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
930                             MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (4 * 1024), 
931                             MHD_OPTION_NOTIFY_COMPLETED, &request_completed_callback, NULL,
932                             MHD_OPTION_END);
933   if (NULL == httpd)
934   {
935     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
936                 _("Failed to start HTTP server\n"));
937     GNUNET_NAMESTORE_disconnect (ns);
938     ns = NULL;
939     return;
940   }
941   run_httpd ();
942   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
943                                 &do_shutdown, NULL);
944 }
945
946
947 /**
948  * The main function for the fcfs daemon.
949  *
950  * @param argc number of arguments from the command line
951  * @param argv command line arguments
952  * @return 0 ok, 1 on error
953  */
954 int
955 main (int argc, char *const *argv)
956 {
957   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
958     GNUNET_GETOPT_OPTION_END
959   };
960
961   int ret;
962
963   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
964     return 2;
965
966   GNUNET_log_setup ("fcfsd", "WARNING", NULL);
967   ret =
968       (GNUNET_OK ==
969        GNUNET_PROGRAM_run (argc, argv, "fcfsd",
970                            _("GNUnet GNS first come first serve registration service"), 
971                            options,
972                            &run, NULL)) ? 0 : 1;
973   GNUNET_free ((void*) argv);
974   return ret;
975 }
976
977 /* end of gnunet-gns-fcfsd.c */