packaging: Bump to 1.17
[platform/upstream/ofono.git] / gatchat / gatsyntax.c
1 /*
2  *
3  *  AT chat library with GLib integration
4  *
5  *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <glib.h>
27
28 #include "gatsyntax.h"
29
30 enum GSMV1_STATE {
31         GSMV1_STATE_IDLE = 0,
32         GSMV1_STATE_INITIAL_CR,
33         GSMV1_STATE_INITIAL_LF,
34         GSMV1_STATE_RESPONSE,
35         GSMV1_STATE_RESPONSE_STRING,
36         GSMV1_STATE_TERMINATOR_CR,
37         GSMV1_STATE_GUESS_MULTILINE_RESPONSE,
38         GSMV1_STATE_MULTILINE_RESPONSE,
39         GSMV1_STATE_MULTILINE_TERMINATOR_CR,
40         GSMV1_STATE_PDU_CHECK_EXTRA_CR,
41         GSMV1_STATE_PDU_CHECK_EXTRA_LF,
42         GSMV1_STATE_PDU,
43         GSMV1_STATE_PDU_CR,
44         GSMV1_STATE_PROMPT,
45         GSMV1_STATE_ECHO,
46         GSMV1_STATE_PPP_DATA,
47         GSMV1_STATE_SHORT_PROMPT,
48         GSMV1_STATE_SHORT_PROMPT_CR,
49 };
50
51 enum GSM_PERMISSIVE_STATE {
52         GSM_PERMISSIVE_STATE_IDLE = 0,
53         GSM_PERMISSIVE_STATE_RESPONSE,
54         GSM_PERMISSIVE_STATE_RESPONSE_STRING,
55         GSM_PERMISSIVE_STATE_GUESS_PDU,
56         GSM_PERMISSIVE_STATE_PDU,
57         GSM_PERMISSIVE_STATE_PROMPT,
58         GSM_PERMISSIVE_STATE_GUESS_SHORT_PROMPT,
59         GSM_PERMISSIVE_STATE_SHORT_PROMPT,
60 };
61
62 static void gsmv1_hint(GAtSyntax *syntax, GAtSyntaxExpectHint hint)
63 {
64         switch (hint) {
65         case G_AT_SYNTAX_EXPECT_PDU:
66                 syntax->state = GSMV1_STATE_PDU_CHECK_EXTRA_CR;
67                 break;
68         case G_AT_SYNTAX_EXPECT_MULTILINE:
69                 syntax->state = GSMV1_STATE_GUESS_MULTILINE_RESPONSE;
70                 break;
71         case G_AT_SYNTAX_EXPECT_SHORT_PROMPT:
72                 syntax->state = GSMV1_STATE_SHORT_PROMPT;
73                 break;
74         default:
75                 break;
76         };
77 }
78
79 static GAtSyntaxResult gsmv1_feed(GAtSyntax *syntax,
80                                         const char *bytes, gsize *len)
81 {
82         gsize i = 0;
83         GAtSyntaxResult res = G_AT_SYNTAX_RESULT_UNSURE;
84
85         while (i < *len) {
86                 char byte = bytes[i];
87
88                 switch (syntax->state) {
89                 case GSMV1_STATE_IDLE:
90                         if (byte == '\r')
91                                 syntax->state = GSMV1_STATE_INITIAL_CR;
92                         else if (byte == '~')
93                                 syntax->state = GSMV1_STATE_PPP_DATA;
94                         else
95                                 syntax->state = GSMV1_STATE_ECHO;
96                         break;
97
98                 case GSMV1_STATE_INITIAL_CR:
99                         if (byte == '\n')
100                                 syntax->state = GSMV1_STATE_INITIAL_LF;
101                         else if (byte == '\r') {
102                                 syntax->state = GSMV1_STATE_IDLE;
103                                 return G_AT_SYNTAX_RESULT_UNRECOGNIZED;
104                         } else
105                                 syntax->state = GSMV1_STATE_ECHO;
106                         break;
107
108                 case GSMV1_STATE_INITIAL_LF:
109                         if (byte == '\r')
110                                 syntax->state = GSMV1_STATE_TERMINATOR_CR;
111                         else if (byte == '>')
112                                 syntax->state = GSMV1_STATE_PROMPT;
113                         else if (byte == '"')
114                                 syntax->state = GSMV1_STATE_RESPONSE_STRING;
115                         else
116                                 syntax->state = GSMV1_STATE_RESPONSE;
117                         break;
118
119                 case GSMV1_STATE_RESPONSE:
120                         if (byte == '\r')
121                                 syntax->state = GSMV1_STATE_TERMINATOR_CR;
122                         else if (byte == '"')
123                                 syntax->state = GSMV1_STATE_RESPONSE_STRING;
124                         break;
125
126                 case GSMV1_STATE_RESPONSE_STRING:
127                         if (byte == '"')
128                                 syntax->state = GSMV1_STATE_RESPONSE;
129                         break;
130
131                 case GSMV1_STATE_TERMINATOR_CR:
132                         syntax->state = GSMV1_STATE_IDLE;
133
134                         if (byte == '\n') {
135                                 i += 1;
136                                 res = G_AT_SYNTAX_RESULT_LINE;
137                         } else
138                                 res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
139
140                         goto out;
141
142                 case GSMV1_STATE_GUESS_MULTILINE_RESPONSE:
143                         if (byte == '\r')
144                                 syntax->state = GSMV1_STATE_INITIAL_CR;
145                         else
146                                 syntax->state = GSMV1_STATE_MULTILINE_RESPONSE;
147                         break;
148
149                 case GSMV1_STATE_MULTILINE_RESPONSE:
150                         if (byte == '\r')
151                                 syntax->state = GSMV1_STATE_MULTILINE_TERMINATOR_CR;
152                         break;
153
154                 case GSMV1_STATE_MULTILINE_TERMINATOR_CR:
155                         syntax->state = GSMV1_STATE_IDLE;
156
157                         if (byte == '\n') {
158                                 i += 1;
159                                 res = G_AT_SYNTAX_RESULT_MULTILINE;
160                         } else
161                                 res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
162
163                         goto out;
164
165                 /* Some 27.007 compliant modems still get this wrong.  They
166                  * insert an extra CRLF between the command and he PDU,
167                  * in effect making them two separate lines.  We try to
168                  * handle this case gracefully
169                  */
170                 case GSMV1_STATE_PDU_CHECK_EXTRA_CR:
171                         if (byte == '\r')
172                                 syntax->state = GSMV1_STATE_PDU_CHECK_EXTRA_LF;
173                         else
174                                 syntax->state = GSMV1_STATE_PDU;
175                         break;
176
177                 case GSMV1_STATE_PDU_CHECK_EXTRA_LF:
178                         res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
179                         syntax->state = GSMV1_STATE_PDU;
180
181                         if (byte == '\n')
182                                 i += 1;
183
184                         goto out;
185
186                 case GSMV1_STATE_PDU:
187                         if (byte == '\r')
188                                 syntax->state = GSMV1_STATE_PDU_CR;
189                         break;
190
191                 case GSMV1_STATE_PDU_CR:
192                         syntax->state = GSMV1_STATE_IDLE;
193
194                         if (byte == '\n') {
195                                 i += 1;
196                                 res = G_AT_SYNTAX_RESULT_PDU;
197                         } else
198                                 res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
199
200                         goto out;
201
202                 case GSMV1_STATE_PROMPT:
203                         if (byte == ' ') {
204                                 syntax->state = GSMV1_STATE_IDLE;
205                                 i += 1;
206                                 res = G_AT_SYNTAX_RESULT_PROMPT;
207                                 goto out;
208                         }
209
210                         syntax->state = GSMV1_STATE_RESPONSE;
211                         return G_AT_SYNTAX_RESULT_UNSURE;
212
213                 case GSMV1_STATE_ECHO:
214                         /* This handles the case of echo of the PDU terminated
215                          * by CtrlZ character
216                          */
217                         if (byte == 26 || byte == '\r') {
218                                 syntax->state = GSMV1_STATE_IDLE;
219                                 res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
220                                 i += 1;
221                                 goto out;
222                         }
223
224                         break;
225
226                 case GSMV1_STATE_PPP_DATA:
227                         if (byte == '~') {
228                                 syntax->state = GSMV1_STATE_IDLE;
229                                 res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
230                                 i += 1;
231                                 goto out;
232                         }
233
234                         break;
235
236                 case GSMV1_STATE_SHORT_PROMPT:
237                         if (byte == '\r')
238                                 syntax->state = GSMV1_STATE_SHORT_PROMPT_CR;
239                         else
240                                 syntax->state = GSMV1_STATE_ECHO;
241
242                         break;
243
244                 case GSMV1_STATE_SHORT_PROMPT_CR:
245                         if (byte == '\n') {
246                                 syntax->state = GSMV1_STATE_IDLE;
247                                 i += 1;
248                                 res = G_AT_SYNTAX_RESULT_PROMPT;
249                                 goto out;
250                         }
251
252                         syntax->state = GSMV1_STATE_RESPONSE;
253                         return G_AT_SYNTAX_RESULT_UNSURE;
254
255                 default:
256                         break;
257                 };
258
259                 i += 1;
260         }
261
262 out:
263         *len = i;
264         return res;
265 }
266
267 static void gsm_permissive_hint(GAtSyntax *syntax, GAtSyntaxExpectHint hint)
268 {
269         if (hint == G_AT_SYNTAX_EXPECT_PDU)
270                 syntax->state = GSM_PERMISSIVE_STATE_GUESS_PDU;
271         else if (hint == G_AT_SYNTAX_EXPECT_SHORT_PROMPT)
272                 syntax->state = GSM_PERMISSIVE_STATE_GUESS_SHORT_PROMPT;
273 }
274
275 static GAtSyntaxResult gsm_permissive_feed(GAtSyntax *syntax,
276                                                 const char *bytes, gsize *len)
277 {
278         gsize i = 0;
279         GAtSyntaxResult res = G_AT_SYNTAX_RESULT_UNSURE;
280
281         while (i < *len) {
282                 char byte = bytes[i];
283
284                 switch (syntax->state) {
285                 case GSM_PERMISSIVE_STATE_IDLE:
286                         if (byte == '\r' || byte == '\n')
287                                 /* ignore */;
288                         else if (byte == '>')
289                                 syntax->state = GSM_PERMISSIVE_STATE_PROMPT;
290                         else if (byte == '"')
291                                 syntax->state =
292                                         GSM_PERMISSIVE_STATE_RESPONSE_STRING;
293                         else
294                                 syntax->state = GSM_PERMISSIVE_STATE_RESPONSE;
295                         break;
296
297                 case GSM_PERMISSIVE_STATE_RESPONSE:
298                         if (byte == '\r') {
299                                 syntax->state = GSM_PERMISSIVE_STATE_IDLE;
300
301                                 i += 1;
302                                 res = G_AT_SYNTAX_RESULT_LINE;
303                                 goto out;
304                         } else if (byte == '"')
305                                 syntax->state =
306                                         GSM_PERMISSIVE_STATE_RESPONSE_STRING;
307                         break;
308
309                 case GSM_PERMISSIVE_STATE_RESPONSE_STRING:
310                         if (byte == '"')
311                                 syntax->state = GSM_PERMISSIVE_STATE_RESPONSE;
312                         break;
313
314                 case GSM_PERMISSIVE_STATE_GUESS_PDU:
315                         if (byte != '\r' && byte != '\n')
316                                 syntax->state = GSM_PERMISSIVE_STATE_PDU;
317                         break;
318
319                 case GSM_PERMISSIVE_STATE_PDU:
320                         if (byte == '\r') {
321                                 syntax->state = GSM_PERMISSIVE_STATE_IDLE;
322
323                                 i += 1;
324                                 res = G_AT_SYNTAX_RESULT_PDU;
325                                 goto out;
326                         }
327                         break;
328
329                 case GSM_PERMISSIVE_STATE_PROMPT:
330                         if (byte == ' ') {
331                                 syntax->state = GSM_PERMISSIVE_STATE_IDLE;
332                                 i += 1;
333                                 res = G_AT_SYNTAX_RESULT_PROMPT;
334                                 goto out;
335                         }
336
337                         syntax->state = GSM_PERMISSIVE_STATE_RESPONSE;
338                         return G_AT_SYNTAX_RESULT_UNSURE;
339
340                 case GSM_PERMISSIVE_STATE_GUESS_SHORT_PROMPT:
341                         if (byte == '\n')
342                                 /* ignore */;
343                         else if (byte == '\r')
344                                 syntax->state =
345                                         GSM_PERMISSIVE_STATE_SHORT_PROMPT;
346                         else
347                                 syntax->state = GSM_PERMISSIVE_STATE_RESPONSE;
348                         break;
349
350                 case GSM_PERMISSIVE_STATE_SHORT_PROMPT:
351                         if (byte == '\n') {
352                                 syntax->state = GSM_PERMISSIVE_STATE_IDLE;
353                                 i += 1;
354                                 res = G_AT_SYNTAX_RESULT_PROMPT;
355                                 goto out;
356                         }
357
358                         syntax->state = GSM_PERMISSIVE_STATE_RESPONSE;
359                         return G_AT_SYNTAX_RESULT_UNSURE;
360
361                 default:
362                         break;
363                 };
364
365                 i += 1;
366         }
367
368 out:
369         *len = i;
370         return res;
371 }
372
373 GAtSyntax *g_at_syntax_new_full(GAtSyntaxFeedFunc feed,
374                                         GAtSyntaxSetHintFunc hint,
375                                         int initial_state)
376 {
377         GAtSyntax *syntax;
378
379         syntax = g_new0(GAtSyntax, 1);
380
381         syntax->feed = feed;
382         syntax->set_hint = hint;
383         syntax->state = initial_state;
384         syntax->ref_count = 1;
385
386         return syntax;
387 }
388
389
390 GAtSyntax *g_at_syntax_new_gsmv1(void)
391 {
392         return g_at_syntax_new_full(gsmv1_feed, gsmv1_hint, GSMV1_STATE_IDLE);
393 }
394
395 GAtSyntax *g_at_syntax_new_gsm_permissive(void)
396 {
397         return g_at_syntax_new_full(gsm_permissive_feed, gsm_permissive_hint,
398                                         GSM_PERMISSIVE_STATE_IDLE);
399 }
400
401 GAtSyntax *g_at_syntax_ref(GAtSyntax *syntax)
402 {
403         if (syntax == NULL)
404                 return NULL;
405
406         g_atomic_int_inc(&syntax->ref_count);
407
408         return syntax;
409 }
410
411 void g_at_syntax_unref(GAtSyntax *syntax)
412 {
413         gboolean is_zero;
414
415         if (syntax == NULL)
416                 return;
417
418         is_zero = g_atomic_int_dec_and_test(&syntax->ref_count);
419
420         if (is_zero)
421                 g_free(syntax);
422 }