b58164368e34d0b3330b9f47b949e531ffd7888c
[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 2, 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_template.c
23  * @brief template for a new transport service
24  * @author Christian Grothoff
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 "plugin_transport.h"
37 #include "gnunet_os_lib.h"
38 #include "microhttpd.h"
39 #include <curl/curl.h>
40
41 #define DEBUG_HTTP GNUNET_NO
42
43 /**
44  * Text of the response sent back after the last bytes of a PUT
45  * request have been received (just to formally obey the HTTP
46  * protocol).
47  */
48 #define HTTP_PUT_RESPONSE "Thank you!"
49
50 /**
51  * After how long do we expire an address that we
52  * learned from another peer if it is not reconfirmed
53  * by anyone?
54  */
55 #define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
56
57 /**
58  * Page returned if request invalid
59  */
60 #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>"
61
62 /**
63  * Timeout for a http connect
64  */
65 #define HTTP_CONNECT_TIMEOUT 30
66
67 /**
68  * Timeout for a http connect
69  */
70 #define HTTP_MESSAGE_INITIAL_BUFFERSIZE GNUNET_SERVER_MAX_MESSAGE_SIZE
71
72
73 /**
74  * Encapsulation of all of the state of the plugin.
75  */
76 struct Plugin;
77
78 /**
79  * Network format for IPv4 addresses.
80  */
81 struct IPv4HttpAddress
82 {
83   /**
84    * IPv4 address, in network byte order.
85    */
86   uint32_t ipv4_addr;
87
88   /**
89    * Port number, in network byte order.
90    */
91   uint16_t u_port;
92
93 };
94
95
96 /**
97  * Network format for IPv6 addresses.
98  */
99 struct IPv6HttpAddress
100 {
101   /**
102    * IPv6 address.
103    */
104   struct in6_addr ipv6_addr;
105
106   /**
107    * Port number, in network byte order.
108    */
109   uint16_t u6_port;
110
111 };
112
113
114
115 /**
116  *  Message to send using http
117  */
118 struct HTTP_Message
119 {
120   /**
121    * Next field for linked list
122    */
123   struct HTTP_Message * next;
124
125   /**
126    * buffer containing data to send
127    */
128   char *buf;
129
130   /**
131    * amount of data already sent
132    */
133   size_t pos;
134
135   /**
136    * amount of data to sent
137    */
138   size_t len;
139 };
140
141
142 /**
143  * Session handle for connections.
144  */
145 struct Session
146 {
147
148   /**
149    * Stored in a linked list.
150    */
151   struct Session *next;
152
153   /**
154    * Pointer to the global plugin struct.
155    */
156   struct Plugin *plugin;
157
158   /**
159    * Continuation function to call once the transmission buffer
160    * has again space available.  NULL if there is no
161    * continuation to call.
162    */
163   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
164
165   /**
166    * Closure for transmit_cont.
167    */
168   void *transmit_cont_cls;
169
170   /**
171    * To whom are we talking to (set to our identity
172    * if we are still waiting for the welcome message)
173    */
174   struct GNUNET_PeerIdentity sender;
175
176   /**
177    * Sender's url
178    */
179   char * url;
180
181   /**
182    * Sender's ip address to distinguish between incoming connections
183    */
184   char * ip;
185
186   /**
187    * Sender's ip address to distinguish between incoming connections
188    */
189   struct sockaddr_in * addr;
190
191   /**
192    * Did we initiate the connection (GNUNET_YES) or the other peer (GNUNET_NO)?
193    */
194   unsigned int is_client;
195
196   /**
197    * Is the connection active (GNUNET_YES) or terminated (GNUNET_NO)?
198    */
199   unsigned int is_active;
200
201   /**
202    * At what time did we reset last_received last?
203    */
204   struct GNUNET_TIME_Absolute last_quota_update;
205
206   /**
207    * How many bytes have we received since the "last_quota_update"
208    * timestamp?
209    */
210   uint64_t last_received;
211
212   /**
213    * Number of bytes per ms that this peer is allowed
214    * to send to us.
215    */
216   uint32_t quota;
217
218   /**
219    * Is there a HTTP/PUT in progress?
220    */
221   unsigned int is_put_in_progress;
222
223   /**
224    * Is there a HTTP/PUT in progress?
225    */
226   unsigned int is_bad_request;
227
228   /**
229    * Encoded hash
230    */
231   struct GNUNET_CRYPTO_HashAsciiEncoded hash;
232
233   struct HTTP_Message * pending_outbound_msg;;
234
235   struct HTTP_Message * pending_inbound_msg;
236
237   CURL *curl_handle;
238 };
239
240 /**
241  * Encapsulation of all of the state of the plugin.
242  */
243 struct Plugin
244 {
245   /**
246    * Our environment.
247    */
248   struct GNUNET_TRANSPORT_PluginEnvironment *env;
249
250   /**
251    * Handle to the network service.
252    */
253   struct GNUNET_SERVICE_Context *service;
254
255   unsigned int port_inbound;
256
257   /**
258    * List of open sessions.
259    */
260   struct Session *sessions;
261
262   /**
263    * Number of active sessions
264    */
265
266   unsigned int session_count;
267 };
268
269 /**
270  * Daemon for listening for new IPv4 connections.
271  */
272 static struct MHD_Daemon *http_daemon_v4;
273
274 /**
275  * Daemon for listening for new IPv6connections.
276  */
277 static struct MHD_Daemon *http_daemon_v6;
278
279 /**
280  * Our primary task for http daemon handling IPv4 connections
281  */
282 static GNUNET_SCHEDULER_TaskIdentifier http_task_v4;
283
284 /**
285  * Our primary task for http daemon handling IPv6 connections
286  */
287 static GNUNET_SCHEDULER_TaskIdentifier http_task_v6;
288
289
290 /**
291  * The task sending data
292  */
293 static GNUNET_SCHEDULER_TaskIdentifier http_task_send;
294
295
296 /**
297  * Information about this plugin
298  */
299 static struct Plugin *plugin;
300
301 /**
302  * cURL Multihandle
303  */
304 static CURLM *multi_handle;
305
306 /**
307  * Our hostname
308  */
309 static char * hostname;
310
311 /**
312  * Our ASCII encoded, hashed peer identity
313  * This string is used to distinguish between connections and is added to the urls
314  */
315 static struct GNUNET_CRYPTO_HashAsciiEncoded my_ascii_hash_ident;
316
317 struct GNUNET_TIME_Relative timeout;
318
319 /**
320  * Finds a http session in our linked list using peer identity as a key
321  * @param peer peeridentity
322  * @return http session corresponding to peer identity
323  */
324 static struct Session * find_session_by_pi( const struct GNUNET_PeerIdentity *peer )
325 {
326   struct Session * cur;
327   GNUNET_HashCode hc_peer;
328   GNUNET_HashCode hc_current;
329
330   cur = plugin->sessions;
331   hc_peer = peer->hashPubKey;
332   while (cur != NULL)
333   {
334     hc_current = cur->sender.hashPubKey;
335     if ( 0 == GNUNET_CRYPTO_hash_cmp( &hc_peer, &hc_current))
336       return cur;
337     cur = plugin->sessions->next;
338   }
339   return NULL;
340 }
341
342 /**
343  * Finds a http session in our linked list using libcurl handle as a key
344  * Needed when sending data with libcurl to differentiate between sessions
345  * @param handle peeridentity
346  * @return http session corresponding to peer identity
347  */
348 static struct Session * find_session_by_curlhandle( CURL* handle )
349 {
350   struct Session * cur;
351
352   cur = plugin->sessions;
353   while (cur != NULL)
354   {
355     if ( handle == cur->curl_handle )
356       return cur;
357     cur = plugin->sessions->next;
358   }
359   return NULL;
360 }
361
362 /**
363  * Create a new session
364  *
365  * @param address address the peer is using
366  * @param peer identity
367  * @return created session object
368  */
369 static struct Session * create_session (struct sockaddr_in *address, const struct GNUNET_PeerIdentity *peer)
370 {
371   struct sockaddr_in  *addrin;
372   struct sockaddr_in6 *addrin6;
373
374   struct Session * ses = GNUNET_malloc ( sizeof( struct Session) );
375   ses->addr = GNUNET_malloc ( sizeof (struct sockaddr_in) );
376
377   ses->next = NULL;
378   ses->plugin = plugin;
379
380   memcpy(ses->addr, address, sizeof (struct sockaddr_in));
381   if ( AF_INET == address->sin_family)
382   {
383     ses->ip = GNUNET_malloc (INET_ADDRSTRLEN);
384     addrin = address;
385     inet_ntop(addrin->sin_family,&(addrin->sin_addr),ses->ip,INET_ADDRSTRLEN);
386   }
387   if ( AF_INET6 == address->sin_family)
388   {
389     ses->ip = GNUNET_malloc (INET6_ADDRSTRLEN);
390     addrin6 = (struct sockaddr_in6 *) address;
391     inet_ntop(addrin6->sin6_family, &(addrin6->sin6_addr) ,ses->ip,INET6_ADDRSTRLEN);
392   }
393   memcpy(&ses->sender, peer, sizeof (struct GNUNET_PeerIdentity));
394   GNUNET_CRYPTO_hash_to_enc(&ses->sender.hashPubKey,&(ses->hash));
395   ses->is_active = GNUNET_NO;
396   ses->pending_inbound_msg = GNUNET_malloc( sizeof (struct HTTP_Message));
397   ses->pending_inbound_msg->buf = GNUNET_malloc(GNUNET_SERVER_MAX_MESSAGE_SIZE);
398   ses->pending_inbound_msg->len = GNUNET_SERVER_MAX_MESSAGE_SIZE;
399   ses->pending_inbound_msg->pos = 0;
400
401
402   return ses;
403 }
404
405 /**
406  * Callback called by MHD when a connection is terminated
407  */
408 static void requestCompletedCallback (void *cls, struct MHD_Connection * connection, void **httpSessionCache)
409 {
410   struct Session * cs;
411
412   cs = *httpSessionCache;
413   if (cs != NULL)
414   {
415     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection from peer `%s' was terminated\n",GNUNET_i2s(&cs->sender));
416     /* session set to inactive */
417     cs->is_active = GNUNET_NO;
418     cs->is_put_in_progress = GNUNET_NO;
419   }
420   return;
421 }
422
423 /**
424  * Check if we are allowed to connect to the given IP.
425  */
426 static int
427 acceptPolicyCallback (void *cls,
428                       const struct sockaddr *addr, socklen_t addr_len)
429 {
430   /* Every connection is accepted, nothing more to do here */
431   return MHD_YES;
432 }
433
434
435 int serror;
436
437 /**
438  * Process GET or PUT request received via MHD.  For
439  * GET, queue response that will send back our pending
440  * messages.  For PUT, process incoming data and send
441  * to GNUnet core.  In either case, check if a session
442  * already exists and create a new one if not.
443  */
444 static int
445 accessHandlerCallback (void *cls,
446                        struct MHD_Connection *session,
447                        const char *url,
448                        const char *method,
449                        const char *version,
450                        const char *upload_data,
451                        size_t * upload_data_size, void **httpSessionCache)
452 {
453   struct MHD_Response *response;
454   struct Session * cs;
455   struct Session * cs_temp;
456   const union MHD_ConnectionInfo * conn_info;
457   struct sockaddr_in  *addrin;
458   struct sockaddr_in6 *addrin6;
459   char * address = NULL;
460   struct GNUNET_PeerIdentity pi_in;
461   int res = GNUNET_NO;
462   struct GNUNET_MessageHeader *gn_msg;
463   int send_error_to_client;
464
465   gn_msg = NULL;
466   send_error_to_client = GNUNET_NO;
467
468   if ( NULL == *httpSessionCache)
469   {
470     /* check url for peer identity */
471     res = GNUNET_CRYPTO_hash_from_string ( &url[1], &(pi_in.hashPubKey));
472     if ( GNUNET_SYSERR == res )
473     {
474       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident\n");
475       response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
476       res = MHD_queue_response (session, MHD_HTTP_NOT_FOUND, response);
477       MHD_destroy_response (response);
478       return res;
479     }
480
481     conn_info = MHD_get_connection_info(session, MHD_CONNECTION_INFO_CLIENT_ADDRESS );
482     /* Incoming IPv4 connection */
483     if ( AF_INET == conn_info->client_addr->sin_family)
484     {
485       address = GNUNET_malloc (INET_ADDRSTRLEN);
486       addrin = conn_info->client_addr;
487       inet_ntop(addrin->sin_family, &(addrin->sin_addr),address,INET_ADDRSTRLEN);
488     }
489     /* Incoming IPv6 connection */
490     if ( AF_INET6 == conn_info->client_addr->sin_family)
491     {
492       address = GNUNET_malloc (INET6_ADDRSTRLEN);
493       addrin6 = (struct sockaddr_in6 *) conn_info->client_addr;
494       inet_ntop(addrin6->sin6_family, &(addrin6->sin6_addr),address,INET6_ADDRSTRLEN);
495     }
496     /* find existing session for address */
497     cs = NULL;
498     if (plugin->session_count > 0)
499     {
500       cs = plugin->sessions;
501       while ( NULL != cs)
502       {
503
504         /* Comparison based on ip address */
505         // res = (0 == memcmp(&(conn_info->client_addr->sin_addr),&(cs->addr->sin_addr), sizeof (struct in_addr))) ? GNUNET_YES : GNUNET_NO;
506
507         /* Comparison based on ip address, port number and address family */
508         // res = (0 == memcmp((conn_info->client_addr),(cs->addr), sizeof (struct sockaddr_in))) ? GNUNET_YES : GNUNET_NO;
509
510         /* Comparison based on PeerIdentity */
511         res = (0 == memcmp(&pi_in,&(cs->sender), sizeof (struct GNUNET_PeerIdentity))) ? GNUNET_YES : GNUNET_NO;
512
513         if ( GNUNET_YES  == res)
514         {
515           /* existing session for this address found */
516           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session `%s' found\n",address);
517           break;
518         }
519         cs = cs->next;
520       }
521     }
522     /* no existing session, create a new one*/
523     if (cs == NULL )
524     {
525       /* create new session object */
526       cs = create_session(conn_info->client_addr, &pi_in);
527
528       /* Insert session into linked list */
529       if ( plugin->sessions == NULL)
530       {
531         plugin->sessions = cs;
532         plugin->session_count = 1;
533       }
534       cs_temp = plugin->sessions;
535       while ( cs_temp->next != NULL )
536       {
537         cs_temp = cs_temp->next;
538       }
539       if (cs_temp != cs )
540       {
541         cs_temp->next = cs;
542         plugin->session_count++;
543       }
544       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"New Session `%s' inserted, count %u \n", address, plugin->session_count);
545     }
546     /* Set closure */
547     if (*httpSessionCache == NULL)
548     {
549       *httpSessionCache = cs;
550     }
551     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"HTTP Daemon has new an incoming `%s' request from peer `%s' (`[%s]:%u')\n",method, GNUNET_i2s(&cs->sender),cs->ip,cs->addr->sin_port);
552   }
553   else
554   {
555     cs = *httpSessionCache;
556   }
557   /* Is it a PUT or a GET request */
558   if ( 0 == strcmp (MHD_HTTP_METHOD_PUT, method) )
559   {
560     /* New  */
561     if ((*upload_data_size == 0) && (cs->is_put_in_progress == GNUNET_NO))
562     {
563       if (cs->pending_inbound_msg->pos !=0 )
564       {
565         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
566                     _("Incoming message from peer `%s', while existing message with %u bytes was not forwarded to transport'\n"),
567                     GNUNET_i2s(&cs->sender), cs->pending_inbound_msg->pos);
568         cs->pending_inbound_msg->pos = 0;
569       }
570       /* not yet ready */
571       cs->is_put_in_progress = GNUNET_YES;
572       cs->is_bad_request = GNUNET_NO;
573       cs->is_active = GNUNET_YES;
574       return MHD_YES;
575     }
576
577     if ((*upload_data_size > 0) && (cs->is_bad_request != GNUNET_YES))
578     {
579       if ((*upload_data_size + cs->pending_inbound_msg->pos < cs->pending_inbound_msg->len) && (*upload_data_size + cs->pending_inbound_msg->pos <= GNUNET_SERVER_MAX_MESSAGE_SIZE))
580       {
581         /* copy uploaded data to buffer */
582         memcpy(&cs->pending_inbound_msg->buf[cs->pending_inbound_msg->pos],upload_data,*upload_data_size);
583         cs->pending_inbound_msg->pos += *upload_data_size;
584         *upload_data_size = 0;
585         return MHD_YES;
586       }
587       else
588       {
589         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"%u bytes not added to message of %u bytes, message to big\n",*upload_data_size, cs->pending_inbound_msg->pos);
590         cs->is_bad_request = GNUNET_YES;
591         /* (*upload_data_size) bytes not processed */
592         return MHD_YES;
593       }
594     }
595
596     if ((cs->is_put_in_progress == GNUNET_YES) && (cs->is_bad_request == GNUNET_YES))
597     {
598       *upload_data_size = 0;
599       response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
600       res = MHD_queue_response (session, MHD_HTTP_REQUEST_ENTITY_TOO_LARGE, response);
601       if (res == MHD_YES)
602       {
603         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Sent HTTP/1.1: 413 ENTITY TOO LARGE as PUT Response\n");
604         cs->is_bad_request = GNUNET_NO;
605         cs->is_put_in_progress =GNUNET_NO;
606       }
607       MHD_destroy_response (response);
608       return MHD_YES;
609     }
610
611     if ((*upload_data_size == 0) && (cs->is_put_in_progress == GNUNET_YES) && (cs->is_bad_request == GNUNET_NO))
612     {
613       send_error_to_client = GNUNET_YES;
614       struct GNUNET_MessageHeader * gn_msg = NULL;
615       /*check message and forward here */
616       /* checking size */
617       if (cs->pending_inbound_msg->pos > sizeof (struct GNUNET_MessageHeader))
618       {
619         gn_msg = GNUNET_malloc (cs->pending_inbound_msg->pos);
620         memcpy (gn_msg,cs->pending_inbound_msg->buf,cs->pending_inbound_msg->pos);
621
622         if ((ntohs(gn_msg->size) == cs->pending_inbound_msg->pos))
623         {
624           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Recieved GNUnet message type %u size %u and payload %u \n",ntohs (gn_msg->type), ntohs (gn_msg->size), ntohs (gn_msg->size)-sizeof(struct GNUNET_MessageHeader));
625           /* forwarding message to transport */
626           plugin->env->receive(plugin->env, &(cs->sender), gn_msg, 1, cs , cs->ip, strlen(cs->ip) );
627           send_error_to_client = GNUNET_NO;
628         }
629       }
630
631       if (send_error_to_client == GNUNET_NO)
632       {
633         response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
634         res = MHD_queue_response (session, MHD_HTTP_OK, response);
635         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Sent HTTP/1.1: 200 OK as PUT Response\n",HTTP_PUT_RESPONSE, strlen (HTTP_PUT_RESPONSE), res );
636         MHD_destroy_response (response);
637       }
638       else
639       {
640         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Recieved malformed message with %u bytes\n", cs->pending_inbound_msg->pos);
641         response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
642         res = MHD_queue_response (session, MHD_HTTP_BAD_REQUEST, response);
643         MHD_destroy_response (response);
644         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Sent HTTP/1.1: 400 BAD REQUEST as PUT Response\n");
645       }
646
647       GNUNET_free_non_null (gn_msg);
648       cs->is_put_in_progress = GNUNET_NO;
649       cs->is_bad_request = GNUNET_NO;
650       cs->pending_inbound_msg->pos = 0;
651       return res;
652     }
653   }
654   if ( 0 == strcmp (MHD_HTTP_METHOD_GET, method) )
655   {
656     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Got GET Request\n");
657     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"URL: `%s'\n",url);
658     response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
659     res = MHD_queue_response (session, MHD_HTTP_OK, response);
660     MHD_destroy_response (response);
661     return res;
662   }
663   return MHD_NO;
664 }
665
666
667 /**
668  * Call MHD to process pending requests and then go back
669  * and schedule the next run.
670  */
671 static void http_daemon_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
672
673 /**
674  * Function that queries MHD's select sets and
675  * starts the task waiting for them.
676  */
677 static GNUNET_SCHEDULER_TaskIdentifier
678 http_daemon_prepare (struct MHD_Daemon *daemon_handle)
679 {
680   GNUNET_SCHEDULER_TaskIdentifier ret;
681   fd_set rs;
682   fd_set ws;
683   fd_set es;
684   struct GNUNET_NETWORK_FDSet *wrs;
685   struct GNUNET_NETWORK_FDSet *wws;
686   struct GNUNET_NETWORK_FDSet *wes;
687   int max;
688   unsigned long long timeout;
689   int haveto;
690   struct GNUNET_TIME_Relative tv;
691
692   FD_ZERO(&rs);
693   FD_ZERO(&ws);
694   FD_ZERO(&es);
695   wrs = GNUNET_NETWORK_fdset_create ();
696   wes = GNUNET_NETWORK_fdset_create ();
697   wws = GNUNET_NETWORK_fdset_create ();
698   max = -1;
699   GNUNET_assert (MHD_YES ==
700                  MHD_get_fdset (daemon_handle,
701                                 &rs,
702                                 &ws,
703                                 &es,
704                                 &max));
705   haveto = MHD_get_timeout (daemon_handle, &timeout);
706   if (haveto == MHD_YES)
707     tv.value = (uint64_t) timeout;
708   else
709     tv = GNUNET_TIME_UNIT_FOREVER_REL;
710   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max);
711   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max);
712   GNUNET_NETWORK_fdset_copy_native (wes, &es, max);
713   ret = GNUNET_SCHEDULER_add_select (plugin->env->sched,
714                                      GNUNET_SCHEDULER_PRIORITY_HIGH,
715                                      GNUNET_SCHEDULER_NO_TASK,
716                                      tv,
717                                      wrs,
718                                      wws,
719                                      &http_daemon_run,
720                                      daemon_handle);
721   GNUNET_NETWORK_fdset_destroy (wrs);
722   GNUNET_NETWORK_fdset_destroy (wws);
723   GNUNET_NETWORK_fdset_destroy (wes);
724   return ret;
725 }
726
727 /**
728  * Call MHD to process pending requests and then go back
729  * and schedule the next run.
730  */
731 static void http_daemon_run (void *cls,
732                              const struct GNUNET_SCHEDULER_TaskContext *tc)
733 {
734   struct MHD_Daemon *daemon_handle = cls;
735
736   if (daemon_handle == http_daemon_v4)
737     http_task_v4 = GNUNET_SCHEDULER_NO_TASK;
738
739   if (daemon_handle == http_daemon_v6)
740     http_task_v6 = GNUNET_SCHEDULER_NO_TASK;
741
742   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
743     return;
744
745   GNUNET_assert (MHD_YES == MHD_run (daemon_handle));
746   if (daemon_handle == http_daemon_v4)
747     http_task_v4 = http_daemon_prepare (daemon_handle);
748   if (daemon_handle == http_daemon_v6)
749     http_task_v6 = http_daemon_prepare (daemon_handle);
750   return;
751 }
752
753 /**
754  * Removes a message from the linked list of messages
755  * @param ses session to remove message from
756  * @param msg message to remove
757  * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
758  */
759
760 static int remove_http_message(struct Session * ses, struct HTTP_Message * msg)
761 {
762   struct HTTP_Message * cur;
763   struct HTTP_Message * next;
764
765   cur = ses->pending_outbound_msg;
766   next = NULL;
767
768   if (cur == NULL)
769     return GNUNET_SYSERR;
770
771   if (cur == msg)
772   {
773     ses->pending_outbound_msg = cur->next;
774     GNUNET_free (cur->buf);
775     GNUNET_free (cur);
776     cur = NULL;
777     return GNUNET_OK;
778   }
779
780   while (cur->next!=msg)
781   {
782     if (cur->next != NULL)
783       cur = cur->next;
784     else
785       return GNUNET_SYSERR;
786   }
787
788   cur->next = cur->next->next;
789   GNUNET_free (cur->next->buf);
790   GNUNET_free (cur->next);
791   cur->next = NULL;
792   return GNUNET_OK;
793 }
794
795
796 /**
797  * Callback method used with libcurl
798  * Method is called when libcurl needs to read data during sending
799  * @param stream pointer where to write data
800  * @param size size of an individual element
801  * @param nmemb count of elements that can be written to the buffer
802  * @param ptr source pointer, passed to the libcurl handle
803  * @return bytes written to stream
804  */
805 static size_t send_read_callback(void *stream, size_t size, size_t nmemb, void *ptr)
806 {
807   struct Session * ses = ptr;
808   struct HTTP_Message * msg = ses->pending_outbound_msg;
809   unsigned int bytes_sent;
810
811   bytes_sent = 0;
812   if (msg->len > (size * nmemb))
813     return CURL_READFUNC_ABORT;
814
815   if (( msg->pos < msg->len) && (msg->len < (size * nmemb)))
816   {
817     memcpy(stream, msg->buf, msg->len);
818     msg->pos = msg->len;
819     bytes_sent = msg->len;
820   }
821
822   return bytes_sent;
823 }
824
825 /**
826 * Callback method used with libcurl
827 * Method is called when libcurl needs to write data during sending
828 * @param stream pointer where to write data
829 * @param size size of an individual element
830 * @param nmemb count of elements that can be written to the buffer
831 * @param ptr destination pointer, passed to the libcurl handle
832 * @return bytes read from stream
833 */
834 static size_t send_write_callback( void *stream, size_t size, size_t nmemb, void *ptr)
835 {
836   char * data = malloc(size*nmemb +1);
837
838   memcpy( data, stream, size*nmemb);
839   data[size*nmemb] = '\0';
840   /* Just a dummy print for the response recieved for the PUT message */
841   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Recieved %u bytes: `%s' \n", size * nmemb, data);
842   free (data);
843   return (size * nmemb);
844
845 }
846
847 /**
848  * Function setting up file descriptors and scheduling task to run
849  * @param session session to send data to
850  * @return bytes sent to peer
851  */
852 static size_t send_prepare(struct Session* session );
853
854 /**
855  * Function setting up curl handle and selecting message to send
856  * @param ses session to send data to
857  * @return bytes sent to peer
858  */
859 static ssize_t send_select_init (struct Session* ses  )
860 {
861   char * url;
862   int bytes_sent = 0;
863   CURLMcode mret;
864   struct HTTP_Message * msg;
865
866   /* FIFO selection, send oldest msg first, perhaps priority here?  */
867   msg = ses->pending_outbound_msg;
868
869   url = GNUNET_malloc( 7 + strlen(ses->ip) + 7 + strlen ((char *) &(ses->hash)) + 1);
870   /* FIXME: use correct port number */
871   GNUNET_asprintf(&url,"http://%s:%u/%s",ses->ip,12389, (char *) &(ses->hash));
872
873   /* curl_easy_setopt(ses->curl_handle, CURLOPT_VERBOSE, 1L); */
874   curl_easy_setopt(ses->curl_handle, CURLOPT_URL, url);
875   curl_easy_setopt(ses->curl_handle, CURLOPT_PUT, 1L);
876   curl_easy_setopt(ses->curl_handle, CURLOPT_READFUNCTION, send_read_callback);
877   curl_easy_setopt(ses->curl_handle, CURLOPT_READDATA, ses);
878   curl_easy_setopt(ses->curl_handle, CURLOPT_WRITEFUNCTION, send_write_callback);
879   curl_easy_setopt(ses->curl_handle, CURLOPT_READDATA, ses);
880   curl_easy_setopt(ses->curl_handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t) msg->len);
881   curl_easy_setopt(ses->curl_handle, CURLOPT_TIMEOUT, (long) (timeout.value / 1000 ));
882   curl_easy_setopt(ses->curl_handle, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT);
883
884   mret = curl_multi_add_handle(multi_handle, ses->curl_handle);
885   if (mret != CURLM_OK)
886   {
887     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
888                 _("%s failed at %s:%d: `%s'\n"),
889                 "curl_multi_add_handle", __FILE__, __LINE__,
890                 curl_multi_strerror (mret));
891     return -1;
892   }
893   bytes_sent = send_prepare (ses );
894   GNUNET_free ( url );
895   return bytes_sent;
896 }
897
898 static void send_execute (void *cls,
899              const struct GNUNET_SCHEDULER_TaskContext *tc)
900 {
901   int running;
902   struct CURLMsg *msg;
903   CURLMcode mret;
904   struct Session * cs = NULL;
905
906   http_task_send = GNUNET_SCHEDULER_NO_TASK;
907   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
908     return;
909
910   do
911     {
912       running = 0;
913       mret = curl_multi_perform (multi_handle, &running);
914       if (running == 0)
915         {
916           do
917             {
918
919               msg = curl_multi_info_read (multi_handle, &running);
920               GNUNET_break (msg != NULL);
921               if (msg == NULL)
922                 break;
923               /* get session for affected curl handle */
924               cs = find_session_by_curlhandle (msg->easy_handle);
925               GNUNET_assert ( cs != NULL );
926               switch (msg->msg)
927                 {
928
929                 case CURLMSG_DONE:
930                   if ( (msg->data.result != CURLE_OK) &&
931                        (msg->data.result != CURLE_GOT_NOTHING) )
932                     {
933
934                     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
935                                _("%s failed for `%s' at %s:%d: `%s'\n"),
936                                "curl_multi_perform",
937                                cs->ip,
938                                __FILE__,
939                                __LINE__,
940                                curl_easy_strerror (msg->data.result));
941                     /* sending msg failed*/
942                     if ( NULL != cs->transmit_cont)
943                       cs->transmit_cont (NULL,&cs->sender,GNUNET_SYSERR);
944                     }
945                   else
946                     {
947                     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
948                                 "Send to %s completed.\n", cs->ip);
949                     if (GNUNET_OK != remove_http_message(cs, cs->pending_outbound_msg))
950                       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Message could not be removed from session `%s'", GNUNET_i2s(&cs->sender));
951
952                     curl_easy_cleanup(cs->curl_handle);
953                     cs->curl_handle=NULL;
954
955                     /* send pending messages */
956                     if (cs->pending_outbound_msg != NULL)
957                       send_select_init (cs);
958
959                     /* Calling transmit continuation  */
960                     if ( NULL != cs->transmit_cont)
961                       cs->transmit_cont (NULL,&cs->sender,GNUNET_OK);
962                     }
963                   return;
964                 default:
965                   break;
966                 }
967
968             }
969           while ( (running > 0) );
970         }
971     }
972   while (mret == CURLM_CALL_MULTI_PERFORM);
973   send_prepare(cls);
974 }
975
976
977 /**
978  * Function setting up file descriptors and scheduling task to run
979  * @param ses session to send data to
980  * @return bytes sent to peer
981  */
982 static size_t send_prepare(struct Session* session )
983 {
984   fd_set rs;
985   fd_set ws;
986   fd_set es;
987   int max;
988   struct GNUNET_NETWORK_FDSet *grs;
989   struct GNUNET_NETWORK_FDSet *gws;
990   long to;
991   CURLMcode mret;
992
993   max = -1;
994   FD_ZERO (&rs);
995   FD_ZERO (&ws);
996   FD_ZERO (&es);
997   mret = curl_multi_fdset (multi_handle, &rs, &ws, &es, &max);
998   if (mret != CURLM_OK)
999     {
1000       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1001                   _("%s failed at %s:%d: `%s'\n"),
1002                   "curl_multi_fdset", __FILE__, __LINE__,
1003                   curl_multi_strerror (mret));
1004       return -1;
1005     }
1006   mret = curl_multi_timeout (multi_handle, &to);
1007   if (mret != CURLM_OK)
1008     {
1009       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1010                   _("%s failed at %s:%d: `%s'\n"),
1011                   "curl_multi_timeout", __FILE__, __LINE__,
1012                   curl_multi_strerror (mret));
1013       return -1;
1014     }
1015
1016   grs = GNUNET_NETWORK_fdset_create ();
1017   gws = GNUNET_NETWORK_fdset_create ();
1018   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1019   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1020   http_task_send = GNUNET_SCHEDULER_add_select (plugin->env->sched,
1021                                    GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1022                                    GNUNET_SCHEDULER_NO_TASK,
1023                                    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 0),
1024                                    grs,
1025                                    gws,
1026                                    &send_execute,
1027                                    session);
1028   GNUNET_NETWORK_fdset_destroy (gws);
1029   GNUNET_NETWORK_fdset_destroy (grs);
1030
1031   /* FIXME: return bytes REALLY sent */
1032   return 0;
1033 }
1034
1035 /**
1036  * Function that can be used by the transport service to transmit
1037  * a message using the plugin.
1038  *
1039  * @param cls closure
1040  * @param target who should receive this message
1041  * @param priority how important is the message
1042  * @param msgbuf the message to transmit
1043  * @param msgbuf_size number of bytes in 'msgbuf'
1044  * @param to when should we time out
1045  * @param session which session must be used (or NULL for "any")
1046  * @param addr the address to use (can be NULL if the plugin
1047  *                is "on its own" (i.e. re-use existing TCP connection))
1048  * @param addrlen length of the address in bytes
1049  * @param force_address GNUNET_YES if the plugin MUST use the given address,
1050  *                otherwise the plugin may use other addresses or
1051  *                existing connections (if available)
1052  * @param cont continuation to call once the message has
1053  *        been transmitted (or if the transport is ready
1054  *        for the next transmission call; or if the
1055  *        peer disconnected...)
1056  * @param cont_cls closure for cont
1057  * @return number of bytes used (on the physical network, with overheads);
1058  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
1059  *         and does NOT mean that the message was not transmitted (DV)
1060  */
1061 static ssize_t
1062 http_plugin_send (void *cls,
1063                       const struct GNUNET_PeerIdentity *target,
1064                       const char *msgbuf,
1065                       size_t msgbuf_size,
1066                       unsigned int priority,
1067                       struct GNUNET_TIME_Relative to,
1068                       struct Session *session,
1069                       const void *addr,
1070                       size_t addrlen,
1071                       int force_address,
1072                       GNUNET_TRANSPORT_TransmitContinuation cont,
1073                       void *cont_cls)
1074 {
1075   struct Session* ses;
1076   struct Session* ses_temp;
1077   struct HTTP_Message * msg;
1078   struct HTTP_Message * tmp;
1079   int bytes_sent = 0;
1080
1081   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Transport told plugin to send to peer `%s'\n",GNUNET_i2s(target));
1082
1083   /* find session for peer */
1084   ses = find_session_by_pi (target);
1085   if (NULL != ses )
1086     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Existing session for peer `%s' found\n", GNUNET_i2s(target));
1087   if ( ses == NULL)
1088   {
1089     /* create new session object */
1090
1091     /*FIXME: what is const void * really? Assuming struct sockaddr_in * ! */
1092     ses = create_session((struct sockaddr_in *) addr, target);
1093     ses->is_active = GNUNET_YES;
1094     ses->transmit_cont = cont;
1095
1096     /* Insert session into linked list */
1097     if ( plugin->sessions == NULL)
1098     {
1099       plugin->sessions = ses;
1100       plugin->session_count = 1;
1101     }
1102     ses_temp = plugin->sessions;
1103     while ( ses_temp->next != NULL )
1104     {
1105       ses_temp = ses_temp->next;
1106     }
1107     if (ses_temp != ses )
1108     {
1109       ses_temp->next = ses;
1110       plugin->session_count++;
1111     }
1112     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"New Session `%s' inserted, count %u \n", GNUNET_i2s(target), plugin->session_count);
1113   }
1114
1115   ses->curl_handle = curl_easy_init();
1116   if( NULL == ses->curl_handle)
1117   {
1118     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Getting cURL handle failed\n");
1119     return -1;
1120   }
1121
1122   if ( NULL != cont)
1123     ses->transmit_cont = cont;
1124   timeout = to;
1125   /* setting up message */
1126   msg = GNUNET_malloc (sizeof (struct HTTP_Message));
1127   msg->next = NULL;
1128   msg->len = msgbuf_size;
1129   msg->pos = 0;
1130   msg->buf = GNUNET_malloc (msgbuf_size);
1131   memcpy (msg->buf,msgbuf, msgbuf_size);
1132
1133   /* insert created message in list of pending messages */
1134
1135   if (ses->pending_outbound_msg == NULL)
1136   {
1137     ses->pending_outbound_msg = msg;
1138   }
1139   tmp = ses->pending_outbound_msg;
1140   while ( NULL != tmp->next)
1141   {
1142     tmp = tmp->next;
1143   }
1144   if ( tmp != msg)
1145     tmp->next = msg;
1146
1147   bytes_sent = send_select_init (ses);
1148   return bytes_sent;
1149
1150 }
1151
1152
1153
1154 /**
1155  * Function that can be used to force the plugin to disconnect
1156  * from the given peer and cancel all previous transmissions
1157  * (and their continuationc).
1158  *
1159  * @param cls closure
1160  * @param target peer from which to disconnect
1161  */
1162 static void
1163 http_plugin_disconnect (void *cls,
1164                             const struct GNUNET_PeerIdentity *target)
1165 {
1166   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"HTTP Plugin: http_plugin_disconnect\n");
1167   // struct Plugin *plugin = cls;
1168   // FIXME
1169 }
1170
1171
1172 /**
1173  * Convert the transports address to a nice, human-readable
1174  * format.
1175  *
1176  * @param cls closure
1177  * @param type name of the transport that generated the address
1178  * @param addr one of the addresses of the host, NULL for the last address
1179  *        the specific address format depends on the transport
1180  * @param addrlen length of the address
1181  * @param numeric should (IP) addresses be displayed in numeric form?
1182  * @param timeout after how long should we give up?
1183  * @param asc function to call on each string
1184  * @param asc_cls closure for asc
1185  */
1186 static void
1187 http_plugin_address_pretty_printer (void *cls,
1188                                         const char *type,
1189                                         const void *addr,
1190                                         size_t addrlen,
1191                                         int numeric,
1192                                         struct GNUNET_TIME_Relative timeout,
1193                                         GNUNET_TRANSPORT_AddressStringCallback
1194                                         asc, void *asc_cls)
1195 {
1196   const struct IPv4HttpAddress *t4;
1197   const struct IPv6HttpAddress *t6;
1198   struct sockaddr_in a4;
1199   struct sockaddr_in6 a6;
1200   char * address;
1201   char * ret;
1202   unsigned int port;
1203
1204   if (addrlen == sizeof (struct IPv6HttpAddress))
1205     {
1206       address = GNUNET_malloc (INET6_ADDRSTRLEN);
1207       t6 = addr;
1208       a6.sin6_addr = t6->ipv6_addr;
1209       inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
1210       port = ntohs(t6->u6_port);
1211     }
1212   else if (addrlen == sizeof (struct IPv4HttpAddress))
1213     {
1214       address = GNUNET_malloc (INET_ADDRSTRLEN);
1215       t4 = addr;
1216       a4.sin_addr.s_addr =  t4->ipv4_addr;
1217       inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
1218       port = ntohs(t4->u_port);
1219     }
1220   else
1221     {
1222       /* invalid address */
1223       GNUNET_break_op (0);
1224       asc (asc_cls, NULL);
1225       return;
1226     }
1227
1228   ret = GNUNET_malloc(strlen(address) +14);
1229   GNUNET_asprintf(&ret,"http://%s:%u/",address,port);
1230   GNUNET_free (address);
1231   asc (asc_cls, ret);
1232 }
1233
1234
1235
1236 /**
1237  * Another peer has suggested an address for this
1238  * peer and transport plugin.  Check that this could be a valid
1239  * address.  If so, consider adding it to the list
1240  * of addresses.
1241  *
1242  * @param cls closure
1243  * @param addr pointer to the address
1244  * @param addrlen length of addr
1245  * @return GNUNET_OK if this is a plausible address for this peer
1246  *         and transport
1247  */
1248 static int
1249 http_plugin_address_suggested (void *cls,
1250                                   void *addr, size_t addrlen)
1251 {
1252   struct IPv4HttpAddress *v4;
1253   struct IPv6HttpAddress *v6;
1254
1255   if ((addrlen != sizeof (struct IPv4HttpAddress)) &&
1256       (addrlen != sizeof (struct IPv6HttpAddress)))
1257     {
1258       GNUNET_break_op (0);
1259       return GNUNET_SYSERR;
1260     }
1261   if (addrlen == sizeof (struct IPv4HttpAddress))
1262     {
1263       v4 = (struct IPv4HttpAddress *) addr;
1264
1265       v4->u_port = ntohs (v4->u_port);
1266       if (v4->u_port != plugin->port_inbound)
1267       {
1268         GNUNET_break_op (0);
1269         return GNUNET_SYSERR;
1270       }
1271     }
1272   else
1273     {
1274       v6 = (struct IPv6HttpAddress *) addr;
1275       if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr))
1276         {
1277           GNUNET_break_op (0);
1278           return GNUNET_SYSERR;
1279         }
1280       v6->u6_port = ntohs (v6->u6_port);
1281       if (v6->u6_port != plugin->port_inbound)
1282       {
1283         GNUNET_break_op (0);
1284         return GNUNET_SYSERR;
1285       }
1286
1287     }
1288   return GNUNET_OK;
1289 }
1290
1291
1292 /**
1293  * Function called for a quick conversion of the binary address to
1294  * a numeric address.  Note that the caller must not free the
1295  * address and that the next call to this function is allowed
1296  * to override the address again.
1297  *
1298  * @param cls closure
1299  * @param addr binary address
1300  * @param addrlen length of the address
1301  * @return string representing the same address
1302  */
1303 static const char*
1304 http_plugin_address_to_string (void *cls,
1305                                    const void *addr,
1306                                    size_t addrlen)
1307 {
1308   const struct IPv4HttpAddress *t4;
1309   const struct IPv6HttpAddress *t6;
1310   struct sockaddr_in a4;
1311   struct sockaddr_in6 a6;
1312   char * address;
1313   char * ret;
1314   unsigned int port;
1315
1316   if (addrlen == sizeof (struct IPv6HttpAddress))
1317     {
1318       address = GNUNET_malloc (INET6_ADDRSTRLEN);
1319       t6 = addr;
1320       a6.sin6_addr = t6->ipv6_addr;
1321       inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
1322       port = ntohs(t6->u6_port);
1323     }
1324   else if (addrlen == sizeof (struct IPv4HttpAddress))
1325     {
1326       address = GNUNET_malloc (INET_ADDRSTRLEN);
1327       t4 = addr;
1328       a4.sin_addr.s_addr =  t4->ipv4_addr;
1329       inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
1330       port = ntohs(t4->u_port);
1331     }
1332   else
1333     {
1334       /* invalid address */
1335       return NULL;
1336     }
1337
1338   ret = GNUNET_malloc(strlen(address) +6);
1339   GNUNET_asprintf(&ret,"%s:%u",address,port);
1340   GNUNET_free (address);
1341   return ret;
1342 }
1343
1344 /**
1345  * Add the IP of our network interface to the list of
1346  * our external IP addresses.
1347  *
1348  * @param cls the 'struct Plugin*'
1349  * @param name name of the interface
1350  * @param isDefault do we think this may be our default interface
1351  * @param addr address of the interface
1352  * @param addrlen number of bytes in addr
1353  * @return GNUNET_OK to continue iterating
1354  */
1355 static int
1356 process_interfaces (void *cls,
1357                     const char *name,
1358                     int isDefault,
1359                     const struct sockaddr *addr, socklen_t addrlen)
1360 {
1361   struct IPv4HttpAddress t4;
1362   struct IPv6HttpAddress t6;
1363   int af;
1364   void *arg;
1365   uint16_t args;
1366
1367
1368
1369   af = addr->sa_family;
1370   if (af == AF_INET)
1371     {
1372       if (INADDR_LOOPBACK == ntohl(((struct sockaddr_in *) addr)->sin_addr.s_addr))
1373       {
1374         /* skip loopback addresses */
1375         return GNUNET_OK;
1376       }
1377       t4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
1378       t4.u_port = htons (plugin->port_inbound);
1379       arg = &t4;
1380       args = sizeof (t4);
1381     }
1382   else if (af == AF_INET6)
1383     {
1384       if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr))
1385         {
1386           /* skip link local addresses */
1387           return GNUNET_OK;
1388         }
1389       if (IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *) addr)->sin6_addr))
1390         {
1391           /* skip loopback addresses */
1392           return GNUNET_OK;
1393         }
1394       memcpy (&t6.ipv6_addr,
1395               &((struct sockaddr_in6 *) addr)->sin6_addr,
1396               sizeof (struct in6_addr));
1397       t6.u6_port = htons (plugin->port_inbound);
1398       arg = &t6;
1399       args = sizeof (t6);
1400     }
1401   else
1402     {
1403       GNUNET_break (0);
1404       return GNUNET_OK;
1405     }
1406   plugin->env->notify_address(plugin->env->cls,"http",arg, args, GNUNET_TIME_UNIT_FOREVER_REL);
1407
1408   return GNUNET_OK;
1409 }
1410
1411 /**
1412  * Exit point from the plugin.
1413  */
1414 void *
1415 libgnunet_plugin_transport_http_done (void *cls)
1416 {
1417   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1418   struct Plugin *plugin = api->cls;
1419   struct Session * cs;
1420   struct Session * cs_next;
1421   CURLMcode mret;
1422
1423   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Unloading http plugin...\n");
1424
1425   if ( http_task_v4 != GNUNET_SCHEDULER_NO_TASK)
1426   {
1427     GNUNET_SCHEDULER_cancel(plugin->env->sched, http_task_v4);
1428     http_task_v4 = GNUNET_SCHEDULER_NO_TASK;
1429   }
1430
1431   if ( http_task_v6 != GNUNET_SCHEDULER_NO_TASK)
1432   {
1433     GNUNET_SCHEDULER_cancel(plugin->env->sched, http_task_v6);
1434     http_task_v6 = GNUNET_SCHEDULER_NO_TASK;
1435   }
1436
1437   if ( http_task_send != GNUNET_SCHEDULER_NO_TASK)
1438   {
1439     GNUNET_SCHEDULER_cancel(plugin->env->sched, http_task_send);
1440     http_task_send = GNUNET_SCHEDULER_NO_TASK;
1441   }
1442
1443   if (http_daemon_v4 != NULL)
1444   {
1445     MHD_stop_daemon (http_daemon_v4);
1446     http_daemon_v4 = NULL;
1447   }
1448   if (http_daemon_v6 != NULL)
1449   {
1450     MHD_stop_daemon (http_daemon_v6);
1451     http_daemon_v6 = NULL;
1452   }
1453
1454   mret = curl_multi_cleanup(multi_handle);
1455   if ( CURLM_OK != mret)
1456     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"curl multihandle clean up failed");
1457
1458   /* free all sessions */
1459   cs = plugin->sessions;
1460
1461   while ( NULL != cs)
1462     {
1463       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Freeing session to `%s'\n",cs->ip);
1464
1465       cs_next = cs->next;
1466       /* freeing messages */
1467       struct HTTP_Message *cur;
1468       struct HTTP_Message *tmp;
1469       cur = cs->pending_outbound_msg;
1470
1471       while (cur != NULL)
1472       {
1473          tmp = cur->next;
1474          if (NULL != cur->buf)
1475            GNUNET_free (cur->buf);
1476          GNUNET_free (cur);
1477          cur = tmp;
1478       }
1479       GNUNET_free (cs->pending_inbound_msg->buf);
1480       GNUNET_free (cs->pending_inbound_msg);
1481       GNUNET_free (cs->ip);
1482       GNUNET_free (cs->addr);
1483       GNUNET_free (cs);
1484       plugin->session_count--;
1485       cs = cs_next;
1486
1487     }
1488
1489   /* GNUNET_SERVICE_stop (plugin->service); */
1490   GNUNET_free (hostname);
1491   GNUNET_free (plugin);
1492   GNUNET_free (api);
1493   return NULL;
1494 }
1495
1496
1497 /**
1498  * Entry point for the plugin.
1499  */
1500 void *
1501 libgnunet_plugin_transport_http_init (void *cls)
1502 {
1503   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1504   struct GNUNET_TRANSPORT_PluginFunctions *api;
1505   struct GNUNET_SERVICE_Context *service;
1506   unsigned int timeout;
1507   struct GNUNET_TIME_Relative gn_timeout;
1508   long long unsigned int port;
1509
1510   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting http plugin...\n");
1511
1512   service = NULL;
1513   /*
1514   service = GNUNET_SERVICE_start ("transport-http", env->sched, env->cfg);
1515   if (service == NULL)
1516     {
1517       GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "", _
1518                        ("Failed to start service for `%s' transport plugin.\n"),
1519                        "http");
1520       return NULL;
1521     }
1522     */
1523
1524   plugin = GNUNET_malloc (sizeof (struct Plugin));
1525   plugin->env = env;
1526   plugin->sessions = NULL;
1527   plugin->service = service;
1528   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1529   api->cls = plugin;
1530   api->send = &http_plugin_send;
1531   api->disconnect = &http_plugin_disconnect;
1532   api->address_pretty_printer = &http_plugin_address_pretty_printer;
1533   api->check_address = &http_plugin_address_suggested;
1534   api->address_to_string = &http_plugin_address_to_string;
1535
1536   hostname = GNUNET_RESOLVER_local_fqdn_get ();
1537
1538   /* Hashing our identity to use it in URLs */
1539   GNUNET_CRYPTO_hash_to_enc ( &(plugin->env->my_identity->hashPubKey), &my_ascii_hash_ident);
1540
1541   /* Reading port number from config file */
1542   if ((GNUNET_OK !=
1543        GNUNET_CONFIGURATION_get_value_number (env->cfg,
1544                                               "transport-http",
1545                                               "PORT",
1546                                               &port)) ||
1547       (port > 65535) )
1548     {
1549       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1550                        "http",
1551                        _
1552                        ("Require valid port number for transport plugin `%s' in configuration!\n"),
1553                        "transport-http");
1554       libgnunet_plugin_transport_http_done (api);
1555       return NULL;
1556     }
1557   plugin->port_inbound = port;
1558   gn_timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
1559   timeout = ( gn_timeout.value / 1000);
1560   if ((http_daemon_v4 == NULL) && (http_daemon_v6 == NULL) && (port != 0))
1561     {
1562     http_daemon_v6 = MHD_start_daemon (MHD_USE_IPv6,
1563                                        port,
1564                                        &acceptPolicyCallback,
1565                                        NULL , &accessHandlerCallback, NULL,
1566                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 16,
1567                                        MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
1568                                        MHD_OPTION_CONNECTION_TIMEOUT, timeout,
1569                                        /* FIXME: set correct limit */
1570                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
1571                                        MHD_OPTION_NOTIFY_COMPLETED, &requestCompletedCallback, NULL,
1572                                        MHD_OPTION_END);
1573     http_daemon_v4 = MHD_start_daemon (MHD_NO_FLAG,
1574                                        port,
1575                                        &acceptPolicyCallback,
1576                                        NULL , &accessHandlerCallback, NULL,
1577                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 16,
1578                                        MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
1579                                        MHD_OPTION_CONNECTION_TIMEOUT, timeout,
1580                                        /* FIXME: set correct limit */
1581                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
1582                                        MHD_OPTION_NOTIFY_COMPLETED, &requestCompletedCallback, NULL,
1583                                        MHD_OPTION_END);
1584     }
1585   if (http_daemon_v4 != NULL)
1586     http_task_v4 = http_daemon_prepare (http_daemon_v4);
1587   if (http_daemon_v6 != NULL)
1588     http_task_v6 = http_daemon_prepare (http_daemon_v6);
1589
1590   if (http_task_v4 != GNUNET_SCHEDULER_NO_TASK)
1591     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 on port %u\n",port);
1592   if (http_task_v6 != GNUNET_SCHEDULER_NO_TASK)
1593     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 and IPv6 on port %u\n",port);
1594
1595
1596   /* Initializing cURL */
1597   multi_handle = curl_multi_init();
1598   if ( NULL == multi_handle )
1599   {
1600     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1601                      "http",
1602                      _("Could not initialize curl multi handle, failed to start http plugin!\n"),
1603                      "transport-http");
1604     libgnunet_plugin_transport_http_done (api);
1605     return NULL;
1606   }
1607
1608   GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
1609
1610   return api;
1611 }
1612
1613 /* end of plugin_transport_template.c */