fix 7-bit encoding
[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 }