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