19205f8bdead22670051b454dfe12efbf83701d1
[oweals/gnunet.git] / src / transport / plugin_transport_http.c
1 /*
2      This file is part of GNUnet
3      (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 /**
22  * @file transport/plugin_transport_http.c
23  * @brief http transport service plugin
24  * @author Matthias Wachs
25  */
26
27 #include "platform.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_connection_lib.h"
31 #include "gnunet_server_lib.h"
32 #include "gnunet_service_lib.h"
33 #include "gnunet_statistics_service.h"
34 #include "gnunet_transport_service.h"
35 #include "gnunet_resolver_service.h"
36 #include "gnunet_server_lib.h"
37 #include "gnunet_container_lib.h"
38 #include "plugin_transport.h"
39 #include "gnunet_os_lib.h"
40 #include "microhttpd.h"
41 #include <curl/curl.h>
42
43
44 #define DEBUG_CURL GNUNET_YES
45 #define DEBUG_HTTP GNUNET_YES
46
47 /**
48  * Text of the response sent back after the last bytes of a PUT
49  * request have been received (just to formally obey the HTTP
50  * protocol).
51  */
52 #define HTTP_PUT_RESPONSE "Thank you!"
53
54 /**
55  * After how long do we expire an address that we
56  * learned from another peer if it is not reconfirmed
57  * by anyone?
58  */
59 #define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
60
61 /**
62  * Page returned if request invalid
63  */
64 #define HTTP_ERROR_RESPONSE "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\"><HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD><BODY><H1>Not Found</H1>The requested URL was not found on this server.<P><HR><ADDRESS></ADDRESS></BODY></HTML>"
65
66 /**
67  * Timeout for a http connect
68  */
69 #define HTTP_CONNECT_TIMEOUT 30
70
71 /**
72  * Network format for IPv4 addresses.
73  */
74 struct IPv4HttpAddress
75 {
76   /**
77    * IPv4 address, in network byte order.
78    */
79   uint32_t ipv4_addr GNUNET_PACKED;
80
81   /**
82    * Port number, in network byte order.
83    */
84   uint16_t u_port GNUNET_PACKED;
85
86 };
87
88
89 /**
90  * Network format for IPv6 addresses.
91  */
92 struct IPv6HttpAddress
93 {
94   /**
95    * IPv6 address.
96    */
97   struct in6_addr ipv6_addr GNUNET_PACKED;
98
99   /**
100    * Port number, in network byte order.
101    */
102   uint16_t u6_port GNUNET_PACKED;
103
104 };
105
106
107 /**
108  *  Message to send using http
109  */
110 struct HTTP_Message
111 {
112   /**
113    * next pointer for double linked list
114    */
115   struct HTTP_Message * next;
116
117   /**
118    * previous pointer for double linked list
119    */
120   struct HTTP_Message * prev;
121
122   /**
123    * buffer containing data to send
124    */
125   char *buf;
126
127   /**
128    * amount of data already sent
129    */
130   size_t pos;
131
132   /**
133    * buffer length
134    */
135   size_t size;
136   
137   /**
138    * Continuation function to call once the transmission buffer
139    * has again space available.  NULL if there is no
140    * continuation to call.
141    */
142   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
143
144   /**
145    * Closure for transmit_cont.
146    */
147   void *transmit_cont_cls;
148 };
149
150
151 struct HTTP_Connection
152 {
153   struct HTTP_Connection * next;
154
155   struct HTTP_Connection * prev;
156
157   void * addr;
158   size_t addrlen;
159
160   struct HTTP_Message * pending_msgs_head;
161   struct HTTP_Message * pending_msgs_tail;
162
163   char * url;
164   unsigned int put_connected;
165   unsigned int put_send_paused;
166
167   unsigned int get_connected;
168
169   /**
170    * curl handle for sending data using HTTP/PUT
171    * outbound data
172    */
173   CURL *put_curl_handle;
174
175   /**
176    * curl handle for recieving data using HTTP/GET transmission
177    * inbound data
178    */
179   CURL *get_curl_handle;
180
181   struct Session * session;
182
183   struct GNUNET_SERVER_MessageStreamTokenizer * msgtok;
184 };
185
186 struct HTTP_Connection_in
187 {
188   struct HTTP_Connection_in * next;
189
190   struct HTTP_Connection_in * prev;
191
192   void * addr;
193   size_t addrlen;
194
195   unsigned int connected;
196   unsigned int send_paused;
197
198   struct GNUNET_SERVER_MessageStreamTokenizer * msgtok;
199
200   struct Session * session;
201
202   /**
203    * Is there a HTTP/PUT in progress?
204    */
205   int is_put_in_progress;
206
207   /**
208    * Is the http request invalid?
209    */
210   int is_bad_request;
211 };
212
213 /**
214  * Session handle for connections.
215  */
216 struct Session
217 {
218
219   /**
220    * API requirement.
221    */
222   struct SessionHeader header;
223
224   /**
225    * Stored in a linked list.
226    */
227   struct Session *next;
228
229   /**
230    * Pointer to the global plugin struct.
231    */
232   struct Plugin *plugin;
233
234   /**
235    * To whom are we talking to (set to our identity
236    * if we are still waiting for the welcome message)
237    */
238   struct GNUNET_PeerIdentity identity;
239
240   /**
241    * Sender's ip address to distinguish between incoming connections
242    */
243   void * addr_in;
244
245   size_t addr_in_len;
246
247   void * addr_out;
248
249   size_t addr_out_len;
250
251   /**
252    * Did we initiate the connection (GNUNET_YES) or the other peer (GNUNET_NO)?
253    */
254   int is_client;
255
256   /**
257    * At what time did we reset last_received last?
258    */
259   struct GNUNET_TIME_Absolute last_quota_update;
260
261   /**
262    * How many bytes have we received since the "last_quota_update"
263    * timestamp?
264    */
265   uint64_t last_received;
266
267   /**
268    * Number of bytes per ms that this peer is allowed
269    * to send to us.
270    */
271   uint32_t quota;
272
273   /**
274    * Encoded hash
275    */
276   struct GNUNET_CRYPTO_HashAsciiEncoded hash;
277
278   /**
279    * curl handle for outbound transmissions
280    */
281   CURL *curl_handle;
282
283   /**
284    * Message tokenizer for incoming data
285    */
286   //struct GNUNET_SERVER_MessageStreamTokenizer * msgtok;
287
288   struct HTTP_Connection *outbound_connections_head;
289   struct HTTP_Connection *outbound_connections_tail;
290
291   struct HTTP_Connection_in *inbound_connections_head;
292   struct HTTP_Connection_in *inbound_connections_tail;
293 };
294
295 /**
296  * Encapsulation of all of the state of the plugin.
297  */
298 struct Plugin
299 {
300   /**
301    * Our environment.
302    */
303   struct GNUNET_TRANSPORT_PluginEnvironment *env;
304
305   unsigned int port_inbound;
306
307   /**
308    * Hashmap for all existing sessions.
309    */
310   struct GNUNET_CONTAINER_MultiHashMap *sessions;
311
312   /**
313    * Daemon for listening for new IPv4 connections.
314    */
315   struct MHD_Daemon *http_server_daemon_v4;
316
317   /**
318    * Daemon for listening for new IPv6connections.
319    */
320   struct MHD_Daemon *http_server_daemon_v6;
321
322   /**
323    * Our primary task for http daemon handling IPv4 connections
324    */
325   GNUNET_SCHEDULER_TaskIdentifier http_server_task_v4;
326
327   /**
328    * Our primary task for http daemon handling IPv6 connections
329    */
330   GNUNET_SCHEDULER_TaskIdentifier http_server_task_v6;
331
332   /**
333    * The task sending data
334    */
335   GNUNET_SCHEDULER_TaskIdentifier http_server_task_send;
336
337   /**
338    * cURL Multihandle
339    */
340   CURLM * multi_handle;
341
342   /**
343    * Our ASCII encoded, hashed peer identity
344    * This string is used to distinguish between connections and is added to the urls
345    */
346   struct GNUNET_CRYPTO_HashAsciiEncoded my_ascii_hash_ident;
347 };
348
349
350 /**
351  * Function called for a quick conversion of the binary address to
352  * a numeric address.  Note that the caller must not free the
353  * address and that the next call to this function is allowed
354  * to override the address again.
355  *
356  * @param cls closure
357  * @param addr binary address
358  * @param addrlen length of the address
359  * @return string representing the same address
360  */
361 static const char*
362 http_plugin_address_to_string (void *cls,
363                                    const void *addr,
364                                    size_t addrlen);
365
366 /**
367  * Create a new session
368  * @param cls plugin as closure
369  * @param addr_in address the peer is using inbound
370  * @param addr_len_in address length
371  * @param addr_out address the peer is using outbound
372  * @param addr_len_out address length
373  * @param peer identity
374  * @return created session object
375  */
376 static struct Session * 
377 create_session (void * cls, 
378                 char * addr_in, 
379                 size_t addrlen_in,
380                 char * addr_out, 
381                 size_t addrlen_out, 
382                 const struct GNUNET_PeerIdentity *peer)
383 {
384   struct Plugin *plugin = cls;
385   struct Session * cs = GNUNET_malloc ( sizeof( struct Session) );
386
387   GNUNET_assert(cls !=NULL);
388   if (addrlen_in != 0)
389   {
390     cs->addr_in = GNUNET_malloc (addrlen_in);
391     cs->addr_in_len = addrlen_in;
392     memcpy(cs->addr_in,addr_in,addrlen_in);
393   }
394
395   if (addrlen_out != 0)
396   {
397     cs->addr_out = GNUNET_malloc (addrlen_out);
398     cs->addr_out_len = addrlen_out;
399     memcpy(cs->addr_out,addr_out,addrlen_out);
400   }
401   cs->plugin = plugin;
402   memcpy(&cs->identity, peer, sizeof (struct GNUNET_PeerIdentity));
403   GNUNET_CRYPTO_hash_to_enc(&cs->identity.hashPubKey,&(cs->hash));
404   cs->outbound_connections_head = NULL;
405   cs->outbound_connections_tail = NULL;
406   return cs;
407 }
408
409 /**
410  * Check if session for this peer is already existing, otherwise create it
411  * @param cls the plugin used
412  * @param p peer to get session for
413  * @return session found or created
414  */
415 static struct Session * session_get (void * cls, const struct GNUNET_PeerIdentity *p)
416 {
417   struct Plugin *plugin = cls;
418   struct Session *cs;
419   unsigned int res;
420
421   cs = GNUNET_CONTAINER_multihashmap_get (plugin->sessions, &p->hashPubKey);
422   if (cs == NULL)
423   {
424     cs = create_session(plugin, NULL, 0, NULL, 0, p);
425     res = GNUNET_CONTAINER_multihashmap_put ( plugin->sessions,
426                                         &cs->identity.hashPubKey,
427                                         cs,
428                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
429     if (res == GNUNET_OK)
430       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
431                   "New Session `%s' inserted\n", GNUNET_i2s(p));
432   }
433   return cs;
434 }
435
436 static char * create_url(void * cls, const void * addr, size_t addrlen)
437 {
438   struct Plugin *plugin = cls;
439   char *url = NULL;
440
441   GNUNET_assert ((addr!=NULL) && (addrlen != 0));
442   GNUNET_asprintf(&url,
443                   "http://%s/%s",
444                   http_plugin_address_to_string(NULL, addr, addrlen),
445                   (char *) (&plugin->my_ascii_hash_ident));
446
447   return url;
448 }
449
450 /**
451  * Check if session already knows this address for a outbound connection to this peer
452  * If address not in session, add it to the session
453  * @param cls the plugin used
454  * @param cs the session
455  * @param addr address
456  * @param addr_len address length
457  * @return the found or created address
458  */
459 static struct HTTP_Connection * session_check_outbound_address (void * cls, struct Session *cs, const void * addr, size_t addr_len)
460 {
461   struct Plugin *plugin = cls;
462   struct HTTP_Connection * cc = cs->outbound_connections_head;
463   struct HTTP_Connection * con = NULL;
464
465   GNUNET_assert((addr_len == sizeof (struct IPv4HttpAddress)) || (addr_len == sizeof (struct IPv6HttpAddress)));
466
467   while (cc!=NULL)
468   {
469     if (addr_len == cc->addrlen)
470     {
471       if (0 == memcmp(cc->addr, addr, addr_len))
472       {
473         con = cc;
474         break;
475       }
476     }
477     cc=cc->next;
478   }
479
480   if (con==NULL)
481   {
482     con = GNUNET_malloc(sizeof(struct HTTP_Connection) + addr_len);
483     con->addrlen = addr_len;
484     con->addr=&con[1];
485     con->url=create_url(plugin, addr, addr_len);
486     con->put_curl_handle = NULL;
487     con->put_connected = GNUNET_NO;
488     con->get_curl_handle = NULL;
489     con->get_connected = GNUNET_NO;
490     con->session = cs;
491     memcpy(con->addr, addr, addr_len);
492     GNUNET_CONTAINER_DLL_insert(cs->outbound_connections_head,cs->outbound_connections_tail,con);
493     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Created new connection %X to peer `%s'\n",con,GNUNET_i2s(&cs->identity));
494   }
495   return con;
496 }
497
498
499 /**
500  * Check if session already knows this address for a inbound connection to this peer
501  * If address not in session, add it to the session
502  * @param cls not needed, can be NULL
503  * @param cs the session
504  * @param addr address
505  * @param addr_len address length
506  * @return the found or created address
507  */
508 static struct HTTP_Connection_in * session_check_inbound_address (void * cls, struct Session *cs, const void * addr, size_t addr_len)
509 {
510   struct HTTP_Connection_in * cc = cs->inbound_connections_head;
511   struct HTTP_Connection_in * con = NULL;
512
513   GNUNET_assert((addr_len == sizeof (struct IPv4HttpAddress)) || (addr_len == sizeof (struct IPv6HttpAddress)));
514
515   while (cc!=NULL)
516   {
517     if (addr_len == cc->addrlen)
518     {
519       if (0 == memcmp(cc->addr, addr, addr_len))
520       {
521         con = cc;
522         break;
523       }
524     }
525     cc=cc->next;
526   }
527
528
529   if (con==NULL)
530   {
531     con = GNUNET_malloc(sizeof(struct HTTP_Connection_in) + addr_len);
532     con->addrlen = addr_len;
533     con->addr=&con[1];
534     con->connected = GNUNET_NO;
535     con->session = cs;
536     memcpy(con->addr, addr, addr_len);
537     GNUNET_CONTAINER_DLL_insert(cs->inbound_connections_head,cs->inbound_connections_tail,con);
538   }
539
540   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X for inbound address %s (%s) was found\n",
541               con,
542               GNUNET_i2s(&cs->identity),
543               http_plugin_address_to_string(NULL,con->addr,con->addrlen));
544   return con;
545 }
546
547
548 /**
549  * Callback called by MHD when a connection is terminated
550  */
551 static void requestCompletedCallback (void *cls, struct MHD_Connection * connection, void **httpSessionCache)
552 {
553   struct HTTP_Connection_in * con;
554
555   con = *httpSessionCache;
556   if (con == NULL)
557     return;
558   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection from peer `%s' was terminated\n",GNUNET_i2s(&con->session->identity));
559   /* session set to inactive */
560   con->is_put_in_progress = GNUNET_NO;
561   con->is_bad_request = GNUNET_NO;
562 }
563
564 static void messageTokenizerCallback (void *cls,
565                                       void *client,
566                                       const struct GNUNET_MessageHeader *message)
567 {
568   struct HTTP_Connection_in * con = cls;
569   GNUNET_assert(con != NULL);
570
571   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
572               "Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n",
573               ntohs(message->type),
574               ntohs(message->size),
575               GNUNET_i2s(&(con->session->identity)),http_plugin_address_to_string(NULL,con->addr,con->addrlen));
576
577   con->session->plugin->env->receive (con->session->plugin->env->cls,
578                             &con->session->identity,
579                             message, 1, con->session,
580                             NULL,
581                             0);
582 }
583
584 /**
585  * Check if ip is allowed to connect.
586  */
587 static int
588 acceptPolicyCallback (void *cls,
589                       const struct sockaddr *addr, socklen_t addr_len)
590 {
591 #if 0
592   struct Plugin *plugin = cls;
593 #endif
594   /* Every connection is accepted, nothing more to do here */
595   return MHD_YES;
596 }
597
598 int server_read_callback (void *cls, uint64_t pos, char *buf, int max)
599 {
600   static int i = 0;
601
602   char * test ="Hello World!";
603   int bytes_read = -1;
604   if (i<10)
605   {
606     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "server_read_callback\n");
607     memcpy(buf,test,strlen(test));
608     bytes_read = strlen(test);
609     i++;
610   }
611   return bytes_read;
612 }
613
614 /**
615  * Process GET or PUT request received via MHD.  For
616  * GET, queue response that will send back our pending
617  * messages.  For PUT, process incoming data and send
618  * to GNUnet core.  In either case, check if a session
619  * already exists and create a new one if not.
620  */
621 static int
622 accessHandlerCallback (void *cls,
623                        struct MHD_Connection *mhd_connection,
624                        const char *url,
625                        const char *method,
626                        const char *version,
627                        const char *upload_data,
628                        size_t * upload_data_size, void **httpSessionCache)
629 {
630   struct Plugin *plugin = cls;
631   struct MHD_Response *response;
632   struct Session * cs;
633   struct HTTP_Connection_in * con;
634   const union MHD_ConnectionInfo * conn_info;
635   struct sockaddr_in  *addrin;
636   struct sockaddr_in6 *addrin6;
637   char address[INET6_ADDRSTRLEN+14];
638   struct GNUNET_PeerIdentity pi_in;
639   int res = GNUNET_NO;
640   int send_error_to_client;
641   struct IPv4HttpAddress ipv4addr;
642   struct IPv6HttpAddress ipv6addr;
643
644   GNUNET_assert(cls !=NULL);
645   send_error_to_client = GNUNET_NO;
646   if (NULL == *httpSessionCache)
647   {
648     /* check url for peer identity , if invalid send HTTP 404*/
649     res = GNUNET_CRYPTO_hash_from_string ( &url[1], &(pi_in.hashPubKey));
650     if ( GNUNET_SYSERR == res )
651     {
652       response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
653       res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response);
654       MHD_destroy_response (response);
655       if (res == MHD_YES)
656         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident, sent HTTP 1.1/404\n");
657       else
658         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident, could not send error\n");
659       return res;
660     }
661   }
662   else
663   {
664     con = *httpSessionCache;
665     cs = con->session;
666   }
667
668   if (NULL == *httpSessionCache)
669   {
670     /* get session for peer identity */
671     cs = session_get (plugin ,&pi_in);
672
673     conn_info = MHD_get_connection_info(mhd_connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS );
674     /* Incoming IPv4 connection */
675     if ( AF_INET == conn_info->client_addr->sin_family)
676     {
677       addrin = conn_info->client_addr;
678       inet_ntop(addrin->sin_family, &(addrin->sin_addr),address,INET_ADDRSTRLEN);
679       memcpy(&ipv4addr.ipv4_addr,&(addrin->sin_addr),sizeof(struct in_addr));
680       ipv4addr.u_port = addrin->sin_port;
681       con = session_check_inbound_address (plugin, cs, (const void *) &ipv4addr, sizeof (struct IPv4HttpAddress));
682     }
683     /* Incoming IPv6 connection */
684     if ( AF_INET6 == conn_info->client_addr->sin_family)
685     {
686       addrin6 = (struct sockaddr_in6 *) conn_info->client_addr;
687       inet_ntop(addrin6->sin6_family, &(addrin6->sin6_addr),address,INET6_ADDRSTRLEN);
688       memcpy(&ipv6addr.ipv6_addr,&(addrin6->sin6_addr),sizeof(struct in6_addr));
689       ipv6addr.u6_port = addrin6->sin6_port;
690       con = session_check_inbound_address (plugin, cs, &ipv6addr, sizeof (struct IPv6HttpAddress));
691     }
692     /* Set closure and update current session*/
693
694     *httpSessionCache = con;
695     if (con->msgtok==NULL)
696       con->msgtok = GNUNET_SERVER_mst_create (GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, &messageTokenizerCallback, con);
697
698     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"HTTP Daemon has new an incoming `%s' request from peer `%s' (`%s')\n",
699                 method,
700                 GNUNET_i2s(&cs->identity),
701                 http_plugin_address_to_string(NULL, con->addr, con->addrlen));
702   }
703
704   /* Is it a PUT or a GET request */
705   if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
706   {
707     if ((*upload_data_size == 0) && (con->is_put_in_progress==GNUNET_NO))
708     {
709       con->is_put_in_progress = GNUNET_YES;
710       return MHD_YES;
711     }
712
713     /* Transmission of all data complete */
714     if ((*upload_data_size == 0) && (con->is_put_in_progress == GNUNET_YES))
715     {
716         response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
717         res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
718         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Sent HTTP/1.1: 200 OK as PUT Response\n",HTTP_PUT_RESPONSE, strlen (HTTP_PUT_RESPONSE), res );
719         MHD_destroy_response (response);
720         return MHD_YES;
721
722       con->is_put_in_progress = GNUNET_NO;
723       con->is_bad_request = GNUNET_NO;
724       return res;
725     }
726
727     /* Recieving data */
728     if ((*upload_data_size > 0) && (con->is_put_in_progress == GNUNET_YES))
729     {
730       res = GNUNET_SERVER_mst_receive(con->msgtok, con, upload_data,*upload_data_size, GNUNET_NO, GNUNET_NO);
731       (*upload_data_size) = 0;
732       return MHD_YES;
733     }
734     else
735       return MHD_NO;
736   }
737   if ( 0 == strcmp (MHD_HTTP_METHOD_GET, method) )
738   {
739     response = MHD_create_response_from_callback(-1,32 * 1024, &server_read_callback, cs, NULL);
740     res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
741     MHD_destroy_response (response);
742
743     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"HTTP Daemon has new an incoming `%s' request from peer `%s' (`%s')\n",
744                 method,
745                 GNUNET_i2s(&cs->identity),
746                 http_plugin_address_to_string(NULL, con->addr, con->addrlen));
747
748
749     return res;
750
751   }
752   return MHD_NO;
753 }
754
755
756 /**
757  * Call MHD to process pending ipv4 requests and then go back
758  * and schedule the next run.
759  */
760 static void http_server_daemon_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
761 /**
762  * Call MHD to process pending ipv6 requests and then go back
763  * and schedule the next run.
764  */
765 static void http_server_daemon_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
766
767 /**
768  * Function that queries MHD's select sets and
769  * starts the task waiting for them.
770  */
771 static GNUNET_SCHEDULER_TaskIdentifier
772 http_server_daemon_prepare (void * cls, struct MHD_Daemon *daemon_handle)
773 {
774   struct Plugin *plugin = cls;
775   GNUNET_SCHEDULER_TaskIdentifier ret;
776   fd_set rs;
777   fd_set ws;
778   fd_set es;
779   struct GNUNET_NETWORK_FDSet *wrs;
780   struct GNUNET_NETWORK_FDSet *wws;
781   struct GNUNET_NETWORK_FDSet *wes;
782   int max;
783   unsigned long long timeout;
784   int haveto;
785   struct GNUNET_TIME_Relative tv;
786
787   GNUNET_assert(cls !=NULL);
788   ret = GNUNET_SCHEDULER_NO_TASK;
789   FD_ZERO(&rs);
790   FD_ZERO(&ws);
791   FD_ZERO(&es);
792   wrs = GNUNET_NETWORK_fdset_create ();
793   wes = GNUNET_NETWORK_fdset_create ();
794   wws = GNUNET_NETWORK_fdset_create ();
795   max = -1;
796   GNUNET_assert (MHD_YES ==
797                  MHD_get_fdset (daemon_handle,
798                                 &rs,
799                                 &ws,
800                                 &es,
801                                 &max));
802   haveto = MHD_get_timeout (daemon_handle, &timeout);
803   if (haveto == MHD_YES)
804     tv.value = (uint64_t) timeout;
805   else
806     tv = GNUNET_TIME_UNIT_FOREVER_REL;
807   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max);
808   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max);
809   GNUNET_NETWORK_fdset_copy_native (wes, &es, max);
810   if (daemon_handle == plugin->http_server_daemon_v4)
811   {
812     ret = GNUNET_SCHEDULER_add_select (plugin->env->sched,
813                                        GNUNET_SCHEDULER_PRIORITY_DEFAULT,
814                                        GNUNET_SCHEDULER_NO_TASK,
815                                        tv,
816                                        wrs,
817                                        wws,
818                                        &http_server_daemon_v4_run,
819                                        plugin);
820   }
821   if (daemon_handle == plugin->http_server_daemon_v6)
822   {
823     ret = GNUNET_SCHEDULER_add_select (plugin->env->sched,
824                                        GNUNET_SCHEDULER_PRIORITY_DEFAULT,
825                                        GNUNET_SCHEDULER_NO_TASK,
826                                        tv,
827                                        wrs,
828                                        wws,
829                                        &http_server_daemon_v6_run,
830                                        plugin);
831   }
832   GNUNET_NETWORK_fdset_destroy (wrs);
833   GNUNET_NETWORK_fdset_destroy (wws);
834   GNUNET_NETWORK_fdset_destroy (wes);
835   return ret;
836 }
837
838 /**
839  * Call MHD to process pending requests and then go back
840  * and schedule the next run.
841  */
842 static void http_server_daemon_v4_run (void *cls,
843                              const struct GNUNET_SCHEDULER_TaskContext *tc)
844 {
845   struct Plugin *plugin = cls;
846
847   GNUNET_assert(cls !=NULL);
848   if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
849     plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
850
851   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
852     return;
853
854   GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v4));
855   plugin->http_server_task_v4 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
856   return;
857 }
858
859
860 /**
861  * Call MHD to process pending requests and then go back
862  * and schedule the next run.
863  */
864 static void http_server_daemon_v6_run (void *cls,
865                              const struct GNUNET_SCHEDULER_TaskContext *tc)
866 {
867   struct Plugin *plugin = cls;
868
869   GNUNET_assert(cls !=NULL);
870   if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
871     plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
872
873   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
874     return;
875
876   GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v6));
877   plugin->http_server_task_v6 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
878   return;
879 }
880
881 /**
882  * Removes a message from the linked list of messages
883  * @param con connection to remove message from
884  * @param msg message to remove
885  * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
886  */
887
888 static int remove_http_message(struct HTTP_Connection * con, struct HTTP_Message * msg)
889 {
890   GNUNET_CONTAINER_DLL_remove(con->pending_msgs_head,con->pending_msgs_tail,msg);
891   GNUNET_free(msg);
892   return GNUNET_OK;
893 }
894
895
896 static size_t curl_header_function( void *ptr, size_t size, size_t nmemb, void *stream)
897 {
898   struct HTTP_Connection * con = stream;
899
900   char * tmp;
901   size_t len = size * nmemb;
902   long http_result = 0;
903   int res;
904
905   /* Getting last http result code */
906   GNUNET_assert(NULL!=con);
907   res = curl_easy_getinfo(con->get_curl_handle, CURLINFO_RESPONSE_CODE, &http_result);
908
909   if ((CURLE_OK == res) && (con->get_connected==GNUNET_NO))
910   {
911     if (http_result == 200)
912     {
913       con->get_connected = GNUNET_YES;
914       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound connected\n",con);
915     }
916     else
917     {
918       con->get_connected = GNUNET_NO;
919       //GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound connected\n",con);
920     }
921   }
922
923   tmp = NULL;
924   if ((size * nmemb) < SIZE_MAX)
925     tmp = GNUNET_malloc (len+1);
926
927   if ((tmp != NULL) && (len > 0))
928   {
929     memcpy(tmp,ptr,len);
930     if (len>=2)
931     {
932       if (tmp[len-2] == 13)
933         tmp[len-2]= '\0';
934     }
935 #if DEBUG_HTTP
936     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Header: `%s' %u \n",tmp, http_result);
937 #endif
938   }
939   if (NULL != tmp)
940     GNUNET_free (tmp);
941
942   return size * nmemb;
943 }
944
945 /**
946  * Callback method used with libcurl
947  * Method is called when libcurl needs to read data during sending
948  * @param stream pointer where to write data
949  * @param size size of an individual element
950  * @param nmemb count of elements that can be written to the buffer
951  * @param ptr source pointer, passed to the libcurl handle
952  * @return bytes written to stream
953  */
954 static size_t send_curl_read_callback(void *stream, size_t size, size_t nmemb, void *ptr)
955 {
956   struct HTTP_Connection * con = ptr;
957   struct HTTP_Message * msg = con->pending_msgs_tail;
958   size_t bytes_sent;
959   size_t len;
960
961   if (con->pending_msgs_tail == NULL)
962   {
963     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: No Message to send, pausing connection\n",con);
964     con->put_send_paused = GNUNET_YES;
965     return CURL_READFUNC_PAUSE;
966   }
967
968   msg = con->pending_msgs_tail;
969   /* data to send */
970   if (msg->pos < msg->size)
971   {
972     /* data fit in buffer */
973     if ((msg->size - msg->pos) <= (size * nmemb))
974     {
975       len = (msg->size - msg->pos);
976       memcpy(stream, &msg->buf[msg->pos], len);
977       msg->pos += len;
978       bytes_sent = len;
979     }
980     else
981     {
982       len = size*nmemb;
983       memcpy(stream, &msg->buf[msg->pos], len);
984       msg->pos += len;
985       bytes_sent = len;
986     }
987   }
988   /* no data to send */
989   else
990   {
991     bytes_sent = 0;
992   }
993
994   if ( msg->pos == msg->size)
995   {
996     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: Message with %u bytes sent, removing message from queue \n",con, msg->pos);
997     /* Calling transmit continuation  */
998     if (( NULL != con->pending_msgs_tail) && (NULL != con->pending_msgs_tail->transmit_cont))
999       msg->transmit_cont (con->pending_msgs_tail->transmit_cont_cls,&(con->session)->identity,GNUNET_OK);
1000     remove_http_message(con, msg);
1001   }
1002   return bytes_sent;
1003 }
1004
1005 /**
1006 * Callback method used with libcurl
1007 * Method is called when libcurl needs to write data during sending
1008 * @param stream pointer where to write data
1009 * @param size size of an individual element
1010 * @param nmemb count of elements that can be written to the buffer
1011 * @param ptr destination pointer, passed to the libcurl handle
1012 * @return bytes read from stream
1013 */
1014 static size_t send_curl_write_callback( void *stream, size_t size, size_t nmemb, void *ptr)
1015 {
1016   struct HTTP_Connection * con = ptr;
1017   char * data = NULL;
1018
1019   data = GNUNET_malloc(size*nmemb +1);
1020   if (data != NULL)
1021   {
1022     memcpy( data, stream, size*nmemb);
1023     data[size*nmemb] = '\0';
1024     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: recieved %s\n",con,data);
1025     free (data);
1026   }
1027   return (size * nmemb);
1028
1029 }
1030
1031 /**
1032  * Function setting up file descriptors and scheduling task to run
1033  * @param cls closure
1034  * @param ses session to send data to
1035  * @return bytes sent to peer
1036  */
1037 static size_t send_schedule(void *cls, struct Session* ses );
1038
1039
1040
1041 /**
1042  * Function setting up curl handle and selecting message to send
1043  * @param cls plugin
1044  * @param ses session to send data to
1045  * @param con connection
1046  * @return bytes sent to peer
1047  */
1048 static ssize_t send_check_connections (void *cls, struct Session* ses , struct HTTP_Connection *con)
1049 {
1050   struct Plugin *plugin = cls;
1051   int bytes_sent = 0;
1052   CURLMcode mret;
1053   struct HTTP_Message * msg;
1054   struct GNUNET_TIME_Relative timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
1055
1056   GNUNET_assert(cls !=NULL);
1057
1058   if (con->get_connected == GNUNET_NO)
1059   {
1060       if (con->get_curl_handle == NULL)
1061       {
1062         con->get_curl_handle = curl_easy_init();
1063 #if DEBUG_CURL
1064       curl_easy_setopt(con->get_curl_handle, CURLOPT_VERBOSE, 1L);
1065 #endif
1066       curl_easy_setopt(con->get_curl_handle, CURLOPT_URL, con->url);
1067       //curl_easy_setopt(con->put_curl_handle, CURLOPT_PUT, 1L);
1068       curl_easy_setopt(con->get_curl_handle, CURLOPT_HEADERFUNCTION, &curl_header_function);
1069       curl_easy_setopt(con->get_curl_handle, CURLOPT_WRITEHEADER, con);
1070       curl_easy_setopt(con->get_curl_handle, CURLOPT_READFUNCTION, send_curl_read_callback);
1071       curl_easy_setopt(con->get_curl_handle, CURLOPT_READDATA, con);
1072       curl_easy_setopt(con->get_curl_handle, CURLOPT_WRITEFUNCTION, send_curl_write_callback);
1073       curl_easy_setopt(con->get_curl_handle, CURLOPT_WRITEDATA, con);
1074       curl_easy_setopt(con->get_curl_handle, CURLOPT_TIMEOUT, (long) timeout.value);
1075       curl_easy_setopt(con->get_curl_handle, CURLOPT_PRIVATE, con);
1076       curl_easy_setopt(con->get_curl_handle, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT);
1077       curl_easy_setopt(con->get_curl_handle, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE);
1078
1079       mret = curl_multi_add_handle(plugin->multi_handle, con->get_curl_handle);
1080       if (mret != CURLM_OK)
1081       {
1082         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1083                     _("%s failed at %s:%d: `%s'\n"),
1084                     "curl_multi_add_handle", __FILE__, __LINE__,
1085                     curl_multi_strerror (mret));
1086         return -1;
1087       }
1088       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound not connected, initiating connection\n",con);
1089     }
1090   }
1091
1092   /* PUT already connected, no need to initiate connection */
1093   if ((con->put_connected == GNUNET_YES) && (con->put_curl_handle != NULL))
1094   {
1095     if (con->put_send_paused == GNUNET_NO)
1096     {
1097       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound active, enqueueing message\n",con);
1098       return bytes_sent;
1099     }
1100     if (con->put_send_paused == GNUNET_YES)
1101     {
1102       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound paused, unpausing existing connection and enqueueing message\n",con);
1103       curl_easy_pause(con->put_curl_handle,CURLPAUSE_CONT);
1104       con->put_send_paused=GNUNET_NO;
1105       return bytes_sent;
1106     }
1107   }
1108
1109   /* not connected, initiate connection */
1110   if ( NULL == con->put_curl_handle)
1111     con->put_curl_handle = curl_easy_init();
1112   GNUNET_assert (con->put_curl_handle != NULL);
1113   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound not connected, initiating connection\n",con);
1114
1115   GNUNET_assert (NULL != con->pending_msgs_tail);
1116   msg = con->pending_msgs_tail;
1117
1118 #if DEBUG_CURL
1119   curl_easy_setopt(con->put_curl_handle, CURLOPT_VERBOSE, 1L);
1120 #endif
1121   curl_easy_setopt(con->put_curl_handle, CURLOPT_URL, con->url);
1122   curl_easy_setopt(con->put_curl_handle, CURLOPT_PUT, 1L);
1123   curl_easy_setopt(con->put_curl_handle, CURLOPT_READFUNCTION, send_curl_read_callback);
1124   curl_easy_setopt(con->put_curl_handle, CURLOPT_READDATA, con);
1125   curl_easy_setopt(con->put_curl_handle, CURLOPT_WRITEFUNCTION, send_curl_write_callback);
1126   curl_easy_setopt(con->put_curl_handle, CURLOPT_READDATA, con);
1127   curl_easy_setopt(con->put_curl_handle, CURLOPT_TIMEOUT, (long) timeout.value);
1128   curl_easy_setopt(con->put_curl_handle, CURLOPT_PRIVATE, con);
1129   curl_easy_setopt(con->put_curl_handle, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT);
1130   curl_easy_setopt(con->put_curl_handle, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE);
1131
1132   mret = curl_multi_add_handle(plugin->multi_handle, con->put_curl_handle);
1133   if (mret != CURLM_OK)
1134   {
1135     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1136                 _("%s failed at %s:%d: `%s'\n"),
1137                 "curl_multi_add_handle", __FILE__, __LINE__,
1138                 curl_multi_strerror (mret));
1139     return -1;
1140   }
1141   con->put_connected = GNUNET_YES;
1142   bytes_sent = send_schedule (plugin, ses);
1143   return bytes_sent;
1144 }
1145
1146 static void send_execute (void *cls,
1147              const struct GNUNET_SCHEDULER_TaskContext *tc)
1148 {
1149   struct Plugin *plugin = cls;
1150   static unsigned int handles_last_run;
1151   int running;
1152   struct CURLMsg *msg;
1153   CURLMcode mret;
1154   struct HTTP_Connection * con = NULL;
1155   struct Session * cs = NULL;
1156   long http_result;
1157
1158   GNUNET_assert(cls !=NULL);
1159   plugin->http_server_task_send = GNUNET_SCHEDULER_NO_TASK;
1160   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1161     return;
1162
1163   do
1164     {
1165       running = 0;
1166       mret = curl_multi_perform (plugin->multi_handle, &running);
1167       if (running < handles_last_run)
1168         {
1169           do
1170             {
1171
1172               msg = curl_multi_info_read (plugin->multi_handle, &running);
1173               if (msg == NULL)
1174                 break;
1175               /* get session for affected curl handle */
1176               GNUNET_assert ( msg->easy_handle != NULL );
1177               curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char *) &con);
1178               GNUNET_assert ( con != NULL );
1179               cs = con->session;
1180               GNUNET_assert ( cs != NULL );
1181               switch (msg->msg)
1182                 {
1183
1184                 case CURLMSG_DONE:
1185                   if ( (msg->data.result != CURLE_OK) &&
1186                        (msg->data.result != CURLE_GOT_NOTHING) )
1187                   {
1188                     /* sending msg failed*/
1189                     if (msg->easy_handle == con->put_curl_handle)
1190                     {
1191                       GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1192                                  _("Connection %X: HTTP PUT to peer `%s' (`%s') failed: `%s' `%s'\n"),
1193                                  con,
1194                                  GNUNET_i2s(&cs->identity),
1195                                  http_plugin_address_to_string(NULL, con->addr, con->addrlen),
1196                                  "curl_multi_perform",
1197                                  curl_easy_strerror (msg->data.result));
1198
1199                       con->put_connected = GNUNET_NO;
1200                       curl_easy_cleanup(con->put_curl_handle);
1201                       con->put_curl_handle=NULL;
1202                       if (( NULL != con->pending_msgs_tail) && ( NULL != con->pending_msgs_tail->transmit_cont))
1203                         con->pending_msgs_tail->transmit_cont (con->pending_msgs_tail->transmit_cont_cls,&con->session->identity,GNUNET_SYSERR);
1204                     }
1205                     /* GET connection failed */
1206                     if (msg->easy_handle == con->get_curl_handle)
1207                     {
1208                       GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1209                            _("Connection %X: HTTP GET to peer `%s' (`%s') failed: `%s' `%s'\n"),
1210                            con,
1211                            GNUNET_i2s(&cs->identity),
1212                            http_plugin_address_to_string(NULL, con->addr, con->addrlen),
1213                            "curl_multi_perform",
1214                            curl_easy_strerror (msg->data.result));
1215                       con->get_connected = GNUNET_NO;
1216                       curl_easy_cleanup(con->get_curl_handle);
1217                       con->get_curl_handle=NULL;
1218                     }
1219                   }
1220                   else
1221                   {
1222                     if (msg->easy_handle == con->put_curl_handle)
1223                     {
1224                       GNUNET_assert (CURLE_OK == curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &http_result));
1225                       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1226                                   "Connection %X: HTTP PUT connection to peer `%s' (`%s') was closed with HTTP code %u\n",
1227                                    con,
1228                                    GNUNET_i2s(&cs->identity),
1229                                    http_plugin_address_to_string(NULL, con->addr, con->addrlen),
1230                                    http_result);
1231
1232                       /* Calling transmit continuation  */
1233                       if (( NULL != con->pending_msgs_tail) && (NULL != con->pending_msgs_tail->transmit_cont))
1234                       {
1235                         /* HTTP 1xx : Last message before here was informational */
1236                         if ((http_result >=100) && (http_result < 200))
1237                           con->pending_msgs_tail->transmit_cont (con->pending_msgs_tail->transmit_cont_cls,&cs->identity,GNUNET_OK);
1238                         /* HTTP 2xx: successful operations */
1239                         if ((http_result >=200) && (http_result < 300))
1240                           con->pending_msgs_tail->transmit_cont (con->pending_msgs_tail->transmit_cont_cls,&cs->identity,GNUNET_OK);
1241                         /* HTTP 3xx..5xx: error */
1242                         if ((http_result >=300) && (http_result < 600))
1243                           con->pending_msgs_tail->transmit_cont (con->pending_msgs_tail->transmit_cont_cls,&cs->identity,GNUNET_SYSERR);
1244                       }
1245                       curl_easy_cleanup(con->put_curl_handle);
1246                       con->put_connected = GNUNET_NO;
1247                       con->put_curl_handle=NULL;
1248                     }
1249                     if (msg->easy_handle == con->get_curl_handle)
1250                     {
1251                       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1252                                   "Connection %X: HTTP GET connection to peer `%s' (`%s') was closed with HTTP code %u\n",
1253                                    con,
1254                                    GNUNET_i2s(&cs->identity),
1255                                    http_plugin_address_to_string(NULL, con->addr, con->addrlen),
1256                                    http_result);
1257
1258                       con->get_connected = GNUNET_NO;
1259                       curl_easy_cleanup(con->get_curl_handle);
1260                       con->get_curl_handle=NULL;
1261                     }
1262                   }
1263                   if (con->pending_msgs_tail != NULL)
1264                   {
1265                     if (con->pending_msgs_tail->pos>0)
1266                       remove_http_message(con, con->pending_msgs_tail);
1267                     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Message could not be removed from session `%s'\n", GNUNET_i2s(&cs->identity));
1268                   }
1269                   return;
1270                 default:
1271                   break;
1272                 }
1273
1274             }
1275           while ( (running > 0) );
1276         }
1277       handles_last_run = running;
1278     }
1279   while (mret == CURLM_CALL_MULTI_PERFORM);
1280   send_schedule(plugin, cls);
1281 }
1282
1283
1284 /**
1285  * Function setting up file descriptors and scheduling task to run
1286  * @param ses session to send data to
1287  * @return bytes sent to peer
1288  */
1289 static size_t send_schedule(void *cls, struct Session* ses )
1290 {
1291   struct Plugin *plugin = cls;
1292   fd_set rs;
1293   fd_set ws;
1294   fd_set es;
1295   int max;
1296   struct GNUNET_NETWORK_FDSet *grs;
1297   struct GNUNET_NETWORK_FDSet *gws;
1298   long to;
1299   CURLMcode mret;
1300
1301   GNUNET_assert(cls !=NULL);
1302   max = -1;
1303   FD_ZERO (&rs);
1304   FD_ZERO (&ws);
1305   FD_ZERO (&es);
1306   mret = curl_multi_fdset (plugin->multi_handle, &rs, &ws, &es, &max);
1307   if (mret != CURLM_OK)
1308     {
1309       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1310                   _("%s failed at %s:%d: `%s'\n"),
1311                   "curl_multi_fdset", __FILE__, __LINE__,
1312                   curl_multi_strerror (mret));
1313       return -1;
1314     }
1315   mret = curl_multi_timeout (plugin->multi_handle, &to);
1316   if (mret != CURLM_OK)
1317     {
1318       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1319                   _("%s failed at %s:%d: `%s'\n"),
1320                   "curl_multi_timeout", __FILE__, __LINE__,
1321                   curl_multi_strerror (mret));
1322       return -1;
1323     }
1324
1325   grs = GNUNET_NETWORK_fdset_create ();
1326   gws = GNUNET_NETWORK_fdset_create ();
1327   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1328   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1329   plugin->http_server_task_send = GNUNET_SCHEDULER_add_select (plugin->env->sched,
1330                                    GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1331                                    GNUNET_SCHEDULER_NO_TASK,
1332                                    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 0),
1333                                    grs,
1334                                    gws,
1335                                    &send_execute,
1336                                    plugin);
1337   GNUNET_NETWORK_fdset_destroy (gws);
1338   GNUNET_NETWORK_fdset_destroy (grs);
1339
1340   /* FIXME: return bytes REALLY sent */
1341   return 0;
1342 }
1343
1344
1345 /**
1346  * Function that can be used by the transport service to transmit
1347  * a message using the plugin.   Note that in the case of a
1348  * peer disconnecting, the continuation MUST be called
1349  * prior to the disconnect notification itself.  This function
1350  * will be called with this peer's HELLO message to initiate
1351  * a fresh connection to another peer.
1352  *
1353  * @param cls closure
1354  * @param target who should receive this message
1355  * @param msgbuf the message to transmit
1356  * @param msgbuf_size number of bytes in 'msgbuf'
1357  * @param priority how important is the message (most plugins will
1358  *                 ignore message priority and just FIFO)
1359  * @param timeout how long to wait at most for the transmission (does not
1360  *                require plugins to discard the message after the timeout,
1361  *                just advisory for the desired delay; most plugins will ignore
1362  *                this as well)
1363  * @param session which session must be used (or NULL for "any")
1364  * @param addr the address to use (can be NULL if the plugin
1365  *                is "on its own" (i.e. re-use existing TCP connection))
1366  * @param addrlen length of the address in bytes
1367  * @param force_address GNUNET_YES if the plugin MUST use the given address,
1368  *                GNUNET_NO means the plugin may use any other address and
1369  *                GNUNET_SYSERR means that only reliable existing
1370  *                bi-directional connections should be used (regardless
1371  *                of address)
1372  * @param cont continuation to call once the message has
1373  *        been transmitted (or if the transport is ready
1374  *        for the next transmission call; or if the
1375  *        peer disconnected...); can be NULL
1376  * @param cont_cls closure for cont
1377  * @return number of bytes used (on the physical network, with overheads);
1378  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
1379  *         and does NOT mean that the message was not transmitted (DV)
1380  */
1381 static ssize_t
1382 http_plugin_send (void *cls,
1383                   const struct GNUNET_PeerIdentity *target,
1384                   const char *msgbuf,
1385                   size_t msgbuf_size,
1386                   unsigned int priority,
1387                   struct GNUNET_TIME_Relative to,
1388                   struct Session *session,
1389                   const void *addr,
1390                   size_t addrlen,
1391                   int force_address,
1392                   GNUNET_TRANSPORT_TransmitContinuation cont,
1393                   void *cont_cls)
1394 {
1395   struct Plugin *plugin = cls;
1396   struct Session *cs;
1397   struct HTTP_Message *msg;
1398   struct HTTP_Connection *con;
1399   //unsigned int ret;
1400
1401   GNUNET_assert(cls !=NULL);
1402   GNUNET_assert ((addr!=NULL) && (addrlen != 0));
1403
1404   /* get session from hashmap */
1405   cs = session_get(plugin, target);
1406   con = session_check_outbound_address(plugin, cs, addr, addrlen);
1407
1408   char * force = GNUNET_malloc(30);
1409
1410   if (force_address == GNUNET_YES)
1411     strcpy(force,"forced addr.");
1412   if (force_address == GNUNET_NO)
1413     strcpy(force,"any addr.");
1414   if (force_address == GNUNET_SYSERR)
1415     strcpy(force,"reliable bi-direc. address addr.");
1416   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Transport tells me to send %u bytes to `%s' %s (%s), session: %X\n",
1417                                       msgbuf_size,
1418                                       GNUNET_i2s(&cs->identity),
1419                                       force,
1420                                       http_plugin_address_to_string(NULL, addr, addrlen),
1421                                       session);
1422
1423   //GNUNET_free(force);
1424   /* create msg */
1425   msg = GNUNET_malloc (sizeof (struct HTTP_Message) + msgbuf_size);
1426   msg->next = NULL;
1427   msg->size = msgbuf_size;
1428   msg->pos = 0;
1429   msg->buf = (char *) &msg[1];
1430   msg->transmit_cont = cont;
1431   msg->transmit_cont_cls = cont_cls;
1432   memcpy (msg->buf,msgbuf, msgbuf_size);
1433
1434   /* must use this address */
1435   if (force_address == GNUNET_YES)
1436   {
1437     /* enqueue in connection message queue */
1438     GNUNET_CONTAINER_DLL_insert(con->pending_msgs_head,con->pending_msgs_tail,msg);
1439   }
1440   /* can use existing connection to send */
1441   else
1442   {
1443     /* enqueue in connection message queue */
1444     GNUNET_CONTAINER_DLL_insert(con->pending_msgs_head,con->pending_msgs_tail,msg);
1445   }
1446   return send_check_connections (plugin, cs, con);
1447 }
1448
1449
1450
1451 /**
1452  * Function that can be used to force the plugin to disconnect
1453  * from the given peer and cancel all previous transmissions
1454  * (and their continuationc).
1455  *
1456  * @param cls closure
1457  * @param target peer from which to disconnect
1458  */
1459 static void
1460 http_plugin_disconnect (void *cls,
1461                             const struct GNUNET_PeerIdentity *target)
1462 {
1463   struct Plugin *plugin = cls;
1464   struct HTTP_Connection *con;
1465   struct Session *cs;
1466
1467   /* get session from hashmap */
1468   cs = session_get(plugin, target);
1469   con = cs->outbound_connections_head;
1470
1471   while (con!=NULL)
1472   {
1473     if (con->put_curl_handle!=NULL)
1474       curl_easy_cleanup(con->put_curl_handle);
1475     con->put_curl_handle=NULL;
1476     con->put_connected = GNUNET_NO;
1477     while (con->pending_msgs_head!=NULL)
1478     {
1479       remove_http_message(con, con->pending_msgs_head);
1480     }
1481     con=con->next;
1482   }
1483 }
1484
1485
1486 /**
1487  * Convert the transports address to a nice, human-readable
1488  * format.
1489  *
1490  * @param cls closure
1491  * @param type name of the transport that generated the address
1492  * @param addr one of the addresses of the host, NULL for the last address
1493  *        the specific address format depends on the transport
1494  * @param addrlen length of the address
1495  * @param numeric should (IP) addresses be displayed in numeric form?
1496  * @param timeout after how long should we give up?
1497  * @param asc function to call on each string
1498  * @param asc_cls closure for asc
1499  */
1500 static void
1501 http_plugin_address_pretty_printer (void *cls,
1502                                         const char *type,
1503                                         const void *addr,
1504                                         size_t addrlen,
1505                                         int numeric,
1506                                         struct GNUNET_TIME_Relative timeout,
1507                                         GNUNET_TRANSPORT_AddressStringCallback
1508                                         asc, void *asc_cls)
1509 {
1510   const struct IPv4HttpAddress *t4;
1511   const struct IPv6HttpAddress *t6;
1512   struct sockaddr_in a4;
1513   struct sockaddr_in6 a6;
1514   char * address;
1515   char * ret;
1516   unsigned int port;
1517   unsigned int res;
1518
1519   GNUNET_assert(cls !=NULL);
1520   if (addrlen == sizeof (struct IPv6HttpAddress))
1521   {
1522     address = GNUNET_malloc (INET6_ADDRSTRLEN);
1523     t6 = addr;
1524     a6.sin6_addr = t6->ipv6_addr;
1525     inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
1526     port = ntohs(t6->u6_port);
1527   }
1528   else if (addrlen == sizeof (struct IPv4HttpAddress))
1529   {
1530     address = GNUNET_malloc (INET_ADDRSTRLEN);
1531     t4 = addr;
1532     a4.sin_addr.s_addr =  t4->ipv4_addr;
1533     inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
1534     port = ntohs(t4->u_port);
1535   }
1536   else
1537   {
1538     /* invalid address */
1539     GNUNET_break_op (0);
1540     asc (asc_cls, NULL);
1541     return;
1542   }
1543   res = GNUNET_asprintf(&ret,"http://%s:%u/",address,port);
1544   GNUNET_free (address);
1545   GNUNET_assert(res != 0);
1546
1547   asc (asc_cls, ret);
1548 }
1549
1550
1551
1552 /**
1553  * Another peer has suggested an address for this
1554  * peer and transport plugin.  Check that this could be a valid
1555  * address.  If so, consider adding it to the list
1556  * of addresses.
1557  *
1558  * @param cls closure
1559  * @param addr pointer to the address
1560  * @param addrlen length of addr
1561  * @return GNUNET_OK if this is a plausible address for this peer
1562  *         and transport
1563  */
1564 static int
1565 http_plugin_address_suggested (void *cls,
1566                                const void *addr, size_t addrlen)
1567 {
1568   struct Plugin *plugin = cls;
1569   struct IPv4HttpAddress *v4;
1570   struct IPv6HttpAddress *v6;
1571   unsigned int port;
1572
1573   GNUNET_assert(cls !=NULL);
1574   if ((addrlen != sizeof (struct IPv4HttpAddress)) &&
1575       (addrlen != sizeof (struct IPv6HttpAddress)))
1576     {
1577       return GNUNET_SYSERR;
1578     }
1579   if (addrlen == sizeof (struct IPv4HttpAddress))
1580     {
1581       v4 = (struct IPv4HttpAddress *) addr;
1582       if (INADDR_LOOPBACK == ntohl(v4->ipv4_addr))
1583       {
1584         return GNUNET_SYSERR;
1585       }
1586       port = ntohs (v4->u_port);
1587       if (port != plugin->port_inbound)
1588       {
1589         return GNUNET_SYSERR;
1590       }
1591     }
1592   else
1593     {
1594       v6 = (struct IPv6HttpAddress *) addr;
1595       if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr))
1596         {
1597           return GNUNET_SYSERR;
1598         }
1599       port = ntohs (v6->u6_port);
1600       if (port != plugin->port_inbound)
1601       {
1602         return GNUNET_SYSERR;
1603       }
1604     }
1605   return GNUNET_OK;
1606 }
1607
1608
1609 /**
1610  * Function called for a quick conversion of the binary address to
1611  * a numeric address.  Note that the caller must not free the
1612  * address and that the next call to this function is allowed
1613  * to override the address again.
1614  *
1615  * @param cls closure
1616  * @param addr binary address
1617  * @param addrlen length of the address
1618  * @return string representing the same address
1619  */
1620 static const char*
1621 http_plugin_address_to_string (void *cls,
1622                                    const void *addr,
1623                                    size_t addrlen)
1624 {
1625   const struct IPv4HttpAddress *t4;
1626   const struct IPv6HttpAddress *t6;
1627   struct sockaddr_in a4;
1628   struct sockaddr_in6 a6;
1629   char * address;
1630   char * ret;
1631   uint16_t port;
1632   unsigned int res;
1633
1634   if (addrlen == sizeof (struct IPv6HttpAddress))
1635     {
1636       address = GNUNET_malloc (INET6_ADDRSTRLEN);
1637       t6 = addr;
1638       a6.sin6_addr = t6->ipv6_addr;
1639       inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
1640       port = ntohs(t6->u6_port);
1641     }
1642   else if (addrlen == sizeof (struct IPv4HttpAddress))
1643     {
1644       address = GNUNET_malloc (INET_ADDRSTRLEN);
1645       t4 = addr;
1646       a4.sin_addr.s_addr =  t4->ipv4_addr;
1647       inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
1648       port = ntohs(t4->u_port);
1649     }
1650   else
1651     {
1652       /* invalid address */
1653       return NULL;
1654     }
1655   res = GNUNET_asprintf(&ret,"%s:%u",address,port);
1656   GNUNET_free (address);
1657   GNUNET_assert(res != 0);
1658   return ret;
1659 }
1660
1661 /**
1662  * Add the IP of our network interface to the list of
1663  * our external IP addresses.
1664  *
1665  * @param cls the 'struct Plugin*'
1666  * @param name name of the interface
1667  * @param isDefault do we think this may be our default interface
1668  * @param addr address of the interface
1669  * @param addrlen number of bytes in addr
1670  * @return GNUNET_OK to continue iterating
1671  */
1672 static int
1673 process_interfaces (void *cls,
1674                     const char *name,
1675                     int isDefault,
1676                     const struct sockaddr *addr, socklen_t addrlen)
1677 {
1678   struct Plugin *plugin = cls;
1679   struct IPv4HttpAddress * t4;
1680   struct IPv6HttpAddress * t6;
1681   int af;
1682
1683   GNUNET_assert(cls !=NULL);
1684   af = addr->sa_family;
1685   if (af == AF_INET)
1686     {
1687       t4 = GNUNET_malloc(sizeof(struct IPv4HttpAddress));
1688       if (INADDR_LOOPBACK == ntohl(((struct sockaddr_in *) addr)->sin_addr.s_addr))
1689       {
1690         /* skip loopback addresses */
1691         return GNUNET_OK;
1692       }
1693       t4->ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
1694       t4->u_port = htons (plugin->port_inbound);
1695       plugin->env->notify_address(plugin->env->cls,"http",t4, sizeof (struct IPv4HttpAddress), GNUNET_TIME_UNIT_FOREVER_REL);
1696
1697     }
1698   else if (af == AF_INET6)
1699     {
1700       t6 = GNUNET_malloc(sizeof(struct IPv6HttpAddress));
1701       if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr))
1702         {
1703           /* skip link local addresses */
1704           return GNUNET_OK;
1705         }
1706       if (IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *) addr)->sin6_addr))
1707         {
1708           /* skip loopback addresses */
1709           return GNUNET_OK;
1710         }
1711       memcpy (&t6->ipv6_addr,
1712               &((struct sockaddr_in6 *) addr)->sin6_addr,
1713               sizeof (struct in6_addr));
1714       t6->u6_port = htons (plugin->port_inbound);
1715       plugin->env->notify_address(plugin->env->cls,"http",t6,sizeof (struct IPv6HttpAddress) , GNUNET_TIME_UNIT_FOREVER_REL);
1716     }
1717   return GNUNET_OK;
1718 }
1719 int hashMapFreeIterator (void *cls, const GNUNET_HashCode *key, void *value)
1720 {
1721   struct Session * cs = value;
1722   struct HTTP_Connection * con = cs->outbound_connections_head;
1723   struct HTTP_Connection * tmp_con = cs->outbound_connections_head;
1724   struct HTTP_Message * msg = NULL;
1725   struct HTTP_Message * tmp_msg = NULL;
1726
1727   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Freeing session for peer `%s'\n",GNUNET_i2s(&cs->identity));
1728
1729   /* freeing connections */
1730   while (con!=NULL)
1731   {
1732     GNUNET_free(con->url);
1733     if (con->put_curl_handle!=NULL)
1734       curl_easy_cleanup(con->put_curl_handle);
1735     con->put_curl_handle = NULL;
1736     msg = con->pending_msgs_head;
1737     while (msg!=NULL)
1738     {
1739       tmp_msg=msg->next;
1740       GNUNET_free(msg);
1741       msg = tmp_msg;
1742     }
1743     tmp_con=con->next;
1744     GNUNET_free(con);
1745     con=tmp_con;
1746   }
1747   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"All sessions freed \n");
1748
1749   GNUNET_free (cs);
1750   return GNUNET_YES;
1751 }
1752
1753 /**
1754  * Exit point from the plugin.
1755  */
1756 void *
1757 libgnunet_plugin_transport_http_done (void *cls)
1758 {
1759   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1760   struct Plugin *plugin = api->cls;
1761   CURLMcode mret;
1762
1763   GNUNET_assert(cls !=NULL);
1764
1765
1766   if ( plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
1767   {
1768     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v4);
1769     plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
1770   }
1771
1772   if ( plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
1773   {
1774     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v6);
1775     plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
1776   }
1777
1778   if ( plugin->http_server_task_send != GNUNET_SCHEDULER_NO_TASK)
1779   {
1780     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_send);
1781     plugin->http_server_task_send = GNUNET_SCHEDULER_NO_TASK;
1782   }
1783
1784   if (plugin->http_server_daemon_v4 != NULL)
1785   {
1786     MHD_stop_daemon (plugin->http_server_daemon_v4);
1787     plugin->http_server_daemon_v4 = NULL;
1788   }
1789   if (plugin->http_server_daemon_v6 != NULL)
1790   {
1791     MHD_stop_daemon (plugin->http_server_daemon_v6);
1792     plugin->http_server_daemon_v6 = NULL;
1793   }
1794
1795   /* free all sessions */
1796   GNUNET_CONTAINER_multihashmap_iterate (plugin->sessions,
1797                                          &hashMapFreeIterator,
1798                                          NULL);
1799
1800   GNUNET_CONTAINER_multihashmap_destroy (plugin->sessions);
1801
1802   mret = curl_multi_cleanup(plugin->multi_handle);
1803   if ( CURLM_OK != mret)
1804     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"curl multihandle clean up failed");
1805
1806   GNUNET_free (plugin);
1807   GNUNET_free (api);
1808   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Unload http plugin complete...\n");
1809   return NULL;
1810 }
1811
1812
1813 /**
1814  * Entry point for the plugin.
1815  */
1816 void *
1817 libgnunet_plugin_transport_http_init (void *cls)
1818 {
1819   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1820   struct Plugin *plugin;
1821   struct GNUNET_TRANSPORT_PluginFunctions *api;
1822   struct GNUNET_TIME_Relative gn_timeout;
1823   long long unsigned int port;
1824
1825   GNUNET_assert(cls !=NULL);
1826   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting http plugin...\n");
1827
1828   plugin = GNUNET_malloc (sizeof (struct Plugin));
1829   plugin->env = env;
1830   plugin->sessions = NULL;
1831
1832   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1833   api->cls = plugin;
1834   api->send = &http_plugin_send;
1835   api->disconnect = &http_plugin_disconnect;
1836   api->address_pretty_printer = &http_plugin_address_pretty_printer;
1837   api->check_address = &http_plugin_address_suggested;
1838   api->address_to_string = &http_plugin_address_to_string;
1839
1840   /* Hashing our identity to use it in URLs */
1841   GNUNET_CRYPTO_hash_to_enc ( &(plugin->env->my_identity->hashPubKey), &plugin->my_ascii_hash_ident);
1842
1843   /* Reading port number from config file */
1844   if ((GNUNET_OK !=
1845        GNUNET_CONFIGURATION_get_value_number (env->cfg,
1846                                               "transport-http",
1847                                               "PORT",
1848                                               &port)) ||
1849       (port > 65535) )
1850     {
1851       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1852                        "http",
1853                        _
1854                        ("Require valid port number for transport plugin `%s' in configuration!\n"),
1855                        "transport-http");
1856       libgnunet_plugin_transport_http_done (api);
1857       return NULL;
1858     }
1859   GNUNET_assert ((port > 0) && (port <= 65535));
1860   plugin->port_inbound = port;
1861   gn_timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
1862   if ((plugin->http_server_daemon_v4 == NULL) && (plugin->http_server_daemon_v6 == NULL) && (port != 0))
1863     {
1864     plugin->http_server_daemon_v6 = MHD_start_daemon (MHD_USE_IPv6,
1865                                        port,
1866                                        &acceptPolicyCallback,
1867                                        plugin , &accessHandlerCallback, plugin,
1868                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 16,
1869                                        MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
1870                                        MHD_OPTION_CONNECTION_TIMEOUT, (gn_timeout.value / 1000),
1871                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
1872                                        MHD_OPTION_NOTIFY_COMPLETED, &requestCompletedCallback, NULL,
1873                                        MHD_OPTION_END);
1874     plugin->http_server_daemon_v4 = MHD_start_daemon (MHD_NO_FLAG,
1875                                        port,
1876                                        &acceptPolicyCallback,
1877                                        plugin , &accessHandlerCallback, plugin,
1878                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 16,
1879                                        MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
1880                                        MHD_OPTION_CONNECTION_TIMEOUT, (gn_timeout.value / 1000),
1881                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
1882                                        MHD_OPTION_NOTIFY_COMPLETED, &requestCompletedCallback, NULL,
1883                                        MHD_OPTION_END);
1884     }
1885   if (plugin->http_server_daemon_v4 != NULL)
1886     plugin->http_server_task_v4 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
1887   if (plugin->http_server_daemon_v6 != NULL)
1888     plugin->http_server_task_v6 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
1889
1890   if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
1891     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 on port %u\n",port);
1892   else if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
1893     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 and IPv6 on port %u\n",port);
1894   else
1895   {
1896     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No MHD was started, transport plugin not functional!\n");
1897     libgnunet_plugin_transport_http_done (api);
1898     return NULL;
1899   }
1900
1901   /* Initializing cURL */
1902   curl_global_init(CURL_GLOBAL_ALL);
1903   plugin->multi_handle = curl_multi_init();
1904
1905   if ( NULL == plugin->multi_handle )
1906   {
1907     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1908                      "http",
1909                      _("Could not initialize curl multi handle, failed to start http plugin!\n"),
1910                      "transport-http");
1911     libgnunet_plugin_transport_http_done (api);
1912     return NULL;
1913   }
1914
1915   plugin->sessions = GNUNET_CONTAINER_multihashmap_create (10);
1916   GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
1917
1918   return api;
1919 }
1920
1921 /* end of plugin_transport_http.c */