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