xref: /aosp_15_r20/external/autotest/client/deps/fakemodem/src/fakemodem.c (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li /*
2*9c5db199SXin Li  * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
3*9c5db199SXin Li  * Use of this source code is governed by a BSD-style license that can be
4*9c5db199SXin Li  * found in the LICENSE file.
5*9c5db199SXin Li  */
6*9c5db199SXin Li 
7*9c5db199SXin Li #define _POSIX_C_SOURCE 201108L
8*9c5db199SXin Li #define _XOPEN_SOURCE 600
9*9c5db199SXin Li 
10*9c5db199SXin Li #include <assert.h>
11*9c5db199SXin Li #include <ctype.h>
12*9c5db199SXin Li #include <fcntl.h>
13*9c5db199SXin Li #include <stdio.h>
14*9c5db199SXin Li #include <stdlib.h>
15*9c5db199SXin Li #include <string.h>
16*9c5db199SXin Li #include <termios.h>
17*9c5db199SXin Li #include <unistd.h>
18*9c5db199SXin Li 
19*9c5db199SXin Li #include <glib.h>
20*9c5db199SXin Li #include <dbus/dbus-glib.h>
21*9c5db199SXin Li 
22*9c5db199SXin Li GIOChannel* ioc;
23*9c5db199SXin Li int primaryfd;
24*9c5db199SXin Li 
25*9c5db199SXin Li typedef struct {
26*9c5db199SXin Li   GRegex *command;
27*9c5db199SXin Li   char *reply; // generic text
28*9c5db199SXin Li   char *responsetext; // ERROR, +CMS ERROR, etc.
29*9c5db199SXin Li } Pattern;
30*9c5db199SXin Li 
31*9c5db199SXin Li typedef struct _FakeModem {
32*9c5db199SXin Li   GObject parent;
33*9c5db199SXin Li   gboolean echo;
34*9c5db199SXin Li   gboolean verbose;
35*9c5db199SXin Li   GPtrArray *patterns;
36*9c5db199SXin Li } FakeModem;
37*9c5db199SXin Li 
38*9c5db199SXin Li typedef struct _FakeModemClass
39*9c5db199SXin Li {
40*9c5db199SXin Li   GObjectClass parent_class;
41*9c5db199SXin Li } FakeModemClass;
42*9c5db199SXin Li 
43*9c5db199SXin Li GType fakemodem_get_type (void) G_GNUC_CONST;
44*9c5db199SXin Li 
45*9c5db199SXin Li #define FAKEMODEM_TYPE         (fake_modem_get_type ())
46*9c5db199SXin Li #define FAKEMODEM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), FAKEMODEM_TYPE, FakeModem))
47*9c5db199SXin Li 
G_DEFINE_TYPE(FakeModem,fake_modem,G_TYPE_OBJECT)48*9c5db199SXin Li G_DEFINE_TYPE (FakeModem, fake_modem, G_TYPE_OBJECT)
49*9c5db199SXin Li 
50*9c5db199SXin Li static void
51*9c5db199SXin Li fake_modem_init (FakeModem* self)
52*9c5db199SXin Li {
53*9c5db199SXin Li 
54*9c5db199SXin Li   self->echo = TRUE;
55*9c5db199SXin Li   self->verbose = TRUE;
56*9c5db199SXin Li   self->patterns = NULL;
57*9c5db199SXin Li }
58*9c5db199SXin Li 
59*9c5db199SXin Li static void
fake_modem_class_init(FakeModemClass * self)60*9c5db199SXin Li fake_modem_class_init (FakeModemClass* self)
61*9c5db199SXin Li {
62*9c5db199SXin Li }
63*9c5db199SXin Li 
64*9c5db199SXin Li static gboolean primary_read (GIOChannel *source, GIOCondition condition,
65*9c5db199SXin Li                              gpointer data);
66*9c5db199SXin Li 
67*9c5db199SXin Li static const gchar *handle_cmd (FakeModem *fakemodem, const gchar *cmd);
68*9c5db199SXin Li 
69*9c5db199SXin Li static gboolean send_unsolicited (FakeModem* fakemodem, const gchar* text);
70*9c5db199SXin Li static gboolean set_response (FakeModem* fakemodem, const gchar* command,
71*9c5db199SXin Li                               const gchar* reply, const gchar* response);
72*9c5db199SXin Li static gboolean remove_response (FakeModem* fakemodem, const gchar* command);
73*9c5db199SXin Li 
74*9c5db199SXin Li #include "fakemodem-dbus.h"
75*9c5db199SXin Li 
76*9c5db199SXin Li GPtrArray *
parse_pattern_files(char ** pattern_files,GError ** error)77*9c5db199SXin Li parse_pattern_files(char **pattern_files, GError **error)
78*9c5db199SXin Li {
79*9c5db199SXin Li   gint linenum;
80*9c5db199SXin Li   GRegex *skip, *parts;
81*9c5db199SXin Li   GPtrArray *patterns;
82*9c5db199SXin Li   int i;
83*9c5db199SXin Li 
84*9c5db199SXin Li   patterns = g_ptr_array_new();
85*9c5db199SXin Li 
86*9c5db199SXin Li   skip = g_regex_new ("^\\s*(#.*)?$", 0, 0, error);
87*9c5db199SXin Li   if (skip == NULL)
88*9c5db199SXin Li     return NULL;
89*9c5db199SXin Li   parts = g_regex_new ("^(\\S+)\\s*(\"([^\"]*)\")?\\s*(.*)$", 0, 0, error);
90*9c5db199SXin Li   if (parts == NULL)
91*9c5db199SXin Li     return NULL;
92*9c5db199SXin Li 
93*9c5db199SXin Li   for (i = 0 ; pattern_files[i] != NULL; i++) {
94*9c5db199SXin Li     GIOChannel *pf;
95*9c5db199SXin Li     gchar *pattern_file;
96*9c5db199SXin Li     gchar *line;
97*9c5db199SXin Li     gsize len, term;
98*9c5db199SXin Li 
99*9c5db199SXin Li     pattern_file = pattern_files[i];
100*9c5db199SXin Li 
101*9c5db199SXin Li     pf = g_io_channel_new_file (pattern_file, "r", error);
102*9c5db199SXin Li     if (pf == NULL)
103*9c5db199SXin Li       return NULL;
104*9c5db199SXin Li 
105*9c5db199SXin Li     linenum = 0;
106*9c5db199SXin Li     while (g_io_channel_read_line (pf, &line, &len, &term, error) ==
107*9c5db199SXin Li            G_IO_STATUS_NORMAL) {
108*9c5db199SXin Li       /* Don't need the terminator */
109*9c5db199SXin Li       line[term] = '\0';
110*9c5db199SXin Li       linenum++;
111*9c5db199SXin Li 
112*9c5db199SXin Li       if (!g_regex_match (skip, line, 0, NULL)) {
113*9c5db199SXin Li         GMatchInfo *info;
114*9c5db199SXin Li         gboolean ret;
115*9c5db199SXin Li         gchar *command, *responsetext;
116*9c5db199SXin Li         ret = g_regex_match (parts, line, 0, &info);
117*9c5db199SXin Li         if (ret) {
118*9c5db199SXin Li           Pattern *pat;
119*9c5db199SXin Li           pat = g_malloc (sizeof (*pat));
120*9c5db199SXin Li           command = g_match_info_fetch (info, 1);
121*9c5db199SXin Li           pat->command = g_regex_new (command,
122*9c5db199SXin Li                                       G_REGEX_ANCHORED |
123*9c5db199SXin Li                                       G_REGEX_CASELESS |
124*9c5db199SXin Li                                       G_REGEX_RAW |
125*9c5db199SXin Li                                       G_REGEX_OPTIMIZE,
126*9c5db199SXin Li                                       0,
127*9c5db199SXin Li                                       error);
128*9c5db199SXin Li           g_free (command);
129*9c5db199SXin Li           if (pat->command == NULL) {
130*9c5db199SXin Li             printf ("error: %s\n", (*error)->message);
131*9c5db199SXin Li             g_error_free (*error);
132*9c5db199SXin Li             *error = NULL;
133*9c5db199SXin Li           }
134*9c5db199SXin Li           responsetext = g_match_info_fetch (info, 3);
135*9c5db199SXin Li           if (strlen (responsetext) == 0) {
136*9c5db199SXin Li             g_free (responsetext);
137*9c5db199SXin Li             responsetext = NULL;
138*9c5db199SXin Li           }
139*9c5db199SXin Li           pat->responsetext = responsetext;
140*9c5db199SXin Li           pat->reply = g_match_info_fetch (info, 4);
141*9c5db199SXin Li           while (pat->reply[strlen (pat->reply) - 1] == '\\') {
142*9c5db199SXin Li             gchar *origstr;
143*9c5db199SXin Li             pat->reply[strlen (pat->reply) - 1] = '\0';
144*9c5db199SXin Li             g_free (line); /* probably invalidates fields in 'info' */
145*9c5db199SXin Li             g_io_channel_read_line (pf, &line, &len, &term, error);
146*9c5db199SXin Li             line[term] = '\0';
147*9c5db199SXin Li             linenum++;
148*9c5db199SXin Li             origstr = pat->reply;
149*9c5db199SXin Li             pat->reply = g_strjoin ("\r\n", origstr, line, NULL);
150*9c5db199SXin Li             g_free (origstr);
151*9c5db199SXin Li           }
152*9c5db199SXin Li           g_ptr_array_add (patterns, pat);
153*9c5db199SXin Li         } else {
154*9c5db199SXin Li           printf (" Line %d '%s' was not parsed"
155*9c5db199SXin Li                   " as a command-response pattern\n",
156*9c5db199SXin Li                   linenum, line);
157*9c5db199SXin Li         }
158*9c5db199SXin Li         g_match_info_free (info);
159*9c5db199SXin Li       }
160*9c5db199SXin Li       g_free (line);
161*9c5db199SXin Li     }
162*9c5db199SXin Li     g_io_channel_shutdown (pf, TRUE, NULL);
163*9c5db199SXin Li   }
164*9c5db199SXin Li 
165*9c5db199SXin Li   g_regex_unref (skip);
166*9c5db199SXin Li   g_regex_unref (parts);
167*9c5db199SXin Li 
168*9c5db199SXin Li   return patterns;
169*9c5db199SXin Li }
170*9c5db199SXin Li 
171*9c5db199SXin Li #define FM_DBUS_SERVICE "org.chromium.FakeModem"
172*9c5db199SXin Li 
173*9c5db199SXin Li static DBusGProxy *
create_dbus_proxy(DBusGConnection * bus)174*9c5db199SXin Li create_dbus_proxy (DBusGConnection *bus)
175*9c5db199SXin Li {
176*9c5db199SXin Li     DBusGProxy *proxy;
177*9c5db199SXin Li     GError *err = NULL;
178*9c5db199SXin Li     int request_name_result;
179*9c5db199SXin Li 
180*9c5db199SXin Li     proxy = dbus_g_proxy_new_for_name (bus,
181*9c5db199SXin Li                                        "org.freedesktop.DBus",
182*9c5db199SXin Li                                        "/org/freedesktop/DBus",
183*9c5db199SXin Li                                        "org.freedesktop.DBus");
184*9c5db199SXin Li 
185*9c5db199SXin Li     if (!dbus_g_proxy_call (proxy, "RequestName", &err,
186*9c5db199SXin Li                             G_TYPE_STRING, FM_DBUS_SERVICE,
187*9c5db199SXin Li                             G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE,
188*9c5db199SXin Li                             G_TYPE_INVALID,
189*9c5db199SXin Li                             G_TYPE_UINT, &request_name_result,
190*9c5db199SXin Li                             G_TYPE_INVALID)) {
191*9c5db199SXin Li         g_print ("Could not acquire the %s service.\n"
192*9c5db199SXin Li                  "  Message: '%s'\n", FM_DBUS_SERVICE, err->message);
193*9c5db199SXin Li 
194*9c5db199SXin Li         g_error_free (err);
195*9c5db199SXin Li         g_object_unref (proxy);
196*9c5db199SXin Li         proxy = NULL;
197*9c5db199SXin Li     } else if (request_name_result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
198*9c5db199SXin Li         g_print ("Could not acquire the " FM_DBUS_SERVICE
199*9c5db199SXin Li                  " service as it is already taken. Return: %d\n",
200*9c5db199SXin Li                  request_name_result);
201*9c5db199SXin Li 
202*9c5db199SXin Li         g_object_unref (proxy);
203*9c5db199SXin Li         proxy = NULL;
204*9c5db199SXin Li     } else {
205*9c5db199SXin Li         dbus_g_proxy_add_signal (proxy, "NameOwnerChanged",
206*9c5db199SXin Li                                  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
207*9c5db199SXin Li                                  G_TYPE_INVALID);
208*9c5db199SXin Li     }
209*9c5db199SXin Li 
210*9c5db199SXin Li     return proxy;
211*9c5db199SXin Li }
212*9c5db199SXin Li 
213*9c5db199SXin Li int
main(int argc,char * argv[])214*9c5db199SXin Li main (int argc, char *argv[])
215*9c5db199SXin Li {
216*9c5db199SXin Li   DBusGConnection *bus;
217*9c5db199SXin Li   DBusGProxy *proxy;
218*9c5db199SXin Li   GMainLoop* loop;
219*9c5db199SXin Li   const char *helperdevice;
220*9c5db199SXin Li   struct termios t;
221*9c5db199SXin Li   FakeModem *fakemodem;
222*9c5db199SXin Li   GOptionContext *opt_ctx;
223*9c5db199SXin Li   char **pattern_files = NULL;
224*9c5db199SXin Li   gboolean session = FALSE;
225*9c5db199SXin Li   GError *err = NULL;
226*9c5db199SXin Li 
227*9c5db199SXin Li   GOptionEntry entries[] = {
228*9c5db199SXin Li     { "patternfile", 0, 0, G_OPTION_ARG_STRING_ARRAY, &pattern_files,
229*9c5db199SXin Li       "Path to pattern file", NULL},
230*9c5db199SXin Li     { "session", 0, 0, G_OPTION_ARG_NONE, &session,
231*9c5db199SXin Li       "Bind to session bus", NULL},
232*9c5db199SXin Li     { "system", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &session,
233*9c5db199SXin Li       "Bind to system bus (default)", NULL},
234*9c5db199SXin Li     { NULL }
235*9c5db199SXin Li   };
236*9c5db199SXin Li 
237*9c5db199SXin Li   #if !GLIB_CHECK_VERSION(2,35,0)
238*9c5db199SXin Li   g_type_init ();
239*9c5db199SXin Li   #endif
240*9c5db199SXin Li 
241*9c5db199SXin Li   opt_ctx = g_option_context_new (NULL);
242*9c5db199SXin Li   g_option_context_set_summary (opt_ctx,
243*9c5db199SXin Li                                 "Emulate a modem with a set of "
244*9c5db199SXin Li                                 "regexp-programmed responses.");
245*9c5db199SXin Li   g_option_context_add_main_entries (opt_ctx, entries, NULL);
246*9c5db199SXin Li   if (!g_option_context_parse (opt_ctx, &argc, &argv, &err)) {
247*9c5db199SXin Li     g_warning ("%s\n", err->message);
248*9c5db199SXin Li     g_error_free (err);
249*9c5db199SXin Li     exit (1);
250*9c5db199SXin Li   }
251*9c5db199SXin Li 
252*9c5db199SXin Li   g_option_context_free (opt_ctx);
253*9c5db199SXin Li 
254*9c5db199SXin Li   fakemodem = g_object_new (FAKEMODEM_TYPE, NULL);
255*9c5db199SXin Li   if (pattern_files) {
256*9c5db199SXin Li     fakemodem->patterns = parse_pattern_files (pattern_files, &err);
257*9c5db199SXin Li     if (fakemodem->patterns == NULL) {
258*9c5db199SXin Li       g_warning ("%s\n", err->message);
259*9c5db199SXin Li       g_error_free (err);
260*9c5db199SXin Li       exit (1);
261*9c5db199SXin Li     }
262*9c5db199SXin Li   } else
263*9c5db199SXin Li     fakemodem->patterns = g_ptr_array_sized_new (0);
264*9c5db199SXin Li 
265*9c5db199SXin Li   loop = g_main_loop_new (NULL, FALSE);
266*9c5db199SXin Li 
267*9c5db199SXin Li   dbus_g_object_type_install_info (FAKEMODEM_TYPE,
268*9c5db199SXin Li                                    &dbus_glib_fakemodem_object_info);
269*9c5db199SXin Li 
270*9c5db199SXin Li   err = NULL;
271*9c5db199SXin Li   if (session)
272*9c5db199SXin Li     bus = dbus_g_bus_get (DBUS_BUS_SESSION, &err);
273*9c5db199SXin Li   else
274*9c5db199SXin Li     bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
275*9c5db199SXin Li 
276*9c5db199SXin Li   if (bus == NULL) {
277*9c5db199SXin Li       g_warning ("%s\n", err->message);
278*9c5db199SXin Li       g_error_free (err);
279*9c5db199SXin Li       exit (1);
280*9c5db199SXin Li   }
281*9c5db199SXin Li 
282*9c5db199SXin Li   proxy = create_dbus_proxy (bus);
283*9c5db199SXin Li   if (!proxy)
284*9c5db199SXin Li     exit (1);
285*9c5db199SXin Li 
286*9c5db199SXin Li   dbus_g_connection_register_g_object (bus,
287*9c5db199SXin Li                                        "/",
288*9c5db199SXin Li                                        G_OBJECT (fakemodem));
289*9c5db199SXin Li 
290*9c5db199SXin Li   primaryfd = posix_openpt (O_RDWR | O_NOCTTY);
291*9c5db199SXin Li 
292*9c5db199SXin Li   if (primaryfd == -1
293*9c5db199SXin Li       || grantpt (primaryfd) == -1
294*9c5db199SXin Li       || unlockpt (primaryfd) == -1
295*9c5db199SXin Li       || (helperdevice = ptsname (primaryfd)) == NULL)
296*9c5db199SXin Li     exit (1);
297*9c5db199SXin Li 
298*9c5db199SXin Li   printf ("%s\n", helperdevice);
299*9c5db199SXin Li   fflush (stdout);
300*9c5db199SXin Li 
301*9c5db199SXin Li   /* Echo is actively harmful here */
302*9c5db199SXin Li   tcgetattr (primaryfd, &t);
303*9c5db199SXin Li   t.c_lflag &= ~ECHO;
304*9c5db199SXin Li   tcsetattr (primaryfd, TCSANOW, &t);
305*9c5db199SXin Li 
306*9c5db199SXin Li   ioc = g_io_channel_unix_new (primaryfd);
307*9c5db199SXin Li   g_io_channel_set_encoding (ioc, NULL, NULL);
308*9c5db199SXin Li   g_io_channel_set_line_term (ioc, "\r", 1);
309*9c5db199SXin Li   g_io_add_watch (ioc, G_IO_IN, primary_read, fakemodem);
310*9c5db199SXin Li 
311*9c5db199SXin Li   g_main_loop_run (loop);
312*9c5db199SXin Li 
313*9c5db199SXin Li   g_main_loop_unref (loop);
314*9c5db199SXin Li 
315*9c5db199SXin Li   g_object_unref (fakemodem);
316*9c5db199SXin Li   return 0;
317*9c5db199SXin Li }
318*9c5db199SXin Li 
319*9c5db199SXin Li 
320*9c5db199SXin Li /*
321*9c5db199SXin Li  * &?[A-CE-RT-Z][0-9]*
322*9c5db199SXin Li  * S[0-9]+?
323*9c5db199SXin Li  * S[0-9]+=(([0-9A-F]+|"[^"]*")?,)+
324*9c5db199SXin Li  */
325*9c5db199SXin Li 
326*9c5db199SXin Li /*
327*9c5db199SXin Li  * action +[A-Z][A-Z0-9%-./:_]{0,15}
328*9c5db199SXin Li  * test   +[A-Z][A-Z0-9%-./:_]{0,15}=?
329*9c5db199SXin Li  * get    +[A-Z][A-Z0-9%-./:_]{0,15}?
330*9c5db199SXin Li  * set    +[A-Z][A-Z0-9%-./:_]{0,15}=(([0-9A-F]+|"[^"]*")?,)+
331*9c5db199SXin Li  */
332*9c5db199SXin Li 
333*9c5db199SXin Li 
334*9c5db199SXin Li #define VALUE "([0-9A-F]+|\"[^\"]*\")"
335*9c5db199SXin Li #define CVALUE VALUE "?(," VALUE "?)*"
336*9c5db199SXin Li static char *command_patterns[] =
337*9c5db199SXin Li {"\\s*(&?[A-CE-RT-Z][0-9]*)",
338*9c5db199SXin Li  "\\s*(S[0-9]+\\?)",
339*9c5db199SXin Li  "\\s*(S[0-9]+=" CVALUE ")",
340*9c5db199SXin Li  /* ATD... (dial string) handling is missing */
341*9c5db199SXin Li  "\\s*;?\\s*([+*%&][A-Z][A-Z0-9%-./:_]{0,15}=\\?)",
342*9c5db199SXin Li  "\\s*;?\\s*([+*%&][A-Z][A-Z0-9%-./:_]{0,15}=" CVALUE ")",
343*9c5db199SXin Li  "\\s*;?\\s*([+*%&][A-Z][A-Z0-9%-./:_]{0,15}(\\?)?)",
344*9c5db199SXin Li };
345*9c5db199SXin Li 
346*9c5db199SXin Li #undef VALUE
347*9c5db199SXin Li #undef CVALUE
348*9c5db199SXin Li 
primary_read(GIOChannel * source,GIOCondition condition,gpointer data)349*9c5db199SXin Li static gboolean primary_read (GIOChannel *source, GIOCondition condition,
350*9c5db199SXin Li                              gpointer data)
351*9c5db199SXin Li {
352*9c5db199SXin Li   FakeModem *fakemodem = data;
353*9c5db199SXin Li   gchar *line, *next;
354*9c5db199SXin Li   const gchar *response;
355*9c5db199SXin Li   gsize term;
356*9c5db199SXin Li   GError *error = NULL;
357*9c5db199SXin Li   GIOStatus status;
358*9c5db199SXin Li   int i, rval;
359*9c5db199SXin Li 
360*9c5db199SXin Li   static GPtrArray *commands;
361*9c5db199SXin Li 
362*9c5db199SXin Li   if (commands == NULL) {
363*9c5db199SXin Li     int n;
364*9c5db199SXin Li     n = sizeof (command_patterns) / sizeof (command_patterns[0]);
365*9c5db199SXin Li     commands = g_ptr_array_sized_new (n);
366*9c5db199SXin Li     for (i = 0 ; i < n ; i++) {
367*9c5db199SXin Li       GRegex *re = g_regex_new (command_patterns[i],
368*9c5db199SXin Li                                 G_REGEX_CASELESS |
369*9c5db199SXin Li                                 G_REGEX_ANCHORED |
370*9c5db199SXin Li                                 G_REGEX_RAW |
371*9c5db199SXin Li                                 G_REGEX_OPTIMIZE,
372*9c5db199SXin Li                                 0,
373*9c5db199SXin Li                                 &error);
374*9c5db199SXin Li       if (re == NULL) {
375*9c5db199SXin Li         g_warning ("Couldn't generate command regex: %s\n", error->message);
376*9c5db199SXin Li         g_error_free (error);
377*9c5db199SXin Li         exit (1);
378*9c5db199SXin Li       }
379*9c5db199SXin Li       g_ptr_array_add (commands, re);
380*9c5db199SXin Li     }
381*9c5db199SXin Li   }
382*9c5db199SXin Li 
383*9c5db199SXin Li   status = g_io_channel_read_line (source, &line, NULL, &term, &error);
384*9c5db199SXin Li   if (status == G_IO_STATUS_ERROR)
385*9c5db199SXin Li     return FALSE;
386*9c5db199SXin Li   line[term] = '\0';
387*9c5db199SXin Li 
388*9c5db199SXin Li   printf ("Line: '%s'\n", line);
389*9c5db199SXin Li 
390*9c5db199SXin Li   if (fakemodem->echo) {
391*9c5db199SXin Li     rval = write (primaryfd, line, term);
392*9c5db199SXin Li     assert(term == rval);
393*9c5db199SXin Li     rval = write (primaryfd, "\r\n", 2);
394*9c5db199SXin Li     assert(2 == rval);
395*9c5db199SXin Li   }
396*9c5db199SXin Li 
397*9c5db199SXin Li   if (g_ascii_strncasecmp (line, "AT", 2) != 0) {
398*9c5db199SXin Li     if (line[0] == '\0')
399*9c5db199SXin Li       goto out;
400*9c5db199SXin Li     response = "ERROR";
401*9c5db199SXin Li     goto done;
402*9c5db199SXin Li   }
403*9c5db199SXin Li 
404*9c5db199SXin Li   response = NULL;
405*9c5db199SXin Li   next = line + 2;
406*9c5db199SXin Li 
407*9c5db199SXin Li   while (!response && *next) {
408*9c5db199SXin Li     for (i = 0 ; i < commands->len; i++) {
409*9c5db199SXin Li       GMatchInfo *info;
410*9c5db199SXin Li       if (g_regex_match (g_ptr_array_index (commands, i), next, 0, &info)) {
411*9c5db199SXin Li         gint start, end;
412*9c5db199SXin Li         gchar *cmd;
413*9c5db199SXin Li         g_match_info_fetch_pos (info, 1, &start, &end);
414*9c5db199SXin Li         cmd = g_strndup (next + start, end - start);
415*9c5db199SXin Li         response = handle_cmd (fakemodem, cmd);
416*9c5db199SXin Li         g_free (cmd);
417*9c5db199SXin Li         g_match_info_free (info);
418*9c5db199SXin Li         next += end;
419*9c5db199SXin Li         break;
420*9c5db199SXin Li       }
421*9c5db199SXin Li       g_match_info_free (info);
422*9c5db199SXin Li     }
423*9c5db199SXin Li     if (i == commands->len) {
424*9c5db199SXin Li       response = "ERROR";
425*9c5db199SXin Li       break;
426*9c5db199SXin Li     }
427*9c5db199SXin Li   }
428*9c5db199SXin Li 
429*9c5db199SXin Li 
430*9c5db199SXin Li done:
431*9c5db199SXin Li   if (fakemodem->verbose) {
432*9c5db199SXin Li     gchar *rstr;
433*9c5db199SXin Li     if (response == NULL)
434*9c5db199SXin Li       response = "OK";
435*9c5db199SXin Li     rstr = g_strdup_printf("\r\n%s\r\n", response);
436*9c5db199SXin Li     rval = write (primaryfd, rstr, strlen (rstr));
437*9c5db199SXin Li     assert(strlen(rstr) == rval);
438*9c5db199SXin Li     g_free (rstr);
439*9c5db199SXin Li   } else {
440*9c5db199SXin Li     gchar *rstr;
441*9c5db199SXin Li     rstr = g_strdup_printf("%s\n", response);
442*9c5db199SXin Li     rval = write (primaryfd, rstr, strlen (rstr));
443*9c5db199SXin Li     assert(strlen(rstr) == rval);
444*9c5db199SXin Li     g_free (rstr);
445*9c5db199SXin Li   }
446*9c5db199SXin Li 
447*9c5db199SXin Li out:
448*9c5db199SXin Li   g_free (line);
449*9c5db199SXin Li   return TRUE;
450*9c5db199SXin Li }
451*9c5db199SXin Li 
452*9c5db199SXin Li static const gchar *
handle_cmd(FakeModem * fakemodem,const gchar * cmd)453*9c5db199SXin Li handle_cmd(FakeModem *fakemodem, const gchar *cmd)
454*9c5db199SXin Li {
455*9c5db199SXin Li   guint i;
456*9c5db199SXin Li   Pattern *pat = NULL;
457*9c5db199SXin Li 
458*9c5db199SXin Li   printf (" Cmd:  '%s'\n", cmd);
459*9c5db199SXin Li 
460*9c5db199SXin Li   if (toupper (cmd[0]) >= 'A' && toupper (cmd[0]) <= 'Z') {
461*9c5db199SXin Li     switch (toupper (cmd[0])) {
462*9c5db199SXin Li       case 'E':
463*9c5db199SXin Li         if (cmd[1] == '0')
464*9c5db199SXin Li           fakemodem->echo = FALSE;
465*9c5db199SXin Li         else if (cmd[1] == '1')
466*9c5db199SXin Li           fakemodem->echo = TRUE;
467*9c5db199SXin Li         else
468*9c5db199SXin Li           return "ERROR";
469*9c5db199SXin Li         return "OK";
470*9c5db199SXin Li       case 'V':
471*9c5db199SXin Li         if (cmd[1] == '0')
472*9c5db199SXin Li           fakemodem->verbose = FALSE;
473*9c5db199SXin Li         else if (cmd[1] == '1')
474*9c5db199SXin Li           fakemodem->verbose = TRUE;
475*9c5db199SXin Li         else
476*9c5db199SXin Li           return "ERROR";
477*9c5db199SXin Li         return "OK";
478*9c5db199SXin Li       case 'Z':
479*9c5db199SXin Li         fakemodem->echo = TRUE;
480*9c5db199SXin Li         fakemodem->verbose = TRUE;
481*9c5db199SXin Li         return "OK";
482*9c5db199SXin Li     }
483*9c5db199SXin Li   }
484*9c5db199SXin Li 
485*9c5db199SXin Li   for (i = 0 ; i < fakemodem->patterns->len; i++) {
486*9c5db199SXin Li     pat = (Pattern *)g_ptr_array_index (fakemodem->patterns, i);
487*9c5db199SXin Li     if (g_regex_match (pat->command, cmd, 0, NULL)) {
488*9c5db199SXin Li       break;
489*9c5db199SXin Li     }
490*9c5db199SXin Li   }
491*9c5db199SXin Li 
492*9c5db199SXin Li   if (i == fakemodem->patterns->len)
493*9c5db199SXin Li     return "ERROR";
494*9c5db199SXin Li 
495*9c5db199SXin Li   if (pat->reply && pat->reply[0]) {
496*9c5db199SXin Li     int rval;
497*9c5db199SXin Li     printf (" Reply: '%s'\n", pat->reply);
498*9c5db199SXin Li     rval = write (primaryfd, pat->reply, strlen (pat->reply));
499*9c5db199SXin Li     assert(strlen(pat->reply) == rval);
500*9c5db199SXin Li     rval = write (primaryfd, "\r\n", 2);
501*9c5db199SXin Li     assert(2 == rval);
502*9c5db199SXin Li   }
503*9c5db199SXin Li 
504*9c5db199SXin Li   return pat->responsetext; /* NULL implies "OK" and keep processing */
505*9c5db199SXin Li }
506*9c5db199SXin Li 
507*9c5db199SXin Li 
508*9c5db199SXin Li static gboolean
send_unsolicited(FakeModem * fakemodem,const gchar * text)509*9c5db199SXin Li send_unsolicited (FakeModem *fakemodem, const gchar* text)
510*9c5db199SXin Li {
511*9c5db199SXin Li   int rval;
512*9c5db199SXin Li 
513*9c5db199SXin Li   rval = write (primaryfd, "\r\n", 2);
514*9c5db199SXin Li   rval = write (primaryfd, text, strlen (text));
515*9c5db199SXin Li   assert(strlen(text) == rval);
516*9c5db199SXin Li   rval = write (primaryfd, "\r\n", 2);
517*9c5db199SXin Li   assert(2 == rval);
518*9c5db199SXin Li 
519*9c5db199SXin Li   return TRUE;
520*9c5db199SXin Li }
521*9c5db199SXin Li 
522*9c5db199SXin Li static gboolean
set_response(FakeModem * fakemodem,const gchar * command,const gchar * reply,const gchar * response)523*9c5db199SXin Li set_response (FakeModem *fakemodem,
524*9c5db199SXin Li               const gchar* command,
525*9c5db199SXin Li               const gchar* reply,
526*9c5db199SXin Li               const gchar* response)
527*9c5db199SXin Li {
528*9c5db199SXin Li   int i;
529*9c5db199SXin Li   Pattern *pat;
530*9c5db199SXin Li 
531*9c5db199SXin Li   if (strlen (response) == 0)
532*9c5db199SXin Li     response = "OK";
533*9c5db199SXin Li 
534*9c5db199SXin Li   for (i = 0 ; i < fakemodem->patterns->len; i++) {
535*9c5db199SXin Li     pat = (Pattern *)g_ptr_array_index (fakemodem->patterns, i);
536*9c5db199SXin Li     if (strcmp (g_regex_get_pattern (pat->command), command) == 0) {
537*9c5db199SXin Li       g_free (pat->reply);
538*9c5db199SXin Li       pat->reply = g_strdup (reply);
539*9c5db199SXin Li       g_free (pat->responsetext);
540*9c5db199SXin Li       pat->responsetext = g_strdup (response);
541*9c5db199SXin Li       break;
542*9c5db199SXin Li     }
543*9c5db199SXin Li   }
544*9c5db199SXin Li 
545*9c5db199SXin Li   if (i == fakemodem->patterns->len) {
546*9c5db199SXin Li     GError *error = NULL;
547*9c5db199SXin Li     pat = g_malloc (sizeof (*pat));
548*9c5db199SXin Li     pat->command = g_regex_new (command,
549*9c5db199SXin Li                                 G_REGEX_ANCHORED |
550*9c5db199SXin Li                                 G_REGEX_CASELESS |
551*9c5db199SXin Li                                 G_REGEX_RAW |
552*9c5db199SXin Li                                 G_REGEX_OPTIMIZE,
553*9c5db199SXin Li                                 0,
554*9c5db199SXin Li                                 &error);
555*9c5db199SXin Li     if (pat->command == NULL) {
556*9c5db199SXin Li       printf ("error: %s\n", error->message);
557*9c5db199SXin Li       g_free (pat);
558*9c5db199SXin Li       return FALSE;
559*9c5db199SXin Li     }
560*9c5db199SXin Li     pat->responsetext = g_strdup (response);
561*9c5db199SXin Li     pat->reply = g_strdup (reply);
562*9c5db199SXin Li     g_ptr_array_add (fakemodem->patterns, pat);
563*9c5db199SXin Li   }
564*9c5db199SXin Li 
565*9c5db199SXin Li   return TRUE;
566*9c5db199SXin Li }
567*9c5db199SXin Li 
568*9c5db199SXin Li static gboolean
remove_response(FakeModem * fakemodem,const gchar * command)569*9c5db199SXin Li remove_response (FakeModem* fakemodem, const gchar* command)
570*9c5db199SXin Li {
571*9c5db199SXin Li   int i;
572*9c5db199SXin Li   gboolean found;
573*9c5db199SXin Li   Pattern *pat;
574*9c5db199SXin Li 
575*9c5db199SXin Li   found = FALSE;
576*9c5db199SXin Li   for (i = 0 ; i < fakemodem->patterns->len; i++) {
577*9c5db199SXin Li     pat = (Pattern *)g_ptr_array_index (fakemodem->patterns, i);
578*9c5db199SXin Li     if (strcmp (g_regex_get_pattern (pat->command), command) == 0) {
579*9c5db199SXin Li       g_ptr_array_remove_index (fakemodem->patterns, i);
580*9c5db199SXin Li       found = TRUE;
581*9c5db199SXin Li       break;
582*9c5db199SXin Li     }
583*9c5db199SXin Li   }
584*9c5db199SXin Li 
585*9c5db199SXin Li   return found;
586*9c5db199SXin Li }
587