GNUnet 0.22.2
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
43static int exit_code = 0;
44
50static char *device = NULL;
51
52#if HAVE_PNG
58static char *pngfilename = NULL;
59#endif
60
64static unsigned int verbosity = 0;
65
70
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
96static void
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
110
118static void
119wait_child (void *cls,
121 long unsigned int code)
122{
123 char *uri = cls;
125 childproc = NULL;
126 waitchildproc = NULL;
127
128
129 if (0 != exit_code)
130 {
131 fprintf (stdout, _ ("Failed to add URI %s\n"), uri);
132 }
133 else
134 {
135 fprintf (stdout, _ ("Added URI %s\n"), uri);
136 }
137
139
141}
142
143
152static void
153handle_uri (void *cls,
154 const char *uri,
155 const char *cfgfile,
156 const struct GNUNET_CONFIGURATION_Handle *cfg)
157{
158 const char *cursor = uri;
159 const char *slash;
160 char *subsystem;
161 char *program = NULL;
162
163 if (0 != strncasecmp ("gnunet://", uri, strlen ("gnunet://")))
164 {
165 fprintf (stderr,
166 _ ("Invalid URI: does not start with `gnunet://'\n"));
167 exit_code = 1;
168 return;
169 }
170
171 cursor += strlen ("gnunet://");
172
173 slash = strchr (cursor, '/');
174 if (NULL == slash)
175 {
176 fprintf (stderr, _ ("Invalid URI: fails to specify a subsystem\n"));
177 exit_code = 1;
178 return;
179 }
180
181 subsystem = GNUNET_strndup (cursor, slash - cursor);
182 program = NULL;
183
184 if (GNUNET_OK !=
186 {
187 fprintf (stderr, _ ("No known handler for subsystem `%s'\n"), subsystem);
189 exit_code = 1;
190 return;
191 }
192
194
195 {
196 char **childargv = NULL;
197 unsigned int childargc = 0;
198
199 for (const char *token = strtok (program, " ");
200 NULL!=token;
201 token = strtok (NULL, " "))
202 {
203 GNUNET_array_append (childargv, childargc, GNUNET_strdup (token));
204 }
205 GNUNET_array_append (childargv, childargc, GNUNET_strdup (uri));
206 GNUNET_array_append (childargv, childargc, NULL);
207
209 NULL,
210 NULL,
211 NULL,
212 childargv[0],
213 childargv);
214 for (size_t i = 0; i<childargc - 1; ++i)
215 {
216 GNUNET_free (childargv[i]);
217 }
218
219 GNUNET_array_grow (childargv, childargc, 0);
220
221 if (NULL == childproc)
222 {
224 _ ("Unable to start child process `%s'\n"),
225 program);
226 GNUNET_free (program);
227 exit_code = 1;
228 return;
229 }
230
232 }
233}
234
235
242static const zbar_symbol_t *
243get_symbol (zbar_processor_t *proc)
244{
245 const zbar_symbol_set_t *symbols;
246 int r, n;
247
248 if (0 != zbar_processor_parse_config (proc, "enable"))
249 {
250 GNUNET_break (0);
251 return NULL;
252 }
253
254 r = zbar_processor_init (proc, device, 1);
255 if (0 != r)
256 {
258 _ ("Failed to open device: `%s': %d\n"),
259 device,
260 r);
261 return NULL;
262 }
263
264 r = zbar_processor_set_visible (proc, 1);
265 r += zbar_processor_set_active (proc, 1);
266 if (0 != r)
267 {
268 GNUNET_break (0);
269 return NULL;
270 }
271
272 LOG (_ ("Capturing...\n"));
273
274 n = zbar_process_one (proc, -1);
275
276 zbar_processor_set_active (proc, 0);
277 zbar_processor_set_visible (proc, 0);
278
279 if (-1 == n)
280 {
281 LOG (_ ("No captured images\n"));
282 return NULL;
283 }
284
285 LOG (_ ("Got %d images\n"), n);
286
287 symbols = zbar_processor_get_results (proc);
288 if (NULL == symbols)
289 {
290 GNUNET_break (0);
291 return NULL;
292 }
293
294 return zbar_symbol_set_first_symbol (symbols);
295}
296
297
303static char *
305{
306 zbar_processor_t *proc = zbar_processor_create (1);
307 const zbar_symbol_t *symbol;
308 const char *data;
309 char *copy;
310
311 if (NULL == proc)
312 {
313 GNUNET_break (0);
314 return NULL;
315 }
316
317 if (NULL == device)
318 {
319 device = GNUNET_strdup ("/dev/video0");
320 }
321
322 symbol = get_symbol (proc);
323 if (NULL == symbol)
324 {
325 zbar_processor_destroy (proc);
326 return NULL;
327 }
328
329 data = zbar_symbol_get_data (symbol);
330 if (NULL == data)
331 {
332 GNUNET_break (0);
333 zbar_processor_destroy (proc);
334 return NULL;
335 }
336
337 LOG (_ ("Found %s: \"%s\"\n"),
338 zbar_get_symbol_name (zbar_symbol_get_type (symbol)),
339 data);
340
341 copy = GNUNET_strdup (data);
342
343 zbar_processor_destroy (proc);
345
346 return copy;
347}
348
349
350#if HAVE_PNG
357static char *
358png_parse (uint32_t *width, uint32_t *height)
359{
360 if (NULL == width || NULL == height)
361 {
362 return NULL;
363 }
364
365 FILE *pngfile = fopen (pngfilename, "rb");
366 if (NULL == pngfile)
367 {
368 return NULL;
369 }
370
371 unsigned char header[8];
372 if (8 != fread (header, 1, 8, pngfile))
373 {
374 fclose (pngfile);
375 return NULL;
376 }
377
378 if (png_sig_cmp (header, 0, 8))
379 {
380 fclose (pngfile);
382 _ ("%s is not a PNG file\n"),
383 pngfilename);
384 fprintf (stderr, _ ("%s is not a PNG file\n"), pngfilename);
385 return NULL;
386 }
387
388 /* libpng's default error handling might or might not conflict with GNUnet's
389 scheduler and event loop. Beware of strange interactions. */
390 png_structp png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
391 NULL,
392 NULL,
393 NULL);
394 if (NULL == png)
395 {
396 GNUNET_break (0);
397 fclose (pngfile);
398 return NULL;
399 }
400
401 png_infop pnginfo = png_create_info_struct (png);
402 if (NULL == pnginfo)
403 {
404 GNUNET_break (0);
405 png_destroy_read_struct (&png, NULL, NULL);
406 fclose (pngfile);
407 return NULL;
408 }
409
410 if (setjmp (png_jmpbuf (png)))
411 {
412 GNUNET_break (0);
413 png_destroy_read_struct (&png, &pnginfo, NULL);
414 fclose (pngfile);
415 return NULL;
416 }
417
418 png_init_io (png, pngfile);
419 png_set_sig_bytes (png, 8);
420
421 png_read_info (png, pnginfo);
422
423 png_byte pngcolor = png_get_color_type (png, pnginfo);
424 png_byte pngdepth = png_get_bit_depth (png, pnginfo);
425
426 /* Normalize picture --- based on a zbar example */
427 if (0 != (pngcolor & PNG_COLOR_TYPE_PALETTE))
428 {
429 png_set_palette_to_rgb (png);
430 }
431
432 if (pngcolor == PNG_COLOR_TYPE_GRAY && pngdepth < 8)
433 {
434 png_set_expand_gray_1_2_4_to_8 (png);
435 }
436
437 if (16 == pngdepth)
438 {
439 png_set_strip_16 (png);
440 }
441
442 if (0 != (pngcolor & PNG_COLOR_MASK_ALPHA))
443 {
444 png_set_strip_alpha (png);
445 }
446
447 if (0 != (pngcolor & PNG_COLOR_MASK_COLOR))
448 {
449 png_set_rgb_to_gray_fixed (png, 1, -1, -1);
450 }
451
452 png_uint_32 pngwidth = png_get_image_width (png, pnginfo);
453 png_uint_32 pngheight = png_get_image_height (png, pnginfo);
454
455 char *buffer = GNUNET_new_array (pngwidth * pngheight, char);
456 png_bytepp rows = GNUNET_new_array (pngheight, png_bytep);
457
458 for (png_uint_32 i = 0; i<pngheight; ++i)
459 {
460 rows[i] = (unsigned char *) buffer + (pngwidth * i);
461 }
462
463 png_read_image (png, rows);
464
465 GNUNET_free (rows);
466 fclose (pngfile);
467
468 *width = pngwidth;
469 *height = pngheight;
470
471 return buffer;
472}
473
474
480static char *
481run_png_reader (void)
482{
483 uint32_t width = 0;
484 uint32_t height = 0;
485 char *buffer = png_parse (&width, &height);
486 if (NULL == buffer)
487 {
488 return NULL;
489 }
490
491 zbar_image_scanner_t *scanner = zbar_image_scanner_create ();
492 zbar_image_scanner_set_config (scanner,0, ZBAR_CFG_ENABLE, 1);
493
494 zbar_image_t *zimage = zbar_image_create ();
495 zbar_image_set_format (zimage, zbar_fourcc ('Y', '8', '0', '0'));
496 zbar_image_set_size (zimage, width, height);
497 zbar_image_set_data (zimage, buffer, width * height, &zbar_image_free_data);
498
499 int n = zbar_scan_image (scanner, zimage);
500
501 if (-1 == n)
502 {
503 LOG (_ ("No captured images\n"));
504 return NULL;
505 }
506
507 LOG (_ ("Got %d images\n"), n);
508
509 const zbar_symbol_t *symbol = zbar_image_first_symbol (zimage);
510
511 const char *data = zbar_symbol_get_data (symbol);
512 if (NULL == data)
513 {
514 GNUNET_break (0);
515 zbar_image_destroy (zimage);
516 zbar_image_scanner_destroy (scanner);
517 return NULL;
518 }
519
520 LOG (_ ("Found %s: \"%s\"\n"),
521 zbar_get_symbol_name (zbar_symbol_get_type (symbol)),
522 data);
523
524 char *copy = GNUNET_strdup (data);
525
526 zbar_image_destroy (zimage);
527 zbar_image_scanner_destroy (scanner);
528
529 return copy;
530}
531
532
533#endif
534
543static void
544run (void *cls,
545 char *const *args,
546 const char *cfgfile,
547 const struct GNUNET_CONFIGURATION_Handle *cfg)
548{
549 char *data = NULL;
550
552
553#if HAVE_PNG
554 if (NULL != pngfilename)
555 {
556 data = run_png_reader ();
557 }
558 else
559#endif
560 {
561 data = run_zbar ();
562 }
563
564 if (NULL == data)
565 {
566 LOG (_ ("No data found\n"));
567 exit_code = 1;
569 return;
570 }
571
572 handle_uri (cls, data, cfgfile, cfg);
573
574 if (0 != exit_code)
575 {
576 fprintf (stdout, _ ("Failed to add URI %s\n"), data);
579 return;
580 }
581
582 LOG (_ ("Dispatching the URI\n"));
583}
584
585
586int
587main (int argc, char *const *argv)
588{
591 'd',
592 "device",
593 "DEVICE",
594 gettext_noop ("use the video device DEVICE (defaults to /dev/video0)"),
595 &device),
596#if HAVE_PNG
598 'f',
599 "file",
600 "FILE",
601 gettext_noop ("read from the PNG-encoded file FILE"),
602 &pngfilename),
603#endif
606 };
607
610 argc,
611 argv,
612 "gnunet-qr",
613 gettext_noop ("Scan a QR code and import the URI read"),
614 options,
615 &run,
616 NULL);
617
618 return ((GNUNET_OK == ret) && (0 == exit_code)) ? 0 : 1;
619}
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:74
static int ret
Final status code.
Definition: gnunet-arm.c:93
static struct GNUNET_CONFIGURATION_Handle * cfg
Our configuration.
Definition: gnunet-arm.c:108
static char * data
The data to insert into the dht.
static uint32_t type
Type string converted to DNS type 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 unsigned int verbosity
Requested verbosity.
Definition: gnunet-qr.c:64
#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:119
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:544
int main(int argc, char *const *argv)
Definition: gnunet-qr.c:587
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:153
static char * run_zbar(void)
Run the zbar QR code parser.
Definition: gnunet-qr.c:304
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 const zbar_symbol_t * get_symbol(zbar_processor_t *proc)
Obtain a QR code symbol from proc.
Definition: gnunet-qr.c:243
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.
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_GenericReturnValue
Named constants for return values.
@ 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.
const struct GNUNET_OS_ProjectData * GNUNET_OS_project_data_gnunet(void)
Return default project data used by 'libgnunetutil' for GNUnet.
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
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_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(const struct GNUNET_OS_ProjectData *pd, 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:407
void GNUNET_SCHEDULER_shutdown(void)
Request the shutdown of a scheduler.
Definition: scheduler.c:567
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:1339
#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.