-must notify client on timeout
[oweals/gnunet.git] / src / peerinfo / peerinfo_api_notify.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2004, 2005, 2007, 2009, 2010 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 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   GNUNET_SCHEDULER_TaskIdentifier 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  * @param tc scheduler context
104  */
105 static void
106 reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
107 {
108   struct GNUNET_PEERINFO_NotifyContext *nc = cls;
109
110   nc->task = GNUNET_SCHEDULER_NO_TASK;
111   nc->client = GNUNET_CLIENT_connect ("peerinfo", nc->cfg);
112   if (NULL == nc->client)
113   {
114     /* ugh */
115     nc->task =
116         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &reconnect, nc);
117     return;
118   }
119   request_notifications (nc);
120 }
121
122
123 /**
124  * Receive a peerinfo information message, process it and
125  * go for more.
126  *
127  * @param cls closure
128  * @param msg message received, NULL on timeout or fatal error
129  */
130 static void
131 process_notification (void *cls, const struct GNUNET_MessageHeader *msg)
132 {
133   struct GNUNET_PEERINFO_NotifyContext *nc = cls;
134   const struct InfoMessage *im;
135   const struct GNUNET_HELLO_Message *hello;
136   uint16_t ms;
137
138   if (msg == NULL)
139   {
140     GNUNET_CLIENT_disconnect (nc->client);
141     reconnect (nc, NULL);
142     return;
143   }
144   ms = ntohs (msg->size);
145   if ((ms < sizeof (struct InfoMessage)) ||
146       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_PEERINFO_INFO))
147   {
148     GNUNET_break (0);
149     GNUNET_CLIENT_disconnect (nc->client);
150     nc->client = GNUNET_CLIENT_connect ("peerinfo", nc->cfg);
151     request_notifications (nc);
152     return;
153   }
154   im = (const struct InfoMessage *) msg;
155   hello = NULL;
156   if (ms > sizeof (struct InfoMessage) + sizeof (struct GNUNET_MessageHeader))
157   {
158     hello = (const struct GNUNET_HELLO_Message *) &im[1];
159     if (ms != sizeof (struct InfoMessage) + GNUNET_HELLO_size (hello))
160     {
161       GNUNET_break (0);
162       GNUNET_CLIENT_disconnect (nc->client);
163       nc->client = GNUNET_CLIENT_connect ("peerinfo", nc->cfg);
164       request_notifications (nc);
165       return;
166     }
167   }
168
169   LOG (GNUNET_ERROR_TYPE_DEBUG,
170        "Received information about peer `%s' from peerinfo database\n",
171        GNUNET_i2s (&im->peer));
172   nc->callback (nc->callback_cls, &im->peer, hello, NULL);
173   receive_notifications (nc);
174 }
175
176
177 /**
178  * Read notifications from the client handle and pass them
179  * to the callback.
180  *
181  * @param nc our context
182  */
183 static void
184 receive_notifications (struct GNUNET_PEERINFO_NotifyContext *nc)
185 {
186   GNUNET_CLIENT_receive (nc->client, &process_notification, nc,
187                          GNUNET_TIME_UNIT_FOREVER_REL);
188 }
189
190
191 /**
192  * Transmit our init-notify request, start receiving.
193  *
194  * @param cls closure (our 'struct GNUNET_PEERINFO_NotifyContext')
195  * @param size number of bytes available in buf
196  * @param buf where the callee should write the message
197  * @return number of bytes written to buf
198  */
199 static size_t
200 transmit_notify_request (void *cls, size_t size, void *buf)
201 {
202   struct GNUNET_PEERINFO_NotifyContext *nc = cls;
203   struct NotifyMessage nm;
204
205   nc->init = NULL;
206   if (buf == NULL)
207   {
208     GNUNET_CLIENT_disconnect (nc->client);
209     nc->client = GNUNET_CLIENT_connect ("peerinfo", nc->cfg);
210     request_notifications (nc);
211     return 0;
212   }
213   GNUNET_assert (size >= sizeof (struct NotifyMessage));
214   nm.header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY);
215   nm.header.size = htons (sizeof (struct NotifyMessage));
216   nm.include_friend_only = htonl (nc->include_friend_only);
217   memcpy (buf, &nm, sizeof (struct NotifyMessage));
218   receive_notifications (nc);
219   return sizeof (struct NotifyMessage);
220 }
221
222
223 /**
224  * Send a request to the peerinfo service to start being
225  * notified about all changes to peer information.
226  *
227  * @param nc our context
228  */
229 static void
230 request_notifications (struct GNUNET_PEERINFO_NotifyContext *nc)
231 {
232   GNUNET_assert (NULL == nc->init);
233   nc->init =
234       GNUNET_CLIENT_notify_transmit_ready (nc->client,
235                                            sizeof (struct NotifyMessage),
236                                            GNUNET_TIME_UNIT_FOREVER_REL,
237                                            GNUNET_YES, &transmit_notify_request,
238                                            nc);
239 }
240
241
242 /**
243  * Call a method whenever our known information about peers
244  * changes.  Initially calls the given function for all known
245  * peers and then only signals changes.
246  *
247  * If include_friend_only is set to GNUNET_YES peerinfo will include HELLO
248  * messages which are intended for friend to friend mode and which do not
249  * have to be gossiped. Otherwise these messages are skipped.
250  *
251  * @param cfg configuration to use
252  * @param include_friend_only include HELLO messages for friends only
253  * @param callback the method to call for each peer
254  * @param callback_cls closure for callback
255  * @return NULL on error
256  */
257 struct GNUNET_PEERINFO_NotifyContext *
258 GNUNET_PEERINFO_notify (const struct GNUNET_CONFIGURATION_Handle *cfg,
259                                                                                                 int include_friend_only,
260                         GNUNET_PEERINFO_Processor callback, void *callback_cls)
261 {
262   struct GNUNET_PEERINFO_NotifyContext *nc;
263   struct GNUNET_CLIENT_Connection *client;
264
265   client = GNUNET_CLIENT_connect ("peerinfo", cfg);
266   if (client == NULL)
267   {
268     LOG (GNUNET_ERROR_TYPE_WARNING, _("Could not connect to `%s' service.\n"),
269          "peerinfo");
270     return NULL;
271   }
272   nc = GNUNET_new (struct GNUNET_PEERINFO_NotifyContext);
273   nc->cfg = cfg;
274   nc->client = client;
275   nc->callback = callback;
276   nc->callback_cls = callback_cls;
277   nc->include_friend_only = include_friend_only;
278   request_notifications (nc);
279   return nc;
280 }
281
282
283 /**
284  * Stop notifying about changes.
285  *
286  * @param nc context to stop notifying
287  */
288 void
289 GNUNET_PEERINFO_notify_cancel (struct GNUNET_PEERINFO_NotifyContext *nc)
290 {
291   if (NULL != nc->init)
292   {
293     GNUNET_CLIENT_notify_transmit_ready_cancel (nc->init);
294     nc->init = NULL;
295   }
296   if (NULL != nc->client)
297     GNUNET_CLIENT_disconnect (nc->client);
298   if (GNUNET_SCHEDULER_NO_TASK != nc->task)
299     GNUNET_SCHEDULER_cancel (nc->task);
300   GNUNET_free (nc);
301 }
302
303 /* end of peerinfo_api_notify.c */