uncrustify as demanded.
[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      SPDX-License-Identifier: AGPL3.0-or-later
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    * 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 */