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