f1896b6cc02c9b3462b73f0f73f266c02e6f0d04
[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_common.h"
29 #include "gnunet_constants.h"
30 #include "gnunet_protocols.h"
31 #include "gnunet_connection_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 #define DEBUG_HTTP GNUNET_NO
44 #define DEBUG_CURL GNUNET_NO
45 #define DEBUG_MHD GNUNET_NO
46 #define DEBUG_CONNECTIONS GNUNET_NO
47 #define DEBUG_SESSION_SELECTION GNUNET_NO
48
49 #define CURL_TCP_NODELAY GNUNET_YES
50
51 #define INBOUND GNUNET_NO
52 #define OUTBOUND GNUNET_YES
53
54 #define PROTOCOL_PREFIX "http"
55
56 /**
57  * Text of the response sent back after the last bytes of a PUT
58  * request have been received (just to formally obey the HTTP
59  * protocol).
60  */
61 #define HTTP_PUT_RESPONSE "Thank you!"
62
63 /**
64  * After how long do we expire an address that we
65  * learned from another peer if it is not reconfirmed
66  * by anyone?
67  */
68 #define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
69
70 /**
71  * Page returned if request invalid
72  */
73 #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>"
74
75 /**
76  * Timeout for a http connect
77  */
78 #define HTTP_CONNECT_TIMEOUT 30
79
80
81 /**
82  * Network format for IPv4 addresses.
83  */
84 struct IPv4HttpAddress
85 {
86   /**
87    * IPv4 address, in network byte order.
88    */
89   uint32_t ipv4_addr GNUNET_PACKED;
90
91   /**
92    * Port number, in network byte order.
93    */
94   uint16_t u_port GNUNET_PACKED;
95
96 };
97
98
99 /**
100  * Network format for IPv6 addresses.
101  */
102 struct IPv6HttpAddress
103 {
104   /**
105    * IPv6 address.
106    */
107   struct in6_addr ipv6_addr GNUNET_PACKED;
108
109   /**
110    * Port number, in network byte order.
111    */
112   uint16_t u6_port GNUNET_PACKED;
113
114 };
115
116
117 /**
118  *  Message to send using http
119  */
120 struct HTTP_Message
121 {
122   /**
123    * next pointer for double linked list
124    */
125   struct HTTP_Message * next;
126
127   /**
128    * previous pointer for double linked list
129    */
130   struct HTTP_Message * prev;
131
132   /**
133    * buffer containing data to send
134    */
135   char *buf;
136
137   /**
138    * amount of data already sent
139    */
140   size_t pos;
141
142   /**
143    * buffer length
144    */
145   size_t size;
146
147   /**
148    * Continuation function to call once the transmission buffer
149    * has again space available.  NULL if there is no
150    * continuation to call.
151    */
152   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
153
154   /**
155    * Closure for transmit_cont.
156    */
157   void *transmit_cont_cls;
158 };
159
160
161 struct HTTP_PeerContext
162 {
163   /**
164    * peer's identity
165    */
166   struct GNUNET_PeerIdentity identity;
167
168   /**
169    * Pointer to the global plugin struct.
170    */
171   struct Plugin *plugin;
172
173   /**
174    * Linked list of connections with this peer
175    * head
176    */
177   struct Session * head;
178
179   /**
180    * Linked list of connections with this peer
181    * tail
182    */
183   struct Session * tail;
184
185   /**
186    * id for next session
187    */
188   size_t session_id_counter;
189
190   /**
191    * Last session used to send data
192    */
193   struct Session * last_session;
194 };
195
196
197 struct Session
198 {
199   /**
200    * API requirement.
201    */
202   struct SessionHeader header;
203
204   /**
205    * next session in linked list
206    */
207   struct Session * next;
208
209   /**
210    * previous session in linked list
211    */
212   struct Session * prev;
213
214   /**
215    * address of this session
216    */
217   void * addr;
218
219   /**
220    * address length
221    */
222   size_t addrlen;
223
224   /**
225    * target url
226    */
227   char * url;
228
229   /**
230    * Message queue for outbound messages
231    * head of queue
232    */
233   struct HTTP_Message * pending_msgs_head;
234
235   /**
236    * Message queue for outbound messages
237    * tail of queue
238    */
239   struct HTTP_Message * pending_msgs_tail;
240
241   /**
242    * partner peer this connection belongs to
243    */
244   struct HTTP_PeerContext * peercontext;
245
246   /**
247    * message stream tokenizer for incoming data
248    */
249   struct GNUNET_SERVER_MessageStreamTokenizer *msgtok;
250
251   /**
252    * session direction
253    * outbound: OUTBOUND (GNUNET_YES)
254    * inbound : INBOUND (GNUNET_NO)
255    */
256   unsigned int direction;
257
258   /**
259    * is session connected to send data?
260    */
261   unsigned int send_connected;
262
263   /**
264    * is send connection active?
265    */
266   unsigned int send_active;
267
268   /**
269    * connection disconnect forced (e.g. from transport)
270    */
271   unsigned int send_force_disconnect;
272
273   /**
274    * is session connected to receive data?
275    */
276   unsigned int recv_connected;
277
278   /**
279    * is receive connection active?
280    */
281   unsigned int recv_active;
282
283   /**
284    * connection disconnect forced (e.g. from transport)
285    */
286   unsigned int recv_force_disconnect;
287
288   /**
289    * id for next session
290    * NOTE: 0 is not an ID, zero is not defined. A correct ID is always > 0
291    */
292   size_t session_id;
293
294   /**
295    * entity managing sending data
296    * outbound session: CURL *
297    * inbound session: mhd_connection *
298    */
299   void * send_endpoint;
300
301   /**
302    * entity managing recieving data
303    * outbound session: CURL *
304    * inbound session: mhd_connection *
305    */
306   void * recv_endpoint;
307 };
308
309 /**
310  * Encapsulation of all of the state of the plugin.
311  */
312 struct Plugin
313 {
314   /**
315    * Our environment.
316    */
317   struct GNUNET_TRANSPORT_PluginEnvironment *env;
318
319   /**
320    * Handle for reporting statistics.
321    */
322   struct GNUNET_STATISTICS_Handle *stats;
323
324   /**
325    * Plugin Port
326    */
327   unsigned int port_inbound;
328
329   struct GNUNET_CONTAINER_MultiHashMap *peers;
330
331   /**
332    * Daemon for listening for new IPv4 connections.
333    */
334   struct MHD_Daemon *http_server_daemon_v4;
335
336   /**
337    * Daemon for listening for new IPv6connections.
338    */
339   struct MHD_Daemon *http_server_daemon_v6;
340
341   /**
342    * Our primary task for http daemon handling IPv4 connections
343    */
344   GNUNET_SCHEDULER_TaskIdentifier http_server_task_v4;
345
346   /**
347    * Our primary task for http daemon handling IPv6 connections
348    */
349   GNUNET_SCHEDULER_TaskIdentifier http_server_task_v6;
350
351   /**
352    * The task sending data
353    */
354   GNUNET_SCHEDULER_TaskIdentifier http_curl_task;
355
356   /**
357    * cURL Multihandle
358    */
359   CURLM * multi_handle;
360
361   /**
362    * Our ASCII encoded, hashed peer identity
363    * This string is used to distinguish between connections and is added to the urls
364    */
365   struct GNUNET_CRYPTO_HashAsciiEncoded my_ascii_hash_ident;
366
367   /**
368    * IPv4 Address the plugin binds to
369    */
370   struct sockaddr_in * bind4_address;
371
372   /**
373    * IPv6 Address the plugins binds to
374    */
375   struct sockaddr_in6 * bind6_address;
376
377   /**
378    * Hostname to bind to
379    */
380   char * bind_hostname;
381
382   /**
383    * Is IPv4 enabled?
384    */
385   int use_ipv6;
386
387   /**
388    * Is IPv6 enabled?
389    */
390   int use_ipv4;
391
392   /**
393    * Closure passed by MHD to the mhd_logger function
394    */
395   void * mhd_log;
396 };
397
398
399 /**
400  * Function called for a quick conversion of the binary address to
401  * a numeric address.  Note that the caller must not free the
402  * address and that the next call to this function is allowed
403  * to override the address again.
404  *
405  * @param cls closure
406  * @param addr binary address
407  * @param addrlen length of the address
408  * @return string representing the same address
409  */
410 static const char*
411 http_plugin_address_to_string (void *cls,
412                                    const void *addr,
413                                    size_t addrlen);
414
415
416 /**
417  * Call MHD to process pending ipv4 requests and then go back
418  * and schedule the next run.
419  */
420 static void http_server_daemon_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
421 /**
422  * Call MHD to process pending ipv6 requests and then go back
423  * and schedule the next run.
424  */
425 static void http_server_daemon_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
426
427 /**
428  * Function setting up curl handle and selecting message to send
429  * @param cls plugin
430  * @param ses session to send data to
431  * @param con connection
432  * @return bytes sent to peer
433  */
434 static ssize_t send_check_connections (void *cls, struct Session *ps);
435
436 /**
437  * Function setting up file descriptors and scheduling task to run
438  * @param cls closure
439  * @param ses session to send data to
440  * @param
441  */
442 static int curl_schedule(void *cls );
443
444
445 /**
446  * Creates a valid url from passed address and id
447  * @param cls plugin as closure
448  * @param addr address to create url from
449  * @param addrlen address lenth
450  * @param id session id
451  * @return the created url
452  */
453 static char * create_url(void * cls, const void * addr, size_t addrlen, size_t id)
454 {
455   struct Plugin *plugin = cls;
456   char *url = NULL;
457   char *addr_str = (char *) http_plugin_address_to_string(NULL, addr, addrlen);
458
459   GNUNET_assert ((addr!=NULL) && (addrlen != 0));
460   GNUNET_asprintf(&url,
461                   "%s://%s/%s;%u", PROTOCOL_PREFIX, addr_str,
462                   (char *) (&plugin->my_ascii_hash_ident),id);
463   GNUNET_free_non_null(addr_str);
464   return url;
465 }
466
467 /**
468  * Removes a message from the linked list of messages
469  * @param ps session
470  * @param msg message
471  * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
472  */
473 static int remove_http_message (struct Session * ps, struct HTTP_Message * msg)
474 {
475   GNUNET_CONTAINER_DLL_remove(ps->pending_msgs_head,ps->pending_msgs_tail,msg);
476   GNUNET_free(msg);
477   return GNUNET_OK;
478 }
479
480 /**
481  * Iterator to remove peer context
482  * @param cls the plugin
483  * @key the peers public key hashcode
484  * @value the peer context
485  * @return GNUNET_YES on success
486  */
487 int remove_peer_context_Iterator (void *cls, const GNUNET_HashCode *key, void *value)
488 {
489   struct Plugin *plugin = cls;
490   struct HTTP_PeerContext * pc = value;
491   struct Session * ps = pc->head;
492   struct Session * tmp = NULL;
493   struct HTTP_Message * msg = NULL;
494   struct HTTP_Message * msg_tmp = NULL;
495 #if DEBUG_HTTP
496   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Freeing context for peer `%s'\n",GNUNET_i2s(&pc->identity));
497 #endif
498   GNUNET_CONTAINER_multihashmap_remove (plugin->peers, &pc->identity.hashPubKey, pc);
499   while (ps!=NULL)
500   {
501         plugin->env->session_end(plugin, &pc->identity, ps);
502         tmp = ps->next;
503
504     GNUNET_free_non_null (ps->addr);
505     GNUNET_free(ps->url);
506     if (ps->msgtok != NULL)
507       GNUNET_SERVER_mst_destroy (ps->msgtok);
508
509     msg = ps->pending_msgs_head;
510     while (msg!=NULL)
511     {
512       msg_tmp = msg->next;
513       GNUNET_free(msg);
514       msg = msg_tmp;
515     }
516     if (ps->direction==OUTBOUND)
517     {
518       if (ps->send_endpoint!=NULL)
519         curl_easy_cleanup(ps->send_endpoint);
520       if (ps->recv_endpoint!=NULL)
521         curl_easy_cleanup(ps->recv_endpoint);
522     }
523
524     GNUNET_free(ps);
525     ps=tmp;
526   }
527   GNUNET_free(pc);
528   GNUNET_STATISTICS_update (plugin->env->stats,
529                             gettext_noop ("# HTTP peers active"),
530                             -1,
531                             GNUNET_NO);
532   return GNUNET_YES;
533 }
534
535
536 /**
537  * Removes a session from the linked list of sessions
538  * @param pc peer context
539  * @param ps session
540  * @param call_msg_cont GNUNET_YES to call pending message continuations, otherwise no
541  * @param call_msg_cont_result result to call message continuations with
542  * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
543  */
544 static int remove_session (struct HTTP_PeerContext * pc, struct Session * ps,  int call_msg_cont, int call_msg_cont_result)
545 {
546   struct HTTP_Message * msg;
547   struct Plugin * plugin = ps->peercontext->plugin;
548
549 #if DEBUG_CONNECTIONS
550   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: removing %s session %X with id %u\n", ps, (ps->direction == INBOUND) ? "inbound" : "outbound", ps, ps->session_id);
551 #endif
552   plugin->env->session_end(plugin, &pc->identity, ps);
553
554   GNUNET_free_non_null (ps->addr);
555   GNUNET_SERVER_mst_destroy (ps->msgtok);
556   GNUNET_free(ps->url);
557
558   if (ps->direction==INBOUND)
559   {
560           if (ps->recv_endpoint != NULL)
561           {
562                   curl_easy_cleanup(ps->recv_endpoint);
563                   ps->recv_endpoint = NULL;
564           }
565           if (ps->send_endpoint != NULL)
566           {
567                   curl_easy_cleanup(ps->send_endpoint);
568                   ps->send_endpoint = NULL;
569           }
570   }
571
572   msg = ps->pending_msgs_head;
573   while (msg!=NULL)
574   {
575     if ((call_msg_cont == GNUNET_YES) && (msg->transmit_cont!=NULL))
576     {
577       msg->transmit_cont (msg->transmit_cont_cls,&pc->identity,call_msg_cont_result);
578     }
579     GNUNET_CONTAINER_DLL_remove(ps->pending_msgs_head,ps->pending_msgs_head,msg);
580     GNUNET_free(msg);
581     msg = ps->pending_msgs_head;
582   }
583
584   GNUNET_CONTAINER_DLL_remove(pc->head,pc->tail,ps);
585   GNUNET_free(ps);
586   ps = NULL;
587
588   /* no sessions left remove peer */
589   if (pc->head==NULL)
590   {
591 #if DEBUG_HTTP
592   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No sessions left for peer `%s', removing context\n",GNUNET_i2s(&pc->identity));
593 #endif
594         remove_peer_context_Iterator(plugin, &pc->identity.hashPubKey, pc);
595   }
596
597   return GNUNET_OK;
598 }
599
600
601 /**
602  * Add the IP of our network interface to the list of
603  * our external IP addresses.
604  *
605  * @param cls the 'struct Plugin*'
606  * @param name name of the interface
607  * @param isDefault do we think this may be our default interface
608  * @param addr address of the interface
609  * @param addrlen number of bytes in addr
610  * @return GNUNET_OK to continue iterating
611  */
612 static int
613 process_interfaces (void *cls,
614                     const char *name,
615                     int isDefault,
616                     const struct sockaddr *addr, socklen_t addrlen)
617 {
618   struct Plugin *plugin = cls;
619   struct IPv4HttpAddress * t4;
620   struct IPv6HttpAddress * t6;
621   int af;
622
623
624   GNUNET_assert(cls !=NULL);
625   af = addr->sa_family;
626   if ((af == AF_INET) && (plugin->use_ipv4 == GNUNET_YES) && (plugin->bind6_address == NULL))
627     {
628           struct in_addr bnd_cmp = ((struct sockaddr_in *) addr)->sin_addr;
629       t4 = GNUNET_malloc(sizeof(struct IPv4HttpAddress));
630       /* Not skipping loopback addresses
631       if (INADDR_LOOPBACK == ntohl(((struct sockaddr_in *) addr)->sin_addr.s_addr))
632       {
633
634         return GNUNET_OK;
635       }
636       */
637       t4->ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
638       t4->u_port = htons (plugin->port_inbound);
639       if (plugin->bind4_address != NULL)
640       {
641           if (0 == memcmp(&plugin->bind4_address->sin_addr, &bnd_cmp, sizeof (struct in_addr)))
642           {
643                   plugin->env->notify_address(plugin->env->cls,"http",t4, sizeof (struct IPv4HttpAddress), GNUNET_TIME_UNIT_FOREVER_REL);
644           }
645       }
646       else
647       {
648           plugin->env->notify_address(plugin->env->cls,"http",t4, sizeof (struct IPv4HttpAddress), GNUNET_TIME_UNIT_FOREVER_REL);
649       }
650       GNUNET_free (t4);
651     }
652   else if ((af == AF_INET6) && (plugin->use_ipv6 == GNUNET_YES)  && (plugin->bind4_address == NULL))
653     {
654           struct in6_addr bnd_cmp6 = ((struct sockaddr_in6 *) addr)->sin6_addr;
655       if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr))
656         {
657           return GNUNET_OK;
658         }
659       t6 = GNUNET_malloc(sizeof(struct IPv6HttpAddress));
660       GNUNET_assert(t6 != NULL);
661       if (plugin->bind6_address != NULL)
662       {
663           if (0 == memcmp(&plugin->bind6_address->sin6_addr, &bnd_cmp6, sizeof (struct in6_addr)))
664           {
665               memcpy (&t6->ipv6_addr,
666                       &((struct sockaddr_in6 *) addr)->sin6_addr,
667                       sizeof (struct in6_addr));
668               t6->u6_port = htons (plugin->port_inbound);
669               plugin->env->notify_address(plugin->env->cls,"http",t6,sizeof (struct IPv6HttpAddress) , GNUNET_TIME_UNIT_FOREVER_REL);
670           }
671       }
672       else
673       {
674           memcpy (&t6->ipv6_addr,
675                   &((struct sockaddr_in6 *) addr)->sin6_addr,
676                   sizeof (struct in6_addr));
677           t6->u6_port = htons (plugin->port_inbound);
678           plugin->env->notify_address(plugin->env->cls,"http",t6,sizeof (struct IPv6HttpAddress) , GNUNET_TIME_UNIT_FOREVER_REL);
679       }
680       GNUNET_free (t6);
681     }
682   return GNUNET_OK;
683 }
684
685
686 /**
687  * External logging function for MHD
688  * @param arg arguments
689  * @param fmt format string
690  * @param ap  list of arguments
691  */
692 void mhd_logger (void * arg, const char * fmt, va_list ap)
693 {
694         char text[1024];
695         vsnprintf(text, 1024, fmt, ap);
696         va_end(ap);
697         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,"MHD: %s \n", text);
698 }
699
700 /**
701  * Callback called by MHD when a connection is terminated
702  * @param cls closure
703  * @param connection the terminated connection
704  * @httpSessionCache the mhd session reference
705  */
706 static void mhd_termination_cb (void *cls, struct MHD_Connection * connection, void **httpSessionCache)
707 {
708   struct Session * ps = *httpSessionCache;
709   if (ps == NULL)
710     return;
711   struct HTTP_PeerContext * pc = ps->peercontext;
712
713   if (connection==ps->recv_endpoint)
714   {
715 #if DEBUG_CONNECTIONS
716     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound connection from peer `%s' was terminated\n", ps, GNUNET_i2s(&pc->identity));
717 #endif
718     ps->recv_active = GNUNET_NO;
719     ps->recv_connected = GNUNET_NO;
720     ps->recv_endpoint = NULL;
721   }
722   if (connection==ps->send_endpoint)
723   {
724
725     ps->send_active = GNUNET_NO;
726     ps->send_connected = GNUNET_NO;
727     ps->send_endpoint = NULL;
728 #if DEBUG_CONNECTIONS
729     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound connection from peer `%s' was terminated\n", ps, GNUNET_i2s(&pc->identity));
730 #endif
731   }
732
733   /* if both connections disconnected, remove session */
734   if ((ps->send_connected == GNUNET_NO) && (ps->recv_connected == GNUNET_NO))
735   {
736       GNUNET_STATISTICS_update (pc->plugin->env->stats,
737                             gettext_noop ("# HTTP inbound sessions for peers active"),
738                             -1,
739                             GNUNET_NO);
740     remove_session(pc,ps,GNUNET_YES,GNUNET_SYSERR);
741   }
742 }
743
744 /**
745  * Callback called by MessageStreamTokenizer when a message has arrived
746  * @param cls current session as closure
747  * @param client clien
748  * @param message the message to be forwarded to transport service
749  */
750
751 static void mhd_write_mst_cb (void *cls,
752                               void *client,
753                               const struct GNUNET_MessageHeader *message)
754 {
755
756   struct Session *ps  = cls;
757   GNUNET_assert(ps != NULL);
758
759   struct HTTP_PeerContext *pc = ps->peercontext;
760   GNUNET_assert(pc != NULL);
761 #if DEBUG_HTTP
762   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
763               "Connection %X: Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n",
764               ps,
765               ntohs(message->type),
766               ntohs(message->size),
767               GNUNET_i2s(&(ps->peercontext)->identity),http_plugin_address_to_string(NULL,ps->addr,ps->addrlen));
768 #endif
769   pc->plugin->env->receive (ps->peercontext->plugin->env->cls,
770                             &pc->identity,
771                             message, 1, ps,
772                             NULL,
773                             0);
774 }
775
776 /**
777  * Check if incoming connection is accepted.
778  * NOTE: Here every connection is accepted
779  * @param cls plugin as closure
780  * @param addr address of incoming connection
781  * @param addr_len address length of incoming connection
782  * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected
783  *
784  */
785 static int
786 mhd_accept_cb (void *cls,
787                       const struct sockaddr *addr, socklen_t addr_len)
788 {
789 #if 0
790   struct Plugin *plugin = cls;
791 #endif
792   /* Every connection is accepted, nothing more to do here */
793   return MHD_YES;
794 }
795
796
797 /**
798  * Callback called by MHD when it needs data to send
799  * @param cls current session
800  * @param pos position in buffer
801  * @param buf the buffer to write data to
802  * @param max max number of bytes available in buffer
803  * @return bytes written to buffer
804  */
805 int mhd_send_callback (void *cls, uint64_t pos, char *buf, int max)
806 {
807   int bytes_read = 0;
808   struct Session * ps = cls;
809   struct HTTP_PeerContext * pc;
810   struct HTTP_Message * msg;
811   GNUNET_assert (ps!=NULL);
812   pc = ps->peercontext;
813   msg = ps->pending_msgs_tail;
814   if (ps->send_force_disconnect==GNUNET_YES)
815   {
816 #if DEBUG_CONNECTIONS
817     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound forced to disconnect\n",ps);
818 #endif
819     return -1;
820   }
821
822   if (msg!=NULL)
823   {
824     if ((msg->size-msg->pos) <= max)
825     {
826       memcpy(buf,&msg->buf[msg->pos],(msg->size-msg->pos));
827       bytes_read = msg->size-msg->pos;
828       msg->pos+=(msg->size-msg->pos);
829     }
830     else
831     {
832       memcpy(buf,&msg->buf[msg->pos],max);
833       msg->pos+=max;
834       bytes_read = max;
835     }
836
837     if (msg->pos==msg->size)
838     {
839       if (NULL!=msg->transmit_cont)
840         msg->transmit_cont (msg->transmit_cont_cls,&pc->identity,GNUNET_OK);
841       remove_http_message(ps,msg);
842     }
843   }
844   return bytes_read;
845 }
846
847 /**
848  * Process GET or PUT request received via MHD.  For
849  * GET, queue response that will send back our pending
850  * messages.  For PUT, process incoming data and send
851  * to GNUnet core.  In either case, check if a session
852  * already exists and create a new one if not.
853  */
854 static int
855 mdh_access_cb (void *cls,
856                        struct MHD_Connection *mhd_connection,
857                        const char *url,
858                        const char *method,
859                        const char *version,
860                        const char *upload_data,
861                        size_t * upload_data_size, void **httpSessionCache)
862 {
863   struct Plugin *plugin = cls;
864   struct MHD_Response *response;
865   const union MHD_ConnectionInfo * conn_info;
866
867   struct sockaddr_in  *addrin;
868   struct sockaddr_in6 *addrin6;
869
870   char address[INET6_ADDRSTRLEN+14];
871   struct GNUNET_PeerIdentity pi_in;
872   size_t id_num = 0;
873
874   struct IPv4HttpAddress ipv4addr;
875   struct IPv6HttpAddress ipv6addr;
876
877   struct HTTP_PeerContext *pc;
878   struct Session *ps = NULL;
879   struct Session *ps_tmp = NULL;
880
881   int res = GNUNET_NO;
882   int send_error_to_client;
883   void * addr = NULL;
884   size_t addr_len = 0 ;
885
886   GNUNET_assert(cls !=NULL);
887   send_error_to_client = GNUNET_NO;
888
889   if (NULL == *httpSessionCache)
890   {
891     /* check url for peer identity , if invalid send HTTP 404*/
892     size_t len = strlen(&url[1]);
893     char * peer = GNUNET_malloc(104+1);
894
895     if ((len>104) && (url[104]==';'))
896     {
897         char * id = GNUNET_malloc((len-104)+1);
898         strcpy(id,&url[105]);
899         memcpy(peer,&url[1],103);
900         peer[103] = '\0';
901         id_num = strtoul ( id, NULL , 10);
902         GNUNET_free(id);
903     }
904     res = GNUNET_CRYPTO_hash_from_string (peer, &(pi_in.hashPubKey));
905     GNUNET_free(peer);
906     if ( GNUNET_SYSERR == res )
907     {
908       response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
909       res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response);
910       MHD_destroy_response (response);
911 #if DEBUG_CONNECTIONS
912       if (res == MHD_YES)
913         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident, sent HTTP 1.1/404\n");
914       else
915         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident, could not send error\n");
916 #endif
917       return res;
918     }
919   }
920   else
921   {
922     ps = *httpSessionCache;
923     pc = ps->peercontext;
924   }
925
926   if (NULL == *httpSessionCache)
927   {
928     /* get peer context */
929     pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &pi_in.hashPubKey);
930     /* Peer unknown */
931     if (pc==NULL)
932     {
933       pc = GNUNET_malloc(sizeof (struct HTTP_PeerContext));
934       pc->plugin = plugin;
935       pc->session_id_counter=1;
936       pc->last_session = NULL;
937       memcpy(&pc->identity, &pi_in, sizeof(struct GNUNET_PeerIdentity));
938       GNUNET_CONTAINER_multihashmap_put(plugin->peers, &pc->identity.hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
939       GNUNET_STATISTICS_update (plugin->env->stats,
940                             gettext_noop ("# HTTP peers active"),
941                             1,
942                             GNUNET_NO);
943     }
944
945     conn_info = MHD_get_connection_info(mhd_connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS );
946     /* Incoming IPv4 connection */
947     if ( AF_INET == conn_info->client_addr->sin_family)
948     {
949       addrin = conn_info->client_addr;
950       inet_ntop(addrin->sin_family, &(addrin->sin_addr),address,INET_ADDRSTRLEN);
951       memcpy(&ipv4addr.ipv4_addr,&(addrin->sin_addr),sizeof(struct in_addr));
952       ipv4addr.u_port = addrin->sin_port;
953       addr = &ipv4addr;
954       addr_len = sizeof(struct IPv4HttpAddress);
955     }
956     /* Incoming IPv6 connection */
957     if ( AF_INET6 == conn_info->client_addr->sin_family)
958     {
959       addrin6 = (struct sockaddr_in6 *) conn_info->client_addr;
960       inet_ntop(addrin6->sin6_family, &(addrin6->sin6_addr),address,INET6_ADDRSTRLEN);
961       memcpy(&ipv6addr.ipv6_addr,&(addrin6->sin6_addr),sizeof(struct in6_addr));
962       ipv6addr.u6_port = addrin6->sin6_port;
963       addr = &ipv6addr;
964       addr_len = sizeof(struct IPv6HttpAddress);
965     }
966
967     GNUNET_assert (addr != NULL);
968     GNUNET_assert (addr_len != 0);
969
970     ps = NULL;
971     /* only inbound sessions here */
972
973     ps_tmp = pc->head;
974     while (ps_tmp!=NULL)
975     {
976       if ((ps_tmp->direction==INBOUND) && (ps_tmp->session_id == id_num) && (id_num!=0))
977       {
978         if ((ps_tmp->recv_force_disconnect!=GNUNET_YES) && (ps_tmp->send_force_disconnect!=GNUNET_YES))
979         ps=ps_tmp;
980         break;
981       }
982       ps_tmp=ps_tmp->next;
983     }
984
985     if (ps==NULL)
986     {
987       ps = GNUNET_malloc(sizeof (struct Session));
988       ps->addr = GNUNET_malloc(addr_len);
989       memcpy(ps->addr,addr,addr_len);
990       ps->addrlen = addr_len;
991       ps->direction=INBOUND;
992       ps->pending_msgs_head = NULL;
993       ps->pending_msgs_tail = NULL;
994       ps->send_connected=GNUNET_NO;
995       ps->send_active=GNUNET_NO;
996       ps->recv_connected=GNUNET_NO;
997       ps->recv_active=GNUNET_NO;
998       ps->peercontext=pc;
999       ps->session_id =id_num;
1000       ps->url = create_url (plugin, ps->addr, ps->addrlen, ps->session_id);
1001       GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps);
1002       GNUNET_STATISTICS_update (plugin->env->stats,
1003                             gettext_noop ("# HTTP inbound sessions for peers active"),
1004                             1,
1005                             GNUNET_NO);
1006     }
1007
1008     *httpSessionCache = ps;
1009     if (ps->msgtok==NULL)
1010       ps->msgtok = GNUNET_SERVER_mst_create (&mhd_write_mst_cb, ps);
1011 #if DEBUG_HTTP
1012     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: HTTP Daemon has new an incoming `%s' request from peer `%s' (`%s')\n",
1013                 ps,
1014                 method,
1015                 GNUNET_i2s(&pc->identity),
1016                 http_plugin_address_to_string(NULL, ps->addr, ps->addrlen));
1017 #endif
1018   }
1019
1020   /* Is it a PUT or a GET request */
1021   if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
1022   {
1023     if (ps->recv_force_disconnect == GNUNET_YES)
1024     {
1025 #if DEBUG_CONNECTIONS
1026       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound connection was forced to disconnect\n",ps);
1027 #endif
1028       ps->recv_active = GNUNET_NO;
1029       return MHD_NO;
1030     }
1031     if ((*upload_data_size == 0) && (ps->recv_active==GNUNET_NO))
1032     {
1033       ps->recv_endpoint = mhd_connection;
1034       ps->recv_connected = GNUNET_YES;
1035       ps->recv_active = GNUNET_YES;
1036       ps->recv_force_disconnect = GNUNET_NO;
1037 #if DEBUG_CONNECTIONS
1038       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound PUT connection connected\n",ps);
1039 #endif
1040       return MHD_YES;
1041     }
1042
1043     /* Transmission of all data complete */
1044     if ((*upload_data_size == 0) && (ps->recv_active == GNUNET_YES))
1045     {
1046       response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
1047       res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
1048 #if DEBUG_CONNECTIONS
1049       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: Sent HTTP/1.1: 200 OK as PUT Response\n",ps);
1050 #endif
1051       MHD_destroy_response (response);
1052       ps->recv_active=GNUNET_NO;
1053       return MHD_YES;
1054     }
1055
1056     /* Recieving data */
1057     if ((*upload_data_size > 0) && (ps->recv_active == GNUNET_YES))
1058     {
1059       res = GNUNET_SERVER_mst_receive(ps->msgtok, ps, upload_data,*upload_data_size, GNUNET_NO, GNUNET_NO);
1060       (*upload_data_size) = 0;
1061       return MHD_YES;
1062     }
1063     else
1064       return MHD_NO;
1065   }
1066   if ( 0 == strcmp (MHD_HTTP_METHOD_GET, method) )
1067   {
1068     if (ps->send_force_disconnect == GNUNET_YES)
1069     {
1070 #if DEBUG_CONNECTIONS
1071       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound connection was  forced to disconnect\n",ps);
1072 #endif
1073       ps->send_active = GNUNET_NO;
1074       return MHD_NO;
1075     }
1076           ps->send_connected = GNUNET_YES;
1077           ps->send_active = GNUNET_YES;
1078           ps->send_endpoint = mhd_connection;
1079           ps->send_force_disconnect = GNUNET_NO;
1080 #if DEBUG_CONNECTIONS
1081           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound GET connection connected\n",ps);
1082 #endif
1083           response = MHD_create_response_from_callback(-1,32 * 1024, &mhd_send_callback, ps, NULL);
1084           res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
1085           MHD_destroy_response (response);
1086           return MHD_YES;
1087   }
1088   return MHD_NO;
1089 }
1090
1091 /**
1092  * Function that queries MHD's select sets and
1093  * starts the task waiting for them.
1094  * @param cls plugin as closure
1095  * @param daemon_handle the MHD daemon handle
1096  * @return gnunet task identifier
1097  */
1098 static GNUNET_SCHEDULER_TaskIdentifier
1099 http_server_daemon_prepare (void * cls, struct MHD_Daemon *daemon_handle)
1100 {
1101   struct Plugin *plugin = cls;
1102   GNUNET_SCHEDULER_TaskIdentifier ret;
1103   fd_set rs;
1104   fd_set ws;
1105   fd_set es;
1106   struct GNUNET_NETWORK_FDSet *wrs;
1107   struct GNUNET_NETWORK_FDSet *wws;
1108   struct GNUNET_NETWORK_FDSet *wes;
1109   int max;
1110   unsigned long long timeout;
1111   int haveto;
1112   struct GNUNET_TIME_Relative tv;
1113
1114   GNUNET_assert(cls !=NULL);
1115   ret = GNUNET_SCHEDULER_NO_TASK;
1116   FD_ZERO(&rs);
1117   FD_ZERO(&ws);
1118   FD_ZERO(&es);
1119   wrs = GNUNET_NETWORK_fdset_create ();
1120   wes = GNUNET_NETWORK_fdset_create ();
1121   wws = GNUNET_NETWORK_fdset_create ();
1122   max = -1;
1123   GNUNET_assert (MHD_YES ==
1124                  MHD_get_fdset (daemon_handle,
1125                                 &rs,
1126                                 &ws,
1127                                 &es,
1128                                 &max));
1129   haveto = MHD_get_timeout (daemon_handle, &timeout);
1130   if (haveto == MHD_YES)
1131     tv.value = (uint64_t) timeout;
1132   else
1133     tv = GNUNET_TIME_UNIT_FOREVER_REL;
1134   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max);
1135   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max);
1136   GNUNET_NETWORK_fdset_copy_native (wes, &es, max);
1137   if (daemon_handle == plugin->http_server_daemon_v4)
1138   {
1139         if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
1140         {
1141                 GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v4);
1142                 plugin->http_server_daemon_v4 = GNUNET_SCHEDULER_NO_TASK;
1143         }
1144
1145     ret = GNUNET_SCHEDULER_add_select (plugin->env->sched,
1146                                        GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1147                                        GNUNET_SCHEDULER_NO_TASK,
1148                                        tv,
1149                                        wrs,
1150                                        wws,
1151                                        &http_server_daemon_v4_run,
1152                                        plugin);
1153   }
1154   if (daemon_handle == plugin->http_server_daemon_v6)
1155   {
1156         if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
1157         {
1158                 GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v6);
1159                 plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
1160         }
1161
1162     ret = GNUNET_SCHEDULER_add_select (plugin->env->sched,
1163                                        GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1164                                        GNUNET_SCHEDULER_NO_TASK,
1165                                        tv,
1166                                        wrs,
1167                                        wws,
1168                                        &http_server_daemon_v6_run,
1169                                        plugin);
1170   }
1171   GNUNET_NETWORK_fdset_destroy (wrs);
1172   GNUNET_NETWORK_fdset_destroy (wws);
1173   GNUNET_NETWORK_fdset_destroy (wes);
1174   return ret;
1175 }
1176
1177 /**
1178  * Call MHD IPv4 to process pending requests and then go back
1179  * and schedule the next run.
1180  * @param cls plugin as closure
1181  * @param tc task context
1182  */
1183 static void http_server_daemon_v4_run (void *cls,
1184                              const struct GNUNET_SCHEDULER_TaskContext *tc)
1185 {
1186   struct Plugin *plugin = cls;
1187
1188   GNUNET_assert(cls !=NULL);
1189   plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
1190
1191   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1192     return;
1193
1194   GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v4));
1195   plugin->http_server_task_v4 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
1196  }
1197
1198
1199 /**
1200  * Call MHD IPv6 to process pending requests and then go back
1201  * and schedule the next run.
1202  * @param cls plugin as closure
1203  * @param tc task context
1204  */
1205 static void http_server_daemon_v6_run (void *cls,
1206                              const struct GNUNET_SCHEDULER_TaskContext *tc)
1207 {
1208   struct Plugin *plugin = cls;
1209
1210   GNUNET_assert(cls !=NULL);
1211   plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
1212
1213   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1214     return;
1215
1216   GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v6));
1217   plugin->http_server_task_v6 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
1218 }
1219
1220 static size_t curl_get_header_cb( void *ptr, size_t size, size_t nmemb, void *stream)
1221 {
1222   struct Session * ps = stream;
1223
1224   long http_result = 0;
1225   int res;
1226   /* Getting last http result code */
1227   GNUNET_assert(NULL!=ps);
1228   if (ps->recv_connected==GNUNET_NO)
1229   {
1230     res = curl_easy_getinfo(ps->recv_endpoint, CURLINFO_RESPONSE_CODE, &http_result);
1231     if (CURLE_OK == res)
1232     {
1233       if (http_result == 200)
1234       {
1235         ps->recv_connected = GNUNET_YES;
1236         ps->recv_active = GNUNET_YES;
1237 #if DEBUG_CONNECTIONS
1238         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: connected to recieve data\n",ps);
1239 #endif
1240         // Calling send_check_connections again since receive is established
1241         send_check_connections (ps->peercontext->plugin, ps);
1242       }
1243     }
1244   }
1245
1246 #if DEBUG_CURL
1247   char * tmp;
1248   size_t len = size * nmemb;
1249   tmp = NULL;
1250   if ((size * nmemb) < SIZE_MAX)
1251     tmp = GNUNET_malloc (len+1);
1252
1253   if ((tmp != NULL) && (len > 0))
1254   {
1255     memcpy(tmp,ptr,len);
1256     if (len>=2)
1257     {
1258       if (tmp[len-2] == 13)
1259         tmp[len-2]= '\0';
1260     }
1261     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: Header: %s\n",ps,tmp);
1262   }
1263   GNUNET_free_non_null (tmp);
1264 #endif
1265
1266   return size * nmemb;
1267 }
1268
1269 /**
1270  * Callback called by libcurl when new headers arrive
1271  * Used to get HTTP result for curl operations
1272  * @param ptr stream to read from
1273  * @param size size of one char element
1274  * @param nmemb number of char elements
1275  * @param stream closure set by user
1276  * @return bytes read by function
1277  */
1278
1279 static size_t curl_put_header_cb( void *ptr, size_t size, size_t nmemb, void *stream)
1280 {
1281   struct Session * ps = stream;
1282
1283   char * tmp;
1284   size_t len = size * nmemb;
1285   long http_result = 0;
1286   int res;
1287
1288   /* Getting last http result code */
1289   GNUNET_assert(NULL!=ps);
1290   res = curl_easy_getinfo(ps->send_endpoint, CURLINFO_RESPONSE_CODE, &http_result);
1291   if (CURLE_OK == res)
1292   {
1293     if ((http_result == 100) && (ps->send_connected==GNUNET_NO))
1294     {
1295       ps->send_connected = GNUNET_YES;
1296       ps->send_active = GNUNET_YES;
1297 #if DEBUG_CONNECTIONS
1298       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: connected to send data\n",ps);
1299 #endif
1300     }
1301     if ((http_result == 200) && (ps->send_connected==GNUNET_YES))
1302     {
1303       ps->send_connected = GNUNET_NO;
1304       ps->send_active = GNUNET_NO;
1305 #if DEBUG_CONNECTIONS
1306       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: sending disconnected\n",ps);
1307 #endif
1308     }
1309   }
1310
1311   tmp = NULL;
1312   if ((size * nmemb) < SIZE_MAX)
1313     tmp = GNUNET_malloc (len+1);
1314
1315   if ((tmp != NULL) && (len > 0))
1316   {
1317     memcpy(tmp,ptr,len);
1318     if (len>=2)
1319     {
1320       if (tmp[len-2] == 13)
1321         tmp[len-2]= '\0';
1322     }
1323   }
1324
1325   GNUNET_free_non_null (tmp);
1326
1327   return size * nmemb;
1328 }
1329
1330 /**
1331  * Callback method used with libcurl
1332  * Method is called when libcurl needs to read data during sending
1333  * @param stream pointer where to write data
1334  * @param size size of an individual element
1335  * @param nmemb count of elements that can be written to the buffer
1336  * @param ptr source pointer, passed to the libcurl handle
1337  * @return bytes written to stream
1338  */
1339 static size_t curl_send_cb(void *stream, size_t size, size_t nmemb, void *ptr)
1340 {
1341   struct Session * ps = ptr;
1342   struct HTTP_Message * msg = ps->pending_msgs_tail;
1343   size_t bytes_sent;
1344   size_t len;
1345
1346   if (ps->send_active == GNUNET_NO)
1347   {
1348         return CURL_READFUNC_PAUSE;
1349   }
1350
1351   if ((ps->pending_msgs_tail == NULL) && (ps->send_active == GNUNET_YES))
1352   {
1353 #if DEBUG_CONNECTIONS
1354     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: No Message to send, pausing connection\n",ps);
1355 #endif
1356     ps->send_active = GNUNET_NO;
1357     return CURL_READFUNC_PAUSE;
1358   }
1359
1360   GNUNET_assert (msg!=NULL);
1361
1362   /* data to send */
1363   if (msg->pos < msg->size)
1364   {
1365     /* data fit in buffer */
1366     if ((msg->size - msg->pos) <= (size * nmemb))
1367     {
1368       len = (msg->size - msg->pos);
1369       memcpy(stream, &msg->buf[msg->pos], len);
1370       msg->pos += len;
1371       bytes_sent = len;
1372     }
1373     else
1374     {
1375       len = size*nmemb;
1376       memcpy(stream, &msg->buf[msg->pos], len);
1377       msg->pos += len;
1378       bytes_sent = len;
1379     }
1380   }
1381   /* no data to send */
1382   else
1383   {
1384     bytes_sent = 0;
1385   }
1386
1387   if ( msg->pos == msg->size)
1388   {
1389 #if DEBUG_CONNECTIONS
1390     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: Message with %u bytes sent, removing message from queue \n",ps, msg->pos);
1391 #endif
1392     /* Calling transmit continuation  */
1393     if (NULL != ps->pending_msgs_tail->transmit_cont)
1394       msg->transmit_cont (ps->pending_msgs_tail->transmit_cont_cls,&(ps->peercontext)->identity,GNUNET_OK);
1395     remove_http_message(ps, msg);
1396   }
1397   return bytes_sent;
1398 }
1399
1400 static void curl_receive_mst_cb  (void *cls,
1401                                 void *client,
1402                                 const struct GNUNET_MessageHeader *message)
1403 {
1404   struct Session *ps  = cls;
1405   GNUNET_assert(ps != NULL);
1406
1407   struct HTTP_PeerContext *pc = ps->peercontext;
1408   GNUNET_assert(pc != NULL);
1409 #if DEBUG_HTTP
1410   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1411               "Connection %X: Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n",
1412               ps,
1413               ntohs(message->type),
1414               ntohs(message->size),
1415               GNUNET_i2s(&(pc->identity)),http_plugin_address_to_string(NULL,ps->addr,ps->addrlen));
1416 #endif
1417   pc->plugin->env->receive (pc->plugin->env->cls,
1418                             &pc->identity,
1419                             message, 1, ps,
1420                             ps->addr,
1421                             ps->addrlen);
1422 }
1423
1424
1425 /**
1426 * Callback method used with libcurl
1427 * Method is called when libcurl needs to write data during sending
1428 * @param stream pointer where to write data
1429 * @param size size of an individual element
1430 * @param nmemb count of elements that can be written to the buffer
1431 * @param ptr destination pointer, passed to the libcurl handle
1432 * @return bytes read from stream
1433 */
1434 static size_t curl_receive_cb( void *stream, size_t size, size_t nmemb, void *ptr)
1435 {
1436   struct Session * ps = ptr;
1437 #if DEBUG_CONNECTIONS
1438   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: %u bytes received\n",ps, size*nmemb);
1439 #endif
1440   GNUNET_SERVER_mst_receive(ps->msgtok, ps, stream, size*nmemb, GNUNET_NO, GNUNET_NO);
1441   return (size * nmemb);
1442
1443 }
1444
1445 static void curl_handle_finished (struct Plugin *plugin)
1446 {
1447         struct Session *ps = NULL;
1448         struct HTTP_PeerContext *pc = NULL;
1449         struct CURLMsg *msg;
1450         struct HTTP_Message * cur_msg = NULL;
1451
1452         int msgs_in_queue;
1453         char * tmp;
1454         long http_result;
1455
1456         do
1457           {
1458                 msg = curl_multi_info_read (plugin->multi_handle, &msgs_in_queue);
1459                 if ((msgs_in_queue == 0) || (msg == NULL))
1460                   break;
1461                 /* get session for affected curl handle */
1462                 GNUNET_assert ( msg->easy_handle != NULL );
1463                 curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &tmp);
1464                 ps = (struct Session *) tmp;
1465                 GNUNET_assert ( ps != NULL );
1466                 pc = ps->peercontext;
1467                 GNUNET_assert ( pc != NULL );
1468                 switch (msg->msg)
1469                   {
1470
1471                   case CURLMSG_DONE:
1472                         if ( (msg->data.result != CURLE_OK) &&
1473                                  (msg->data.result != CURLE_GOT_NOTHING) )
1474                         {
1475                           /* sending msg failed*/
1476                           if (msg->easy_handle == ps->send_endpoint)
1477                           {
1478         #if DEBUG_CONNECTIONS
1479                                 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1480                                                    _("Connection %X: HTTP PUT to peer `%s' (`%s') failed: `%s' `%s'\n"),
1481                                                    ps,
1482                                                    GNUNET_i2s(&pc->identity),
1483                                                    http_plugin_address_to_string(NULL, ps->addr, ps->addrlen),
1484                                                    "curl_multi_perform",
1485                                                    curl_easy_strerror (msg->data.result));
1486         #endif
1487                                 ps->send_connected = GNUNET_NO;
1488                                 ps->send_active = GNUNET_NO;
1489                                 curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint);
1490                                 //curl_easy_cleanup(ps->send_endpoint);
1491                                 //ps->send_endpoint=NULL;
1492                                 cur_msg = ps->pending_msgs_tail;
1493                                 if (( NULL != cur_msg) && ( NULL != cur_msg->transmit_cont))
1494                                   cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_SYSERR);
1495                           }
1496                           /* GET connection failed */
1497                           if (msg->easy_handle == ps->recv_endpoint)
1498                           {
1499         #if DEBUG_CONNECTIONS
1500                                 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1501                                          _("Connection %X: HTTP GET to peer `%s' (`%s') failed: `%s' `%s'\n"),
1502                                          ps,
1503                                          GNUNET_i2s(&pc->identity),
1504                                          http_plugin_address_to_string(NULL, ps->addr, ps->addrlen),
1505                                          "curl_multi_perform",
1506                                          curl_easy_strerror (msg->data.result));
1507         #endif
1508                                 ps->recv_connected = GNUNET_NO;
1509                                 ps->recv_active = GNUNET_NO;
1510                                 curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint);
1511                                 //curl_easy_cleanup(ps->recv_endpoint);
1512                                 //ps->recv_endpoint=NULL;
1513                           }
1514                         }
1515                         else
1516                         {
1517                           if (msg->easy_handle == ps->send_endpoint)
1518                           {
1519                                 GNUNET_assert (CURLE_OK == curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &http_result));
1520         #if DEBUG_CONNECTIONS
1521                                 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1522                                                         "Connection %X: HTTP PUT connection to peer `%s' (`%s') was closed with HTTP code %u\n",
1523                                                          ps,
1524                                                          GNUNET_i2s(&pc->identity),
1525                                                          http_plugin_address_to_string(NULL, ps->addr, ps->addrlen),
1526                                                          http_result);
1527         #endif
1528                                 /* Calling transmit continuation  */
1529                                 cur_msg = ps->pending_msgs_tail;
1530                                 if (( NULL != cur_msg) && (NULL != cur_msg->transmit_cont))
1531                                 {
1532                                   /* HTTP 1xx : Last message before here was informational */
1533                                   if ((http_result >=100) && (http_result < 200))
1534                                         cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_OK);
1535                                   /* HTTP 2xx: successful operations */
1536                                   if ((http_result >=200) && (http_result < 300))
1537                                         cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_OK);
1538                                   /* HTTP 3xx..5xx: error */
1539                                   if ((http_result >=300) && (http_result < 600))
1540                                         cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_SYSERR);
1541                                 }
1542                                 ps->send_connected = GNUNET_NO;
1543                                 ps->send_active = GNUNET_NO;
1544                                 curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint);
1545                                 //curl_easy_cleanup(ps->send_endpoint);
1546                                 //ps->send_endpoint =NULL;
1547                           }
1548                           if (msg->easy_handle == ps->recv_endpoint)
1549                           {
1550         #if DEBUG_CONNECTIONS
1551                                 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1552                                                         "Connection %X: HTTP GET connection to peer `%s' (`%s') was closed with HTTP code %u\n",
1553                                                          ps,
1554                                                          GNUNET_i2s(&pc->identity),
1555                                                          http_plugin_address_to_string(NULL, ps->addr, ps->addrlen),
1556                                                          http_result);
1557         #endif
1558                                 ps->recv_connected = GNUNET_NO;
1559                                 ps->recv_active = GNUNET_NO;
1560                                 curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint);
1561                                 //curl_easy_cleanup(ps->recv_endpoint);
1562                                 //ps->recv_endpoint=NULL;
1563                           }
1564                         }
1565                         if ((ps->recv_connected == GNUNET_NO) && (ps->send_connected == GNUNET_NO))
1566                           remove_session (pc, ps, GNUNET_YES, GNUNET_SYSERR);
1567                         break;
1568                   default:
1569                         break;
1570                   }
1571           }
1572         while ( (msgs_in_queue > 0) );
1573 }
1574
1575
1576 /**
1577  * Task performing curl operations
1578  * @param cls plugin as closure
1579  * @param tc gnunet scheduler task context
1580  */
1581 static void curl_perform (void *cls,
1582              const struct GNUNET_SCHEDULER_TaskContext *tc)
1583 {
1584   struct Plugin *plugin = cls;
1585   static unsigned int handles_last_run;
1586   int running;
1587   CURLMcode mret;
1588
1589   GNUNET_assert(cls !=NULL);
1590
1591
1592
1593   plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
1594   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1595     return;
1596
1597   do
1598     {
1599       running = 0;
1600       mret = curl_multi_perform (plugin->multi_handle, &running);
1601       if ((running < handles_last_run) && (running>0))
1602           curl_handle_finished(plugin);
1603       handles_last_run = running;
1604     }
1605   while (mret == CURLM_CALL_MULTI_PERFORM);
1606
1607   curl_schedule(plugin);
1608 }
1609
1610
1611 /**
1612  * Function setting up file descriptors and scheduling task to run
1613  *
1614  * @param cls plugin as closure
1615  * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok
1616  */
1617 static int curl_schedule(void *cls)
1618 {
1619   struct Plugin *plugin = cls;
1620   fd_set rs;
1621   fd_set ws;
1622   fd_set es;
1623   int max;
1624   struct GNUNET_NETWORK_FDSet *grs;
1625   struct GNUNET_NETWORK_FDSet *gws;
1626   long to;
1627   CURLMcode mret;
1628
1629   GNUNET_assert(cls !=NULL);
1630
1631   /* Cancel previous scheduled task */
1632   if (plugin->http_curl_task !=  GNUNET_SCHEDULER_NO_TASK)
1633   {
1634           GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_curl_task);
1635           plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
1636   }
1637
1638   max = -1;
1639   FD_ZERO (&rs);
1640   FD_ZERO (&ws);
1641   FD_ZERO (&es);
1642   mret = curl_multi_fdset (plugin->multi_handle, &rs, &ws, &es, &max);
1643   if (mret != CURLM_OK)
1644     {
1645       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1646                   _("%s failed at %s:%d: `%s'\n"),
1647                   "curl_multi_fdset", __FILE__, __LINE__,
1648                   curl_multi_strerror (mret));
1649       return GNUNET_SYSERR;
1650     }
1651   mret = curl_multi_timeout (plugin->multi_handle, &to);
1652   if (mret != CURLM_OK)
1653     {
1654       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1655                   _("%s failed at %s:%d: `%s'\n"),
1656                   "curl_multi_timeout", __FILE__, __LINE__,
1657                   curl_multi_strerror (mret));
1658       return GNUNET_SYSERR;
1659     }
1660
1661   grs = GNUNET_NETWORK_fdset_create ();
1662   gws = GNUNET_NETWORK_fdset_create ();
1663   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1664   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1665   plugin->http_curl_task = GNUNET_SCHEDULER_add_select (plugin->env->sched,
1666                                    GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1667                                    GNUNET_SCHEDULER_NO_TASK,
1668                                                                     (to == -1) ? GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) : GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to),
1669                                    grs,
1670                                    gws,
1671                                    &curl_perform,
1672                                    plugin);
1673   GNUNET_NETWORK_fdset_destroy (gws);
1674   GNUNET_NETWORK_fdset_destroy (grs);
1675   return GNUNET_OK;
1676 }
1677
1678 /**
1679  * Function setting up curl handle and selecting message to send
1680  *
1681  * @param cls plugin
1682  * @param ps session
1683  * @return GNUNET_SYSERR on failure, GNUNET_NO if connecting, GNUNET_YES if ok
1684  */
1685 static ssize_t send_check_connections (void *cls, struct Session *ps)
1686 {
1687   struct Plugin *plugin = cls;
1688   CURLMcode mret;
1689   struct HTTP_Message * msg;
1690
1691   struct GNUNET_TIME_Relative timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
1692
1693   GNUNET_assert(cls !=NULL);
1694
1695   if (ps->direction == OUTBOUND)
1696   {
1697     /* RECV DIRECTION */
1698     /* Check if session is connected to receive data, otherwise connect to peer */
1699     if (ps->recv_connected == GNUNET_NO)
1700     {
1701         int fresh = GNUNET_NO;
1702         if (ps->recv_endpoint == NULL)
1703         {
1704             fresh = GNUNET_YES;
1705                 ps->recv_endpoint = curl_easy_init();
1706         }
1707 #if DEBUG_CURL
1708         curl_easy_setopt(ps->recv_endpoint, CURLOPT_VERBOSE, 1L);
1709 #endif
1710         curl_easy_setopt(ps->recv_endpoint, CURLOPT_URL, ps->url);
1711         curl_easy_setopt(ps->recv_endpoint, CURLOPT_HEADERFUNCTION, &curl_get_header_cb);
1712         curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEHEADER, ps);
1713         curl_easy_setopt(ps->recv_endpoint, CURLOPT_READFUNCTION, curl_send_cb);
1714         curl_easy_setopt(ps->recv_endpoint, CURLOPT_READDATA, ps);
1715         curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEFUNCTION, curl_receive_cb);
1716         curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEDATA, ps);
1717         curl_easy_setopt(ps->recv_endpoint, CURLOPT_TIMEOUT, (long) timeout.value);
1718         curl_easy_setopt(ps->recv_endpoint, CURLOPT_PRIVATE, ps);
1719         curl_easy_setopt(ps->recv_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT);
1720         curl_easy_setopt(ps->recv_endpoint, CURLOPT_BUFFERSIZE, 2*GNUNET_SERVER_MAX_MESSAGE_SIZE);
1721 #if CURL_TCP_NODELAY
1722         curl_easy_setopt(ps->recv_endpoint, CURLOPT_TCP_NODELAY, 1);
1723 #endif
1724
1725         if (fresh==GNUNET_YES)
1726         {
1727                         mret = curl_multi_add_handle(plugin->multi_handle, ps->recv_endpoint);
1728                         if (mret != CURLM_OK)
1729                         {
1730                           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1731                                                   _("Connection: %X: %s failed at %s:%d: `%s'\n"),
1732                                                   ps,
1733                                                   "curl_multi_add_handle", __FILE__, __LINE__,
1734                                                   curl_multi_strerror (mret));
1735                           return GNUNET_SYSERR;
1736                         }
1737         }
1738                 if (plugin->http_curl_task !=  GNUNET_SCHEDULER_NO_TASK)
1739                 {
1740                   GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_curl_task);
1741                   plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
1742                 }
1743                 plugin->http_curl_task = GNUNET_SCHEDULER_add_now (plugin->env->sched, &curl_perform, plugin);
1744     }
1745
1746     /* waiting for receive direction */
1747     if (ps->recv_connected==GNUNET_NO)
1748       return GNUNET_NO;
1749
1750     /* SEND DIRECTION */
1751     /* Check if session is connected to send data, otherwise connect to peer */
1752     if ((ps->send_connected == GNUNET_YES) && (ps->send_endpoint!= NULL))
1753     {
1754       if (ps->send_active == GNUNET_YES)
1755       {
1756 #if DEBUG_CONNECTIONS
1757         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound active, enqueueing message\n",ps);
1758 #endif
1759         return GNUNET_YES;
1760       }
1761       if (ps->send_active == GNUNET_NO)
1762       {
1763 #if DEBUG_CONNECTIONS
1764         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound paused, unpausing existing connection and enqueueing message\n",ps);
1765 #endif
1766         if (CURLE_OK == curl_easy_pause(ps->send_endpoint,CURLPAUSE_CONT))
1767         {
1768                         ps->send_active=GNUNET_YES;
1769                         if (plugin->http_curl_task !=  GNUNET_SCHEDULER_NO_TASK)
1770                         {
1771                           GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_curl_task);
1772                           plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
1773                         }
1774                         plugin->http_curl_task = GNUNET_SCHEDULER_add_now (plugin->env->sched, &curl_perform, plugin);
1775                         return GNUNET_YES;
1776         }
1777         else
1778                 return GNUNET_SYSERR;
1779       }
1780     }
1781     /* not connected, initiate connection */
1782     if (ps->send_connected==GNUNET_NO)
1783     {
1784         int fresh = GNUNET_NO;
1785         if (NULL == ps->send_endpoint)
1786         {
1787                 ps->send_endpoint = curl_easy_init();
1788                 fresh = GNUNET_YES;
1789         }
1790                 GNUNET_assert (ps->send_endpoint != NULL);
1791                 GNUNET_assert (NULL != ps->pending_msgs_tail);
1792 #if DEBUG_CONNECTIONS
1793                 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound not connected, initiating connection\n",ps);
1794 #endif
1795                 ps->send_active = GNUNET_NO;
1796                 msg = ps->pending_msgs_tail;
1797
1798 #if DEBUG_CURL
1799                 curl_easy_setopt(ps->send_endpoint, CURLOPT_VERBOSE, 1L);
1800 #endif
1801                 curl_easy_setopt(ps->send_endpoint, CURLOPT_URL, ps->url);
1802                 curl_easy_setopt(ps->send_endpoint, CURLOPT_PUT, 1L);
1803                 curl_easy_setopt(ps->send_endpoint, CURLOPT_HEADERFUNCTION, &curl_put_header_cb);
1804                 curl_easy_setopt(ps->send_endpoint, CURLOPT_WRITEHEADER, ps);
1805                 curl_easy_setopt(ps->send_endpoint, CURLOPT_READFUNCTION, curl_send_cb);
1806                 curl_easy_setopt(ps->send_endpoint, CURLOPT_READDATA, ps);
1807                 curl_easy_setopt(ps->send_endpoint, CURLOPT_WRITEFUNCTION, curl_receive_cb);
1808                 curl_easy_setopt(ps->send_endpoint, CURLOPT_READDATA, ps);
1809                 curl_easy_setopt(ps->send_endpoint, CURLOPT_TIMEOUT, (long) timeout.value);
1810                 curl_easy_setopt(ps->send_endpoint, CURLOPT_PRIVATE, ps);
1811                 curl_easy_setopt(ps->send_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT);
1812                 curl_easy_setopt(ps->send_endpoint, CURLOPT_BUFFERSIZE, 2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
1813 #if CURL_TCP_NODELAY
1814                 curl_easy_setopt(ps->send_endpoint, CURLOPT_TCP_NODELAY, 1);
1815 #endif
1816
1817                 if (fresh==GNUNET_YES)
1818                 {
1819                         mret = curl_multi_add_handle(plugin->multi_handle, ps->send_endpoint);
1820                         if (mret != CURLM_OK)
1821                         {
1822                           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1823                                                   _("Connection: %X: %s failed at %s:%d: `%s'\n"),
1824                                                   ps,
1825                                                   "curl_multi_add_handle", __FILE__, __LINE__,
1826                                                   curl_multi_strerror (mret));
1827                           return GNUNET_SYSERR;
1828                         }
1829                 }
1830     }
1831         if (plugin->http_curl_task !=  GNUNET_SCHEDULER_NO_TASK)
1832         {
1833           GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_curl_task);
1834           plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
1835         }
1836         plugin->http_curl_task = GNUNET_SCHEDULER_add_now (plugin->env->sched, &curl_perform, plugin);
1837     return GNUNET_YES;
1838   }
1839   if (ps->direction == INBOUND)
1840   {
1841     GNUNET_assert (NULL != ps->pending_msgs_tail);
1842     if ((ps->recv_connected==GNUNET_YES) && (ps->send_connected==GNUNET_YES) &&
1843         (ps->recv_force_disconnect==GNUNET_NO) && (ps->recv_force_disconnect==GNUNET_NO))
1844         return GNUNET_YES;
1845   }
1846   return GNUNET_SYSERR;
1847 }
1848
1849 /**
1850  * select best session to transmit data to peer
1851  *
1852  * @param cls closure
1853  * @param pc peer context of target peer
1854  * @param addr address of target peer
1855  * @param addrlen address length
1856  * @param force_address does transport service enforce address?
1857  * @param session session passed by transport service
1858  * @return selected session
1859  *
1860  */
1861 static struct Session * send_select_session (void * cls, struct HTTP_PeerContext *pc, const void * addr, size_t addrlen, int force_address, struct Session * session)
1862 {
1863         struct Session * tmp = NULL;
1864         int addr_given = GNUNET_NO;
1865
1866         if ((addr!=NULL) && (addrlen>0))
1867                 addr_given = GNUNET_YES;
1868
1869         if (force_address == GNUNET_YES)
1870         {
1871                 /* check session given as argument */
1872                 if ((session != NULL) && (addr_given == GNUNET_YES))
1873                 {
1874                       if (0 == memcmp(session->addr, addr, addrlen))
1875                       {
1876                         /* connection can not be used, since it is disconnected */
1877                         if ((session->recv_force_disconnect==GNUNET_NO) && (session->send_force_disconnect==GNUNET_NO))
1878                         {
1879 #if DEBUG_SESSION_SELECTION
1880                                   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using session passed by transport to send to forced address \n", session);
1881 #endif
1882                                 return session;
1883                         }
1884                       }
1885                 }
1886                 /* check last session used */
1887                 if ((pc->last_session != NULL)&& (addr_given == GNUNET_YES))
1888                 {
1889                       if (0 == memcmp(pc->last_session->addr, addr, addrlen))
1890                       {
1891                         /* connection can not be used, since it is disconnected */
1892                         if ((pc->last_session->recv_force_disconnect==GNUNET_NO) && (pc->last_session->send_force_disconnect==GNUNET_NO))
1893                         {
1894 #if DEBUG_SESSION_SELECTION
1895                                   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using last session used to send to forced address \n", pc->last_session);
1896 #endif
1897                                 return pc->last_session;
1898                         }
1899                       }
1900                 }
1901                 /* find session in existing sessions */
1902                 tmp = pc->head;
1903                 while ((tmp!=NULL) && (addr_given == GNUNET_YES))
1904                 {
1905
1906                           if (0 == memcmp(tmp->addr, addr, addrlen))
1907                       {
1908                         /* connection can not be used, since it is disconnected */
1909                         if ((tmp->recv_force_disconnect==GNUNET_NO) && (tmp->send_force_disconnect==GNUNET_NO))
1910                         {
1911 #if DEBUG_SESSION_SELECTION
1912                                   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using existing session to send to forced address \n", session);
1913 #endif
1914                                   return session;
1915                         }
1916
1917                       }
1918                           tmp=tmp->next;
1919                 }
1920                 /* no session to use */
1921                 return NULL;
1922         }
1923         if ((force_address == GNUNET_NO) || (force_address == GNUNET_SYSERR))
1924         {
1925                 /* check session given as argument */
1926                 if (session != NULL)
1927                 {
1928                         /* connection can not be used, since it is disconnected */
1929                         if ((session->recv_force_disconnect==GNUNET_NO) && (session->send_force_disconnect==GNUNET_NO))
1930                         {
1931 #if DEBUG_SESSION_SELECTION
1932                                   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using session passed by transport to send not-forced address \n", session);
1933 #endif
1934                                   return session;
1935                         }
1936
1937                 }
1938                 /* check last session used */
1939                 if (pc->last_session != NULL)
1940                 {
1941                         /* connection can not be used, since it is disconnected */
1942                         if ((pc->last_session->recv_force_disconnect==GNUNET_NO) && (pc->last_session->send_force_disconnect==GNUNET_NO))
1943                         {
1944 #if DEBUG_SESSION_SELECTION
1945                                   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using last session to send to not-forced address \n", pc->last_session);
1946 #endif
1947                                 return pc->last_session;
1948                         }
1949                 }
1950                 /* find session in existing sessions */
1951                 tmp = pc->head;
1952                 while (tmp!=NULL)
1953                 {
1954                         /* connection can not be used, since it is disconnected */
1955                         if ((tmp->recv_force_disconnect==GNUNET_NO) && (tmp->send_force_disconnect==GNUNET_NO))
1956                         {
1957 #if DEBUG_SESSION_SELECTION
1958                                   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using existing session to send to not-forced address \n", tmp);
1959 #endif
1960                                 return tmp;
1961                         }
1962                         tmp=tmp->next;
1963                 }
1964                 return NULL;
1965         }
1966         return NULL;
1967 }
1968
1969 /**
1970  * Function that can be used by the transport service to transmit
1971  * a message using the plugin.   Note that in the case of a
1972  * peer disconnecting, the continuation MUST be called
1973  * prior to the disconnect notification itself.  This function
1974  * will be called with this peer's HELLO message to initiate
1975  * a fresh connection to another peer.
1976  *
1977  * @param cls closure
1978  * @param target who should receive this message
1979  * @param msgbuf the message to transmit
1980  * @param msgbuf_size number of bytes in 'msgbuf'
1981  * @param priority how important is the message (most plugins will
1982  *                 ignore message priority and just FIFO)
1983  * @param to how long to wait at most for the transmission (does not
1984  *                require plugins to discard the message after the timeout,
1985  *                just advisory for the desired delay; most plugins will ignore
1986  *                this as well)
1987  * @param session which session must be used (or NULL for "any")
1988  * @param addr the address to use (can be NULL if the plugin
1989  *                is "on its own" (i.e. re-use existing TCP connection))
1990  * @param addrlen length of the address in bytes
1991  * @param force_address GNUNET_YES if the plugin MUST use the given address,
1992  *                GNUNET_NO means the plugin may use any other address and
1993  *                GNUNET_SYSERR means that only reliable existing
1994  *                bi-directional connections should be used (regardless
1995  *                of address)
1996  * @param cont continuation to call once the message has
1997  *        been transmitted (or if the transport is ready
1998  *        for the next transmission call; or if the
1999  *        peer disconnected...); can be NULL
2000  * @param cont_cls closure for cont
2001  * @return number of bytes used (on the physical network, with overheads);
2002  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
2003  *         and does NOT mean that the message was not transmitted (DV)
2004  */
2005 static ssize_t
2006 http_plugin_send (void *cls,
2007                   const struct GNUNET_PeerIdentity *target,
2008                   const char *msgbuf,
2009                   size_t msgbuf_size,
2010                   unsigned int priority,
2011                   struct GNUNET_TIME_Relative to,
2012                   struct Session *session,
2013                   const void *addr,
2014                   size_t addrlen,
2015                   int force_address,
2016                   GNUNET_TRANSPORT_TransmitContinuation cont,
2017                   void *cont_cls)
2018 {
2019   struct Plugin *plugin = cls;
2020   struct HTTP_Message *msg;
2021   struct HTTP_PeerContext * pc;
2022   struct Session * ps = NULL;
2023
2024   GNUNET_assert(cls !=NULL);
2025
2026 #if DEBUG_HTTP
2027   char * force;
2028   if (force_address == GNUNET_YES)
2029           GNUNET_asprintf(&force, "forced addr.");
2030   if (force_address == GNUNET_NO)
2031           GNUNET_asprintf(&force, "any addr.");
2032   if (force_address == GNUNET_SYSERR)
2033           GNUNET_asprintf(&force,"reliable bi-direc. address addr.");
2034
2035   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Transport tells me to send %u bytes to `%s' using %s (%s) and session: %X\n",
2036                                       msgbuf_size,
2037                                       GNUNET_i2s(target),
2038                                       force,
2039                                       http_plugin_address_to_string(NULL, addr, addrlen),
2040                                       session);
2041
2042   GNUNET_free(force);
2043 #endif
2044
2045   pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &target->hashPubKey);
2046   /* Peer unknown */
2047   if (pc==NULL)
2048   {
2049     pc = GNUNET_malloc(sizeof (struct HTTP_PeerContext));
2050     pc->plugin = plugin;
2051     pc->session_id_counter=1;
2052     pc->last_session = NULL;
2053     memcpy(&pc->identity, target, sizeof(struct GNUNET_PeerIdentity));
2054     GNUNET_CONTAINER_multihashmap_put(plugin->peers, &pc->identity.hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
2055     GNUNET_STATISTICS_update (plugin->env->stats,
2056                             gettext_noop ("# HTTP peers active"),
2057                             1,
2058                             GNUNET_NO);
2059   }
2060
2061   ps = send_select_session (plugin, pc, addr, addrlen, force_address, session);
2062
2063   /* session not existing, but address forced -> creating new session */
2064   if (ps==NULL)
2065   {
2066     if ((addr!=NULL) && (addrlen!=0))
2067     {
2068       ps = GNUNET_malloc(sizeof (struct Session));
2069 #if DEBUG_SESSION_SELECTION
2070       if (force_address == GNUNET_YES)
2071         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing connection & forced address: creating new session %X to peer %s\n", ps, GNUNET_i2s(target));
2072       if (force_address != GNUNET_YES)
2073         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing connection: creating new session %X to peer %s\n", ps, GNUNET_i2s(target));
2074 #endif
2075       if ((addrlen!=0) && (addr!=NULL))
2076       {
2077       ps->addr = GNUNET_malloc(addrlen);
2078       memcpy(ps->addr,addr,addrlen);
2079       ps->addrlen = addrlen;
2080       }
2081       else
2082       {
2083         ps->addr = NULL;
2084         ps->addrlen = 0;
2085       }
2086       ps->direction=OUTBOUND;
2087       ps->recv_connected = GNUNET_NO;
2088       ps->recv_force_disconnect = GNUNET_NO;
2089       ps->send_connected = GNUNET_NO;
2090       ps->send_force_disconnect = GNUNET_NO;
2091       ps->pending_msgs_head = NULL;
2092       ps->pending_msgs_tail = NULL;
2093       ps->peercontext=pc;
2094       ps->session_id = pc->session_id_counter;
2095       pc->session_id_counter++;
2096       ps->url = create_url (plugin, ps->addr, ps->addrlen, ps->session_id);
2097       if (ps->msgtok == NULL)
2098         ps->msgtok = GNUNET_SERVER_mst_create (&curl_receive_mst_cb, ps);
2099       GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps);
2100 /* FIXME */
2101
2102       GNUNET_STATISTICS_update (plugin->env->stats,
2103                             gettext_noop ("# HTTP outbound sessions for peers active"),
2104                             1,
2105                             GNUNET_NO);
2106     }
2107     else
2108     {
2109 #if DEBUG_HTTP
2110       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing session found & and no address given: no way to send this message to peer `%s'!\n", GNUNET_i2s(target));
2111 #endif
2112       return GNUNET_SYSERR;
2113     }
2114   }
2115
2116   /* create msg */
2117   msg = GNUNET_malloc (sizeof (struct HTTP_Message) + msgbuf_size);
2118   msg->next = NULL;
2119   msg->size = msgbuf_size;
2120   msg->pos = 0;
2121   msg->buf = (char *) &msg[1];
2122   msg->transmit_cont = cont;
2123   msg->transmit_cont_cls = cont_cls;
2124   memcpy (msg->buf,msgbuf, msgbuf_size);
2125   GNUNET_CONTAINER_DLL_insert(ps->pending_msgs_head,ps->pending_msgs_tail,msg);
2126
2127   if (send_check_connections (plugin, ps) == GNUNET_SYSERR)
2128           return GNUNET_SYSERR;
2129           if (force_address != GNUNET_YES)
2130                   pc->last_session = ps;
2131
2132           if (pc->last_session==NULL)
2133                   pc->last_session = ps;
2134           return msg->size;
2135 }
2136
2137
2138
2139 /**
2140  * Function that can be used to force the plugin to disconnect
2141  * from the given peer and cancel all previous transmissions
2142  * (and their continuationc).
2143  *
2144  * @param cls closure
2145  * @param target peer from which to disconnect
2146  */
2147 static void
2148 http_plugin_disconnect (void *cls,
2149                             const struct GNUNET_PeerIdentity *target)
2150 {
2151
2152
2153   struct Plugin *plugin = cls;
2154   struct HTTP_PeerContext *pc = NULL;
2155   struct Session *ps = NULL;
2156   //struct Session *tmp = NULL;
2157
2158   pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &target->hashPubKey);
2159   if (pc==NULL)
2160     return;
2161   ps = pc->head;
2162
2163   while (ps!=NULL)
2164   {
2165     /* Telling transport that session is getting disconnected */
2166     plugin->env->session_end(plugin, target, ps);
2167     if (ps->direction==OUTBOUND)
2168     {
2169       if (ps->send_endpoint!=NULL)
2170       {
2171         //GNUNET_assert(CURLM_OK == curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint));
2172         //curl_easy_cleanup(ps->send_endpoint);
2173         //ps->send_endpoint=NULL;
2174         ps->send_force_disconnect = GNUNET_YES;
2175       }
2176       if (ps->recv_endpoint!=NULL)
2177       {
2178        //GNUNET_assert(CURLM_OK == curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint));
2179        //curl_easy_cleanup(ps->recv_endpoint);
2180        //ps->recv_endpoint=NULL;
2181        ps->recv_force_disconnect = GNUNET_YES;
2182       }
2183     }
2184
2185     if (ps->direction==INBOUND)
2186     {
2187       ps->recv_force_disconnect = GNUNET_YES;
2188       ps->send_force_disconnect = GNUNET_YES;
2189     }
2190
2191     while (ps->pending_msgs_head!=NULL)
2192     {
2193       remove_http_message(ps, ps->pending_msgs_head);
2194     }
2195     ps->recv_active = GNUNET_NO;
2196     ps->send_active = GNUNET_NO;
2197     ps=ps->next;
2198   }
2199 }
2200
2201
2202 /**
2203  * Convert the transports address to a nice, human-readable
2204  * format.
2205  *
2206  * @param cls closure
2207  * @param type name of the transport that generated the address
2208  * @param addr one of the addresses of the host, NULL for the last address
2209  *        the specific address format depends on the transport
2210  * @param addrlen length of the address
2211  * @param numeric should (IP) addresses be displayed in numeric form?
2212  * @param timeout after how long should we give up?
2213  * @param asc function to call on each string
2214  * @param asc_cls closure for asc
2215  */
2216 static void
2217 http_plugin_address_pretty_printer (void *cls,
2218                                         const char *type,
2219                                         const void *addr,
2220                                         size_t addrlen,
2221                                         int numeric,
2222                                         struct GNUNET_TIME_Relative timeout,
2223                                         GNUNET_TRANSPORT_AddressStringCallback
2224                                         asc, void *asc_cls)
2225 {
2226   const struct IPv4HttpAddress *t4;
2227   const struct IPv6HttpAddress *t6;
2228   struct sockaddr_in a4;
2229   struct sockaddr_in6 a6;
2230   char * address;
2231   char * ret;
2232   unsigned int port;
2233   unsigned int res;
2234
2235   GNUNET_assert(cls !=NULL);
2236   if (addrlen == sizeof (struct IPv6HttpAddress))
2237   {
2238     address = GNUNET_malloc (INET6_ADDRSTRLEN);
2239     t6 = addr;
2240     a6.sin6_addr = t6->ipv6_addr;
2241     inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
2242     port = ntohs(t6->u6_port);
2243   }
2244   else if (addrlen == sizeof (struct IPv4HttpAddress))
2245   {
2246     address = GNUNET_malloc (INET_ADDRSTRLEN);
2247     t4 = addr;
2248     a4.sin_addr.s_addr =  t4->ipv4_addr;
2249     inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
2250     port = ntohs(t4->u_port);
2251   }
2252   else
2253   {
2254     /* invalid address */
2255     GNUNET_break_op (0);
2256     asc (asc_cls, NULL);
2257     return;
2258   }
2259   res = GNUNET_asprintf(&ret,"%s://%s:%u/", PROTOCOL_PREFIX, address, port);
2260   GNUNET_free (address);
2261   GNUNET_assert(res != 0);
2262   asc (asc_cls, ret);
2263   GNUNET_free_non_null (ret);
2264 }
2265
2266
2267
2268 /**
2269  * Another peer has suggested an address for this
2270  * peer and transport plugin.  Check that this could be a valid
2271  * address.  If so, consider adding it to the list
2272  * of addresses.
2273  *
2274  * @param cls closure
2275  * @param addr pointer to the address
2276  * @param addrlen length of addr
2277  * @return GNUNET_OK if this is a plausible address for this peer
2278  *         and transport
2279  */
2280 static int
2281 http_plugin_address_suggested (void *cls,
2282                                const void *addr, size_t addrlen)
2283 {
2284   struct Plugin *plugin = cls;
2285   struct IPv4HttpAddress *v4;
2286   struct IPv6HttpAddress *v6;
2287   unsigned int port;
2288
2289   GNUNET_assert(cls !=NULL);
2290   if ((addrlen != sizeof (struct IPv4HttpAddress)) &&
2291       (addrlen != sizeof (struct IPv6HttpAddress)))
2292     {
2293       return GNUNET_SYSERR;
2294     }
2295   if (addrlen == sizeof (struct IPv4HttpAddress))
2296     {
2297       v4 = (struct IPv4HttpAddress *) addr;
2298       /* Not skipping loopback
2299       if (INADDR_LOOPBACK == ntohl(v4->ipv4_addr))
2300       {
2301         return GNUNET_SYSERR;
2302       } */
2303       port = ntohs (v4->u_port);
2304       if (port != plugin->port_inbound)
2305       {
2306         return GNUNET_SYSERR;
2307       }
2308     }
2309   if (addrlen == sizeof (struct IPv6HttpAddress))
2310     {
2311       v6 = (struct IPv6HttpAddress *) addr;
2312       if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr))
2313         {
2314           return GNUNET_SYSERR;
2315         }
2316       port = ntohs (v6->u6_port);
2317       if (port != plugin->port_inbound)
2318       {
2319         return GNUNET_SYSERR;
2320       }
2321     }
2322
2323   return GNUNET_OK;
2324 }
2325
2326
2327 /**
2328  * Function called for a quick conversion of the binary address to
2329  * a numeric address.  Note that the caller must not free the
2330  * address and that the next call to this function is allowed
2331  * to override the address again.
2332  *
2333  * @param cls closure
2334  * @param addr binary address
2335  * @param addrlen length of the address
2336  * @return string representing the same address
2337  */
2338 static const char*
2339 http_plugin_address_to_string (void *cls,
2340                                    const void *addr,
2341                                    size_t addrlen)
2342 {
2343   const struct IPv4HttpAddress *t4;
2344   const struct IPv6HttpAddress *t6;
2345   struct sockaddr_in a4;
2346   struct sockaddr_in6 a6;
2347   char * address;
2348   char * ret;
2349   uint16_t port;
2350   unsigned int res;
2351
2352   if (addrlen == sizeof (struct IPv6HttpAddress))
2353     {
2354       address = GNUNET_malloc (INET6_ADDRSTRLEN);
2355       t6 = addr;
2356       a6.sin6_addr = t6->ipv6_addr;
2357       inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
2358       port = ntohs(t6->u6_port);
2359     }
2360   else if (addrlen == sizeof (struct IPv4HttpAddress))
2361     {
2362       address = GNUNET_malloc (INET_ADDRSTRLEN);
2363       t4 = addr;
2364       a4.sin_addr.s_addr =  t4->ipv4_addr;
2365       inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
2366       port = ntohs(t4->u_port);
2367     }
2368   else
2369     {
2370       /* invalid address */
2371       return NULL;
2372     }
2373   res = GNUNET_asprintf(&ret,"%s:%u",address,port);
2374   GNUNET_free (address);
2375   GNUNET_assert(res != 0);
2376   return ret;
2377 }
2378
2379
2380 /**
2381  * Exit point from the plugin.
2382  */
2383 void *
2384 libgnunet_plugin_transport_http_done (void *cls)
2385 {
2386   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
2387   struct Plugin *plugin = api->cls;
2388   CURLMcode mret;
2389   GNUNET_assert(cls !=NULL);
2390
2391   if (plugin->http_server_daemon_v4 != NULL)
2392   {
2393     MHD_stop_daemon (plugin->http_server_daemon_v4);
2394     plugin->http_server_daemon_v4 = NULL;
2395   }
2396   if (plugin->http_server_daemon_v6 != NULL)
2397   {
2398     MHD_stop_daemon (plugin->http_server_daemon_v6);
2399     plugin->http_server_daemon_v6 = NULL;
2400   }
2401
2402   if ( plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
2403   {
2404     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v4);
2405     plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
2406   }
2407
2408   if ( plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
2409   {
2410     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v6);
2411     plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
2412   }
2413
2414
2415   /* free all peer information */
2416   if (plugin->peers!=NULL)
2417   {
2418           GNUNET_CONTAINER_multihashmap_iterate (plugin->peers,
2419                                                                                          &remove_peer_context_Iterator,
2420                                                                                          plugin);
2421           GNUNET_CONTAINER_multihashmap_destroy (plugin->peers);
2422   }
2423   if (plugin->multi_handle!=NULL)
2424   {
2425           mret = curl_multi_cleanup(plugin->multi_handle);
2426 #if DEBUG_HTTP
2427           if ( CURLM_OK != mret)
2428                 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"curl multihandle clean up failed\n");
2429 #endif
2430           plugin->multi_handle = NULL;
2431   }
2432   curl_global_cleanup();
2433
2434   if ( plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK)
2435   {
2436     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_curl_task);
2437     plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
2438   }
2439
2440   GNUNET_free_non_null (plugin->bind4_address);
2441   GNUNET_free_non_null (plugin->bind6_address);
2442   GNUNET_free_non_null(plugin->bind_hostname);
2443   GNUNET_free (plugin);
2444   GNUNET_free (api);
2445 #if DEBUG_HTTP
2446   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Unload http plugin complete...\n");
2447 #endif
2448   return NULL;
2449 }
2450
2451 #if BUILD_HTTPS
2452 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_https_init
2453 #else
2454 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_http_init
2455 #endif
2456
2457 /**
2458  * Entry point for the plugin.
2459  */
2460 void *
2461 LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
2462 {
2463   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
2464   struct Plugin *plugin;
2465   struct GNUNET_TRANSPORT_PluginFunctions *api;
2466   struct GNUNET_TIME_Relative gn_timeout;
2467   long long unsigned int port;
2468
2469   GNUNET_assert(cls !=NULL);
2470 #if DEBUG_HTTP
2471   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting http plugin...\n");
2472 #endif
2473
2474   plugin = GNUNET_malloc (sizeof (struct Plugin));
2475   plugin->stats = env->stats;
2476   plugin->env = env;
2477   plugin->peers = NULL;
2478   plugin->bind4_address = NULL;
2479   plugin->use_ipv6  = GNUNET_YES;
2480   plugin->use_ipv4  = GNUNET_YES;
2481
2482   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
2483   api->cls = plugin;
2484   api->send = &http_plugin_send;
2485   api->disconnect = &http_plugin_disconnect;
2486   api->address_pretty_printer = &http_plugin_address_pretty_printer;
2487   api->check_address = &http_plugin_address_suggested;
2488   api->address_to_string = &http_plugin_address_to_string;
2489
2490   /* Hashing our identity to use it in URLs */
2491   GNUNET_CRYPTO_hash_to_enc ( &(plugin->env->my_identity->hashPubKey), &plugin->my_ascii_hash_ident);
2492
2493   /* Reading port number from config file */
2494   if (GNUNET_CONFIGURATION_have_value (env->cfg,
2495                                                                    "transport-http", "USE_IPv6"))
2496     {
2497           plugin->use_ipv6 = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2498                                                                                                            "transport-http",
2499                                                                                                            "USE_IPv6");
2500     }
2501   /* Reading port number from config file */
2502   if (GNUNET_CONFIGURATION_have_value (env->cfg,
2503                                                                    "transport-http", "USE_IPv4"))
2504     {
2505           plugin->use_ipv4 = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
2506                                                                                                            "transport-http",
2507                                                                                                            "USE_IPv4");
2508     }
2509   /* Reading port number from config file */
2510   if ((GNUNET_OK !=
2511        GNUNET_CONFIGURATION_get_value_number (env->cfg,
2512                                               "transport-http",
2513                                               "PORT",
2514                                               &port)) ||
2515       (port > 65535) )
2516     {
2517       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
2518                        "http",
2519                        _("Require valid port number for transport plugin `%s' in configuration!\n"),
2520                        "transport-http");
2521       libgnunet_plugin_transport_http_done (api);
2522       return NULL;
2523     }
2524
2525   /* Reading ipv4 addresse to bind to from config file */
2526   if ((plugin->use_ipv4==GNUNET_YES) && (GNUNET_CONFIGURATION_have_value (env->cfg,
2527                                                                    "transport-http", "BINDTO4")))
2528   {
2529           GNUNET_break (GNUNET_OK ==
2530                                         GNUNET_CONFIGURATION_get_value_string (env->cfg,
2531                                                                                                                    "transport-http",
2532                                                                                                                    "BINDTO4",
2533                                                                                                                    &plugin->bind_hostname));
2534           plugin->bind4_address = GNUNET_malloc(sizeof(struct sockaddr_in));
2535           plugin->bind4_address->sin_family = AF_INET;
2536           plugin->bind4_address->sin_port = htons (port);
2537
2538           if (inet_pton(AF_INET,plugin->bind_hostname, &plugin->bind4_address->sin_addr)<=0)
2539           {
2540                   GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
2541                                                    "http",
2542                                                    _("Misconfigured address to bind to in configuration!\n"),
2543                                                    "transport-http");
2544                   GNUNET_free(plugin->bind4_address);
2545                   GNUNET_free(plugin->bind_hostname);
2546                   plugin->bind_hostname = NULL;
2547                   plugin->bind4_address = NULL;
2548           }
2549   }
2550
2551   /* Reading ipv4 addresse to bind to from config file */
2552   if ((plugin->use_ipv6==GNUNET_YES) && (GNUNET_CONFIGURATION_have_value (env->cfg,
2553                                                                    "transport-http", "BINDTO6")))
2554   {
2555           if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (env->cfg,
2556                                                                                                                    "transport-http",
2557                                                                                                                    "BINDTO6",
2558                                                                                                                    &plugin->bind_hostname))
2559           {
2560                   plugin->bind6_address = GNUNET_malloc(sizeof(struct sockaddr_in6));
2561                   plugin->bind6_address->sin6_family = AF_INET6;
2562                   plugin->bind6_address->sin6_port = htons (port);
2563
2564                   if (inet_pton(AF_INET6,plugin->bind_hostname, &plugin->bind6_address->sin6_addr)<=0)
2565                   {
2566                           GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
2567                                                            "http",
2568                                                            _("Misconfigured address to bind to in configuration!\n"),
2569                                                            "transport-http");
2570                           GNUNET_free(plugin->bind6_address);
2571                           GNUNET_free(plugin->bind_hostname);
2572                           plugin->bind_hostname = NULL;
2573                           plugin->bind6_address = NULL;
2574                   }
2575           }
2576   }
2577
2578   GNUNET_assert ((port > 0) && (port <= 65535));
2579   plugin->port_inbound = port;
2580   gn_timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
2581   unsigned int timeout = (gn_timeout.value) / 1000;
2582   if ((plugin->http_server_daemon_v6 == NULL) && (plugin->use_ipv6 == GNUNET_YES) && (port != 0))
2583   {
2584         struct sockaddr * tmp = (struct sockaddr *) plugin->bind6_address;
2585     plugin->http_server_daemon_v6 = MHD_start_daemon (
2586 #if DEBUG_MHD
2587                                                                    MHD_USE_DEBUG |
2588 #endif
2589                                                                    MHD_USE_IPv6,
2590                                        port,
2591                                        &mhd_accept_cb,
2592                                        plugin , &mdh_access_cb, plugin,
2593                                        MHD_OPTION_SOCK_ADDR, tmp,
2594                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 32,
2595                                        //MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 6,
2596                                        MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) timeout,
2597                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (2 * GNUNET_SERVER_MAX_MESSAGE_SIZE),
2598                                        MHD_OPTION_NOTIFY_COMPLETED, &mhd_termination_cb, NULL,
2599                                        MHD_OPTION_EXTERNAL_LOGGER, mhd_logger, plugin->mhd_log,
2600                                        MHD_OPTION_END);
2601   }
2602   if ((plugin->http_server_daemon_v4 == NULL) && (plugin->use_ipv4 == GNUNET_YES) && (port != 0))
2603   {
2604   plugin->http_server_daemon_v4 = MHD_start_daemon (
2605 #if DEBUG_MHD
2606                                                                    MHD_USE_DEBUG |
2607 #endif
2608                                                                    MHD_NO_FLAG,
2609                                        port,
2610                                        &mhd_accept_cb,
2611                                        plugin , &mdh_access_cb, plugin,
2612                                        MHD_OPTION_SOCK_ADDR, (struct sockaddr_in *)plugin->bind4_address,
2613                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 32,
2614                                        //MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 6,
2615                                        MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) timeout,
2616                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (2 * GNUNET_SERVER_MAX_MESSAGE_SIZE),
2617                                        MHD_OPTION_NOTIFY_COMPLETED, &mhd_termination_cb, NULL,
2618                                        MHD_OPTION_EXTERNAL_LOGGER, mhd_logger, plugin->mhd_log,
2619                                        MHD_OPTION_END);
2620   }
2621   if (plugin->http_server_daemon_v4 != NULL)
2622     plugin->http_server_task_v4 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
2623   if (plugin->http_server_daemon_v6 != NULL)
2624     plugin->http_server_task_v6 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
2625
2626
2627   if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
2628   {
2629 #if DEBUG_HTTP
2630           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 bound to %s with port %u\n",(plugin->bind_hostname!=NULL) ? plugin->bind_hostname : "every address",port);
2631 #endif
2632   }
2633   else if ((plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) && (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK))
2634   {
2635 #if DEBUG_HTTP
2636     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv6 bound to %s with port %u\n",(plugin->bind_hostname!=NULL) ? plugin->bind_hostname : "every address", port);
2637 #endif
2638   }
2639   else if ((plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) && (plugin->http_server_task_v4 == GNUNET_SCHEDULER_NO_TASK))
2640   {
2641 #if DEBUG_HTTP
2642     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 and IPv6 bound to %s with port %u\n",(plugin->bind_hostname!=NULL) ? plugin->bind_hostname : "every address", port);
2643 #endif
2644   }
2645   else
2646   {
2647         char * tmp = NULL;
2648         if ((plugin->use_ipv6 == GNUNET_YES) && (plugin->use_ipv4 == GNUNET_YES))
2649                 GNUNET_asprintf(&tmp,"with IPv4 and IPv6 enabled");
2650         if ((plugin->use_ipv6 == GNUNET_NO) && (plugin->use_ipv4 == GNUNET_YES))
2651                 GNUNET_asprintf(&tmp,"with IPv4 enabled");
2652         if ((plugin->use_ipv6 == GNUNET_YES) && (plugin->use_ipv4 == GNUNET_NO))
2653                 GNUNET_asprintf(&tmp,"with IPv6 enabled");
2654         if ((plugin->use_ipv6 == GNUNET_NO) && (plugin->use_ipv4 == GNUNET_NO))
2655                 GNUNET_asprintf(&tmp,"with NO IP PROTOCOL enabled");
2656         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,"HTTP Server with %s could not be started on port %u! https plugin failed!\n",tmp, port);
2657         GNUNET_free(tmp);
2658     libgnunet_plugin_transport_http_done (api);
2659     return NULL;
2660   }
2661
2662   /* Initializing cURL */
2663   curl_global_init(CURL_GLOBAL_ALL);
2664   plugin->multi_handle = curl_multi_init();
2665
2666   if ( NULL == plugin->multi_handle )
2667   {
2668     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
2669                                    "https",
2670                                    _("Could not initialize curl multi handle, failed to start http plugin!\n"),
2671                                    "transport-https");
2672     libgnunet_plugin_transport_http_done (api);
2673     return NULL;
2674   }
2675
2676   plugin->peers = GNUNET_CONTAINER_multihashmap_create (10);
2677   GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
2678
2679   return api;
2680 }
2681
2682 /* end of plugin_transport_http.c */