diff options
Diffstat (limited to 'src/util/gnunet-qr.c')
-rw-r--r-- | src/util/gnunet-qr.c | 377 |
1 files changed, 0 insertions, 377 deletions
diff --git a/src/util/gnunet-qr.c b/src/util/gnunet-qr.c deleted file mode 100644 index 451d61d40..000000000 --- a/src/util/gnunet-qr.c +++ /dev/null | |||
@@ -1,377 +0,0 @@ | |||
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 | */ | ||
20 | /** | ||
21 | * @file util/gnunet-qr.c | ||
22 | * @author Hartmut Goebel (original implementation) | ||
23 | * @author Martin Schanzenbach (integrate gnunet-uri) | ||
24 | * @author Christian Grothoff (error handling) | ||
25 | */ | ||
26 | #include <stdio.h> | ||
27 | #include <zbar.h> | ||
28 | #include <stdbool.h> | ||
29 | #include "platform.h" | ||
30 | #include "gnunet_util_lib.h" | ||
31 | |||
32 | #define LOG(fmt, ...) \ | ||
33 | if (verbose) \ | ||
34 | printf (fmt, ## __VA_ARGS__) | ||
35 | |||
36 | /** | ||
37 | * Video device to capture from. Sane default for GNU/Linux systems. | ||
38 | */ | ||
39 | static char *device; | ||
40 | |||
41 | /** | ||
42 | * --verbose option | ||
43 | */ | ||
44 | static unsigned int verbose; | ||
45 | |||
46 | /** | ||
47 | * --silent option | ||
48 | */ | ||
49 | static int silent = false; | ||
50 | |||
51 | /** | ||
52 | * Handler exit code | ||
53 | */ | ||
54 | static long unsigned int exit_code = 0; | ||
55 | |||
56 | /** | ||
57 | * Helper process we started. | ||
58 | */ | ||
59 | static struct GNUNET_OS_Process *p; | ||
60 | |||
61 | /** | ||
62 | * Child signal handler. | ||
63 | */ | ||
64 | static struct GNUNET_SIGNAL_Context *shc_chld; | ||
65 | |||
66 | /** | ||
67 | * Pipe used to communicate child death via signal. | ||
68 | */ | ||
69 | static struct GNUNET_DISK_PipeHandle *sigpipe; | ||
70 | |||
71 | /** | ||
72 | * Process ID of this process at the time we installed the various | ||
73 | * signal handlers. | ||
74 | */ | ||
75 | static pid_t my_pid; | ||
76 | |||
77 | /** | ||
78 | * Task triggered whenever we receive a SIGCHLD (child | ||
79 | * process died) or when user presses CTRL-C. | ||
80 | * | ||
81 | * @param cls closure, NULL | ||
82 | */ | ||
83 | static void | ||
84 | maint_child_death (void *cls) | ||
85 | { | ||
86 | enum GNUNET_OS_ProcessStatusType type; | ||
87 | |||
88 | if ((GNUNET_OK != GNUNET_OS_process_status (p, &type, &exit_code)) || | ||
89 | (type != GNUNET_OS_PROCESS_EXITED)) | ||
90 | GNUNET_break (0 == GNUNET_OS_process_kill (p, GNUNET_TERM_SIG)); | ||
91 | GNUNET_SIGNAL_handler_uninstall (shc_chld); | ||
92 | shc_chld = NULL; | ||
93 | if (NULL != sigpipe) | ||
94 | { | ||
95 | GNUNET_DISK_pipe_close (sigpipe); | ||
96 | sigpipe = NULL; | ||
97 | } | ||
98 | GNUNET_OS_process_destroy (p); | ||
99 | } | ||
100 | |||
101 | |||
102 | /** | ||
103 | * Signal handler called for signals that causes us to wait for the child process. | ||
104 | */ | ||
105 | static void | ||
106 | sighandler_chld () | ||
107 | { | ||
108 | static char c; | ||
109 | int old_errno = errno; /* backup errno */ | ||
110 | |||
111 | if (getpid () != my_pid) | ||
112 | _exit (1); /* we have fork'ed since the signal handler was created, | ||
113 | * ignore the signal, see https://gnunet.org/vfork discussion */ | ||
114 | GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle | ||
115 | (sigpipe, GNUNET_DISK_PIPE_END_WRITE), | ||
116 | &c, sizeof(c)); | ||
117 | errno = old_errno; | ||
118 | } | ||
119 | |||
120 | |||
121 | /** | ||
122 | * Dispatch URIs to the appropriate GNUnet helper process | ||
123 | * | ||
124 | * @param cls closure | ||
125 | * @param uri uri to dispatch | ||
126 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
127 | * @param cfg configuration | ||
128 | */ | ||
129 | static void | ||
130 | gnunet_uri (void *cls, | ||
131 | const char *uri, | ||
132 | const char *cfgfile, | ||
133 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
134 | { | ||
135 | const char *orig_uri; | ||
136 | const char *slash; | ||
137 | char *subsystem; | ||
138 | char *program; | ||
139 | struct GNUNET_SCHEDULER_Task *rt; | ||
140 | |||
141 | orig_uri = uri; | ||
142 | if (0 != strncasecmp ("gnunet://", uri, strlen ("gnunet://"))) | ||
143 | { | ||
144 | fprintf (stderr, | ||
145 | _ ("Invalid URI: does not start with `%s'\n"), | ||
146 | "gnunet://"); | ||
147 | return; | ||
148 | } | ||
149 | uri += strlen ("gnunet://"); | ||
150 | if (NULL == (slash = strchr (uri, '/'))) | ||
151 | { | ||
152 | fprintf (stderr, _ ("Invalid URI: fails to specify subsystem\n")); | ||
153 | return; | ||
154 | } | ||
155 | subsystem = GNUNET_strndup (uri, slash - uri); | ||
156 | if (GNUNET_OK != | ||
157 | GNUNET_CONFIGURATION_get_value_string (cfg, "uri", subsystem, &program)) | ||
158 | { | ||
159 | fprintf (stderr, _ ("No handler known for subsystem `%s'\n"), subsystem); | ||
160 | GNUNET_free (subsystem); | ||
161 | return; | ||
162 | } | ||
163 | GNUNET_free (subsystem); | ||
164 | sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE); | ||
165 | GNUNET_assert (NULL != sigpipe); | ||
166 | rt = GNUNET_SCHEDULER_add_read_file ( | ||
167 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
168 | GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ), | ||
169 | &maint_child_death, | ||
170 | NULL); | ||
171 | my_pid = getpid (); | ||
172 | shc_chld = GNUNET_SIGNAL_handler_install (SIGCHLD, | ||
173 | &sighandler_chld); | ||
174 | |||
175 | { | ||
176 | char **argv = NULL; | ||
177 | unsigned int argc = 0; | ||
178 | char *u = GNUNET_strdup (program); | ||
179 | |||
180 | for (const char *tok = strtok (u, " "); | ||
181 | NULL != tok; | ||
182 | tok = strtok (NULL, " ")) | ||
183 | GNUNET_array_append (argv, | ||
184 | argc, | ||
185 | GNUNET_strdup (tok)); | ||
186 | GNUNET_array_append (argv, | ||
187 | argc, | ||
188 | GNUNET_strdup (orig_uri)); | ||
189 | GNUNET_array_append (argv, | ||
190 | argc, | ||
191 | NULL); | ||
192 | p = GNUNET_OS_start_process_vap (GNUNET_OS_INHERIT_STD_ALL, | ||
193 | NULL, | ||
194 | NULL, | ||
195 | NULL, | ||
196 | argv[0], | ||
197 | argv); | ||
198 | for (unsigned int i = 0; i<argc - 1; i++) | ||
199 | GNUNET_free (argv[i]); | ||
200 | GNUNET_array_grow (argv, | ||
201 | argc, | ||
202 | 0); | ||
203 | GNUNET_free (u); | ||
204 | } | ||
205 | if (NULL == p) | ||
206 | GNUNET_SCHEDULER_cancel (rt); | ||
207 | GNUNET_free (program); | ||
208 | } | ||
209 | |||
210 | |||
211 | /** | ||
212 | * Obtain QR code 'symbol' from @a proc. | ||
213 | * | ||
214 | * @param proc zbar processor to use | ||
215 | * @return NULL on error | ||
216 | */ | ||
217 | static const zbar_symbol_t * | ||
218 | get_symbol (zbar_processor_t *proc) | ||
219 | { | ||
220 | const zbar_symbol_set_t *symbols; | ||
221 | int rc; | ||
222 | int n; | ||
223 | |||
224 | if (0 != zbar_processor_parse_config (proc, "enable")) | ||
225 | { | ||
226 | GNUNET_break (0); | ||
227 | return NULL; | ||
228 | } | ||
229 | |||
230 | /* initialize the Processor */ | ||
231 | if (NULL == device) | ||
232 | device = GNUNET_strdup ("/dev/video0"); | ||
233 | if (0 != (rc = zbar_processor_init (proc, device, 1))) | ||
234 | { | ||
235 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
236 | "Failed to open device `%s': %d\n", | ||
237 | device, | ||
238 | rc); | ||
239 | return NULL; | ||
240 | } | ||
241 | |||
242 | /* enable the preview window */ | ||
243 | if ((0 != (rc = zbar_processor_set_visible (proc, 1))) || | ||
244 | (0 != (rc = zbar_processor_set_active (proc, 1)))) | ||
245 | { | ||
246 | GNUNET_break (0); | ||
247 | return NULL; | ||
248 | } | ||
249 | |||
250 | /* read at least one barcode (or until window closed) */ | ||
251 | LOG ("Capturing\n"); | ||
252 | n = zbar_process_one (proc, -1); | ||
253 | |||
254 | /* hide the preview window */ | ||
255 | (void) zbar_processor_set_active (proc, 0); | ||
256 | (void) zbar_processor_set_visible (proc, 0); | ||
257 | if (-1 == n) | ||
258 | return NULL; /* likely user closed the window */ | ||
259 | LOG ("Got %i images\n", n); | ||
260 | /* extract results */ | ||
261 | symbols = zbar_processor_get_results (proc); | ||
262 | if (NULL == symbols) | ||
263 | { | ||
264 | GNUNET_break (0); | ||
265 | return NULL; | ||
266 | } | ||
267 | return zbar_symbol_set_first_symbol (symbols); | ||
268 | } | ||
269 | |||
270 | |||
271 | /** | ||
272 | * Run zbar QR code parser. | ||
273 | * | ||
274 | * @return NULL on error, otherwise the URI that we found | ||
275 | */ | ||
276 | static char * | ||
277 | run_zbar () | ||
278 | { | ||
279 | zbar_processor_t *proc; | ||
280 | const char *data; | ||
281 | char *ret; | ||
282 | const zbar_symbol_t *symbol; | ||
283 | |||
284 | /* configure the Processor */ | ||
285 | proc = zbar_processor_create (1); | ||
286 | if (NULL == proc) | ||
287 | { | ||
288 | GNUNET_break (0); | ||
289 | return NULL; | ||
290 | } | ||
291 | |||
292 | symbol = get_symbol (proc); | ||
293 | if (NULL == symbol) | ||
294 | { | ||
295 | zbar_processor_destroy (proc); | ||
296 | return NULL; | ||
297 | } | ||
298 | data = zbar_symbol_get_data (symbol); | ||
299 | if (NULL == data) | ||
300 | { | ||
301 | GNUNET_break (0); | ||
302 | zbar_processor_destroy (proc); | ||
303 | return NULL; | ||
304 | } | ||
305 | LOG ("Found %s \"%s\"\n", | ||
306 | zbar_get_symbol_name (zbar_symbol_get_type (symbol)), | ||
307 | data); | ||
308 | ret = GNUNET_strdup (data); | ||
309 | /* clean up */ | ||
310 | zbar_processor_destroy (proc); | ||
311 | GNUNET_free (device); | ||
312 | return ret; | ||
313 | } | ||
314 | |||
315 | |||
316 | /** | ||
317 | * Main function that will be run by the scheduler. | ||
318 | * | ||
319 | * @param cls closure | ||
320 | * @param args remaining command-line arguments | ||
321 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
322 | * @param cfg configuration | ||
323 | */ | ||
324 | static void | ||
325 | run (void *cls, | ||
326 | char *const *args, | ||
327 | const char *cfgfile, | ||
328 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
329 | { | ||
330 | char *data; | ||
331 | |||
332 | data = run_zbar (); | ||
333 | if (NULL == data) | ||
334 | return; | ||
335 | gnunet_uri (cls, data, cfgfile, cfg); | ||
336 | if (exit_code != 0) | ||
337 | { | ||
338 | printf ("Failed to add URI %s\n", data); | ||
339 | } | ||
340 | else | ||
341 | { | ||
342 | printf ("Added URI %s\n", data); | ||
343 | } | ||
344 | GNUNET_free (data); | ||
345 | }; | ||
346 | |||
347 | |||
348 | int | ||
349 | main (int argc, char *const *argv) | ||
350 | { | ||
351 | int ret; | ||
352 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
353 | GNUNET_GETOPT_option_string ( | ||
354 | 'd', | ||
355 | "device", | ||
356 | "DEVICE", | ||
357 | gettext_noop ("use video-device DEVICE (default: /dev/video0"), | ||
358 | &device), | ||
359 | GNUNET_GETOPT_option_verbose (&verbose), | ||
360 | GNUNET_GETOPT_option_flag ('s', | ||
361 | "silent", | ||
362 | gettext_noop ("do not show preview windows"), | ||
363 | &silent), | ||
364 | GNUNET_GETOPT_OPTION_END | ||
365 | }; | ||
366 | |||
367 | ret = GNUNET_PROGRAM_run ( | ||
368 | argc, | ||
369 | argv, | ||
370 | "gnunet-qr", | ||
371 | gettext_noop ( | ||
372 | "Scan a QR code using a video device and import the uri read"), | ||
373 | options, | ||
374 | &run, | ||
375 | NULL); | ||
376 | return ((GNUNET_OK == ret) && (0 == exit_code)) ? 0 : 1; | ||
377 | } | ||