when zone does not match, do not run through the loop anyway
[oweals/gnunet.git] / src / peerinfo / peerinfo_api_notify.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001, 2002, 2004, 2005, 2007, 2009, 2010 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20  * @file peerinfo/peerinfo_api_notify.c
21  * @brief notify API to access peerinfo service
22  * @author Christian Grothoff
23  */
24 #include "platform.h"
25 #include "gnunet_util_lib.h"
26 #include "gnunet_peerinfo_service.h"
27 #include "gnunet_protocols.h"
28 #include "peerinfo.h"
29
30 #define LOG(kind,...) GNUNET_log_from (kind, "peerinfo-api",__VA_ARGS__)
31
32 /**
33  * Context for the info handler.
34  */
35 struct GNUNET_PEERINFO_NotifyContext
36 {
37
38   /**
39    * Our connection to the PEERINFO service.
40    */
41   struct GNUNET_MQ_Handle *mq;
42
43   /**
44    * Function to call with information.
45    */
46   GNUNET_PEERINFO_Processor callback;
47
48   /**
49    * Closure for @e callback.
50    */
51   void *callback_cls;
52
53   /**
54    * Configuration.
55    */
56   const struct GNUNET_CONFIGURATION_Handle *cfg;
57
58   /**
59    * Tasked used for delayed re-connection attempt.
60    */
61   struct GNUNET_SCHEDULER_Task *task;
62
63   /**
64    * Include friend only HELLOs in callbacks
65    */
66   int include_friend_only;
67 };
68
69
70 /**
71  * Task to re-try connecting to peerinfo.
72  *
73  * @param cls the `struct GNUNET_PEERINFO_NotifyContext *`
74  */
75 static void
76 reconnect (void *cls);
77
78
79 /**
80  * We encountered an error, reconnect to the service.
81  *
82  * @param nc context to reconnect
83  */
84 static void
85 do_reconnect (struct GNUNET_PEERINFO_NotifyContext *nc)
86 {
87   GNUNET_MQ_destroy (nc->mq);
88   nc->mq = NULL;
89   nc->task = GNUNET_SCHEDULER_add_now (&reconnect,
90                                        nc);
91 }
92
93
94 /**
95  * We got a disconnect after asking regex to do the announcement.
96  * Retry.
97  *
98  * @param cls the `struct GNUNET_PEERINFO_NotifyContext` to retry
99  * @param error error code
100  */
101 static void
102 mq_error_handler (void *cls,
103                   enum GNUNET_MQ_Error error)
104 {
105   struct GNUNET_PEERINFO_NotifyContext *nc = cls;
106
107   do_reconnect (nc);
108 }
109
110
111 /**
112  * Check that a peerinfo information message is well-formed.
113  *
114  * @param cls closure
115  * @param im message received
116  * @return #GNUNET_OK if the message is well-formed
117  */
118 static int
119 check_notification (void *cls,
120                     const struct InfoMessage *im)
121 {
122   uint16_t ms = ntohs (im->header.size) - sizeof (*im);
123
124   if (ms >= sizeof (struct GNUNET_MessageHeader))
125   {
126     const struct GNUNET_HELLO_Message *hello;
127
128     hello = (const struct GNUNET_HELLO_Message *) &im[1];
129     if (ms != GNUNET_HELLO_size (hello))
130     {
131       GNUNET_break (0);
132       return GNUNET_SYSERR;
133     }
134     return GNUNET_OK;
135   }
136   if (0 != ms)
137   {
138     GNUNET_break (0);
139     return GNUNET_SYSERR;
140   }
141   return GNUNET_OK;  /* odd... */
142 }
143
144
145 /**
146  * Receive a peerinfo information message, process it.
147  *
148  * @param cls closure
149  * @param im message received
150  */
151 static void
152 handle_notification (void *cls,
153                      const struct InfoMessage *im)
154 {
155   struct GNUNET_PEERINFO_NotifyContext *nc = cls;
156   const struct GNUNET_HELLO_Message *hello;
157   uint16_t ms = ntohs (im->header.size) - sizeof (struct InfoMessage);
158
159   if (0 == ms)
160     return;
161   hello = (const struct GNUNET_HELLO_Message *) &im[1];
162   LOG (GNUNET_ERROR_TYPE_DEBUG,
163        "Received information about peer `%s' from peerinfo database\n",
164        GNUNET_i2s (&im->peer));
165   nc->callback (nc->callback_cls,
166                 &im->peer,
167                 hello,
168                 NULL);
169 }
170
171
172 /**
173  * Type of a function to call when we receive a message from the
174  * service.  Call the iterator with the result and (if applicable)
175  * continue to receive more messages or trigger processing the next
176  * event (if applicable).
177  *
178  * @param cls closure
179  * @param msg message received, NULL on timeout or fatal error
180  */
181 static void
182 handle_end_iteration (void *cls,
183                       const struct GNUNET_MessageHeader *msg)
184 {
185   /* these are ignored by the notify API */
186 }
187
188
189 /**
190  * Task to re-try connecting to peerinfo.
191  *
192  * @param cls the `struct GNUNET_PEERINFO_NotifyContext *`
193  */
194 static void
195 reconnect (void *cls)
196 {
197   struct GNUNET_PEERINFO_NotifyContext *nc = cls;
198   struct GNUNET_MQ_MessageHandler handlers[] = {
199     GNUNET_MQ_hd_var_size (notification,
200                            GNUNET_MESSAGE_TYPE_PEERINFO_INFO,
201                            struct InfoMessage,
202                            nc),
203     GNUNET_MQ_hd_fixed_size (end_iteration,
204                              GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END,
205                              struct GNUNET_MessageHeader,
206                              nc),
207     GNUNET_MQ_handler_end ()
208   };
209   struct GNUNET_MQ_Envelope *env;
210   struct NotifyMessage *nm;
211
212   nc->task = NULL;
213   nc->mq = GNUNET_CLIENT_connect (nc->cfg,
214                                   "peerinfo",
215                                   handlers,
216                                   &mq_error_handler,
217                                   nc);
218   if (NULL == nc->mq)
219     return;
220   env = GNUNET_MQ_msg (nm,
221                        GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY);
222   nm->include_friend_only = htonl (nc->include_friend_only);
223   GNUNET_MQ_send (nc->mq,
224                   env);
225 }
226
227
228 /**
229  * Call a method whenever our known information about peers
230  * changes.  Initially calls the given function for all known
231  * peers and then only signals changes.
232  *
233  * If @a include_friend_only is set to #GNUNET_YES peerinfo will include HELLO
234  * messages which are intended for friend to friend mode and which do not
235  * have to be gossiped. Otherwise these messages are skipped.
236  *
237  * @param cfg configuration to use
238  * @param include_friend_only include HELLO messages for friends only
239  * @param callback the method to call for each peer
240  * @param callback_cls closure for @a callback
241  * @return NULL on error
242  */
243 struct GNUNET_PEERINFO_NotifyContext *
244 GNUNET_PEERINFO_notify (const struct GNUNET_CONFIGURATION_Handle *cfg,
245                         int include_friend_only,
246                         GNUNET_PEERINFO_Processor callback,
247                         void *callback_cls)
248 {
249   struct GNUNET_PEERINFO_NotifyContext *nc;
250
251   nc = GNUNET_new (struct GNUNET_PEERINFO_NotifyContext);
252   nc->cfg = cfg;
253   nc->callback = callback;
254   nc->callback_cls = callback_cls;
255   nc->include_friend_only = include_friend_only;
256   reconnect (nc);
257   if (NULL == nc->mq)
258   {
259     LOG (GNUNET_ERROR_TYPE_WARNING,
260          "Could not connect to PEERINFO service.\n");
261     GNUNET_free (nc);
262     return NULL;
263   }
264   return nc;
265 }
266
267
268 /**
269  * Stop notifying about changes.
270  *
271  * @param nc context to stop notifying
272  */
273 void
274 GNUNET_PEERINFO_notify_cancel (struct GNUNET_PEERINFO_NotifyContext *nc)
275 {
276   if (NULL != nc->mq)
277   {
278     GNUNET_MQ_destroy (nc->mq);
279     nc->mq = NULL;
280   }
281   if (NULL != nc->task)
282   {
283     GNUNET_SCHEDULER_cancel (nc->task);
284     nc->task = NULL;
285   }
286   GNUNET_free (nc);
287 }
288
289 /* end of peerinfo_api_notify.c */