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