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