GNUnet  0.17.6
gnunet-qr.c
Go to the documentation of this file.
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2013-2019 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  */
26 #include <stdio.h>
27 #include <stdbool.h>
28 #include <signal.h>
29 #include <zbar.h>
30 
31 #include "platform.h"
32 #include "gnunet_util_lib.h"
33 
34 #if HAVE_PNG
35 #include <png.h>
36 #endif
37 
42 static int exit_code = 0;
43 
49 static char *device = NULL;
50 
51 #if HAVE_PNG
57 static char *pngfilename = NULL;
58 #endif
59 
63 static unsigned int verbosity = 0;
64 
69 
73 static struct GNUNET_ChildWaitHandle *waitchildproc = NULL;
74 
78 #define LOG(fmt, ...) \
79  do \
80  { \
81  if (0 < verbosity) \
82  { \
83  GNUNET_log (GNUNET_ERROR_TYPE_INFO, fmt, ##__VA_ARGS__); \
84  if (verbosity > 1) \
85  { \
86  fprintf (stdout, fmt, ##__VA_ARGS__); \
87  } \
88  } \
89  } \
90  while (0)
91 
95 static void
96 shutdown_program (void *cls)
97 {
98  if (NULL != waitchildproc)
99  {
101  }
102  if (NULL != childproc)
103  {
104  /* A bit brutal, but this process is terminating so we're out of time */
106  }
107 }
108 
116 static void
117 wait_child (void *cls,
119  long unsigned int code)
120 {
122  childproc = NULL;
123  waitchildproc = NULL;
124 
125  char *uri = cls;
126 
127  if (0 != exit_code)
128  {
129  fprintf (stdout, _("Failed to add URI %s\n"), uri);
130  }
131  else
132  {
133  fprintf (stdout, _("Added URI %s\n"), uri);
134  }
135 
136  GNUNET_free (uri);
137 
139 }
140 
149 static void
150 handle_uri (void *cls,
151  const char *uri,
152  const char *cfgfile,
153  const struct GNUNET_CONFIGURATION_Handle *cfg)
154 {
155  const char *cursor = uri;
156 
157  if (0 != strncasecmp ("gnunet://", uri, strlen ("gnunet://")))
158  {
159  fprintf (stderr,
160  _("Invalid URI: does not start with `gnunet://'\n"));
161  exit_code = 1;
162  return;
163  }
164 
165  cursor += strlen ("gnunet://");
166 
167  const char *slash = strchr (cursor, '/');
168  if (NULL == slash)
169  {
170  fprintf (stderr, _("Invalid URI: fails to specify a subsystem\n"));
171  exit_code = 1;
172  return;
173  }
174 
175  char *subsystem = GNUNET_strndup (cursor, slash - cursor);
176  char *program = NULL;
177 
178  if (GNUNET_OK !=
180  {
181  fprintf (stderr, _("No known handler for subsystem `%s'\n"), subsystem);
183  exit_code = 1;
184  return;
185  }
186 
188 
189  char **childargv = NULL;
190  unsigned int childargc = 0;
191 
192  for (const char *token=strtok (program, " ");
193  NULL!=token;
194  token=strtok(NULL, " "))
195  {
196  GNUNET_array_append (childargv, childargc, GNUNET_strdup (token));
197  }
198  GNUNET_array_append (childargv, childargc, GNUNET_strdup (uri));
199  GNUNET_array_append (childargv, childargc, NULL);
200 
202  NULL,
203  NULL,
204  NULL,
205  childargv[0],
206  childargv);
207  for (size_t i=0; i<childargc-1; ++i)
208  {
209  GNUNET_free (childargv[i]);
210  }
211 
212  GNUNET_array_grow (childargv, childargc, 0);
213 
214  if (NULL == childproc)
215  {
217  _("Unable to start child process `%s'\n"),
218  program);
219  GNUNET_free (program);
220  exit_code = 1;
221  return;
222  }
223 
225 }
226 
233 static const zbar_symbol_t *
234 get_symbol (zbar_processor_t *proc)
235 {
236  if (0 != zbar_processor_parse_config (proc, "enable"))
237  {
238  GNUNET_break (0);
239  return NULL;
240  }
241 
242  int r = zbar_processor_init (proc, device, 1);
243  if (0 != r)
244  {
246  _("Failed to open device: `%s': %d\n"),
247  device,
248  r);
249  return NULL;
250  }
251 
252  r = zbar_processor_set_visible (proc, 1);
253  r += zbar_processor_set_active (proc, 1);
254  if (0 != r)
255  {
256  GNUNET_break (0);
257  return NULL;
258  }
259 
260  LOG (_("Capturing...\n"));
261 
262  int n = zbar_process_one (proc, -1);
263 
264  zbar_processor_set_active (proc, 0);
265  zbar_processor_set_visible (proc, 0);
266 
267  if (-1 == n)
268  {
269  LOG (_("No captured images\n"));
270  return NULL;
271  }
272 
273  LOG(_("Got %d images\n"), n);
274 
275  const zbar_symbol_set_t *symbols = zbar_processor_get_results (proc);
276  if (NULL == symbols)
277  {
278  GNUNET_break (0);
279  return NULL;
280  }
281 
282  return zbar_symbol_set_first_symbol (symbols);
283 }
284 
290 static char *
291 run_zbar (void)
292 {
293  zbar_processor_t *proc = zbar_processor_create (1);
294  if (NULL == proc)
295  {
296  GNUNET_break (0);
297  return NULL;
298  }
299 
300  if (NULL == device)
301  {
302  device = GNUNET_strdup ("/dev/video0");
303  }
304 
305  const zbar_symbol_t *symbol = get_symbol (proc);
306  if (NULL == symbol)
307  {
308  zbar_processor_destroy (proc);
309  return NULL;
310  }
311 
312  const char *data = zbar_symbol_get_data (symbol);
313  if (NULL == data)
314  {
315  GNUNET_break (0);
316  zbar_processor_destroy (proc);
317  return NULL;
318  }
319 
320  LOG (_("Found %s: \"%s\"\n"),
321  zbar_get_symbol_name (zbar_symbol_get_type (symbol)),
322  data);
323 
324  char *copy = GNUNET_strdup (data);
325 
326  zbar_processor_destroy (proc);
328 
329  return copy;
330 }
331 
332 #if HAVE_PNG
339 static char *
340 png_parse (uint32_t *width, uint32_t *height)
341 {
342  if (NULL == width || NULL == height)
343  {
344  return NULL;
345  }
346 
347  FILE *pngfile = fopen (pngfilename, "rb");
348  if (NULL == pngfile)
349  {
350  return NULL;
351  }
352 
353  unsigned char header[8];
354  if (8 != fread (header, 1, 8, pngfile))
355  {
356  fclose (pngfile);
357  return NULL;
358  }
359 
360  if (png_sig_cmp (header, 0, 8))
361  {
362  fclose (pngfile);
364  _("%s is not a PNG file\n"),
365  pngfilename);
366  fprintf (stderr, _("%s is not a PNG file\n"), pngfilename);
367  return NULL;
368  }
369 
370  /* libpng's default error handling might or might not conflict with GNUnet's
371  scheduler and event loop. Beware of strange interactions. */
372  png_structp png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
373  NULL,
374  NULL,
375  NULL);
376  if (NULL == png)
377  {
378  GNUNET_break (0);
379  fclose (pngfile);
380  return NULL;
381  }
382 
383  png_infop pnginfo = png_create_info_struct (png);
384  if (NULL == pnginfo)
385  {
386  GNUNET_break (0);
387  png_destroy_read_struct (&png, NULL, NULL);
388  fclose (pngfile);
389  return NULL;
390  }
391 
392  if (setjmp (png_jmpbuf (png)))
393  {
394  GNUNET_break (0);
395  png_destroy_read_struct (&png, &pnginfo, NULL);
396  fclose (pngfile);
397  return NULL;
398  }
399 
400  png_init_io (png, pngfile);
401  png_set_sig_bytes (png, 8);
402 
403  png_read_info (png, pnginfo);
404 
405  png_byte pngcolor = png_get_color_type (png, pnginfo);
406  png_byte pngdepth = png_get_bit_depth (png, pnginfo);
407 
408  /* Normalize picture --- based on a zbar example */
409  if (0 != (pngcolor & PNG_COLOR_TYPE_PALETTE))
410  {
411  png_set_palette_to_rgb (png);
412  }
413 
414  if (pngcolor == PNG_COLOR_TYPE_GRAY && pngdepth < 8)
415  {
416  png_set_expand_gray_1_2_4_to_8 (png);
417  }
418 
419  if (16 == pngdepth)
420  {
421  png_set_strip_16 (png);
422  }
423 
424  if (0 != (pngcolor & PNG_COLOR_MASK_ALPHA))
425  {
426  png_set_strip_alpha (png);
427  }
428 
429  if (0 != (pngcolor & PNG_COLOR_MASK_COLOR))
430  {
431  png_set_rgb_to_gray_fixed (png, 1, -1, -1);
432  }
433 
434  png_uint_32 pngwidth = png_get_image_width (png, pnginfo);
435  png_uint_32 pngheight = png_get_image_height (png, pnginfo);
436 
437  char *buffer = GNUNET_new_array (pngwidth * pngheight, char);
438  png_bytepp rows = GNUNET_new_array (pngheight, png_bytep);
439 
440  for (png_uint_32 i=0; i<pngheight; ++i)
441  {
442  rows[i] = (unsigned char *)buffer + (pngwidth * i);
443  }
444 
445  png_read_image (png, rows);
446 
447  GNUNET_free (rows);
448  fclose (pngfile);
449 
450  *width = pngwidth;
451  *height = pngheight;
452 
453  return buffer;
454 }
455 
461 static char *
462 run_png_reader (void)
463 {
464  uint32_t width = 0;
465  uint32_t height = 0;
466  char *buffer = png_parse (&width, &height);
467  if (NULL == buffer)
468  {
469  return NULL;
470  }
471 
472  zbar_image_scanner_t *scanner = zbar_image_scanner_create ();
473  zbar_image_scanner_set_config (scanner,0, ZBAR_CFG_ENABLE, 1);
474 
475  zbar_image_t *zimage = zbar_image_create ();
476  zbar_image_set_format (zimage, zbar_fourcc ('Y', '8', '0', '0'));
477  zbar_image_set_size (zimage, width, height);
478  zbar_image_set_data (zimage, buffer, width * height, &zbar_image_free_data);
479 
480  int n = zbar_scan_image (scanner, zimage);
481 
482  if (-1 == n)
483  {
484  LOG (_("No captured images\n"));
485  return NULL;
486  }
487 
488  LOG(_("Got %d images\n"), n);
489 
490  const zbar_symbol_t *symbol = zbar_image_first_symbol (zimage);
491 
492  const char *data = zbar_symbol_get_data (symbol);
493  if (NULL == data)
494  {
495  GNUNET_break (0);
496  zbar_image_destroy (zimage);
497  zbar_image_scanner_destroy (scanner);
498  return NULL;
499  }
500 
501  LOG (_("Found %s: \"%s\"\n"),
502  zbar_get_symbol_name (zbar_symbol_get_type (symbol)),
503  data);
504 
505  char *copy = GNUNET_strdup (data);
506 
507  zbar_image_destroy (zimage);
508  zbar_image_scanner_destroy (scanner);
509 
510  return copy;
511 }
512 #endif
513 
522 static void
523 run (void *cls,
524  char *const *args,
525  const char *cfgfile,
526  const struct GNUNET_CONFIGURATION_Handle *cfg)
527 {
528  char *data = NULL;
529 
531 
532 #if HAVE_PNG
533  if (NULL != pngfilename)
534  {
535  data = run_png_reader ();
536  }
537  else
538 #endif
539  {
540  data = run_zbar ();
541  }
542 
543  if (NULL == data)
544  {
545  LOG (_("No data found\n"));
546  exit_code = 1;
548  return;
549  }
550 
551  handle_uri (cls, data, cfgfile, cfg);
552 
553  if (0 != exit_code)
554  {
555  fprintf (stdout, _("Failed to add URI %s\n"), data);
556  GNUNET_free (data);
558  return;
559  }
560 
561  LOG (_("Dispatching the URI\n"));
562 }
563 
564 int
565 main (int argc, char *const *argv)
566 {
569  'd',
570  "device",
571  "DEVICE",
572  gettext_noop ("use the video device DEVICE (defaults to /dev/video0)"),
573  &device),
574 #if HAVE_PNG
576  'f',
577  "file",
578  "FILE",
579  gettext_noop ("read from the PNG-encoded file FILE"),
580  &pngfilename),
581 #endif
584  };
585 
587  GNUNET_PROGRAM_run (argc,
588  argv,
589  "gnunet-qr",
590  gettext_noop ("Scan a QR code and import the URI read"),
591  options,
592  &run,
593  NULL);
594 
595  return ((GNUNET_OK == ret) && (0 == exit_code)) ? 0 : 1;
596 }
struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_OPTION_END
Definition: 002.c:13
struct GNUNET_GETOPT_CommandLineOption options[]
Definition: 002.c:5
#define gettext_noop(String)
Definition: gettext.h:69
static const struct GNUNET_CONFIGURATION_Handle * cfg
Configuration we are using.
Definition: gnunet-abd.c:36
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81
uint32_t data
The data value.
static struct GNUNET_FS_Uri * uri
Value of URI provided on command-line (when not publishing a file but just creating UBlocks to refer ...
static char * run_zbar(void)
Run the zbar QR code parser.
Definition: gnunet-qr.c:291
static unsigned int verbosity
Requested verbosity.
Definition: gnunet-qr.c:63
static const zbar_symbol_t * get_symbol(zbar_processor_t *proc)
Obtain a QR code symbol from proc.
Definition: gnunet-qr.c:234
#define LOG(fmt,...)
Macro to handle verbosity when printing messages.
Definition: gnunet-qr.c:78
static void shutdown_program(void *cls)
Executed when program is terminating.
Definition: gnunet-qr.c:96
static void wait_child(void *cls, enum GNUNET_OS_ProcessStatusType type, long unsigned int code)
Callback executed when the child process terminates.
Definition: gnunet-qr.c:117
static int exit_code
Global exit code.
Definition: gnunet-qr.c:42
static void run(void *cls, char *const *args, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
Main function executed by the scheduler.
Definition: gnunet-qr.c:523
int main(int argc, char *const *argv)
Definition: gnunet-qr.c:565
static void handle_uri(void *cls, const char *uri, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
Dispatch URIs to the appropriate GNUnet helper process.
Definition: gnunet-qr.c:150
struct GNUNET_OS_Process * childproc
Child process handle.
Definition: gnunet-qr.c:68
static struct GNUNET_ChildWaitHandle * waitchildproc
Child process handle for waiting.
Definition: gnunet-qr.c:73
static char * device
Video device to capture from.
Definition: gnunet-qr.c:49
static char * subsystem
Set to subsystem that we're going to get stats for (or NULL for all).
enum GNUNET_GenericReturnValue GNUNET_CONFIGURATION_get_value_string(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *option, char **value)
Get a configuration value that should be a string.
struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_verbose(unsigned int *level)
Define the '-V' verbosity option.
struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_string(char shortName, const char *name, const char *argumentHelp, const char *description, char **str)
Allow user to specify a string.
#define GNUNET_log(kind,...)
void GNUNET_wait_child_cancel(struct GNUNET_ChildWaitHandle *cwh)
Stop waiting on this child.
GNUNET_GenericReturnValue
Named constants for return values.
Definition: gnunet_common.h:96
struct GNUNET_ChildWaitHandle * GNUNET_wait_child(struct GNUNET_OS_Process *proc, GNUNET_ChildCompletedCallback cb, void *cb_cls)
Starts the handling of the child processes.
@ GNUNET_OK
Definition: gnunet_common.h:99
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur.
@ GNUNET_ERROR_TYPE_ERROR
#define GNUNET_strdup(a)
Wrapper around GNUNET_xstrdup_.
#define GNUNET_array_grow(arr, size, tsize)
Grow a well-typed (!) array.
#define GNUNET_strndup(a, length)
Wrapper around GNUNET_xstrndup_.
#define GNUNET_new_array(n, type)
Allocate a size n array with structs or unions of the given type.
#define GNUNET_array_append(arr, len, element)
Append an element to an array (growing the array by one).
#define GNUNET_free(ptr)
Wrapper around free.
struct GNUNET_OS_Process * GNUNET_OS_start_process_vap(enum GNUNET_OS_InheritStdioFlags std_inheritance, struct GNUNET_DISK_PipeHandle *pipe_stdin, struct GNUNET_DISK_PipeHandle *pipe_stdout, struct GNUNET_DISK_PipeHandle *pipe_stderr, const char *filename, char *const argv[])
Start a process.
Definition: os_priority.c:598
GNUNET_OS_ProcessStatusType
Process status types.
void GNUNET_OS_process_destroy(struct GNUNET_OS_Process *proc)
Cleans up process structure contents (OS-dependent) and deallocates it.
Definition: os_priority.c:280
int GNUNET_OS_process_kill(struct GNUNET_OS_Process *proc, int sig)
Sends a signal to the process.
Definition: os_priority.c:217
@ GNUNET_OS_INHERIT_STD_ALL
Use this option to have all of the standard streams (stdin, stdout and stderror) be inherited.
enum GNUNET_GenericReturnValue GNUNET_PROGRAM_run(int argc, char *const *argv, const char *binaryName, const char *binaryHelp, const struct GNUNET_GETOPT_CommandLineOption *options, GNUNET_PROGRAM_Main task, void *task_cls)
Run a standard GNUnet command startup sequence (initialize loggers and configuration,...
Definition: program.c:399
void GNUNET_SCHEDULER_shutdown(void)
Request the shutdown of a scheduler.
Definition: scheduler.c:533
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_shutdown(GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run on shutdown, that is when a CTRL-C signal is received,...
Definition: scheduler.c:1316
#define _(String)
GNU gettext support macro.
Definition: platform.h:177
Struct which defines a Child Wait handle.
struct GNUNET_OS_Process * proc
Child process which is managed.
Definition of a command line option.
enum GNUNET_TESTBED_UnderlayLinkModelType type
the type of this model