AT: Added AT response parsing engine
[hayes-ril.git] / at.c
1 /**
2  * This file is part of hayes-ril.
3  *
4  * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
5  *
6  * Based on htcgeneric-ril, reference-ril:
7  * Copyright 2006-2011, htcgeneric-ril contributors
8  * Copyright 2006, The Android Open Source Project
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *     http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  */
22
23 #include <string.h>
24 #include <ctype.h>
25
26 #define LOG_TAG "RIL-AT"
27 #include <utils/Log.h>
28
29 #include <hayes-ril.h>
30
31 int at_strings_compare(char *major, char *minor)
32 {
33         if(strncmp(major, minor, strlen(major)) == 0)
34                 return 1;
35         else
36                 return 0;
37 }
38
39 void at_string_clean(char *string, int length)
40 {
41         int i=0;
42
43         for(i=length-1; i >= 0 ; i--) {
44                 if(!isprint(string[i]))
45                         string[i] = '\0';
46         }
47 }
48
49 int at_line_parse_status(char *data, int length, char **error_p)
50 {
51         // TODO: defined rc, fill error_p
52
53         *error_p = NULL;
54
55         if(at_strings_compare("OK", data))
56                 return 1;
57
58         if(at_strings_compare("CONNECT", data))
59                 return 2;
60
61         if(at_strings_compare("ERROR", data))
62                 return -1;
63
64         // TODO: Other error codes
65
66         // TODO: undefined magic
67         return 0;
68 }
69
70 int at_line_parse_command_data(char *data, int length, char **response_command, char **response_data)
71 {
72         char *string = NULL;
73         int string_length = 0;
74         int close_your_eyes = 0;
75         int mark = 0;
76         int i;
77
78
79         for(i=0 ; i < length ; i++) {
80                 if(data[i] == ':' && mark == 0) {
81                         if(i > 0) {
82                                 data[i] = '\0';
83
84                                 string_length = i + 1;
85                                 string = strndup(data, string_length);
86
87                                 if(!isprint(string[0])) {
88                                         free(string);
89                                 } else {
90                                         *response_command = string;
91                                 }
92
93                                 mark = i + 1;
94                         }
95
96                         while(isspace(data[i+1])) {
97                                 mark = i + 2;
98                                 i++;
99                         }
100                 }
101
102                 if(data[i] == '"') {
103                         if(close_your_eyes & AT_PARSE_DOUBLE_QUOTE)
104                                 close_your_eyes &= ~AT_PARSE_DOUBLE_QUOTE;
105                         else
106                                 close_your_eyes |= AT_PARSE_DOUBLE_QUOTE;
107                 }
108
109                 if(data[i] == '\'') {
110                         if(close_your_eyes & AT_PARSE_SINGLE_QUOTE)
111                                 close_your_eyes &= ~AT_PARSE_SINGLE_QUOTE;
112                         else
113                                 close_your_eyes |= AT_PARSE_SINGLE_QUOTE;
114                 }
115
116                 // Found = or ? outside of any quotes: assume no data
117                 if(!close_your_eyes && (data[i] == '=' || data[i] == '?') && mark == 0) {
118                         data[i] = '\0';
119
120                         string_length = i + 1;
121                         string = strndup(data, string_length);
122
123                         if(!isprint(string[0])) {
124                                 free(string);
125                         } else {
126                                 *response_command = string;
127                         }
128
129                         return 0;
130                 }
131         }
132
133
134         if(length - mark > 0) {
135                 string_length = length - mark;
136                 string = strndup(data + mark, string_length);
137
138                 if(mark == 0) {
139                         *response_command = string;
140                         return 0;
141                 } else {
142                         *response_data = string;
143                         return string_length;
144                 }
145         }
146
147         return 0;
148 }
149
150 int at_responses_process_line(struct at_response ***responses_p, int responses_count, char *data, int length)
151 {
152         struct at_response **responses = NULL;
153
154         char *response_command = NULL;
155         char *response_data = NULL;
156         int response_data_length = 0;
157         char *response_error = NULL;
158         int response_status = 0;
159
160         char **response_previous_data = NULL;
161         int response_data_count = 0;
162
163         int index = -1;
164         int count = 0;
165         int i;
166
167         if(responses_p == NULL || data == NULL || length < 0) {
168                 LOGE("Failed to process AT response: wrong arguments!");
169                 return 0;
170         }
171
172         responses = *responses_p;
173
174         // Parse status
175         response_status = at_line_parse_status(data, length, &response_error);
176
177         if(response_status != 0) {
178                 if(responses_count > 0 && responses != NULL) {
179                         for(i=responses_count-1 ; i >= 0; i--) {
180                                 if(responses[i]->status == 0) {
181                                         responses[i]->status = response_status;
182                                         responses[i]->error = response_error;
183
184                                         // Do not alloc a new response
185                                         if(index == -1)
186                                                 index = i;
187                                 }
188                         }
189                 }
190
191                 // Alloc a new response
192                 if(index == -1) {
193                         // Index is the request index in the requests array
194                         index = responses_count;
195                         // Count is the total count of requests in the array
196                         count = index + 1;
197
198                         // Alloc the array with the new size
199                         *responses_p = malloc(sizeof(struct at_response *) * count);
200
201                         // Copy and free previous data
202                         if(responses != NULL) {
203                                 memcpy(*responses_p, responses, sizeof(struct at_response *) * responses_count);
204                                 free(responses);
205                         }
206
207                         responses = *responses_p;
208
209                         // Alloc new structure and copy obtained data
210                         responses[index] = calloc(1, sizeof(struct at_response));
211                         responses[index]->status = response_status;
212                         responses[index]->error = response_error;
213                 }
214         } else {
215                 // Parse command and data
216                 response_data_length = at_line_parse_command_data(data, length, &response_command, &response_data);
217
218                 if(response_command == NULL) {
219                         LOGE("Failed to parse command!");
220                         return responses_count;
221                 }
222
223                 at_string_clean(response_command, strlen(response_command) + 1);
224
225                 if(responses_count > 0 && responses != NULL) {
226                         for(i=responses_count-1 ; i >= 0; i--) {
227                                 if(responses[i]->command != NULL) {
228                                         if(strcmp(responses[i]->command, response_command) == 0)
229                                                 // Do not alloc a new response
230                                                 index = i;
231                                 }
232                         }
233                 }
234
235                 // Alloc a new response
236                 if(index == -1) {
237                         // Index is the request index in the requests array
238                         index = responses_count;
239                         // Count is the total count of requests in the array
240                         count = index + 1;
241
242                         // Alloc the array with the new size
243                         *responses_p = malloc(sizeof(struct at_response *) * count);
244
245                         // Copy and free previous data
246                         if(responses != NULL) {
247                                 memcpy(*responses_p, responses, sizeof(struct at_response *) * responses_count);
248                                 free(responses);
249                         }
250
251                         responses = *responses_p;
252
253                         // Alloc new structure and copy obtained data
254                         responses[index] = calloc(1, sizeof(struct at_response));
255                         responses[index]->command = response_command;
256                 }
257
258                 if(response_data_length > 0 && response_data != NULL) {
259                         at_string_clean(response_data, response_data_length);
260
261                         response_previous_data = responses[index]->data;
262
263                         // Data count is the total count of elements in the request data
264                         response_data_count = responses[index]->data_count + 1;
265
266                         // Alloc the array with the new size
267                         responses[index]->data = malloc(sizeof(char *) * response_data_count);
268
269                         // Copy and free previous data
270                         if(response_previous_data != NULL) {
271                                 memcpy(responses[index]->data, response_previous_data, sizeof(char *) * responses[index]->data_count);
272                                 free(response_previous_data);
273                         }
274
275                         responses[index]->data_count = response_data_count;
276                         responses[index]->data[response_data_count - 1] = response_data;
277                 }
278         }
279
280         return count > responses_count ? count : responses_count;
281 }
282
283 int at_responses_process(struct at_response ***responses_p, char *data, int length)
284 {
285         int responses_count = 0;
286         int count = 0;
287
288         char *string = NULL;
289         int string_length = 0;
290         int mark = 0;
291         int i;
292
293         if(responses_p == NULL || data == NULL || length < 0) {
294                 LOGE("Failed to process AT response: wrong arguments!");
295                 return 0;
296         }
297
298         for(i=0 ; i < length ; i++) {
299                 if(data[i] == '\r' || data[i] == ';') {
300                         if(i - mark > 0) {
301                                 data[i] = '\0';
302
303                                 string_length = i - mark + 1;
304                                 string = strndup(data + mark, string_length);
305
306                                 if(!isprint(string[0])) {
307                                         free(string);
308                                 }
309                                 else {
310                                         count = at_responses_process_line(responses_p, responses_count, string, string_length);
311                                         if(count > responses_count)
312                                                 responses_count = count;
313
314                                         free(string);
315                                 }
316
317                                 mark = i + 1;
318                         }
319
320                         while(isspace(data[i+1])) {
321                                 mark = i + 2;
322                                 i++;
323                         }
324                 }
325         }
326
327         if(length - mark > 0) {
328                 for(i=mark ; i < length ; i++)
329                         if(!isprint(data[i]))
330                                 break;
331
332                 if(i - mark > 0) {
333                         string_length = i - mark + 1;
334                         string = calloc(1, string_length);
335
336                         memcpy(string, data + mark, string_length - 1);
337                         string[string_length - 1] = '\0';
338
339                         count = at_responses_process_line(responses_p, responses_count, string , string_length);
340                         if(count > responses_count)
341                                 responses_count = count;
342
343                         free(string);
344                 }
345         }
346
347         return responses_count;
348 }
349
350 void at_responses_dump(struct at_response **responses, int responses_count)
351 {
352         int i;
353
354         LOGD("== AT RESPONSES DUMP ==\n");
355         for(i=0 ; i < responses_count ; i++) {
356                 LOGD("#%d command=%s status=%d error=%s\n", i, responses[i]->command, responses[i]->status, responses[i]->error);
357                 if(responses[i]->data_count > 0) {
358                         int j;
359                         for(j=0 ; j < responses[i]->data_count ; j++) {
360                                 LOGD("    #%d data='%s'\n", j, responses[i]->data[j]);
361                         }
362                 }
363         }
364 }
365
366 void at_responses_free(struct at_response **responses, int responses_count)
367 {
368         int i, j;
369
370         for(i=0 ; i < responses_count ; i++) {
371                 if(responses[i]->data_count > 0 && responses[i]->data != NULL) {
372                         for(j=0 ; j < responses[i]->data_count ; j++) {
373                                 if(responses[i]->data[j] != NULL) {
374                                         free(responses[i]->data[j]);
375                                         responses[i]->data[j] = NULL;
376                                 }
377                         }
378
379                         free(responses[i]->data);
380                         responses[i]->data_count = 0;
381                 }
382
383                 if(responses[i]->command != NULL) {
384                         free(responses[i]->command);
385                         responses[i]->command = NULL;
386                 }
387
388                 if(responses[i]->error != NULL) {
389                         free(responses[i]->error);
390                         responses[i]->error = NULL;
391                 }
392         }
393 }