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