From d744d49e13fa6175016e8dcfc0f9506b9f170759 Mon Sep 17 00:00:00 2001 From: Alessio Vanni Date: Fri, 5 Nov 2021 22:52:53 +0100 Subject: Allow gnunet-qr to read codes from PNG pictures --- src/util/Makefile.am | 3 + src/util/gnunet-qr.c | 597 +++++++++++++++++++++++++++++++++++---------------- 2 files changed, 411 insertions(+), 189 deletions(-) (limited to 'src/util') diff --git a/src/util/Makefile.am b/src/util/Makefile.am index c3a0feccc..d21ac5e86 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -239,6 +239,9 @@ gnunet_qr_LDADD = \ libgnunetutil.la \ $(GN_LIBINTL) gnunet_qr_LDFLAGS= -lzbar +if HAVE_PNG +gnunet_qr_LDFLAGS += -lpng +endif plugin_LTLIBRARIES = \ libgnunet_plugin_utiltest.la diff --git a/src/util/gnunet-qr.c b/src/util/gnunet-qr.c index 451d61d40..5bccd3916 100644 --- a/src/util/gnunet-qr.c +++ b/src/util/gnunet-qr.c @@ -24,302 +24,500 @@ * @author Christian Grothoff (error handling) */ #include -#include #include +#include +#include + #include "platform.h" #include "gnunet_util_lib.h" -#define LOG(fmt, ...) \ - if (verbose) \ - printf (fmt, ## __VA_ARGS__) +#if HAVE_PNG +#include +#endif /** - * Video device to capture from. Sane default for GNU/Linux systems. + * Global exit code. + * Set to non-zero if an error occurs after the scheduler has started. */ -static char *device; +static int exit_code = 0; /** - * --verbose option + * Video device to capture from. + * Used by default if PNG support is disabled or no PNG file is specified. + * Defaults to /dev/video0. */ -static unsigned int verbose; +static char *device = NULL; +#if HAVE_PNG /** - * --silent option + * Name of the file to read from. + * If the file is not a PNG-encoded image of a QR code, an error will be + * thrown. */ -static int silent = false; +static char *pngfilename = NULL; +#endif /** - * Handler exit code + * Requested verbosity. */ -static long unsigned int exit_code = 0; +static unsigned int verbosity = 0; /** - * Helper process we started. + * Child process handle. */ -static struct GNUNET_OS_Process *p; +struct GNUNET_OS_Process *childproc = NULL; /** - * Child signal handler. + * Child process handle for waiting. */ -static struct GNUNET_SIGNAL_Context *shc_chld; +static struct GNUNET_ChildWaitHandle *waitchildproc = NULL; /** - * Pipe used to communicate child death via signal. + * Macro to handle verbosity when printing messages. */ -static struct GNUNET_DISK_PipeHandle *sigpipe; +#define LOG(fmt, ...) \ + do \ + { \ + if (0 < verbosity) \ + { \ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, fmt, ##__VA_ARGS__); \ + if (verbosity > 1) \ + { \ + fprintf (stdout, fmt, ##__VA_ARGS__); \ + } \ + } \ + } \ + while (0) /** - * Process ID of this process at the time we installed the various - * signal handlers. + * Executed when program is terminating. */ -static pid_t my_pid; +static void +shutdown_program (void *cls) +{ + if (NULL != waitchildproc) + { + GNUNET_wait_child_cancel (waitchildproc); + } + if (NULL != childproc) + { + /* A bit brutal, but this process is terminating so we're out of time */ + GNUNET_OS_process_kill (childproc, SIGKILL); + } +} /** - * Task triggered whenever we receive a SIGCHLD (child - * process died) or when user presses CTRL-C. + * Callback executed when the child process terminates. * - * @param cls closure, NULL + * @param cls closure + * @param type status of the child process + * @param code the exit code of the child process */ static void -maint_child_death (void *cls) +wait_child (void *cls, + enum GNUNET_OS_ProcessStatusType type, + long unsigned int code) { - enum GNUNET_OS_ProcessStatusType type; + GNUNET_OS_process_destroy (childproc); + childproc = NULL; + waitchildproc = NULL; + + char *uri = cls; - if ((GNUNET_OK != GNUNET_OS_process_status (p, &type, &exit_code)) || - (type != GNUNET_OS_PROCESS_EXITED)) - GNUNET_break (0 == GNUNET_OS_process_kill (p, GNUNET_TERM_SIG)); - GNUNET_SIGNAL_handler_uninstall (shc_chld); - shc_chld = NULL; - if (NULL != sigpipe) + if (0 != exit_code) { - GNUNET_DISK_pipe_close (sigpipe); - sigpipe = NULL; + fprintf (stdout, _("Failed to add URI %s\n"), uri); + } + else + { + fprintf (stdout, _("Added URI %s\n"), uri); } - GNUNET_OS_process_destroy (p); -} + GNUNET_free (uri); -/** - * Signal handler called for signals that causes us to wait for the child process. - */ -static void -sighandler_chld () -{ - static char c; - int old_errno = errno; /* backup errno */ - - if (getpid () != my_pid) - _exit (1); /* we have fork'ed since the signal handler was created, - * ignore the signal, see https://gnunet.org/vfork discussion */ - GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle - (sigpipe, GNUNET_DISK_PIPE_END_WRITE), - &c, sizeof(c)); - errno = old_errno; + GNUNET_SCHEDULER_shutdown (); } - /** - * Dispatch URIs to the appropriate GNUnet helper process + * Dispatch URIs to the appropriate GNUnet helper process. * * @param cls closure - * @param uri uri to dispatch - * @param cfgfile name of the configuration file used (for saving, can be NULL!) - * @param cfg configuration + * @param uri URI to dispatch + * @param cfgfile name of the configuration file in use + * @param cfg the configuration in use */ static void -gnunet_uri (void *cls, +handle_uri (void *cls, const char *uri, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg) { - const char *orig_uri; - const char *slash; - char *subsystem; - char *program; - struct GNUNET_SCHEDULER_Task *rt; + const char *cursor = uri; - orig_uri = uri; if (0 != strncasecmp ("gnunet://", uri, strlen ("gnunet://"))) { fprintf (stderr, - _ ("Invalid URI: does not start with `%s'\n"), - "gnunet://"); + _("Invalid URI: does not start with `gnunet://'\n")); + exit_code = 1; return; } - uri += strlen ("gnunet://"); - if (NULL == (slash = strchr (uri, '/'))) + + cursor += strlen ("gnunet://"); + + const char *slash = strchr (cursor, '/'); + if (NULL == slash) { - fprintf (stderr, _ ("Invalid URI: fails to specify subsystem\n")); + fprintf (stderr, _("Invalid URI: fails to specify a subsystem\n")); + exit_code = 1; return; } - subsystem = GNUNET_strndup (uri, slash - uri); + + char *subsystem = GNUNET_strndup (cursor, slash - cursor); + char *program = NULL; + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "uri", subsystem, &program)) { - fprintf (stderr, _ ("No handler known for subsystem `%s'\n"), subsystem); + fprintf (stderr, _("No known handler for subsystem `%s'\n"), subsystem); GNUNET_free (subsystem); + exit_code = 1; return; } + GNUNET_free (subsystem); - sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE); - GNUNET_assert (NULL != sigpipe); - rt = GNUNET_SCHEDULER_add_read_file ( - GNUNET_TIME_UNIT_FOREVER_REL, - GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ), - &maint_child_death, - NULL); - my_pid = getpid (); - shc_chld = GNUNET_SIGNAL_handler_install (SIGCHLD, - &sighandler_chld); - - { - char **argv = NULL; - unsigned int argc = 0; - char *u = GNUNET_strdup (program); - - for (const char *tok = strtok (u, " "); - NULL != tok; - tok = strtok (NULL, " ")) - GNUNET_array_append (argv, - argc, - GNUNET_strdup (tok)); - GNUNET_array_append (argv, - argc, - GNUNET_strdup (orig_uri)); - GNUNET_array_append (argv, - argc, - NULL); - p = GNUNET_OS_start_process_vap (GNUNET_OS_INHERIT_STD_ALL, - NULL, - NULL, - NULL, - argv[0], - argv); - for (unsigned int i = 0; i