-no peerinfo IO on tests
[oweals/gnunet.git] / src / core / gnunet-service-core_clients.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010, 2011 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 core/gnunet-service-core_clients.c
23  * @brief code for managing interactions with clients of core service
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_statistics_service.h"
29 #include "gnunet_transport_service.h"
30 #include "gnunet-service-core.h"
31 #include "gnunet-service-core_clients.h"
32 #include "gnunet-service-core_sessions.h"
33 #include "gnunet-service-core_typemap.h"
34 #include "core.h"
35
36
37 /**
38  * How many messages do we queue up at most for optional
39  * notifications to a client?  (this can cause notifications
40  * about outgoing messages to be dropped).
41  */
42 #define MAX_NOTIFY_QUEUE 1024
43
44
45 /**
46  * Data structure for each client connected to the core service.
47  */
48 struct GSC_Client
49 {
50   /**
51    * Clients are kept in a linked list.
52    */
53   struct GSC_Client *next;
54
55   /**
56    * Clients are kept in a linked list.
57    */
58   struct GSC_Client *prev;
59
60   /**
61    * Handle for the client with the server API.
62    */
63   struct GNUNET_SERVER_Client *client_handle;
64
65   /**
66    * Array of the types of messages this peer cares
67    * about (with "tcnt" entries).  Allocated as part
68    * of this client struct, do not free!
69    */
70   const uint16_t *types;
71
72   /**
73    * Map of peer identities to active transmission requests of this
74    * client to the peer (of type 'struct GSC_ClientActiveRequest').
75    */
76   struct GNUNET_CONTAINER_MultiHashMap *requests;
77
78   /**
79    * Map containing all peers that this client knows we're connected to.
80    */
81   struct GNUNET_CONTAINER_MultiHashMap *connectmap;
82
83   /**
84    * Options for messages this client cares about,
85    * see GNUNET_CORE_OPTION_ values.
86    */
87   uint32_t options;
88
89   /**
90    * Number of types of incoming messages this client
91    * specifically cares about.  Size of the "types" array.
92    */
93   unsigned int tcnt;
94
95 };
96
97
98 /**
99  * Big "or" of all client options.
100  */
101 static uint32_t all_client_options;
102
103 /**
104  * Head of linked list of our clients.
105  */
106 static struct GSC_Client *client_head;
107
108 /**
109  * Tail of linked list of our clients.
110  */
111 static struct GSC_Client *client_tail;
112
113 /**
114  * Context for notifications we need to send to our clients.
115  */
116 static struct GNUNET_SERVER_NotificationContext *notifier;
117
118 /**
119  * Tokenizer for messages received from clients.
120  */
121 static struct GNUNET_SERVER_MessageStreamTokenizer *client_mst;
122
123
124 /**
125  * Lookup our client struct given the server's client handle.
126  *
127  * @param client server client handle to look up
128  * @return our client handle for the client
129  */
130 static struct GSC_Client *
131 find_client (struct GNUNET_SERVER_Client *client)
132 {
133   struct GSC_Client *c;
134
135   c = client_head;
136   while ((c != NULL) && (c->client_handle != client))
137     c = c->next;
138   return c;
139 }
140
141
142 /**
143  * Send a message to one of our clients.
144  *
145  * @param client target for the message
146  * @param msg message to transmit
147  * @param can_drop could this message be dropped if the
148  *        client's queue is getting too large?
149  */
150 static void
151 send_to_client (struct GSC_Client *client,
152                 const struct GNUNET_MessageHeader *msg, int can_drop)
153 {
154   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
155               "Preparing to send %u bytes of message of type %u to client.\n",
156               (unsigned int) ntohs (msg->size),
157               (unsigned int) ntohs (msg->type));
158   GNUNET_SERVER_notification_context_unicast (notifier, client->client_handle,
159                                               msg, can_drop);
160 }
161
162
163 /**
164  * Send a message to one of our clients.
165  *
166  * @param client target for the message
167  * @param msg message to transmit
168  * @param can_drop could this message be dropped if the
169  *        client's queue is getting too large?
170  */
171 void
172 GSC_CLIENTS_send_to_client (struct GNUNET_SERVER_Client *client,
173                             const struct GNUNET_MessageHeader *msg,
174                             int can_drop)
175 {
176   struct GSC_Client *c;
177
178   c = find_client (client);
179   if (NULL == c)
180   {
181     GNUNET_break (0);
182     return;
183   }
184   send_to_client (c, msg, can_drop);
185 }
186
187
188 /**
189  * Test if the client is interested in messages of the given type.
190  *
191  * @param type message type
192  * @param c client to test
193  * @return GNUNET_YES if 'c' is interested, GNUNET_NO if not.
194  */
195 static int
196 type_match (uint16_t type, struct GSC_Client *c)
197 {
198   unsigned int i;
199
200   if (c->tcnt == 0)
201     return GNUNET_YES;          /* peer without handlers matches ALL */
202   for (i = 0; i < c->tcnt; i++)
203     if (type == c->types[i])
204       return GNUNET_YES;
205   return GNUNET_NO;
206 }
207
208
209 /**
210  * Send a message to all of our current clients that have the right
211  * options set.
212  *
213  * @param partner origin (or destination) of the message (used to check that this peer is
214  *        known to be connected to the respective client)
215  * @param msg message to multicast
216  * @param can_drop can this message be discarded if the queue is too long
217  * @param options mask to use
218  * @param type type of the embedded message, 0 for none
219  */
220 static void
221 send_to_all_clients (const struct GNUNET_PeerIdentity *partner,
222                      const struct GNUNET_MessageHeader *msg, int can_drop,
223                      uint32_t options, uint16_t type)
224 {
225   struct GSC_Client *c;
226   int tm;
227
228   for (c = client_head; c != NULL; c = c->next)
229   {
230     tm = type_match (type, c);
231     if (!  ( (0 != (c->options & options)) ||
232              ( (0 != (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) &&
233                (GNUNET_YES == tm) ) ) )
234       continue;  /* neither options nor type match permit the message */
235     if ( (0 != (options & GNUNET_CORE_OPTION_SEND_HDR_INBOUND)) &&
236          ( (0 != (c->options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) ||
237            (GNUNET_YES == tm) ) )
238       continue;
239     if ( (0 != (options & GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND)) &&
240          (0 != (c->options & GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND)) )
241       continue;
242     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
243                 "Sending %u message with %u bytes to client interested in messages of type %u.\n",
244                 options,
245                 ntohs (msg->size),
246                 (unsigned int) type);
247     GNUNET_assert ( (0 == (c->options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) ||
248                     (GNUNET_YES != tm) ||
249                     (GNUNET_YES ==
250                      GNUNET_CONTAINER_multihashmap_contains (c->connectmap,
251                                                              &partner->hashPubKey)) );
252     send_to_client (c, msg, can_drop);
253   }
254 }
255
256
257 /**
258  * Handle CORE_INIT request.
259  *
260  * @param cls unused
261  * @param client new client that sent INIT
262  * @param message the 'struct InitMessage' (presumably)
263  */
264 static void
265 handle_client_init (void *cls, struct GNUNET_SERVER_Client *client,
266                     const struct GNUNET_MessageHeader *message)
267 {
268   const struct InitMessage *im;
269   struct InitReplyMessage irm;
270   struct GSC_Client *c;
271   uint16_t msize;
272   const uint16_t *types;
273   uint16_t *wtypes;
274   unsigned int i;
275
276   /* check that we don't have an entry already */
277   c = find_client (client);
278   if (NULL != c)
279   {
280     GNUNET_break (0);
281     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
282     return;
283   }
284   msize = ntohs (message->size);
285   if (msize < sizeof (struct InitMessage))
286   {
287     GNUNET_break (0);
288     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
289     return;
290   }
291   GNUNET_SERVER_notification_context_add (notifier, client);
292   im = (const struct InitMessage *) message;
293   types = (const uint16_t *) &im[1];
294   msize -= sizeof (struct InitMessage);
295   c = GNUNET_malloc (sizeof (struct GSC_Client) + msize);
296   c->client_handle = client;
297   c->tcnt = msize / sizeof (uint16_t);
298   c->options = ntohl (im->options);
299   all_client_options |= c->options;
300   c->types = (const uint16_t *) &c[1];
301   c->connectmap = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
302   GNUNET_assert (GNUNET_YES ==
303                  GNUNET_CONTAINER_multihashmap_put (c->connectmap,
304                                                     &GSC_my_identity.hashPubKey,
305                                                     NULL,
306                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
307   wtypes = (uint16_t *) & c[1];
308   for (i = 0; i < c->tcnt; i++)
309     wtypes[i] = ntohs (types[i]);
310   GSC_TYPEMAP_add (wtypes, c->tcnt);
311   GNUNET_CONTAINER_DLL_insert (client_head, client_tail, c);
312   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
313               "Client connecting to core service is interested in %u message types\n",
314               (unsigned int) c->tcnt);
315   /* send init reply message */
316   irm.header.size = htons (sizeof (struct InitReplyMessage));
317   irm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY);
318   irm.reserved = htonl (0);
319   irm.my_identity = GSC_my_identity;
320   send_to_client (c, &irm.header, GNUNET_NO);
321   GSC_SESSIONS_notify_client_about_sessions (c);
322   GNUNET_SERVER_receive_done (client, GNUNET_OK);
323 }
324
325
326 /**
327  * Handle CORE_SEND_REQUEST message.
328  *
329  * @param cls unused
330  * @param client new client that sent CORE_SEND_REQUEST
331  * @param message the 'struct SendMessageRequest' (presumably)
332  */
333 static void
334 handle_client_send_request (void *cls, struct GNUNET_SERVER_Client *client,
335                             const struct GNUNET_MessageHeader *message)
336 {
337   const struct SendMessageRequest *req;
338   struct GSC_Client *c;
339   struct GSC_ClientActiveRequest *car;
340   int is_loopback;
341
342   req = (const struct SendMessageRequest *) message;
343   c = find_client (client);
344   if (c == NULL)
345   {
346     /* client did not send INIT first! */
347     GNUNET_break (0);
348     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
349     return;
350   }
351   if (c->requests == NULL)
352     c->requests = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
353   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
354               "Client asked for transmission to `%s'\n",
355               GNUNET_i2s (&req->peer));
356   is_loopback =
357       (0 ==
358        memcmp (&req->peer, &GSC_my_identity,
359                sizeof (struct GNUNET_PeerIdentity)));
360   if ((!is_loopback) &&
361       (GNUNET_YES !=
362        GNUNET_CONTAINER_multihashmap_contains (c->connectmap,
363                                                &req->peer.hashPubKey)))
364   {
365     /* neighbour must have disconnected since request was issued,
366      * ignore (client will realize it once it processes the
367      * disconnect notification) */
368     GNUNET_STATISTICS_update (GSC_stats,
369                               gettext_noop
370                               ("# send requests dropped (disconnected)"), 1,
371                               GNUNET_NO);
372     GNUNET_SERVER_receive_done (client, GNUNET_OK);
373     return;
374   }
375
376   car = GNUNET_CONTAINER_multihashmap_get (c->requests, &req->peer.hashPubKey);
377   if (car == NULL)
378   {
379     /* create new entry */
380     car = GNUNET_malloc (sizeof (struct GSC_ClientActiveRequest));
381     GNUNET_assert (GNUNET_OK ==
382                    GNUNET_CONTAINER_multihashmap_put (c->requests,
383                                                       &req->peer.hashPubKey,
384                                                       car,
385                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
386     car->client_handle = c;
387   }
388   else
389   {
390     GSC_SESSIONS_dequeue_request (car);
391   }
392   car->target = req->peer;
393   car->deadline = GNUNET_TIME_absolute_ntoh (req->deadline);
394   car->priority = ntohl (req->priority);
395   car->msize = ntohs (req->size);
396   car->smr_id = req->smr_id;
397   car->was_solicited = GNUNET_NO;
398   if (is_loopback)
399   {
400     /* loopback, satisfy immediately */
401     GSC_CLIENTS_solicit_request (car);
402     GNUNET_SERVER_receive_done (client, GNUNET_OK);
403     return;
404   }
405   GSC_SESSIONS_queue_request (car);
406   GNUNET_SERVER_receive_done (client, GNUNET_OK);
407 }
408
409
410 /**
411  * Closure for the 'client_tokenizer_callback'.
412  */
413 struct TokenizerContext
414 {
415
416   /**
417    * Active request handle for the message.
418    */
419   struct GSC_ClientActiveRequest *car;
420
421   /**
422    * Is corking allowed (set only once we have the real message).
423    */
424   int cork;
425
426 };
427
428
429 /**
430  * Handle CORE_SEND request.
431  *
432  * @param cls unused
433  * @param client the client issuing the request
434  * @param message the "struct SendMessage"
435  */
436 static void
437 handle_client_send (void *cls, struct GNUNET_SERVER_Client *client,
438                     const struct GNUNET_MessageHeader *message)
439 {
440   const struct SendMessage *sm;
441   struct GSC_Client *c;
442   struct TokenizerContext tc;
443   uint16_t msize;
444
445   msize = ntohs (message->size);
446   if (msize <
447       sizeof (struct SendMessage) + sizeof (struct GNUNET_MessageHeader))
448   {
449     GNUNET_break (0);
450     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
451     return;
452   }
453   sm = (const struct SendMessage *) message;
454   msize -= sizeof (struct SendMessage);
455   GNUNET_break (0 == ntohl (sm->reserved));
456   c = find_client (client);
457   if (c == NULL)
458   {
459     /* client did not send INIT first! */
460     GNUNET_break (0);
461     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
462     return;
463   }
464   tc.car =
465       GNUNET_CONTAINER_multihashmap_get (c->requests, &sm->peer.hashPubKey);
466   if (NULL == tc.car)
467   {
468     /* Must have been that we first approved the request, then got disconnected
469      * (which triggered removal of the 'car') and now the client gives us a message
470      * just *before* the client learns about the disconnect.  Theoretically, we
471      * might also now be *again* connected.  So this can happen (but should be
472      * rare).  If it does happen, the message is discarded. */
473     GNUNET_STATISTICS_update (GSC_stats,
474                               gettext_noop
475                               ("# messages discarded (session disconnected)"),
476                               1, GNUNET_NO);
477     GNUNET_SERVER_receive_done (client, GNUNET_OK);
478     return;
479   }
480   GNUNET_assert (GNUNET_YES ==
481                  GNUNET_CONTAINER_multihashmap_remove (c->requests,
482                                                        &sm->peer.hashPubKey,
483                                                        tc.car));
484   tc.cork = ntohl (sm->cork);
485   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
486               "Client asked for transmission of %u bytes to `%s' %s\n", msize,
487               GNUNET_i2s (&sm->peer), tc.cork ? "now" : "");
488   GNUNET_SERVER_mst_receive (client_mst, &tc, (const char *) &sm[1], msize,
489                              GNUNET_YES, GNUNET_NO);
490   if (0 !=
491       memcmp (&tc.car->target, &GSC_my_identity,
492               sizeof (struct GNUNET_PeerIdentity)))
493     GSC_SESSIONS_dequeue_request (tc.car);
494   GNUNET_free (tc.car);
495   GNUNET_SERVER_receive_done (client, GNUNET_OK);
496 }
497
498
499 /**
500  * Functions with this signature are called whenever a complete
501  * message is received by the tokenizer.  Used by the 'client_mst' for
502  * dispatching messages from clients to either the SESSION subsystem
503  * or other CLIENT (for loopback).
504  *
505  * @param cls closure
506  * @param client reservation request ('struct GSC_ClientActiveRequest')
507  * @param message the actual message
508  */
509 static int
510 client_tokenizer_callback (void *cls, void *client,
511                            const struct GNUNET_MessageHeader *message)
512 {
513   struct TokenizerContext *tc = client;
514   struct GSC_ClientActiveRequest *car = tc->car;
515   char buf[92];
516
517   GNUNET_snprintf (buf, sizeof (buf),
518                    gettext_noop ("# bytes of messages of type %u received"),
519                    (unsigned int) ntohs (message->type));
520   GNUNET_STATISTICS_update (GSC_stats, buf, ntohs (message->size), GNUNET_NO);  
521   if (0 ==
522       memcmp (&car->target, &GSC_my_identity,
523               sizeof (struct GNUNET_PeerIdentity)))
524   {
525     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
526                 "Delivering message of type %u to myself\n",
527                 ntohs (message->type));
528     GSC_CLIENTS_deliver_message (&GSC_my_identity, message,
529                                  ntohs (message->size),
530                                  GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND);
531     GSC_CLIENTS_deliver_message (&GSC_my_identity, message,
532                                  sizeof (struct GNUNET_MessageHeader),
533                                  GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND);
534     GSC_CLIENTS_deliver_message (&GSC_my_identity, message,
535                                  ntohs (message->size),
536                                  GNUNET_CORE_OPTION_SEND_FULL_INBOUND);
537     GSC_CLIENTS_deliver_message (&GSC_my_identity, message,
538                                  sizeof (struct GNUNET_MessageHeader),
539                                  GNUNET_CORE_OPTION_SEND_HDR_INBOUND);    
540   }
541   else
542   {
543     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
544                 "Delivering message of type %u to %s\n", ntohs (message->type),
545                 GNUNET_i2s (&car->target));
546     GSC_CLIENTS_deliver_message (&car->target, message,
547                                  ntohs (message->size),
548                                  GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND);
549     GSC_CLIENTS_deliver_message (&car->target, message,
550                                  sizeof (struct GNUNET_MessageHeader),
551                                  GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND);  
552     GSC_SESSIONS_transmit (car, message, tc->cork);
553   }
554   return GNUNET_OK;
555 }
556
557
558 /**
559  * Free client request records.
560  *
561  * @param cls NULL
562  * @param key identity of peer for which this is an active request
563  * @param value the 'struct GSC_ClientActiveRequest' to free
564  * @return GNUNET_YES (continue iteration)
565  */
566 static int
567 destroy_active_client_request (void *cls, const struct GNUNET_HashCode * key,
568                                void *value)
569 {
570   struct GSC_ClientActiveRequest *car = value;
571
572   GNUNET_assert (GNUNET_YES ==
573                  GNUNET_CONTAINER_multihashmap_remove (car->
574                                                        client_handle->requests,
575                                                        &car->target.hashPubKey,
576                                                        car));
577   GSC_SESSIONS_dequeue_request (car);
578   GNUNET_free (car);
579   return GNUNET_YES;
580 }
581
582
583 /**
584  * A client disconnected, clean up.
585  *
586  * @param cls closure
587  * @param client identification of the client
588  */
589 static void
590 handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
591 {
592   struct GSC_Client *c;
593
594   if (client == NULL)
595     return;
596   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
597               "Client %p has disconnected from core service.\n", client);
598   c = find_client (client);
599   if (c == NULL)
600     return;                     /* client never sent INIT */
601   GNUNET_CONTAINER_DLL_remove (client_head, client_tail, c);
602   if (c->requests != NULL)
603   {
604     GNUNET_CONTAINER_multihashmap_iterate (c->requests,
605                                            &destroy_active_client_request,
606                                            NULL);
607     GNUNET_CONTAINER_multihashmap_destroy (c->requests);
608   }
609   GNUNET_CONTAINER_multihashmap_destroy (c->connectmap);
610   c->connectmap = NULL;
611   GSC_TYPEMAP_remove (c->types, c->tcnt);
612   GNUNET_free (c);
613
614   /* recalculate 'all_client_options' */
615   all_client_options = 0;
616   for (c = client_head; NULL != c ; c = c->next)
617     all_client_options |= c->options;
618 }
619
620
621 /**
622  * Tell a client that we are ready to receive the message.
623  *
624  * @param car request that is now ready; the responsibility
625  *        for the handle remains shared between CLIENTS
626  *        and SESSIONS after this call.
627  */
628 void
629 GSC_CLIENTS_solicit_request (struct GSC_ClientActiveRequest *car)
630 {
631   struct GSC_Client *c;
632   struct SendMessageReady smr;
633
634   c = car->client_handle;
635   if (GNUNET_YES !=
636       GNUNET_CONTAINER_multihashmap_contains (c->connectmap,
637                                               &car->target.hashPubKey))
638   {
639     /* connection has gone down since, drop request */
640     GNUNET_assert (0 !=
641                    memcmp (&car->target, &GSC_my_identity,
642                            sizeof (struct GNUNET_PeerIdentity)));
643     GSC_SESSIONS_dequeue_request (car);
644     GSC_CLIENTS_reject_request (car);
645     return;
646   }
647   smr.header.size = htons (sizeof (struct SendMessageReady));
648   smr.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND_READY);
649   smr.size = htons (car->msize);
650   smr.smr_id = car->smr_id;
651   smr.peer = car->target;
652   send_to_client (c, &smr.header, GNUNET_NO);
653 }
654
655
656 /**
657  * Tell a client that we will never be ready to receive the
658  * given message in time (disconnect or timeout).
659  *
660  * @param car request that now permanently failed; the
661  *        responsibility for the handle is now returned
662  *        to CLIENTS (SESSIONS is done with it).
663  */
664 void
665 GSC_CLIENTS_reject_request (struct GSC_ClientActiveRequest *car)
666 {
667   GNUNET_assert (GNUNET_YES ==
668                  GNUNET_CONTAINER_multihashmap_remove (car->
669                                                        client_handle->requests,
670                                                        &car->target.hashPubKey,
671                                                        car));
672   GNUNET_free (car);
673 }
674
675
676 /**
677  * Notify a particular client about a change to existing connection to
678  * one of our neighbours (check if the client is interested).  Called
679  * from 'GSC_SESSIONS_notify_client_about_sessions'.
680  *
681  * @param client client to notify
682  * @param neighbour identity of the neighbour that changed status
683  * @param atsi performance information about neighbour
684  * @param atsi_count number of entries in 'ats' array
685  * @param tmap_old previous type map for the neighbour, NULL for disconnect
686  * @param tmap_new updated type map for the neighbour, NULL for disconnect
687  */
688 void
689 GSC_CLIENTS_notify_client_about_neighbour (struct GSC_Client *client,
690                                            const struct GNUNET_PeerIdentity
691                                            *neighbour,
692                                            const struct GNUNET_ATS_Information
693                                            *atsi, unsigned int atsi_count,
694                                            const struct GSC_TypeMap *tmap_old,
695                                            const struct GSC_TypeMap *tmap_new)
696 {
697   struct ConnectNotifyMessage *cnm;
698   size_t size;
699   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
700   struct DisconnectNotifyMessage dcm;
701   int old_match;
702   int new_match;
703
704   old_match = GSC_TYPEMAP_test_match (tmap_old, client->types, client->tcnt);
705   new_match = GSC_TYPEMAP_test_match (tmap_new, client->types, client->tcnt);
706   if (old_match == new_match)
707   {
708     GNUNET_assert (old_match ==
709                    GNUNET_CONTAINER_multihashmap_contains (client->connectmap,
710                                                            &neighbour->hashPubKey));
711     return;                     /* no change */
712   }
713   if (old_match == GNUNET_NO)
714   {
715     /* send connect */
716     GNUNET_assert (GNUNET_NO ==
717                    GNUNET_CONTAINER_multihashmap_contains (client->connectmap,
718                                                            &neighbour->hashPubKey));
719     GNUNET_assert (GNUNET_YES ==
720                    GNUNET_CONTAINER_multihashmap_put (client->connectmap,
721                                                       &neighbour->hashPubKey,
722                                                       NULL,
723                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
724     size = sizeof (struct ConnectNotifyMessage);
725     if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
726     {
727       GNUNET_break (0);
728       /* recovery strategy: throw away performance data */
729       atsi_count = 0;
730       size = sizeof (struct ConnectNotifyMessage);
731     }
732     cnm = (struct ConnectNotifyMessage *) buf;
733     cnm->header.size = htons (size);
734     cnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
735     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message to client.\n",
736                 "NOTIFY_CONNECT");
737     cnm->peer = *neighbour;
738     send_to_client (client, &cnm->header, GNUNET_NO);
739   }
740   else
741   {
742     /* send disconnect */
743     GNUNET_assert (GNUNET_YES ==
744                    GNUNET_CONTAINER_multihashmap_contains (client->connectmap,
745                                                            &neighbour->hashPubKey));
746     GNUNET_assert (GNUNET_YES ==
747                    GNUNET_CONTAINER_multihashmap_remove (client->connectmap,
748                                                          &neighbour->hashPubKey,
749                                                          NULL));
750     dcm.header.size = htons (sizeof (struct DisconnectNotifyMessage));
751     dcm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT);
752     dcm.reserved = htonl (0);
753     dcm.peer = *neighbour;
754     send_to_client (client, &dcm.header, GNUNET_NO);
755   }
756 }
757
758
759 /**
760  * Notify all clients about a change to existing session.
761  * Called from SESSIONS whenever there is a change in sessions
762  * or types processed by the respective peer.
763  *
764  * @param neighbour identity of the neighbour that changed status
765  * @param atsi performance information about neighbour
766  * @param atsi_count number of entries in 'ats' array
767  * @param tmap_old previous type map for the neighbour, NULL for disconnect
768  * @param tmap_new updated type map for the neighbour, NULL for disconnect
769  */
770 void
771 GSC_CLIENTS_notify_clients_about_neighbour (const struct GNUNET_PeerIdentity
772                                             *neighbour,
773                                             const struct GNUNET_ATS_Information
774                                             *atsi, unsigned int atsi_count,
775                                             const struct GSC_TypeMap *tmap_old,
776                                             const struct GSC_TypeMap *tmap_new)
777 {
778   struct GSC_Client *c;
779
780   for (c = client_head; c != NULL; c = c->next)
781     GSC_CLIENTS_notify_client_about_neighbour (c, neighbour, atsi, atsi_count,
782                                                tmap_old, tmap_new);
783 }
784
785
786 /**
787  * Deliver P2P message to interested clients.  Caller must have checked
788  * that the sending peer actually lists the given message type as one
789  * of its types.
790  *
791  * @param sender peer who sent us the message
792  * @param msg the message
793  * @param msize number of bytes to transmit
794  * @param options options for checking which clients should
795  *        receive the message
796  */
797 void
798 GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender,
799                              const struct GNUNET_MessageHeader *msg,
800                              uint16_t msize, 
801                              uint32_t options)
802 {
803   size_t size = msize + sizeof (struct NotifyTrafficMessage);
804   char buf[size] GNUNET_ALIGN;
805   struct NotifyTrafficMessage *ntm;
806
807   if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
808   {
809     GNUNET_break (0);
810     /* recovery strategy: throw performance data away... */
811     size = msize + sizeof (struct NotifyTrafficMessage);
812   }
813   if (! ( (0 != (all_client_options & options)) ||
814           (0 != (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) ))
815     return; /* no client cares about this message notification */
816   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
817               "Core service passes message from `%4s' of type %u to client.\n",
818               GNUNET_i2s (sender), (unsigned int) ntohs (msg->type));
819   GSC_SESSIONS_add_to_typemap (sender, ntohs (msg->type));
820   ntm = (struct NotifyTrafficMessage *) buf;
821   ntm->header.size = htons (size);
822   if (0 != (options & (GNUNET_CORE_OPTION_SEND_FULL_INBOUND | GNUNET_CORE_OPTION_SEND_HDR_INBOUND)))
823     ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND);
824   else
825     ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND);
826   ntm->peer = *sender;
827   memcpy (&ntm[1], msg, msize);
828   send_to_all_clients (sender, &ntm->header, GNUNET_YES, options,
829                        ntohs (msg->type));
830 }
831
832
833 /**
834  * Initialize clients subsystem.
835  *
836  * @param server handle to server clients connect to
837  */
838 void
839 GSC_CLIENTS_init (struct GNUNET_SERVER_Handle *server)
840 {
841   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
842     {&handle_client_init, NULL,
843      GNUNET_MESSAGE_TYPE_CORE_INIT, 0},
844     {&GSC_SESSIONS_handle_client_iterate_peers, NULL,
845      GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS,
846      sizeof (struct GNUNET_MessageHeader)},
847     {&GSC_SESSIONS_handle_client_have_peer, NULL,
848      GNUNET_MESSAGE_TYPE_CORE_PEER_CONNECTED,
849      sizeof (struct GNUNET_MessageHeader) +
850      sizeof (struct GNUNET_PeerIdentity)},
851     {&handle_client_send_request, NULL,
852      GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST,
853      sizeof (struct SendMessageRequest)},
854     {&handle_client_send, NULL,
855      GNUNET_MESSAGE_TYPE_CORE_SEND, 0},
856     {NULL, NULL, 0, 0}
857   };
858
859   /* setup notification */
860   client_mst = GNUNET_SERVER_mst_create (&client_tokenizer_callback, NULL);
861   notifier =
862       GNUNET_SERVER_notification_context_create (server, MAX_NOTIFY_QUEUE);
863   GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
864   GNUNET_SERVER_add_handlers (server, handlers);
865 }
866
867
868 /**
869  * Shutdown clients subsystem.
870  */
871 void
872 GSC_CLIENTS_done ()
873 {
874   struct GSC_Client *c;
875
876   while (NULL != (c = client_head))
877     handle_client_disconnect (NULL, c->client_handle);
878   if (NULL != notifier)
879   {
880     GNUNET_SERVER_notification_context_destroy (notifier);
881     notifier = NULL;
882   }
883   if (NULL != client_mst)
884   {
885     GNUNET_SERVER_mst_destroy (client_mst);
886     client_mst = NULL;
887   }
888 }
889
890 /* end of gnunet-service-core_clients.c */