GNUnet  0.19.5
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 "platform.h"
27 #include <stdio.h>
28 #include <stdbool.h>
29 #include <signal.h>
30 #include <zbar.h>
31 
32 
33 #include "gnunet_util_lib.h"
34 
35 #if HAVE_PNG
36 #include <png.h>
37 #endif
38 
43 static int exit_code = 0;
44 
50 static char *device = NULL;
51 
52 #if HAVE_PNG
58 static char *pngfilename = NULL;
59 #endif
60 
64 static unsigned int verbosity = 0;
65 
70 
74 static struct GNUNET_ChildWaitHandle *waitchildproc = NULL;
75 
79 #define LOG(fmt, ...) \
80  do \
81  { \
82  if (0 < verbosity) \
83  { \
84  GNUNET_log (GNUNET_ERROR_TYPE_INFO, fmt, ##__VA_ARGS__); \
85  if (verbosity > 1) \
86  { \
87  fprintf (stdout, fmt, ##__VA_ARGS__); \
88  } \
89  } \
90  } \
91  while (0)
92 
96 static void
97 shutdown_program (void *cls)
98 {
99  if (NULL != waitchildproc)
100  {
102  }
103  if (NULL != childproc)
104  {
105  /* A bit brutal, but this process is terminating so we're out of time */
107  }
108 }
109 
117 static void
118 wait_child (void *cls,
120  long unsigned int code)
121 {
123  childproc = NULL;
124  waitchildproc = NULL;
125 
126  char *uri = cls;
127 
128  if (0 != exit_code)
129  {
130  fprintf (stdout, _("Failed to add URI %s\n"), uri);
131  }
132  else
133  {
134  fprintf (stdout, _("Added URI %s\n"), uri);
135  }
136 
137  GNUNET_free (uri);
138 
140 }
141 
150 static void
151 handle_uri (void *cls,
152  const char *uri,
153  const char *cfgfile,
154  const struct GNUNET_CONFIGURATION_Handle *cfg)
155 {
156  const char *cursor = uri;
157 
158  if (0 != strncasecmp ("gnunet://", uri, strlen ("gnunet://")))
159  {
160  fprintf (stderr,
161  _("Invalid URI: does not start with `gnunet://'\n"));
162  exit_code = 1;
163  return;
164  }
165 
166  cursor += strlen ("gnunet://");
167 
168  const char *slash = strchr (cursor, '/');
169  if (NULL == slash)
170  {
171  fprintf (stderr, _("Invalid URI: fails to specify a subsystem\n"));
172  exit_code = 1;
173  return;
174  }
175 
176  char *subsystem = GNUNET_strndup (cursor, slash - cursor);
177  char *program = NULL;
178 
179  if (GNUNET_OK !=
181  {
182  fprintf (stderr, _("No known handler for subsystem `%s'\n"), subsystem);
184  exit_code = 1;
185  return;
186  }
187 
189 
190  char **childargv = NULL;
191  unsigned int childargc = 0;
192 
193  for (const char *token=strtok (program, " ");
194  NULL!=token;
195  token=strtok(NULL, " "))
196  {
197  GNUNET_array_append (childargv, childargc, GNUNET_strdup (token));
198  }
199  GNUNET_array_append (childargv, childargc, GNUNET_strdup (uri));
200  GNUNET_array_append (childargv, childargc, NULL);
201 
203  NULL,
204  NULL,
205  NULL,
206  childargv[0],
207  childargv);
208  for (size_t i=0; i<childargc-1; ++i)
209  {
210  GNUNET_free (childargv[i]);
211  }
212 
213  GNUNET_array_grow (childargv, childargc, 0);
214 
215  if (NULL == childproc)
216  {
218  _("Unable to start child process `%s'\n"),
219  program);
220  GNUNET_free (program);
221  exit_code = 1;
222  return;
223  }
224 
226 }
227 
234 static const zbar_symbol_t *
235 get_symbol (zbar_processor_t *proc)
236 {
237  if (0 != zbar_processor_parse_config (proc, "enable"))
238  {
239  GNUNET_break (0);
240  return NULL;
241  }
242 
243  int r = zbar_processor_init (proc, device, 1);
244  if (0 != r)
245  {
247  _("Failed to open device: `%s': %d\n"),
248  device,
249  r);
250  return NULL;
251  }
252 
253  r = zbar_processor_set_visible (proc, 1);
254  r += zbar_processor_set_active (proc, 1);
255  if (0 != r)
256  {
257  GNUNET_break (0);
258  return NULL;
259  }
260 
261  LOG (_("Capturing...\n"));
262 
263  int n = zbar_process_one (proc, -1);
264 
265  zbar_processor_set_active (proc, 0);
266  zbar_processor_set_visible (proc, 0);
267 
268  if (-1 == n)
269  {
270  LOG (_("No captured images\n"));
271  return NULL;
272  }
273 
274  LOG(_("Got %d images\n"), n);
275 
276  const zbar_symbol_set_t *symbols = zbar_processor_get_results (proc);
277  if (NULL == symbols)
278  {
279  GNUNET_break (0);
280  return NULL;
281  }
282 
283  return zbar_symbol_set_first_symbol (symbols);
284 }
285 
291 static char *
292 run_zbar (void)
293 {
294  zbar_processor_t *proc = zbar_processor_create (1);
295  if (NULL == proc)
296  {
297  GNUNET_break (0);
298  return NULL;
299  }
300 
301  if (NULL == device)
302  {
303  device = GNUNET_strdup ("/dev/video0");
304  }
305 
306  const zbar_symbol_t *symbol = get_symbol (proc);
307  if (NULL == symbol)
308  {
309  zbar_processor_destroy (proc);
310  return NULL;
311  }
312 
313  const char *data = zbar_symbol_get_data (symbol);
314  if (NULL == data)
315  {
316  GNUNET_break (0);
317  zbar_processor_destroy (proc);
318  return NULL;
319  }
320 
321  LOG (_("Found %s: \"%s\"\n"),
322  zbar_get_symbol_name (zbar_symbol_get_type (symbol)),
323  data);
324 
325  char *copy = GNUNET_strdup (data);
326 
327  zbar_processor_destroy (proc);
329 
330  return copy;
331 }
332 
333 #if HAVE_PNG
340 static char *
341 png_parse (uint32_t *width, uint32_t *height)
342 {
343  if (NULL == width || NULL == height)
344  {
345  return NULL;
346  }
347 
348  FILE *pngfile = fopen (pngfilename, "rb");
349  if (NULL == pngfile)
350  {
351  return NULL;
352  }
353 
354  unsigned char header[8];
355  if (8 != fread (header, 1, 8, pngfile))
356  {
357  fclose (pngfile);
358  return NULL;
359  }
360 
361  if (png_sig_cmp (header, 0, 8))
362  {
363  fclose (pngfile);
365  _("%s is not a PNG file\n"),
366  pngfilename);
367  fprintf (stderr, _("%s is not a PNG file\n"), pngfilename);
368  return NULL;
369  }
370 
371  /* libpng's default error handling might or might not conflict with GNUnet's
372  scheduler and event loop. Beware of strange interactions. */
373  png_structp png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
374  NULL,
375  NULL,
376  NULL);
377  if (NULL == png)
378  {
379  GNUNET_break (0);
380  fclose (pngfile);
381  return NULL;
382  }
383 
384  png_infop pnginfo = png_create_info_struct (png);
385  if (NULL == pnginfo)
386  {
387  GNUNET_break (0);
388  png_destroy_read_struct (&png, NULL, NULL);
389  fclose (pngfile);
390  return NULL;
391  }
392 
393  if (setjmp (png_jmpbuf (png)))
394  {
395  GNUNET_break (0);
396  png_destroy_read_struct (&png, &pnginfo, NULL);
397  fclose (pngfile);
398  return NULL;
399  }
400 
401  png_init_io (png, pngfile);
402  png_set_sig_bytes (png, 8);
403 
404  png_read_info (png, pnginfo);
405 
406  png_byte pngcolor = png_get_color_type (png, pnginfo);
407  png_byte pngdepth = png_get_bit_depth (png, pnginfo);
408 
409  /* Normalize picture --- based on a zbar example */
410  if (0 != (pngcolor & PNG_COLOR_TYPE_PALETTE))
411  {
412  png_set_palette_to_rgb (png);
413  }
414 
415  if (pngcolor == PNG_COLOR_TYPE_GRAY && pngdepth < 8)
416  {
417  png_set_expand_gray_1_2_4_to_8 (png);
418  }
419 
420  if (16 == pngdepth)
421  {
422  png_set_strip_16 (png);
423  }
424 
425  if (0 != (pngcolor & PNG_COLOR_MASK_ALPHA))
426  {
427  png_set_strip_alpha (png);
428  }
429 
430  if (0 != (pngcolor & PNG_COLOR_MASK_COLOR))
431  {
432  png_set_rgb_to_gray_fixed (png, 1, -1, -1);
433  }
434 
435  png_uint_32 pngwidth = png_get_image_width (png, pnginfo);
436  png_uint_32 pngheight = png_get_image_height (png, pnginfo);
437 
438  char *buffer = GNUNET_new_array (pngwidth * pngheight, char);
439  png_bytepp rows = GNUNET_new_array (pngheight, png_bytep);
440 
441  for (png_uint_32 i=0; i<pngheight; ++i)
442  {
443  rows[i] = (unsigned char *)buffer + (pngwidth * i);
444  }
445 
446  png_read_image (png, rows);
447 
448  GNUNET_free (rows);
449  fclose (pngfile);
450 
451  *width = pngwidth;
452  *height = pngheight;
453 
454  return buffer;
455 }
456 
462 static char *
463 run_png_reader (void)
464 {
465  uint32_t width = 0;
466  uint32_t height = 0;
467  char *buffer = png_parse (&width, &height);
468  if (NULL == buffer)
469  {
470  return NULL;
471  }
472 
473  zbar_image_scanner_t *scanner = zbar_image_scanner_create ();
474  zbar_image_scanner_set_config (scanner,0, ZBAR_CFG_ENABLE, 1);
475 
476  zbar_image_t *zimage = zbar_image_create ();
477  zbar_image_set_format (zimage, zbar_fourcc ('Y', '8', '0', '0'));
478  zbar_image_set_size (zimage, width, height);
479  zbar_image_set_data (zimage, buffer, width * height, &zbar_image_free_data);
480 
481  int n = zbar_scan_image (scanner, zimage);
482 
483  if (-1 == n)
484  {
485  LOG (_("No captured images\n"));
486  return NULL;
487  }
488 
489  LOG(_("Got %d images\n"), n);
490 
491  const zbar_symbol_t *symbol = zbar_image_first_symbol (zimage);
492 
493  const char *data = zbar_symbol_get_data (symbol);
494  if (NULL == data)
495  {
496  GNUNET_break (0);
497  zbar_image_destroy (zimage);
498  zbar_image_scanner_destroy (scanner);
499  return NULL;
500  }
501 
502  LOG (_("Found %s: \"%s\"\n"),
503  zbar_get_symbol_name (zbar_symbol_get_type (symbol)),
504  data);
505 
506  char *copy = GNUNET_strdup (data);
507 
508  zbar_image_destroy (zimage);
509  zbar_image_scanner_destroy (scanner);
510 
511  return copy;
512 }
513 #endif
514 
523 static void
524 run (void *cls,
525  char *const *args,
526  const char *cfgfile,
527  const struct GNUNET_CONFIGURATION_Handle *cfg)
528 {
529  char *data = NULL;
530 
532 
533 #if HAVE_PNG
534  if (NULL != pngfilename)
535  {
536  data = run_png_reader ();
537  }
538  else
539 #endif
540  {
541  data = run_zbar ();
542  }
543 
544  if (NULL == data)
545  {
546  LOG (_("No data found\n"));
547  exit_code = 1;
549  return;
550  }
551 
552  handle_uri (cls, data, cfgfile, cfg);
553 
554  if (0 != exit_code)
555  {
556  fprintf (stdout, _("Failed to add URI %s\n"), data);
557  GNUNET_free (data);
559  return;
560  }
561 
562  LOG (_("Dispatching the URI\n"));
563 }
564 
565 int
566 main (int argc, char *const *argv)
567 {
570  'd',
571  "device",
572  "DEVICE",
573  gettext_noop ("use the video device DEVICE (defaults to /dev/video0)"),
574  &device),
575 #if HAVE_PNG
577  'f',
578  "file",
579  "FILE",
580  gettext_noop ("read from the PNG-encoded file FILE"),
581  &pngfilename),
582 #endif
585  };
586 
588  GNUNET_PROGRAM_run (argc,
589  argv,
590  "gnunet-qr",
591  gettext_noop ("Scan a QR code and import the URI read"),
592  options,
593  &run,
594  NULL);
595 
596  return ((GNUNET_OK == ret) && (0 == exit_code)) ? 0 : 1;
597 }
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:70
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:292
static unsigned int verbosity
Requested verbosity.
Definition: gnunet-qr.c:64
static const zbar_symbol_t * get_symbol(zbar_processor_t *proc)
Obtain a QR code symbol from proc.
Definition: gnunet-qr.c:235
#define LOG(fmt,...)
Macro to handle verbosity when printing messages.
Definition: gnunet-qr.c:79
static void shutdown_program(void *cls)
Executed when program is terminating.
Definition: gnunet-qr.c:97
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:118
static int exit_code
Global exit code.
Definition: gnunet-qr.c:43
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:524
int main(int argc, char *const *argv)
Definition: gnunet-qr.c:566
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:151
struct GNUNET_OS_Process * childproc
Child process handle.
Definition: gnunet-qr.c:69
static struct GNUNET_ChildWaitHandle * waitchildproc
Child process handle for waiting.
Definition: gnunet-qr.c:74
static char * device
Video device to capture from.
Definition: gnunet-qr.c:50
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.
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
#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:567
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:260
int GNUNET_OS_process_kill(struct GNUNET_OS_Process *proc, int sig)
Sends a signal to the process.
Definition: os_priority.c:210
@ 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:400
void GNUNET_SCHEDULER_shutdown(void)
Request the shutdown of a scheduler.
Definition: scheduler.c:562
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:1334
#define _(String)
GNU gettext support macro.
Definition: platform.h:178
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