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