-fix
[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
74
75 /**
76  * Send a request to the peerinfo service to start being
77  * notified about all changes to peer information.
78  *
79  * @param nc our context
80  */
81 static void
82 request_notifications (struct GNUNET_PEERINFO_NotifyContext *nc);
83
84
85 /**
86  * Read notifications from the client handle and pass them
87  * to the callback.
88  *
89  * @param nc our context
90  */
91 static void
92 receive_notifications (struct GNUNET_PEERINFO_NotifyContext *nc);
93
94
95 /**
96  * Task to re-try connecting to peerinfo.
97  *
98  * @param cls the 'struct GNUNET_PEERINFO_NotifyContext'
99  * @param tc scheduler context
100  */
101 static void
102 reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
103 {
104   struct GNUNET_PEERINFO_NotifyContext *nc = cls;
105
106   nc->task = GNUNET_SCHEDULER_NO_TASK;
107   nc->client = GNUNET_CLIENT_connect ("peerinfo", nc->cfg);
108   if (NULL == nc->client)
109   {
110     /* ugh */
111     nc->task =
112         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &reconnect, nc);
113     return;
114   }
115   request_notifications (nc);
116 }
117
118
119 /**
120  * Receive a peerinfo information message, process it and
121  * go for more.
122  *
123  * @param cls closure
124  * @param msg message received, NULL on timeout or fatal error
125  */
126 static void
127 process_notification (void *cls, const struct GNUNET_MessageHeader *msg)
128 {
129   struct GNUNET_PEERINFO_NotifyContext *nc = cls;
130   const struct InfoMessage *im;
131   const struct GNUNET_HELLO_Message *hello;
132   uint16_t ms;
133
134   if (msg == NULL)
135   {
136     GNUNET_CLIENT_disconnect (nc->client, GNUNET_NO);
137     reconnect (nc, NULL);
138     return;
139   }
140   ms = ntohs (msg->size);
141   if ((ms < sizeof (struct InfoMessage)) ||
142       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_PEERINFO_INFO))
143   {
144     GNUNET_break (0);
145     GNUNET_CLIENT_disconnect (nc->client, GNUNET_NO);
146     nc->client = GNUNET_CLIENT_connect ("peerinfo", nc->cfg);
147     request_notifications (nc);
148     return;
149   }
150   im = (const struct InfoMessage *) msg;
151   hello = NULL;
152   if (ms > sizeof (struct InfoMessage) + sizeof (struct GNUNET_MessageHeader))
153   {
154     hello = (const struct GNUNET_HELLO_Message *) &im[1];
155     if (ms != sizeof (struct InfoMessage) + GNUNET_HELLO_size (hello))
156     {
157       GNUNET_break (0);
158       GNUNET_CLIENT_disconnect (nc->client, GNUNET_NO);
159       nc->client = GNUNET_CLIENT_connect ("peerinfo", nc->cfg);
160       request_notifications (nc);
161       return;
162     }
163   }
164 #if DEBUG_PEERINFO
165   LOG (GNUNET_ERROR_TYPE_DEBUG,
166        "Received information about peer `%s' from peerinfo database\n",
167        GNUNET_i2s (&im->peer));
168 #endif
169   nc->callback (nc->callback_cls, &im->peer, hello, NULL);
170   receive_notifications (nc);
171 }
172
173
174 /**
175  * Read notifications from the client handle and pass them
176  * to the callback.
177  *
178  * @param nc our context
179  */
180 static void
181 receive_notifications (struct GNUNET_PEERINFO_NotifyContext *nc)
182 {
183   GNUNET_CLIENT_receive (nc->client, &process_notification, nc,
184                          GNUNET_TIME_UNIT_FOREVER_REL);
185 }
186
187
188 /**
189  * Transmit our init-notify request, start receiving.
190  *
191  * @param cls closure (our 'struct GNUNET_PEERINFO_NotifyContext')
192  * @param size number of bytes available in buf
193  * @param buf where the callee should write the message
194  * @return number of bytes written to buf
195  */
196 static size_t
197 transmit_notify_request (void *cls, size_t size, void *buf)
198 {
199   struct GNUNET_PEERINFO_NotifyContext *nc = cls;
200   struct GNUNET_MessageHeader hdr;
201
202   nc->init = NULL;
203   if (buf == NULL)
204   {
205     GNUNET_CLIENT_disconnect (nc->client, GNUNET_NO);
206     nc->client = GNUNET_CLIENT_connect ("peerinfo", nc->cfg);
207     request_notifications (nc);
208     return 0;
209   }
210   GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
211   hdr.size = htons (sizeof (struct GNUNET_MessageHeader));
212   hdr.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY);
213   memcpy (buf, &hdr, sizeof (struct GNUNET_MessageHeader));
214   receive_notifications (nc);
215   return sizeof (struct GNUNET_MessageHeader);
216 }
217
218
219 /**
220  * Send a request to the peerinfo service to start being
221  * notified about all changes to peer information.
222  *
223  * @param nc our context
224  */
225 static void
226 request_notifications (struct GNUNET_PEERINFO_NotifyContext *nc)
227 {
228   GNUNET_assert (NULL == nc->init);
229   nc->init =
230       GNUNET_CLIENT_notify_transmit_ready (nc->client,
231                                            sizeof (struct GNUNET_MessageHeader),
232                                            GNUNET_TIME_UNIT_FOREVER_REL,
233                                            GNUNET_YES, &transmit_notify_request,
234                                            nc);
235 }
236
237
238 /**
239  * Call a method whenever our known information about peers
240  * changes.  Initially calls the given function for all known
241  * peers and then only signals changes.
242  *
243  * @param cfg configuration to use
244  * @param callback the method to call for each peer
245  * @param callback_cls closure for callback
246  * @return NULL on error
247  */
248 struct GNUNET_PEERINFO_NotifyContext *
249 GNUNET_PEERINFO_notify (const struct GNUNET_CONFIGURATION_Handle *cfg,
250                         GNUNET_PEERINFO_Processor callback, void *callback_cls)
251 {
252   struct GNUNET_PEERINFO_NotifyContext *nc;
253   struct GNUNET_CLIENT_Connection *client;
254
255   client = GNUNET_CLIENT_connect ("peerinfo", cfg);
256   if (client == NULL)
257   {
258     LOG (GNUNET_ERROR_TYPE_WARNING, _("Could not connect to `%s' service.\n"),
259          "peerinfo");
260     return NULL;
261   }
262   nc = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_NotifyContext));
263   nc->cfg = cfg;
264   nc->client = client;
265   nc->callback = callback;
266   nc->callback_cls = callback_cls;
267   request_notifications (nc);
268   return nc;
269 }
270
271
272 /**
273  * Stop notifying about changes.
274  *
275  * @param nc context to stop notifying
276  */
277 void
278 GNUNET_PEERINFO_notify_cancel (struct GNUNET_PEERINFO_NotifyContext *nc)
279 {
280   if (NULL != nc->init)
281   {
282     GNUNET_CLIENT_notify_transmit_ready_cancel (nc->init);
283     nc->init = NULL;
284   }
285   if (NULL != nc->client)
286     GNUNET_CLIENT_disconnect (nc->client, GNUNET_NO);
287   if (GNUNET_SCHEDULER_NO_TASK != nc->task)
288     GNUNET_SCHEDULER_cancel (nc->task);
289   GNUNET_free (nc);
290 }
291
292 /* end of peerinfo_api_notify.c */