cd15fbf7dc0e904823f1283a2b6da4e41c1cb4b7
[project/uqmi.git] / commands-wms.c
1 #include "qmi-message.h"
2
3 static void cmd_wms_list_messages_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
4 {
5         struct qmi_wms_list_messages_response res;
6         int i, len = 0;
7
8         qmi_parse_wms_list_messages_response(msg, &res);
9         blobmsg_alloc_string_buffer(&status, "messages", 1);
10         for (i = 0; i < res.data.message_list_n; i++) {
11                 len += sprintf(blobmsg_realloc_string_buffer(&status, len + 12) + len,
12                                " %d" + (len ? 0 : 1),
13                                            res.data.message_list[i].memory_index);
14         }
15         blobmsg_add_string_buffer(&status);
16 }
17
18 static enum qmi_cmd_result
19 cmd_wms_list_messages_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
20 {
21         static struct qmi_wms_list_messages_request mreq = {
22                 QMI_INIT(storage_type, QMI_WMS_STORAGE_TYPE_UIM),
23                 QMI_INIT(message_tag, QMI_WMS_MESSAGE_TAG_TYPE_MT_NOT_READ),
24         };
25
26         qmi_set_wms_list_messages_request(msg, &mreq);
27
28         return QMI_CMD_REQUEST;
29 }
30
31 static int
32 put_unicode_char(char *dest, uint16_t c)
33 {
34         if (c < 0x80) {
35                 *dest = c;
36                 return 1;
37         } else if (c < 0x800) {
38                 *(dest++) = 0xc0 | ((c >> 6) & 0x1f);
39                 *dest = 0x80 | (c & 0x3f);
40                 return 2;
41         } else {
42                 *(dest++) = 0xe0 | ((c >> 12) & 0xf);
43                 *(dest++) = 0x80 | ((c >> 6) & 0x3f);
44                 *dest = 0x80 | (c & 0x3f);
45                 return 3;
46         }
47 }
48
49
50 static int
51 pdu_decode_7bit_char(char *dest, int len, char c, bool *escape)
52 {
53         uint16_t conv_0x20[] = {
54                 0x0040, 0x00A3, 0x0024, 0x00A5, 0x00E8, 0x00E9, 0x00F9, 0x00EC,
55                 0x00F2, 0x00E7, 0x000A, 0x00D8, 0x00F8, 0x000D, 0x00C5, 0x00E5,
56                 0x0394, 0x005F, 0x03A6, 0x0393, 0x039B, 0x03A9, 0x03A0, 0x03A8,
57                 0x03A3, 0x0398, 0x039E, 0x00A0, 0x00C6, 0x00E6, 0x00DF, 0x00C9,
58         };
59         uint16_t conv_0x5b[] = {
60                 0x00C4, 0x00D6, 0x00D1, 0x00DC, 0x00A7, 0x00BF,
61         };
62         uint16_t conv_0x7b[] = {
63                 0x00E4, 0x00F6, 0x00F1, 0x00FC, 0x00E0
64         };
65         int cur_len = 0;
66         uint16_t outc;
67
68         fprintf(stderr, " %02x", c);
69         dest += len;
70         if (*escape) {
71                 switch(c) {
72                 case 0x0A:
73                         *dest = 0x0C;
74                         return 1;
75                 case 0x14:
76                         *dest = 0x5E;
77                         return 1;
78                 case 0x28:
79                         *dest = 0x7B;
80                         return 1;
81                 case 0x29:
82                         *dest = 0x7D;
83                         return 1;
84                 case 0x2F:
85                         *dest = 0x5C;
86                         return 1;
87                 case 0x3C:
88                         *dest = 0x5B;
89                         return 1;
90                 case 0x3D:
91                         *dest = 0x7E;
92                         return 1;
93                 case 0x3E:
94                         *dest = 0x5D;
95                         return 1;
96                 case 0x40:
97                         *dest = 0x7C;
98                         return 1;
99                 case 0x65:
100                         outc = 0x20AC;
101                         goto out;
102                 case 0x1B:
103                         goto normal;
104                 default:
105                         /* invalid */
106                         *(dest++) = conv_0x20[0x1B];
107                         cur_len++;
108                         goto normal;
109                 }
110         }
111
112         if (c == 0x1b) {
113                 *escape = true;
114                 return 0;
115         }
116
117 normal:
118         if (c < 0x20)
119                 outc = conv_0x20[(int) c];
120         else if (c == 0x40)
121                 outc = 0x00A1;
122         else if (c >= 0x5b && c <= 0x60)
123                 outc = conv_0x5b[c - 0x5b];
124         else if (c >= 0x7b && c <= 0x7f)
125                 outc = conv_0x7b[c - 0x7b];
126         else
127                 outc = c;
128
129 out:
130         return cur_len + put_unicode_char(dest, outc);
131 }
132
133 static int
134 pdu_decode_7bit_str(char *dest, const unsigned char *data, int data_len, int bit_offset)
135 {
136         bool escape = false;
137         int len = 0;
138         int i;
139
140         fprintf(stderr, "Raw text:");
141         for (i = 0; i < data_len; i++) {
142                 int pos = (i + bit_offset) % 7;
143
144                 if (pos == 0) {
145                         len += pdu_decode_7bit_char(dest, len, data[i] & 0x7f, &escape);
146                 } else {
147                         if (i)
148                                 len += pdu_decode_7bit_char(dest, len,
149                                                             (data[i - 1] >> (7 + 1 - pos)) |
150                                                             ((data[i] << pos) & 0x7f), &escape);
151
152                         if (pos == 6)
153                                 len += pdu_decode_7bit_char(dest, len, (data[i] >> 1) & 0x7f,
154                                                             &escape);
155                 }
156         }
157         dest[len] = 0;
158         fprintf(stderr, "\n");
159         return len;
160 }
161
162 static void decode_udh(const unsigned char *data)
163 {
164         const unsigned char *end;
165         unsigned int type, len;
166
167         len = *(data++);
168         end = data + len;
169         while (data < end) {
170                 const unsigned char *val;
171
172                 type = data[0];
173                 len = data[1];
174                 val = &data[2];
175                 data += 2 + len;
176                 if (data > end)
177                         break;
178
179                 switch (type) {
180                 case 0:
181                         blobmsg_add_u32(&status, "concat_ref", (uint32_t) val[0]);
182                         blobmsg_add_u32(&status, "concat_part", (uint32_t) val[2] + 1);
183                         blobmsg_add_u32(&status, "concat_parts", (uint32_t) val[1]);
184                         break;
185                 default:
186                         break;
187                 }
188         }
189 }
190
191 static void decode_7bit_field(char *name, const unsigned char *data, int data_len, bool udh)
192 {
193         const unsigned char *udh_start;
194         char *dest;
195         int pos_offset = 0;
196
197         if (udh) {
198                 int len = data[0] + 1;
199
200                 udh_start = data;
201                 data += len;
202                 data_len -= len;
203                 pos_offset = len % 7;
204         }
205
206         dest = blobmsg_alloc_string_buffer(&status, name, 3 * (data_len * 8 / 7) + 2);
207         pdu_decode_7bit_str(dest, data, data_len, pos_offset);
208         blobmsg_add_string_buffer(&status);
209
210         if (udh)
211                 decode_udh(udh_start);
212 }
213
214 static char *pdu_add_semioctet(char *str, char val)
215 {
216         *str = '0' + (val & 0xf);
217         if (*str <= '9')
218                 str++;
219
220         *str = '0' + ((val >> 4) & 0xf);
221         if (*str <= '9')
222                 str++;
223
224         return str;
225 }
226
227 static void
228 pdu_decode_address(char *str, unsigned char *data, int len)
229 {
230         unsigned char toa;
231
232         toa = *(data++);
233         switch (toa & 0x70) {
234         case 0x50:
235                 pdu_decode_7bit_str(str, data, len, 0);
236                 return;
237         case 0x10:
238                 *(str++) = '+';
239                 /* fall through */
240         default:
241                 while (len--) {
242                         str = pdu_add_semioctet(str, *data);
243                         data++;
244                 }
245         }
246
247         *str = 0;
248 }
249
250 static void wms_decode_address(char *str, char *name, unsigned char *data, int len)
251 {
252         str = blobmsg_alloc_string_buffer(&status, name, len * 2 + 2);
253         pdu_decode_address(str, data, len);
254         blobmsg_add_string_buffer(&status);
255 }
256
257 static void cmd_wms_get_message_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
258 {
259         struct qmi_wms_raw_read_response res;
260         unsigned char *data, *end;
261         char *str;
262         int cur_len;
263         bool sent;
264         unsigned char first, dcs;
265
266         qmi_parse_wms_raw_read_response(msg, &res);
267         data = (unsigned char *) res.data.raw_message_data.raw_data;
268         end = data + res.data.raw_message_data.raw_data_n;
269
270         cur_len = *(data++);
271         if (data + cur_len >= end)
272                 return;
273
274         if (cur_len) {
275                 wms_decode_address(str, "smsc", data, cur_len - 1);
276                 data += cur_len;
277         }
278
279         if (data + 3 >= end)
280                 return;
281
282         first = *(data++);
283         sent = (first & 0x3) == 1;
284         if (sent)
285                 data++;
286
287         cur_len = *(data++);
288         if (data + cur_len >= end)
289                 return;
290
291         if (cur_len) {
292                 cur_len = (cur_len + 1) / 2;
293                 wms_decode_address(str, sent ? "receiver" : "sender", data, cur_len);
294                 data += cur_len + 1;
295         }
296
297         if (data + 3 >= end)
298                 return;
299
300         /* Protocol ID */
301         if (*(data++) != 0)
302                 return;
303
304         /* Data Encoding */
305         dcs = *(data++);
306
307         /* only 7-bit encoding supported for now */
308         if (dcs & 0x0c)
309                 return;
310
311         if (dcs & 0x10)
312                 blobmsg_add_u32(&status, "class", (dcs & 3));
313
314         if (sent) {
315                 /* Message validity */
316                 data++;
317         } else {
318                 if (data + 6 >= end)
319                         return;
320
321                 str = blobmsg_alloc_string_buffer(&status, "timestamp", 32);
322
323                 /* year */
324                 *(str++) = '2';
325                 *(str++) = '0';
326                 str = pdu_add_semioctet(str, data[0]);
327                 /* month */
328                 *(str++) = '-';
329                 str = pdu_add_semioctet(str, data[1]);
330                 /* day */
331                 *(str++) = '-';
332                 str = pdu_add_semioctet(str, data[2]);
333
334                 /* hour */
335                 *(str++) = ' ';
336                 str = pdu_add_semioctet(str, data[3]);
337                 /* minute */
338                 *(str++) = ':';
339                 str = pdu_add_semioctet(str, data[4]);
340                 /* second */
341                 *(str++) = ':';
342                 str = pdu_add_semioctet(str, data[5]);
343                 *str = 0;
344
345                 blobmsg_add_string_buffer(&status);
346
347                 data += 7;
348         }
349
350         cur_len = *(data++);
351         decode_7bit_field("text", data, end - data, !!(first & 0x40));
352 }
353
354 static enum qmi_cmd_result
355 cmd_wms_get_message_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
356 {
357         static struct qmi_wms_raw_read_request mreq = {
358                 QMI_INIT_SEQUENCE(message_memory_storage_id,
359                         .storage_type = QMI_WMS_STORAGE_TYPE_UIM,
360                 ),
361                 QMI_INIT(message_mode, QMI_WMS_MESSAGE_MODE_GSM_WCDMA),
362         };
363         char *err;
364         int id;
365
366         id = strtoul(arg, &err, 10);
367         if (err && *err) {
368                 blobmsg_add_string(&status, "error", "Invalid message ID");
369                 return QMI_CMD_EXIT;
370         }
371
372         mreq.data.message_memory_storage_id.memory_index = id;
373         qmi_set_wms_raw_read_request(msg, &mreq);
374
375         return QMI_CMD_REQUEST;
376 }
377
378
379 static void cmd_wms_get_raw_message_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg)
380 {
381         struct qmi_wms_raw_read_response res;
382         unsigned char *data;
383         char *str;
384         int i;
385
386         qmi_parse_wms_raw_read_response(msg, &res);
387         data = (unsigned char *) res.data.raw_message_data.raw_data;
388         str = blobmsg_alloc_string_buffer(&status, "data", res.data.raw_message_data.raw_data_n * 3);
389         for (i = 0; i < res.data.raw_message_data.raw_data_n; i++) {
390                 str += sprintf(str, " %02x" + (i ? 0 : 1), data[i]);
391         }
392         blobmsg_add_string_buffer(&status);
393 }
394
395 #define cmd_wms_get_raw_message_prepare cmd_wms_get_message_prepare
396
397
398 static struct {
399         const char *smsc;
400         const char *target;
401         bool flash;
402 } send;
403
404
405 #define cmd_wms_send_message_smsc_cb no_cb
406 static enum qmi_cmd_result
407 cmd_wms_send_message_smsc_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
408 {
409         send.smsc = arg;
410         return QMI_CMD_DONE;
411 }
412
413 #define cmd_wms_send_message_target_cb no_cb
414 static enum qmi_cmd_result
415 cmd_wms_send_message_target_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
416 {
417         send.target = arg;
418         return QMI_CMD_DONE;
419 }
420
421 #define cmd_wms_send_message_flash_cb no_cb
422 static enum qmi_cmd_result
423 cmd_wms_send_message_flash_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
424 {
425         send.flash = true;
426         return QMI_CMD_DONE;
427 }
428
429 static int
430 pdu_encode_semioctet(unsigned char *dest, const char *str)
431 {
432         int len = 0;
433         bool lower = true;
434
435         while (*str) {
436                 char digit = *str - '0';
437
438                 if (lower)
439                         dest[len] = 0xf0 | digit;
440                 else
441                         dest[len++] &= (digit << 4) | 0xf;
442
443                 lower = !lower;
444                 str++;
445         }
446
447         return len;
448 }
449
450 static int
451 pdu_encode_7bit_str(unsigned char *data, const char *str)
452 {
453         unsigned char c;
454         int len = 0;
455         int ofs = 0;
456
457         while(1) {
458                 c = *(str++) & 0x7f;
459                 if (!c)
460                         break;
461
462                 switch(ofs) {
463                 case 0:
464                         data[len] = c;
465                         break;
466                 default:
467                         data[len++] |= c << (8 - ofs);
468                         data[len] = c >> ofs;
469                         break;
470                 }
471
472                 ofs = (ofs + 1) % 8;
473         }
474
475         return len + 1;
476 }
477
478 static int
479 pdu_encode_number(unsigned char *dest, const char *str, bool smsc)
480 {
481         unsigned char format;
482         bool ascii = false;
483         int len = 0;
484         int i;
485
486         dest[len++] = 0;
487         if (*str == '+') {
488                 str++;
489                 format = 0x91;
490         } else {
491                 format = 0x81;
492         }
493
494         for (i = 0; str[i]; i++) {
495                 if (str[i] >= '0' || str[i] <= '9')
496                         continue;
497
498                 ascii = true;
499                 break;
500         }
501
502         if (ascii)
503                 format |= 0x40;
504
505         dest[len++] = format;
506         if (!ascii)
507                 len += pdu_encode_semioctet(&dest[len], str);
508         else
509                 len += pdu_encode_7bit_str(&dest[len], str);
510
511         if (smsc)
512                 dest[0] = len - 1;
513         else
514                 dest[0] = strlen(str);
515
516         return len;
517 }
518
519 static int
520 pdu_encode_data(unsigned char *dest, const char *str)
521 {
522         int len = 0;
523
524         dest[len++] = 0;
525         len += pdu_encode_7bit_str(&dest[len], str);
526         dest[0] = len - 1;
527
528         return len;
529 }
530
531 #define cmd_wms_send_message_cb no_cb
532 static enum qmi_cmd_result
533 cmd_wms_send_message_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg)
534 {
535         static unsigned char buf[512];
536         static struct qmi_wms_raw_send_request mreq = {
537                 QMI_INIT_SEQUENCE(raw_message_data,
538                         .format = QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_POINT_TO_POINT,
539                         .raw_data = buf,
540                 ),
541         };
542         unsigned char *cur = buf;
543         unsigned char first_octet = 0x11;
544         unsigned char protocol_id = 0x00;
545         unsigned char dcs = 0x00;
546
547         if (!send.smsc || !*send.smsc || !send.target || !*send.target) {
548                 blobmsg_add_string(&status, "error", "Missing argument");
549                 return QMI_CMD_EXIT;
550         }
551
552         if (strlen(send.smsc) > 16 || strlen(send.target) > 16 || strlen(arg) > 160) {
553                 blobmsg_add_string(&status, "error", "Argument too long");
554                 return QMI_CMD_EXIT;
555         }
556
557         if (send.flash)
558                 dcs |= 0x10;
559
560         cur += pdu_encode_number(cur, send.smsc, true);
561         *(cur++) = first_octet;
562         *(cur++) = 0; /* reference */
563
564         cur += pdu_encode_number(cur, send.target, false);
565         *(cur++) = protocol_id;
566         *(cur++) = dcs;
567
568         *(cur++) = 0xff; /* validity */
569         cur += pdu_encode_data(cur, arg);
570
571         mreq.data.raw_message_data.raw_data_n = cur - buf;
572         qmi_set_wms_raw_send_request(msg, &mreq);
573
574         return QMI_CMD_REQUEST;
575 }