Merge branch 'master' of ssh://gnunet.org/gnunet
[oweals/gnunet.git] / src / scalarproduct / gnunet-scalarproduct.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2013, 2014 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 scalarproduct/gnunet-scalarproduct.c
21  * @brief scalarproduct client
22  * @author Christian M. Fuchs
23  */
24 #define GCRYPT_NO_DEPRECATED
25 #include <gcrypt.h>
26 #include <inttypes.h>
27
28 #include "platform.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet_scalarproduct_service.h"
31 #include "gnunet_protocols.h"
32 #include "scalarproduct.h"
33
34 #define LOG(kind,...) GNUNET_log_from (kind, "gnunet-scalarproduct",__VA_ARGS__)
35
36
37 /**
38  * the session key identifying this computation
39  */
40 static struct GNUNET_HashCode session_key;
41
42 /**
43  * PeerID we want to compute a scalar product with
44  */
45 static struct GNUNET_PeerIdentity peer_id;
46
47 /**
48  * Option -p: destination peer identity for checking message-ids with
49  */
50 static char *input_peer_id;
51
52 /**
53  * Option -p: destination peer identity for checking message-ids with
54  */
55 static char *input_session_key;
56
57 /**
58  * Option -e: vector to calculate a scalarproduct with
59  */
60 static char *input_elements;
61
62 /**
63  * Global return value
64  */
65 static int ret = -1;
66
67 /**
68  * our Scalarproduct Computation handle
69  */
70 static struct GNUNET_SCALARPRODUCT_ComputationHandle *computation;
71
72
73 /**
74  * Callback called if we are initiating a new computation session
75  *
76  * @param cls unused
77  * @param status if our job was successfully processed
78  */
79 static void
80 responder_callback (void *cls,
81                     enum GNUNET_SCALARPRODUCT_ResponseStatus status)
82 {
83   switch (status)
84   {
85   case GNUNET_SCALARPRODUCT_STATUS_SUCCESS:
86     ret = 0;
87     LOG (GNUNET_ERROR_TYPE_INFO,
88          "Session %s concluded.\n",
89          GNUNET_h2s (&session_key));
90     break;
91   case GNUNET_SCALARPRODUCT_STATUS_INVALID_RESPONSE:
92     LOG (GNUNET_ERROR_TYPE_ERROR,
93          "Session %s failed: invalid response\n",
94          GNUNET_h2s (&session_key));
95     break;
96   case GNUNET_SCALARPRODUCT_STATUS_FAILURE:
97     LOG (GNUNET_ERROR_TYPE_ERROR,
98          "Session %s failed: service failure\n",
99          GNUNET_h2s (&session_key));
100     break;
101   case GNUNET_SCALARPRODUCT_STATUS_DISCONNECTED:
102     LOG (GNUNET_ERROR_TYPE_ERROR,
103          "Session %s failed: service disconnect!\n",
104          GNUNET_h2s (&session_key));
105     break;
106   default:
107     LOG (GNUNET_ERROR_TYPE_ERROR,
108          "Session %s failed: return code %d\n",
109          GNUNET_h2s (&session_key),
110          status);
111   }
112   computation = NULL;
113   GNUNET_SCHEDULER_shutdown ();
114 }
115
116
117 /**
118  * Callback called if we are initiating a new computation session
119  *
120  * @param cls unused
121  * @param status if our job was successfully processed
122  * @param result the result in gnu/gcry MPI format
123  */
124 static void
125 requester_callback (void *cls,
126                     enum GNUNET_SCALARPRODUCT_ResponseStatus status,
127                     gcry_mpi_t result)
128 {
129   unsigned char *buf;
130   gcry_error_t rc;
131
132   switch (status)
133   {
134   case GNUNET_SCALARPRODUCT_STATUS_SUCCESS:
135     if (0 == (rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buf, NULL, result)))
136     {
137       ret = 0;
138       fprintf (stdout,
139                "%s\n",
140                buf);
141       fflush (stdout);
142     }
143     else
144       LOG_GCRY (GNUNET_ERROR_TYPE_ERROR,
145                 "gcry_mpi_aprint",
146                 rc);
147     break;
148   case GNUNET_SCALARPRODUCT_STATUS_INVALID_RESPONSE:
149     LOG (GNUNET_ERROR_TYPE_ERROR,
150          "Session %s with peer %s failed: invalid response received\n",
151          GNUNET_h2s (&session_key),
152          GNUNET_i2s (&peer_id));
153     break;
154   case GNUNET_SCALARPRODUCT_STATUS_FAILURE:
155     LOG (GNUNET_ERROR_TYPE_ERROR,
156          "Session %s with peer %s failed: API failure\n",
157          GNUNET_h2s (&session_key),
158          GNUNET_i2s (&peer_id));
159     break;
160   case GNUNET_SCALARPRODUCT_STATUS_DISCONNECTED:
161     LOG (GNUNET_ERROR_TYPE_ERROR,
162          "Session %s with peer %s was disconnected from service.\n",
163          GNUNET_h2s (&session_key),
164          GNUNET_i2s (&peer_id));
165     break;
166   default:
167     LOG (GNUNET_ERROR_TYPE_ERROR,
168          "Session %s with peer %s failed: return code %d\n",
169          GNUNET_h2s (&session_key),
170          GNUNET_i2s (&peer_id),
171          status);
172   }
173   computation = NULL;
174   GNUNET_SCHEDULER_shutdown ();
175 }
176
177
178 /**
179  * Task run during shutdown.
180  *
181  * @param cls unused
182  * @param tc unused
183  */
184 static void
185 shutdown_task (void *cls)
186 {
187   if (NULL != computation)
188   {
189     GNUNET_SCALARPRODUCT_cancel (computation);
190     ret = 1; /* aborted */
191   }
192 }
193
194
195 /**
196  * Main function that will be run by the scheduler.
197  *
198  * @param cls closure
199  * @param args remaining command-line arguments
200  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
201  * @param cfg configuration
202  */
203 static void
204 run (void *cls,
205      char *const *args,
206      const char *cfgfile,
207      const struct GNUNET_CONFIGURATION_Handle *cfg)
208 {
209   char *begin = input_elements;
210   char *end;
211   unsigned int i;
212   struct GNUNET_SCALARPRODUCT_Element *elements;
213   uint32_t element_count = 0;
214
215   if (NULL == input_elements)
216   {
217     LOG (GNUNET_ERROR_TYPE_ERROR,
218          _("You must specify at least one message ID to check!\n"));
219     return;
220   }
221   if ( (NULL == input_session_key) ||
222        (0 == strlen (input_session_key)) )
223   {
224     LOG (GNUNET_ERROR_TYPE_ERROR,
225          _("This program needs a session identifier for comparing vectors.\n"));
226     return;
227   }
228   GNUNET_CRYPTO_hash (input_session_key,
229                       strlen (input_session_key),
230                       &session_key);
231   if ( (NULL != input_peer_id) &&
232        (GNUNET_OK !=
233         GNUNET_CRYPTO_eddsa_public_key_from_string (input_peer_id,
234                                                     strlen (input_peer_id),
235                                                     &peer_id.public_key)) )
236   {
237     LOG (GNUNET_ERROR_TYPE_ERROR,
238          _("Tried to set initiator mode, as peer ID was given. "
239            "However, `%s' is not a valid peer identifier.\n"),
240          input_peer_id);
241     return;
242   }
243   if ( ('\'' == *begin) &&
244        ('\'' == begin[strlen(begin)-1]) )
245   {
246     begin[strlen(begin)-1] = '\0';
247     if (strlen (begin) > 0)
248       begin++;
249   }
250   for (end = begin; 0 != *end; end++)
251     if (*end == ';')
252       element_count++;
253   if (0 == element_count)
254   {
255     LOG (GNUNET_ERROR_TYPE_ERROR,
256          _("Need elements to compute the scalarproduct, got none.\n"));
257     return;
258   }
259
260   elements = GNUNET_malloc (sizeof(struct GNUNET_SCALARPRODUCT_Element) * element_count);
261
262   for (i = 0; i < element_count;i++)
263   {
264     struct GNUNET_SCALARPRODUCT_Element element;
265     char* separator = NULL;
266
267     /* get the length of the current key,value; tupel */
268     for (end = begin; *end != ';'; end++)
269       if (*end == ',')
270         separator = end;
271
272     /* final element */
273     if ( (NULL == separator) ||
274          (begin == separator) ||
275          (separator == end - 1) )
276     {
277       LOG (GNUNET_ERROR_TYPE_ERROR,
278            _("Malformed input, could not parse `%s'\n"),
279            begin);
280       GNUNET_free (elements);
281       return;
282     }
283     *separator = 0;
284     /* read the element's key */
285     GNUNET_CRYPTO_hash (begin,
286                         strlen (begin),
287                         &element.key);
288
289     /* read the element's value */
290     if (1 !=
291         sscanf (separator + 1,
292                 "%" SCNd64 ";",
293                 &element.value) )
294     {
295       LOG (GNUNET_ERROR_TYPE_ERROR,
296            _("Could not convert `%s' to int64_t.\n"),
297            begin);
298       GNUNET_free (elements);
299       return;
300     }
301     element.value = GNUNET_htonll (element.value);
302     elements[i] = element;
303     begin = end + 1;
304   }
305
306   if ( ( (NULL != input_peer_id) &&
307          (NULL == (computation
308                    = GNUNET_SCALARPRODUCT_start_computation (cfg,
309                                                              &session_key,
310                                                              &peer_id,
311                                                              elements, element_count,
312                                                              &requester_callback,
313                                                              NULL))) ) ||
314        ( (NULL == input_peer_id) &&
315          (NULL == (computation
316                    = GNUNET_SCALARPRODUCT_accept_computation (cfg,
317                                                               &session_key,
318                                                               elements, element_count,
319                                                               &responder_callback,
320                                                               NULL))) ) )
321   {
322     fprintf (stderr,
323              _("Failed to initiate computation, were all keys unique?\n"));
324     GNUNET_free (elements);
325     return;
326   }
327   GNUNET_free (elements);
328   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
329                                  NULL);
330   ret = 0;
331 }
332
333
334 /**
335  * The main function to the scalarproduct client.
336  *
337  * @param argc number of arguments from the command line
338  * @param argv command line arguments
339  * @return 0 ok, 1 on error
340  */
341 int
342 main (int argc, char *const *argv)
343 {
344   struct GNUNET_GETOPT_CommandLineOption options[] = {
345
346     GNUNET_GETOPT_option_string ('e',
347                                  "elements",
348                                  "\"key1,val1;key2,val2;...,keyn,valn;\"",
349                                  gettext_noop ("A comma separated list of elements to compare as vector with our remote peer."),
350                                  &input_elements),
351
352     GNUNET_GETOPT_option_string ('e',
353                                  "elements",
354                                  "\"key1,val1;key2,val2;...,keyn,valn;\"",
355                                  gettext_noop ("A comma separated list of elements to compare as vector with our remote peer."),
356                                  &input_elements),
357
358     GNUNET_GETOPT_option_string ('p',
359                                  "peer",
360                                  "PEERID",
361                                  gettext_noop ("[Optional] peer to calculate our scalarproduct with. If this parameter is not given, the service will wait for a remote peer to compute the request."),
362                                  &input_peer_id),
363
364     GNUNET_GETOPT_option_string ('k',
365                                  "key",
366                                  "TRANSACTION_ID",
367                                  gettext_noop ("Transaction ID shared with peer."),
368                                  &input_session_key),
369
370     GNUNET_GETOPT_OPTION_END
371   };
372
373   return (GNUNET_OK ==
374           GNUNET_PROGRAM_run (argc,
375                               argv,
376                               "gnunet-scalarproduct",
377                               gettext_noop ("Calculate the Vectorproduct with a GNUnet peer."),
378                               options, &run, NULL)) ? ret : 1;
379 }
380
381 /* end of gnunet-scalarproduct.c */