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