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