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