small API change: do no longer pass rarely needed GNUNET_SCHEDULER_TaskContext to...
[oweals/gnunet.git] / src / peerinfo / peerinfo_api_notify.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001, 2002, 2004, 2005, 2007, 2009, 2010 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file peerinfo/peerinfo_api_notify.c
23  * @brief notify API to access peerinfo service
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_peerinfo_service.h"
29 #include "gnunet_protocols.h"
30 #include "peerinfo.h"
31
32 #define LOG(kind,...) GNUNET_log_from (kind, "nse-api",__VA_ARGS__)
33
34 /**
35  * Context for the info handler.
36  */
37 struct GNUNET_PEERINFO_NotifyContext
38 {
39
40   /**
41    * Our connection to the PEERINFO service.
42    */
43   struct GNUNET_CLIENT_Connection *client;
44
45   /**
46    * Function to call with information.
47    */
48   GNUNET_PEERINFO_Processor callback;
49
50   /**
51    * Closure for callback.
52    */
53   void *callback_cls;
54
55   /**
56    * Handle to our initial request for message transmission to
57    * the peerinfo service.
58    */
59   struct GNUNET_CLIENT_TransmitHandle *init;
60
61   /**
62    * Configuration.
63    */
64   const struct GNUNET_CONFIGURATION_Handle *cfg;
65
66   /**
67    * Tasked used for delayed re-connection attempt.
68    */
69   struct GNUNET_SCHEDULER_Task * task;
70
71   /**
72    * Include friend only HELLOs in callbacks
73    */
74
75   int include_friend_only;
76 };
77
78
79 /**
80  * Send a request to the peerinfo service to start being
81  * notified about all changes to peer information.
82  *
83  * @param nc our context
84  */
85 static void
86 request_notifications (struct GNUNET_PEERINFO_NotifyContext *nc);
87
88
89 /**
90  * Read notifications from the client handle and pass them
91  * to the callback.
92  *
93  * @param nc our context
94  */
95 static void
96 receive_notifications (struct GNUNET_PEERINFO_NotifyContext *nc);
97
98
99 /**
100  * Task to re-try connecting to peerinfo.
101  *
102  * @param cls the `struct GNUNET_PEERINFO_NotifyContext *`
103  */
104 static void
105 reconnect (void *cls)
106 {
107   struct GNUNET_PEERINFO_NotifyContext *nc = cls;
108
109   nc->task = NULL;
110   nc->client = GNUNET_CLIENT_connect ("peerinfo", nc->cfg);
111   if (NULL == nc->client)
112   {
113     /* ugh */
114     nc->task =
115         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &reconnect, nc);
116     return;
117   }
118   request_notifications (nc);
119 }
120
121
122 /**
123  * Receive a peerinfo information message, process it and
124  * go for more.
125  *
126  * @param cls closure
127  * @param msg message received, NULL on timeout or fatal error
128  */
129 static void
130 process_notification (void *cls, const struct GNUNET_MessageHeader *msg)
131 {
132   struct GNUNET_PEERINFO_NotifyContext *nc = cls;
133   const struct InfoMessage *im;
134   const struct GNUNET_HELLO_Message *hello;
135   uint16_t ms;
136
137   if (msg == NULL)
138   {
139     GNUNET_CLIENT_disconnect (nc->client);
140     reconnect (nc);
141     return;
142   }
143   ms = ntohs (msg->size);
144   if ((ms < sizeof (struct InfoMessage)) ||
145       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_PEERINFO_INFO))
146   {
147     GNUNET_break (0);
148     GNUNET_CLIENT_disconnect (nc->client);
149     nc->client = GNUNET_CLIENT_connect ("peerinfo", nc->cfg);
150     request_notifications (nc);
151     return;
152   }
153   im = (const struct InfoMessage *) msg;
154   hello = NULL;
155   if (ms > sizeof (struct InfoMessage) + sizeof (struct GNUNET_MessageHeader))
156   {
157     hello = (const struct GNUNET_HELLO_Message *) &im[1];
158     if (ms != sizeof (struct InfoMessage) + GNUNET_HELLO_size (hello))
159     {
160       GNUNET_break (0);
161       GNUNET_CLIENT_disconnect (nc->client);
162       nc->client = GNUNET_CLIENT_connect ("peerinfo", nc->cfg);
163       request_notifications (nc);
164       return;
165     }
166   }
167
168   LOG (GNUNET_ERROR_TYPE_DEBUG,
169        "Received information about peer `%s' from peerinfo database\n",
170        GNUNET_i2s (&im->peer));
171   nc->callback (nc->callback_cls, &im->peer, hello, NULL);
172   receive_notifications (nc);
173 }
174
175
176 /**
177  * Read notifications from the client handle and pass them
178  * to the callback.
179  *
180  * @param nc our context
181  */
182 static void
183 receive_notifications (struct GNUNET_PEERINFO_NotifyContext *nc)
184 {
185   GNUNET_CLIENT_receive (nc->client, &process_notification, nc,
186                          GNUNET_TIME_UNIT_FOREVER_REL);
187 }
188
189
190 /**
191  * Transmit our init-notify request, start receiving.
192  *
193  * @param cls closure (our 'struct GNUNET_PEERINFO_NotifyContext')
194  * @param size number of bytes available in buf
195  * @param buf where the callee should write the message
196  * @return number of bytes written to buf
197  */
198 static size_t
199 transmit_notify_request (void *cls, size_t size, void *buf)
200 {
201   struct GNUNET_PEERINFO_NotifyContext *nc = cls;
202   struct NotifyMessage nm;
203
204   nc->init = NULL;
205   if (buf == NULL)
206   {
207     GNUNET_CLIENT_disconnect (nc->client);
208     nc->client = GNUNET_CLIENT_connect ("peerinfo", nc->cfg);
209     request_notifications (nc);
210     return 0;
211   }
212   GNUNET_assert (size >= sizeof (struct NotifyMessage));
213   nm.header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY);
214   nm.header.size = htons (sizeof (struct NotifyMessage));
215   nm.include_friend_only = htonl (nc->include_friend_only);
216   memcpy (buf, &nm, sizeof (struct NotifyMessage));
217   receive_notifications (nc);
218   return sizeof (struct NotifyMessage);
219 }
220
221
222 /**
223  * Send a request to the peerinfo service to start being
224  * notified about all changes to peer information.
225  *
226  * @param nc our context
227  */
228 static void
229 request_notifications (struct GNUNET_PEERINFO_NotifyContext *nc)
230 {
231   GNUNET_assert (NULL == nc->init);
232   nc->init =
233       GNUNET_CLIENT_notify_transmit_ready (nc->client,
234                                            sizeof (struct NotifyMessage),
235                                            GNUNET_TIME_UNIT_FOREVER_REL,
236                                            GNUNET_YES, &transmit_notify_request,
237                                            nc);
238 }
239
240
241 /**
242  * Call a method whenever our known information about peers
243  * changes.  Initially calls the given function for all known
244  * peers and then only signals changes.
245  *
246  * If include_friend_only is set to GNUNET_YES peerinfo will include HELLO
247  * messages which are intended for friend to friend mode and which do not
248  * have to be gossiped. Otherwise these messages are skipped.
249  *
250  * @param cfg configuration to use
251  * @param include_friend_only include HELLO messages for friends only
252  * @param callback the method to call for each peer
253  * @param callback_cls closure for callback
254  * @return NULL on error
255  */
256 struct GNUNET_PEERINFO_NotifyContext *
257 GNUNET_PEERINFO_notify (const struct GNUNET_CONFIGURATION_Handle *cfg,
258                                                                                                 int include_friend_only,
259                         GNUNET_PEERINFO_Processor callback, void *callback_cls)
260 {
261   struct GNUNET_PEERINFO_NotifyContext *nc;
262   struct GNUNET_CLIENT_Connection *client;
263
264   client = GNUNET_CLIENT_connect ("peerinfo", cfg);
265   if (client == NULL)
266   {
267     LOG (GNUNET_ERROR_TYPE_WARNING, _("Could not connect to `%s' service.\n"),
268          "peerinfo");
269     return NULL;
270   }
271   nc = GNUNET_new (struct GNUNET_PEERINFO_NotifyContext);
272   nc->cfg = cfg;
273   nc->client = client;
274   nc->callback = callback;
275   nc->callback_cls = callback_cls;
276   nc->include_friend_only = include_friend_only;
277   request_notifications (nc);
278   return nc;
279 }
280
281
282 /**
283  * Stop notifying about changes.
284  *
285  * @param nc context to stop notifying
286  */
287 void
288 GNUNET_PEERINFO_notify_cancel (struct GNUNET_PEERINFO_NotifyContext *nc)
289 {
290   if (NULL != nc->init)
291   {
292     GNUNET_CLIENT_notify_transmit_ready_cancel (nc->init);
293     nc->init = NULL;
294   }
295   if (NULL != nc->client)
296     GNUNET_CLIENT_disconnect (nc->client);
297   if (NULL != nc->task)
298     GNUNET_SCHEDULER_cancel (nc->task);
299   GNUNET_free (nc);
300 }
301
302 /* end of peerinfo_api_notify.c */