1 /*------------------------------------------------------------------------
2  *  Copyright 2019 (c) Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
3  *
4  *  This file is part of the ZBar Bar Code Reader.
5  *
6  *  The ZBar Bar Code Reader is free software; you can redistribute it
7  *  and/or modify it under the terms of the GNU Lesser Public License as
8  *  published by the Free Software Foundation; either version 2.1 of
9  *  the License, or (at your option) any later version.
10  *
11  *  The ZBar Bar Code Reader is distributed in the hope that it will be
12  *  useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13  *  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU Lesser Public License for more details.
15  *------------------------------------------------------------------------*/
16 
17 #include <argp.h>
18 #include <stdarg.h>
19 #include <stdbool.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <dbus/dbus.h>
24 
25 #define ZBAR_INTERFACE "org.linuxtv.Zbar1.Code"
26 #define ZBAR_SIGNAL_CODE "Code"
27 #define ZBAR_SIGNAL_TYPE "Type"
28 #define ZBAR_SIGNAL_DATA "Data"
29 #define ZBAR_SIGNAL_BINARY_DATA "BinaryData"
30 
31 #define PROGRAM_NAME	"test_dbus"
32 
33 static const char doc[] = "\nTest if ZBar is sending codes via D-Bus\n";
34 
35 static const struct argp_option options[] = {
36     {"count",   'c', "#codes",   0, "Stop after received #codes", 0},
37     {"time",    't', "#seconds", 0, "Stop after #seconds",        0},
38     {"log",     'l', "#file",    0, "Write log to #file",         0},
39     {"bin-log", 'b', "#file",    0, "Write binary log to #file",  0},
40     {"help",    '?', 0,          0, "Give this help list",       -1},
41     {"usage",   -3,  0,          0, "Give a short usage message", 0},
42     { 0 }
43 };
44 
45 static int max_msg = 0;
46 static int timeout = 0;
47 static FILE *log   = NULL;
48 static FILE *bin_log  = NULL;
49 
parse_opt(int k,char * optarg,struct argp_state * state)50 static error_t parse_opt(int k, char *optarg, struct argp_state *state)
51 {
52     switch (k) {
53     case 'c':
54         max_msg = strtoul(optarg, NULL, 0);
55         break;
56     case 't':
57         timeout = strtoul(optarg, NULL, 0);
58         break;
59     case 'l':
60         log = fopen(optarg, "wb");
61         break;
62     case 'b':
63         bin_log = fopen(optarg, "wb");
64         break;
65     case '?':
66         argp_state_help(state, state->out_stream,
67                         ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG |
68                         ARGP_HELP_DOC);
69         exit(0);
70     case -3:
71         argp_state_help(state, state->out_stream, ARGP_HELP_USAGE);
72         exit(0);
73     default:
74         return ARGP_ERR_UNKNOWN;
75     };
76     return 0;
77 }
78 
79 static const struct argp argp = {
80 	.options = options,
81 	.parser = parse_opt,
82 	.doc = doc,
83 };
84 
main(int argc,char * argv[])85 int main(int argc, char *argv[])
86 {
87     DBusMessage* msg;
88     DBusMessageIter args, entry, dict, val;
89     DBusConnection* conn;
90     DBusError err;
91     char *str, *property;
92     int count = 0, length = 0;
93 
94     if (argp_parse(&argp, argc, argv, ARGP_NO_HELP | ARGP_NO_EXIT, 0, 0)) {
95         argp_help(&argp, stderr, ARGP_HELP_SHORT_USAGE, PROGRAM_NAME);
96         return -1;
97     }
98 
99     if (!log)
100 	log = fdopen(dup(fileno(stderr)), "w+");
101 
102     if (!bin_log)
103 	bin_log = fdopen(dup(fileno(stderr)), "w+");
104 
105     // initialise the error value
106     dbus_error_init(&err);
107 
108     // connect to the DBUS system bus, and check for errors
109     conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
110     if (dbus_error_is_set(&err)) {
111         fprintf(stderr, "Connection Error (%s)\n", err.message);
112         dbus_error_free(&err);
113     }
114     if (!conn) {
115         fprintf(stderr, "Connection Null\n");
116         return -1;
117     }
118 
119     dbus_bus_add_match(conn,
120 		       "type='signal',interface='" ZBAR_INTERFACE "'",
121 		       &err);
122    dbus_connection_flush(conn);
123    if (dbus_error_is_set(&err)) {
124       fprintf(stderr, "Match Error (%s)\n", err.message);
125       exit(1);
126    }
127 
128    if (timeout)
129        alarm(timeout);
130 
131    /* loop listening for signals being emitted */
132    fprintf(stderr, "Waiting for Zbar events\n");
133    while (true) {
134       // non blocking read of the next available message
135       dbus_connection_read_write(conn, 0);
136       msg = dbus_connection_pop_message(conn);
137 
138       // loop again if we haven't read a message
139       if (NULL == msg) {
140          sleep(1);
141          continue;
142       }
143 
144       // check if the message is a signal from the correct interface and with the correct name
145       if (dbus_message_is_signal(msg, ZBAR_INTERFACE, ZBAR_SIGNAL_CODE)) {
146          // read the parameters
147          if (!dbus_message_iter_init(msg, &args))
148             fprintf(stderr, "Message has no arguments!\n");
149          else if (DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type(&args))
150             fprintf(stderr, "Argument is not array!\n");
151          else {
152             while (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_INVALID) {
153               dbus_message_iter_recurse(&args, &entry);
154               if (DBUS_TYPE_DICT_ENTRY != dbus_message_iter_get_arg_type(&entry)) {
155                 fprintf(stderr, "Element is not dict entry!\n");
156               } else {
157                 while (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_INVALID) {
158                   dbus_message_iter_recurse(&entry, &dict);
159                   if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&dict)) {
160                     fprintf(stderr, "Dict Entry key is not string!\n");
161                   } else {
162                     dbus_message_iter_get_basic(&dict, &property);
163                     dbus_message_iter_next(&dict);
164                     if (DBUS_TYPE_VARIANT != dbus_message_iter_get_arg_type(&dict)) {
165                       fprintf(stderr, "Dict Entry value is not variant!\n");
166                     } else {
167                       dbus_message_iter_recurse(&dict, &val);
168                       if (strcmp(property, ZBAR_SIGNAL_TYPE) == 0) {
169                         if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&val)) {
170                           fprintf(stderr, "Dict Entry value for barcode type is not string!\n");
171                         } else {
172                           dbus_message_iter_get_basic(&val, &str);
173                           fprintf(stderr, "Type = %s\n", str);
174                         }
175                       } else if (strcmp(property, ZBAR_SIGNAL_DATA) == 0) {
176                         if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&val)) {
177                           fprintf(stderr, "Dict Entry value for barcode text data is not string!\n");
178                         } else {
179                           dbus_message_iter_get_basic(&val, &str);
180                           fprintf(stderr, "Value = %s\n", str);
181                           fprintf(log, "%s\n", str);
182                         }
183                       } else if (strcmp(property, ZBAR_SIGNAL_BINARY_DATA) == 0) {
184                         if (DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type(&val)) {
185                           fprintf(stderr, "Dict Entry value for barcode binary data is not array!\n");
186                         } else {
187                           dbus_message_iter_recurse(&val, &val);
188                           if (DBUS_TYPE_BYTE != dbus_message_iter_get_arg_type(&val)) {
189                             fprintf(stderr, "Dict Entry value for barcode binary data is not array of bytes!\n");
190                           } else {
191                             dbus_message_iter_get_fixed_array(&val, &str, &length);
192                             fprintf(stderr, "BinaryData[%d]\n", length);
193                             fwrite(str, sizeof(*str), length, bin_log);
194                           }
195                         }
196                       }
197                     }
198                   }
199                   dbus_message_iter_next(&entry);
200                 }
201                 /* If max_msg > 0, stops after receiving 'count' messages */
202                 if (++count == max_msg) {
203                     dbus_message_unref(msg);
204                     return 0;
205                 }
206               }
207               dbus_message_iter_next(&args);
208             }
209          }
210       }
211       // free the message
212       dbus_message_unref(msg);
213    }
214 
215    fclose(log);
216    fclose(bin_log);
217 
218    return 0;
219 }
220