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