1 /*
2
3 G N O K I I
4
5 A Linux/Unix toolset and driver for the mobile phones.
6
7 This file is part of gnokii.
8
9 Gnokii is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 Gnokii is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with gnokii; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
23 Copyright (C) 2001-2011 Pawel Kot
24 Copyright (C) 2001-2002 Markus Plail, Pavel Machek <pavel@ucw.cz>
25 Copyright (C) 2002-2003 Ladis Michl
26 Copyright (C) 2003-2004 BORBELY Zoltan
27 Copyright (C) 2006 Igor Popik
28
29 Library for parsing and creating Short Messages (SMS).
30
31 */
32
33 #include "config.h"
34
35 #include <glib.h>
36
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40
41 #include "gnokii-internal.h"
42 #include "gnokii.h"
43
44 #include "sms-nokia.h"
45
46 #ifdef ENABLE_NLS
47 # include <locale.h>
48 #endif
49
50 #undef ERROR
51 #define ERROR() do { if (error != GN_ERR_NONE) return error; } while (0)
52
53 struct sms_udh_data {
54 unsigned int length;
55 char *header;
56 };
57
58 #define MAX_SMS_PART 140
59
60 /* User data headers */
61 static struct sms_udh_data headers[] = {
62 { 0x00, "" },
63 { 0x05, "\x00\x03\x01\x00\x00" }, /* Concatenated messages */
64 { 0x06, "\x05\x04\x15\x81\x00\x00" }, /* Ringtones */
65 { 0x06, "\x05\x04\x15\x82\x00\x00" }, /* Operator logos */
66 { 0x06, "\x05\x04\x15\x83\x00\x00" }, /* Caller logos */
67 { 0x06, "\x05\x04\x15\x8a\x00\x00" }, /* Multipart Message */
68 { 0x06, "\x05\x04\x23\xf4\x00\x00" }, /* WAP vCard */
69 { 0x06, "\x05\x04\x23\xf5\x00\x00" }, /* WAP vCalendar */
70 { 0x06, "\x05\x04\x23\xf6\x00\x00" }, /* WAP vCardSecure */
71 { 0x06, "\x05\x04\x23\xf7\x00\x00" }, /* WAP vCalendarSecure */
72 { 0x04, "\x01\x02\x00\x00" }, /* Voice Messages */
73 { 0x04, "\x01\x02\x01\x00" }, /* Fax Messages */
74 { 0x04, "\x01\x02\x02\x00" }, /* Email Messages */
75 { 0x06, "\x05\x04\x0b\x84\x23\xf0" }, /* WAP PUSH */
76 { 0x06, "\x05\x04\x0b\x84\x0b\x84" },
77 { 0x00, "" },
78 };
79
80
81 /**
82 * sms_default - fills in SMS structure with the default values
83 * @sms: pointer to a structure we need to fill in
84 *
85 * Default settings:
86 * - no delivery report
87 * - no Class Message
88 * - no compression
89 * - 7 bit data
90 * - message validity for 3 days
91 * - unsent status
92 */
sms_default(gn_sms * sms)93 static void sms_default(gn_sms *sms)
94 {
95 memset(sms, 0, sizeof(gn_sms));
96
97 sms->type = GN_SMS_MT_Deliver;
98 sms->delivery_report = false;
99 sms->status = GN_SMS_Unsent;
100 sms->validity = 4320; /* 4320 minutes == 72 hours */
101 sms->dcs.type = GN_SMS_DCS_GeneralDataCoding;
102 sms->dcs.u.general.compressed = false;
103 sms->dcs.u.general.alphabet = GN_SMS_DCS_DefaultAlphabet;
104 sms->dcs.u.general.m_class = 0;
105 }
106
gn_sms_default_submit(gn_sms * sms)107 GNOKII_API void gn_sms_default_submit(gn_sms *sms)
108 {
109 sms_default(sms);
110 sms->type = GN_SMS_MT_Submit;
111 sms->memory_type = GN_MT_SM;
112 }
113
gn_sms_default_deliver(gn_sms * sms)114 GNOKII_API void gn_sms_default_deliver(gn_sms *sms)
115 {
116 sms_default(sms);
117 sms->type = GN_SMS_MT_Deliver;
118 sms->memory_type = GN_MT_ME;
119 }
120
121
122 /***
123 *** Util functions
124 ***/
125
126 /**
127 * sms_timestamp_print - formats date and time in the human readable manner
128 * @number: binary coded date and time
129 *
130 * The function converts binary date time into an easily readable form.
131 * It is Y2K compliant.
132 */
sms_timestamp_print(u8 * number)133 static char *sms_timestamp_print(u8 *number)
134 {
135 #ifdef DEBUG
136 #define LOCAL_DATETIME_MAX_LENGTH 26
137 static char buffer[LOCAL_DATETIME_MAX_LENGTH];
138 char buf[5];
139 int i;
140
141 if (!number) return NULL;
142
143 memset(buffer, 0, LOCAL_DATETIME_MAX_LENGTH);
144
145 /* Ugly hack, but according to the GSM specs, the year is stored
146 * as the 2 digit number. */
147 if ((10*(number[0] & 0x0f) + (number[0] >> 4)) < 70)
148 snprintf(buffer, sizeof(buffer), "20");
149 else
150 snprintf(buffer, sizeof(buffer), "19");
151
152 for (i = 0; i < 6; i++) {
153 int c;
154 char buf2[4];
155 switch (i) {
156 case 0:
157 case 1:
158 c = '-';
159 break;
160 case 3:
161 case 4:
162 c = ':';
163 break;
164 default:
165 c = ' ';
166 break;
167 }
168 snprintf(buf2, 4, "%d%d%c", number[i] & 0x0f, number[i] >> 4, c);
169 strncat(buffer, buf2, sizeof(buffer) - strlen(buffer));
170 }
171
172 /* The GSM spec is not clear what is the sign of the timezone when the
173 * 6th bit is set. Some SMSCs are compatible with our interpretation,
174 * some are not. If your operator does use incompatible SMSC and wrong
175 * sign disturbs you, change the sign here.
176 */
177 if (number[6] & 0x08)
178 strncat(buffer, "-", sizeof(buffer) - strlen(buffer));
179 else
180 strncat(buffer, "+", sizeof(buffer) - strlen(buffer));
181 /* The timezone is given in quarters. The base is GMT. */
182 snprintf(buf, sizeof(buf), "%02d00", (10 * (number[6] & 0x07) + (number[6] >> 4)) / 4);
183 strncat(buffer, buf, sizeof(buffer) - strlen(buffer));
184
185 return buffer;
186 #undef LOCAL_DATETIME_MAX_LENGTH
187 #else
188 return NULL;
189 #endif /* DEBUG */
190 }
191
192 /**
193 * sms_timestamp_unpack - converts binary datetime to the gnokii's datetime struct
194 * @number: binary coded date and time
195 * @dt: datetime structure to be filled
196 *
197 * The function fills int gnokii datetime structure basing on the given datetime
198 * in the binary format. It is Y2K compliant.
199 */
sms_timestamp_unpack(unsigned char * number,gn_timestamp * dt)200 gn_timestamp *sms_timestamp_unpack(unsigned char *number, gn_timestamp *dt)
201 {
202 if (!dt)
203 return NULL;
204 memset(dt, 0, sizeof(gn_timestamp));
205 if (!number)
206 return dt;
207
208 dt->year = 10 * (number[0] & 0x0f) + (number[0] >> 4);
209
210 /* Ugly hack, but according to the GSM specs, the year is stored
211 * as the 2 digit number. */
212 if (dt->year < 70)
213 dt->year += 2000;
214 else
215 dt->year += 1900;
216
217 dt->month = 10 * (number[1] & 0x0f) + (number[1] >> 4);
218 dt->day = 10 * (number[2] & 0x0f) + (number[2] >> 4);
219 dt->hour = 10 * (number[3] & 0x0f) + (number[3] >> 4);
220 dt->minute = 10 * (number[4] & 0x0f) + (number[4] >> 4);
221 dt->second = 10 * (number[5] & 0x0f) + (number[5] >> 4);
222
223 /* The timezone is given in quarters. The base is GMT */
224 dt->timezone = (10 * (number[6] & 0x07) + (number[6] >> 4)) / 4;
225 /* The GSM spec is not clear what is the sign of the timezone when the
226 * 6th bit is set. Some SMSCs are compatible with our interpretation,
227 * some are not. If your operator does use incompatible SMSC and wrong
228 * sign disturbs you, change the sign here.
229 */
230 if (number[6] & 0x08)
231 dt->timezone = -dt->timezone;
232
233 return dt;
234 }
235
236 /**
237 * sms_timestamp_pack - converts gnokii's datetime struct to the binary datetime
238 * @dt: datetime structure to be read
239 * @number: binary variable to be filled
240 *
241 */
sms_timestamp_pack(gn_timestamp * dt,unsigned char * number)242 unsigned char *sms_timestamp_pack(gn_timestamp *dt, unsigned char *number)
243 {
244 if (!number)
245 return NULL;
246 memset(number, 0, GN_SMS_DATETIME_MAX_LENGTH);
247 if (!dt)
248 return number;
249
250 /* Ugly hack, but according to the GSM specs, the year is stored
251 * as the 2 digit number. */
252 if (dt->year < 2000)
253 dt->year -= 1900;
254 else
255 dt->year -= 2000;
256
257 number[0] = (dt->year / 10) | ((dt->year % 10) << 4);
258 number[1] = (dt->month / 10) | ((dt->month % 10) << 4);
259 number[2] = (dt->day / 10) | ((dt->day % 10) << 4);
260 number[3] = (dt->hour / 10) | ((dt->hour % 10) << 4);
261 number[4] = (dt->minute / 10) | ((dt->minute % 10) << 4);
262 number[5] = (dt->second / 10) | ((dt->second % 10) << 4);
263
264 /* The timezone is given in quarters. The base is GMT */
265 number[6] = (dt->timezone / 10) | ((dt->second % 10) << 4) * 4;
266 /* The GSM spec is not clear what is the sign of the timezone when the
267 * 6th bit is set. Some SMSCs are compatible with our interpretation,
268 * some are not. If your operator does use incompatible SMSC and wrong
269 * sign disturbs you, change the sign here.
270 */
271 if (dt->timezone < 0)
272 number[6] |= 0x08;
273
274 return number;
275 }
276
277 /***
278 *** DECODING SMS
279 ***/
280
281 /**
282 * sms_status - decodes the error code contained in the delivery report.
283 * @status: error code to decode
284 * @sms: SMS struct to store the result
285 *
286 * This function checks for the error (or success) code contained in the
287 * received SMS - delivery report and fills in the SMS structure with
288 * the correct message according to the GSM specification.
289 * This function only applies to the delivery reports. Delivery reports
290 * contain only one part, so it is assumed all other parts except 0th are
291 * NULL.
292 */
sms_status(unsigned char status,gn_sms * sms)293 static gn_error sms_status(unsigned char status, gn_sms *sms)
294 {
295 sms->user_data[0].type = GN_SMS_DATA_Text;
296 sms->user_data[1].type = GN_SMS_DATA_None;
297 if (status < 0x03) {
298 sms->user_data[0].dr_status = GN_SMS_DR_Status_Delivered;
299 snprintf(sms->user_data[0].u.text, sizeof(sms->user_data[0].u.text), "%s", _("Delivered"));
300 switch (status) {
301 case 0x00:
302 dprintf("SM received by the SME");
303 break;
304 case 0x01:
305 dprintf("SM forwarded by the SC to the SME but the SC is unable to confirm delivery");
306 break;
307 case 0x02:
308 dprintf("SM replaced by the SC");
309 break;
310 }
311 } else if (status & 0x40) {
312 snprintf(sms->user_data[0].u.text, sizeof(sms->user_data[0].u.text), "%s", _("Failed"));
313 /* more detailed reason only for debug */
314 if (status & 0x20) {
315 dprintf("Temporary error, SC is not making any more transfer attempts\n");
316 sms->user_data[0].dr_status = GN_SMS_DR_Status_Failed_Permanent;
317 switch (status) {
318 case 0x60:
319 dprintf("Congestion");
320 break;
321 case 0x61:
322 dprintf("SME busy");
323 break;
324 case 0x62:
325 dprintf("No response from SME");
326 break;
327 case 0x63:
328 dprintf("Service rejected");
329 break;
330 case 0x64:
331 dprintf("Quality of service not available");
332 break;
333 case 0x65:
334 dprintf("Error in SME");
335 break;
336 default:
337 dprintf("Reserved/Specific to SC: %x", status);
338 break;
339 }
340 } else {
341 dprintf("Permanent error, SC is not making any more transfer attempts\n");
342 sms->user_data[0].dr_status = GN_SMS_DR_Status_Failed_Temporary;
343 switch (status) {
344 case 0x40:
345 dprintf("Remote procedure error");
346 break;
347 case 0x41:
348 dprintf("Incompatibile destination");
349 break;
350 case 0x42:
351 dprintf("Connection rejected by SME");
352 break;
353 case 0x43:
354 dprintf("Not obtainable");
355 break;
356 case 0x44:
357 dprintf("Quality of service not available");
358 break;
359 case 0x45:
360 dprintf("No internetworking available");
361 break;
362 case 0x46:
363 dprintf("SM Validity Period Expired");
364 break;
365 case 0x47:
366 dprintf("SM deleted by originating SME");
367 break;
368 case 0x48:
369 dprintf("SM Deleted by SC Administration");
370 break;
371 case 0x49:
372 dprintf("SM does not exist");
373 break;
374 default:
375 dprintf("Reserved/Specific to SC: 0x%02x", status);
376 break;
377 }
378 }
379 } else if (status & 0x20) {
380 sms->user_data[0].dr_status = GN_SMS_DR_Status_Pending;
381 snprintf(sms->user_data[0].u.text, sizeof(sms->user_data[0].u.text), "%s", _("Pending"));
382 /* more detailed reason only for debug */
383 dprintf("Temporary error, SC still trying to transfer SM\n");
384 switch (status) {
385 case 0x20:
386 dprintf("Congestion");
387 break;
388 case 0x21:
389 dprintf("SME busy");
390 break;
391 case 0x22:
392 dprintf("No response from SME");
393 break;
394 case 0x23:
395 dprintf("Service rejected");
396 break;
397 case 0x24:
398 dprintf("Quality of service not available");
399 break;
400 case 0x25:
401 dprintf("Error in SME");
402 break;
403 default:
404 dprintf("Reserved/Specific to SC: 0x%02x", status);
405 break;
406 }
407 } else {
408 sms->user_data[0].dr_status = GN_SMS_DR_Status_Invalid;
409 snprintf(sms->user_data[0].u.text, sizeof(sms->user_data[0].u.text), "%s", _("Unknown"));
410
411 /* more detailed reason only for debug */
412 dprintf("Reserved/Specific to SC: 0x%02x", status);
413 }
414 dprintf("\n");
415 sms->user_data[0].length = strlen(sms->user_data[0].u.text);
416 return GN_ERR_NONE;
417 }
418
419 /**
420 * sms_data_decode - decodes encoded text from the SMS.
421 * @message: encoded text
422 * @output: room for the encoded text
423 * @length: decoded text length
424 * @size: encoded text length
425 * @udhlen: udh length
426 * @dcs: data coding scheme
427 *
428 * This function decodes either 7bit or 8bit or Unicode text to the
429 * readable text format according to the locale set.
430 */
sms_data_decode(unsigned char * message,unsigned char * output,unsigned int length,unsigned int size,unsigned int udhlen,gn_sms_dcs dcs)431 static gn_error sms_data_decode(unsigned char *message, unsigned char *output, unsigned int length,
432 unsigned int size, unsigned int udhlen, gn_sms_dcs dcs)
433 {
434 /* Unicode */
435 if (dcs.type & 0x20) {
436 dprintf("Compressed message\n");
437 return GN_ERR_NOTIMPLEMENTED;
438 }
439 if ((dcs.type & 0x08) == 0x08) {
440 dprintf("Unicode message\n");
441 /*
442 * length is rawsms->length which is number of characters in
443 * the decoded text. 3rd argument of char_unicode_decode is
444 * number of bytes.
445 */
446 char_unicode_decode(output, message, 2 * length);
447 } else {
448 /* 8bit SMS */
449 if ((dcs.type & 0xf4) == 0xf4) {
450 dprintf("8bit message\n");
451 memcpy(output, message + udhlen, length);
452 /* 7bit SMS */
453 } else {
454 char *aux;
455
456 dprintf("Default Alphabet\n");
457 length = length - (udhlen * 8 + ((7-(udhlen%7))%7)) / 7;
458 aux = calloc(length + 1, 1);
459 char_7bit_unpack((7-udhlen)%7, size, length, message, aux);
460 char_default_alphabet_decode(output, aux, length);
461 free(aux);
462 }
463 }
464 dprintf("%s\n", output);
465 return GN_ERR_NONE;
466 }
467
468 /**
469 * sms_udh_decode - interprets the User Data Header contents
470 * @message: received UDH
471 * @sms: SMS structure to fill in
472 *
473 * At this stage we already need to know that SMS has UDH.
474 * This function decodes UDH as described in:
475 * - GSM 03.40 version 6.1.0 Release 1997, section 9.2.3.24
476 * - Smart Messaging Specification, Revision 1.0.0, September 15, 1997
477 */
sms_udh_decode(unsigned char * message,gn_sms_udh * udh)478 static gn_error sms_udh_decode(unsigned char *message, gn_sms_udh *udh)
479 {
480 unsigned char length, pos, nr;
481
482 udh->length = length = message[0];
483 pos = 1;
484 nr = 0;
485 while (length > 1) {
486 unsigned char udh_length;
487
488 udh_length = message[pos+1];
489 switch (message[pos]) {
490 case 0x00: /* Concatenated short messages */
491 dprintf("Concatenated messages\n");
492 udh->udh[nr].type = GN_SMS_UDH_ConcatenatedMessages;
493 udh->udh[nr].u.concatenated_short_message.reference_number = message[pos + 2];
494 udh->udh[nr].u.concatenated_short_message.maximum_number = message[pos + 3];
495 udh->udh[nr].u.concatenated_short_message.current_number = message[pos + 4];
496 break;
497 case 0x08: /* Concatenated short messages, 16-bit reference number */
498 dprintf("Concatenated messages, 16-bit reference number\n");
499 udh->udh[nr].type = GN_SMS_UDH_ConcatenatedMessages;
500 udh->udh[nr].u.concatenated_short_message.reference_number = 256 * message[pos + 2] + message[pos + 3];
501 udh->udh[nr].u.concatenated_short_message.maximum_number = message[pos + 4];
502 udh->udh[nr].u.concatenated_short_message.current_number = message[pos + 5];
503 break;
504 case 0x01: /* Special SMS Message Indication */
505 switch (message[pos + 2] & 0x03) {
506 case 0x00:
507 dprintf("Voice Message\n");
508 udh->udh[nr].type = GN_SMS_UDH_VoiceMessage;
509 break;
510 case 0x01:
511 dprintf("Fax Message\n");
512 udh->udh[nr].type = GN_SMS_UDH_FaxMessage;
513 break;
514 case 0x02:
515 dprintf("Email Message\n");
516 udh->udh[nr].type = GN_SMS_UDH_EmailMessage;
517 break;
518 default:
519 dprintf("Unknown\n");
520 udh->udh[nr].type = GN_SMS_UDH_Unknown;
521 break;
522 }
523 udh->udh[nr].u.special_sms_message_indication.store = (message[pos + 2] & 0x80) >> 7;
524 udh->udh[nr].u.special_sms_message_indication.message_count = message[pos + 3];
525 break;
526 case 0x05: /* Application port addressing scheme, 16 bit address */
527 switch (((0x00ff & message[pos + 2]) << 8) | (0x00ff & message[pos + 3])) {
528 case 0x1581:
529 dprintf("Ringtone\n");
530 udh->udh[nr].type = GN_SMS_UDH_Ringtone;
531 break;
532 case 0x1582:
533 dprintf("Operator Logo\n");
534 udh->udh[nr].type = GN_SMS_UDH_OpLogo;
535 break;
536 case 0x1583:
537 dprintf("Caller Icon\n");
538 udh->udh[nr].type = GN_SMS_UDH_CallerIDLogo;
539 break;
540 case 0x158a:
541 dprintf("Multipart Message\n");
542 udh->udh[nr].type = GN_SMS_UDH_MultipartMessage;
543 break;
544 case 0x23f4:
545 dprintf("WAP vCard\n");
546 udh->udh[nr].type = GN_SMS_UDH_WAPvCard;
547 break;
548 case 0x23f5:
549 dprintf("WAP vCalendar\n");
550 udh->udh[nr].type = GN_SMS_UDH_WAPvCalendar;
551 break;
552 case 0x23f6:
553 dprintf("WAP vCardSecure\n");
554 udh->udh[nr].type = GN_SMS_UDH_WAPvCardSecure;
555 break;
556 case 0x23f7:
557 dprintf("WAP vCalendarSecure\n");
558 udh->udh[nr].type = GN_SMS_UDH_WAPvCalendarSecure;
559 break;
560 case 0x0b84:
561 dprintf("WAP Push\n");
562 udh->udh[nr].type = GN_SMS_UDH_WAPPush;
563 break;
564 default:
565 dprintf("Unknown\n");
566 udh->udh[nr].type = GN_SMS_UDH_Unknown;
567 break;
568 }
569 break;
570 case 0x04: /* Application port addressing scheme, 8 bit address */
571 case 0x06: /* SMSC Control Parameters */
572 case 0x07: /* UDH Source Indicator */
573 default:
574 udh->udh[nr].type = GN_SMS_UDH_Unknown;
575 dprintf("User Data Header type 0x%02x isn't supported\n", message[pos]);
576 break;
577 }
578 length -= (udh_length + 2);
579 pos += (udh_length + 2);
580 nr++;
581 }
582 udh->number = nr;
583
584 /* We need to add the length field itself */
585 udh->length++;
586
587 return GN_ERR_NONE;
588 }
589
590 /**
591 * sms_header_decode - Decodes PDU SMS header
592 * @rawsms:
593 * @sms:
594 * @udh:
595 *
596 * This function parses received SMS header information and stores
597 * them in higher level SMS struct. It also checks for the UDH and when
598 * it's found calls the function to extract the UDH.
599 */
sms_header_decode(gn_sms_raw * rawsms,gn_sms * sms,gn_sms_udh * udh)600 static gn_error sms_header_decode(gn_sms_raw *rawsms, gn_sms *sms, gn_sms_udh *udh)
601 {
602 switch (sms->type = rawsms->type) {
603 case GN_SMS_MT_Deliver:
604 dprintf("Mobile Terminated message:\n");
605 break;
606 case GN_SMS_MT_DeliveryReport:
607 dprintf("Delivery Report:\n");
608 break;
609 case GN_SMS_MT_Submit:
610 dprintf("Mobile Originated (stored) message:\n");
611 break;
612 case GN_SMS_MT_StatusReport:
613 dprintf("Status Report message:\n");
614 break;
615 case GN_SMS_MT_Picture:
616 dprintf("Picture Message:\n");
617 break;
618 case GN_SMS_MT_TextTemplate:
619 dprintf("Text Template:\n");
620 break;
621 case GN_SMS_MT_PictureTemplate:
622 dprintf("Picture Template:\n");
623 break;
624 case GN_SMS_MT_SubmitSent:
625 dprintf("Mobile Originated (sent) message:\n");
626 break;
627 default:
628 dprintf("SMS message type 0x%02x isn't supported\n", sms->type);
629 return GN_ERR_NOTSUPPORTED;
630 }
631
632 /* Sending date */
633 sms_timestamp_unpack(rawsms->smsc_time, &(sms->smsc_time));
634 dprintf("\tDate: %s\n", sms_timestamp_print(rawsms->smsc_time));
635
636 /* Remote number */
637 rawsms->remote_number[0] = (rawsms->remote_number[0] + 1) / 2 + 1;
638 snprintf(sms->remote.number, sizeof(sms->remote.number), "%s", char_bcd_number_get(rawsms->remote_number));
639 dprintf("\tRemote number (recipient or sender): %s\n", sms->remote.number);
640
641 /* Short Message Center */
642 snprintf(sms->smsc.number, sizeof(sms->smsc.number), "%s", char_bcd_number_get(rawsms->message_center));
643 dprintf("\tSMS center number: %s\n", sms->smsc.number);
644
645 /* Delivery time */
646 if (sms->type == GN_SMS_MT_StatusReport) {
647 sms_timestamp_unpack(rawsms->time, &(sms->time));
648 dprintf("\tDelivery date: %s\n", sms_timestamp_print(rawsms->time));
649 }
650
651 /* Data Coding Scheme */
652 sms->dcs.type = rawsms->dcs;
653
654 /* User Data Header */
655 if (rawsms->udh_indicator & 0x40) { /* UDH header available */
656 dprintf("UDH found\n");
657 return sms_udh_decode(rawsms->user_data, udh);
658 }
659
660 return GN_ERR_NONE;
661 }
662
663 /**
664 * sms_pdu_decode - This function decodes the PDU SMS
665 * @rawsms - SMS read by the phone driver
666 * @sms - place to store the decoded message
667 *
668 * This function decodes SMS as described in GSM 03.40 version 6.1.0
669 * Release 1997, section 9
670 */
sms_pdu_decode(gn_sms_raw * rawsms,gn_sms * sms)671 static gn_error sms_pdu_decode(gn_sms_raw *rawsms, gn_sms *sms)
672 {
673 unsigned int size = 0;
674 gn_error error;
675
676 error = sms_header_decode(rawsms, sms, &sms->udh);
677 ERROR();
678 sms->user_data[0].dr_status = GN_SMS_DR_Status_None;
679 switch (sms->type) {
680 case GN_SMS_MT_DeliveryReport:
681 case GN_SMS_MT_StatusReport:
682 sms_status(rawsms->report_status, sms);
683 break;
684 case GN_SMS_MT_PictureTemplate:
685 case GN_SMS_MT_Picture:
686 /* This is incredible. Nokia violates it's own format in 6210 */
687 /* Indicate that it is Multipart Message. Remove it if not needed */
688 /* [I believe Nokia said in their manuals that any order is permitted --pavel] */
689 sms->udh.number = 1;
690 sms->udh.udh[0].type = GN_SMS_UDH_MultipartMessage;
691 if ((rawsms->user_data[0] == 0x48) && (rawsms->user_data[1] == 0x1c)) {
692
693 dprintf("First picture then text!\n");
694
695 /* First part is a Picture */
696 sms->user_data[0].type = GN_SMS_DATA_Bitmap;
697 gn_bmp_sms_read(GN_BMP_PictureMessage, rawsms->user_data,
698 NULL, &sms->user_data[0].u.bitmap);
699 #ifdef DEBUG
700 gn_bmp_print(&sms->user_data[0].u.bitmap, stderr);
701 #endif
702 size = rawsms->user_data_length - 4 - sms->user_data[0].u.bitmap.size;
703 /* Second part is a text */
704 sms->user_data[1].type = GN_SMS_DATA_NokiaText;
705 sms_data_decode(rawsms->user_data + 5 + sms->user_data[0].u.bitmap.size,
706 (unsigned char *)&(sms->user_data[1].u.text),
707 rawsms->length - sms->user_data[0].u.bitmap.size - 4,
708 size, 0, sms->dcs);
709 } else {
710
711 dprintf("First text then picture!\n");
712
713 /* First part is a text */
714 sms->user_data[1].type = GN_SMS_DATA_NokiaText;
715 sms_data_decode(rawsms->user_data + 3,
716 (unsigned char *)&(sms->user_data[1].u.text),
717 rawsms->user_data[1], rawsms->user_data[0], 0, sms->dcs);
718
719 /* Second part is a Picture */
720 sms->user_data[0].type = GN_SMS_DATA_Bitmap;
721 gn_bmp_sms_read(GN_BMP_PictureMessage,
722 rawsms->user_data + rawsms->user_data[0] + 7,
723 NULL, &sms->user_data[0].u.bitmap);
724 #ifdef DEBUG
725 gn_bmp_print(&sms->user_data[0].u.bitmap, stderr);
726 #endif
727 }
728 break;
729 /* Plain text message */
730 default:
731 return sms_data_decode(rawsms->user_data + sms->udh.length, /* Skip the UDH */
732 (unsigned char *)&sms->user_data[0].u.text, /* With a plain text message we have only 1 part */
733 rawsms->length, /* Length of the decoded text */
734 rawsms->user_data_length, /* Length of the encoded text (in full octets) without UDH */
735 sms->udh.length, /* To skip the certain number of bits when unpacking 7bit message */
736 sms->dcs);
737 break;
738 }
739
740 return GN_ERR_NONE;
741 }
742
743 /**
744 * gn_sms_parse - High-level function for the SMS parsing
745 * @data: GSM data from the phone driver
746 *
747 * This function parses the SMS message from the lowlevel raw_sms to
748 * the highlevel SMS. In data->raw_sms there's SMS read by the phone
749 * driver, data->sms is the place for the parsed SMS.
750 */
gn_sms_parse(gn_data * data)751 gn_error gn_sms_parse(gn_data *data)
752 {
753 if (!data->raw_sms || !data->sms) return GN_ERR_INTERNALERROR;
754 /* Let's assume at the moment that all messages are PDU coded */
755 return sms_pdu_decode(data->raw_sms, data->sms);
756 }
757
758 /**
759 * gn_sms_pdu2raw - Copy PDU data into gn_sms_raw structure
760 * @rawsms: gn_sms_raw structure to be filled
761 * @pdu: buffer containing PDU data
762 * @pdu_len: length of buffer containing PDU data
763 * @flags: GN_SMS_PDU_* flags to control copying
764 *
765 * This function fills a gn_sms_raw structure copying raw values from
766 * a buffer containing PDU data.
767 */
gn_sms_pdu2raw(gn_sms_raw * rawsms,unsigned char * pdu,int pdu_len,int flags)768 gn_error gn_sms_pdu2raw(gn_sms_raw *rawsms, unsigned char *pdu, int pdu_len, int flags)
769 {
770 #define COPY_REMOTE_NUMBER(pdu, offset) { \
771 l = (pdu[offset] % 2) ? pdu[offset] + 1 : pdu[offset]; \
772 l = l / 2 + 2; \
773 if (l + offset + extraoffset > pdu_len || l > GN_SMS_NUMBER_MAX_LENGTH) { \
774 dprintf("Invalid remote number length (%d)\n", l); \
775 ret = GN_ERR_INTERNALERROR; \
776 goto out; \
777 } \
778 memcpy(rawsms->remote_number, pdu + offset, l); \
779 offset += l; \
780 }
781
782 #define COPY_USER_DATA(pdu, offset) { \
783 rawsms->length = pdu[offset++]; \
784 rawsms->user_data_length = rawsms->length; \
785 if (rawsms->length > 0) { \
786 if (rawsms->udh_indicator) \
787 rawsms->user_data_length -= pdu[offset] + 1; \
788 if (pdu_len - offset > GN_SMS_USER_DATA_MAX_LENGTH) { \
789 dprintf("Phone gave as poisonous (too short?) reply, either phone went crazy or communication went out of sync\n"); \
790 ret = GN_ERR_INTERNALERROR; \
791 goto out; \
792 } \
793 memcpy(rawsms->user_data, pdu + offset, pdu_len - offset); \
794 } \
795 }
796
797 gn_error ret = GN_ERR_NONE;
798 unsigned int l, extraoffset, offset = 0;
799 int parameter_indicator;
800
801 if (!flags & GN_SMS_PDU_NOSMSC) {
802 l = pdu[offset] + 1;
803 if (l > pdu_len || l > GN_SMS_SMSC_NUMBER_MAX_LENGTH) {
804 dprintf("Invalid message center length (%d)\n", l);
805 ret = GN_ERR_INTERNALERROR;
806 goto out;
807 }
808 memcpy(rawsms->message_center, pdu, l);
809 offset += l;
810 }
811
812 rawsms->reject_duplicates = 0;
813 rawsms->report_status = 0;
814 rawsms->reference = 0;
815 rawsms->reply_via_same_smsc = 0;
816 rawsms->report = 0;
817
818 /* NOTE: offsets are taken from subclause 9.2.3 "Definition of the TPDU parameters"
819 of 3GPP TS 23.040 version 7.0.1 Release 7 == ETSI TS 123 040 V7.0.1 (2007-03)
820 *not* from the tables in subclause 9.2.2 "PDU Type repertoire at SM-TL" which ignore unused bits
821 */
822
823 /* NOTE: this function handles only short messages received by the phone.
824 Since the same TP-MTI value is used both for SC->MS and for MS->SC,
825 it can't simply shift to get the enum value: (pdu[offset] & 0x03) << 1
826 */
827
828 /* TP-MTI TP-Message-Type-Indicator 9.2.3.1 */
829 switch (pdu[offset] & 0x03) {
830 case 0x00:
831 rawsms->type = GN_SMS_MT_Deliver;
832 break;
833 case 0x01:
834 rawsms->type = GN_SMS_MT_Submit;
835 break;
836 case 0x02:
837 rawsms->type = GN_SMS_MT_StatusReport;
838 break;
839 case 0x03:
840 dprintf("Reserved TP-MTI found\n");
841 return GN_ERR_INTERNALERROR;
842 }
843 switch (rawsms->type) {
844 case GN_SMS_MT_Deliver:
845 dprintf("SMS-DELIVER found\n");
846 /* TP-MMS TP-More-Messages-to-Send */
847 rawsms->more_messages = pdu[offset] & 0x04;
848 /* Bits 3 and 4 of the first octet are unused */
849 /* TP-SRI TP-Status-Report-Indication */
850 rawsms->report_status = pdu[offset] & 0x20;
851 /* TP-UDHI TP-User-Data-Header-Indication */
852 rawsms->udh_indicator = pdu[offset] & 0x40;
853 /* TP-RP TP-Reply-Path */
854 rawsms->reply_via_same_smsc = pdu[offset] & 0x80;
855 offset++;
856 /* TP-OA TP-Originating-Address */
857 extraoffset = 10;
858 COPY_REMOTE_NUMBER(pdu, offset);
859 /* TP-PID TP-Protocol-Identifier */
860 rawsms->pid = pdu[offset++];
861 /* TP-DCS TP-Data-Coding-Scheme */
862 rawsms->dcs = pdu[offset++];
863 /* TP-SCTS TP-Service-Centre-Time-Stamp */
864 memcpy(rawsms->smsc_time, pdu + offset, 7);
865 offset += 7;
866 /* TP-UDL TP-User-Data-Length */
867 /* TP-UD TP-User-Data */
868 COPY_USER_DATA(pdu, offset);
869 break;
870 case GN_SMS_MT_Submit:
871 dprintf("SMS-SUBMIT found\n");
872 /* TP-RD TP-Reject-Duplicates */
873 rawsms->reject_duplicates = pdu[offset] & 0x04;
874 /* TP-VPF TP-Validity-Period-Format */
875 rawsms->validity_indicator = (pdu[offset] & 0x18) >> 3;
876 /* TP-SRR TP-Status-Report-Request */
877 rawsms->report = pdu[offset] & 0x20;
878 /* TP-UDHI TP-User-Data-Header-Indication */
879 rawsms->udh_indicator = pdu[offset] & 0x40;
880 /* TP-RP TP-Reply-Path */
881 rawsms->reply_via_same_smsc = pdu[offset] & 0x80;
882 offset++;
883 /* TP-MR TP-Message-Reference */
884 rawsms->reference = pdu[offset++];
885 /* TP-DA TP-Destination-Address */
886 extraoffset = 3;
887 COPY_REMOTE_NUMBER(pdu, offset);
888 /* TP-PID TP-Protocol-Identifier */
889 rawsms->pid = pdu[offset++];
890 /* TP-DCS TP-Data-Coding-Scheme */
891 rawsms->dcs = pdu[offset++];
892 /* TP-VP TP-Validity-Period */
893 switch (rawsms->validity_indicator) {
894 case GN_SMS_VP_None:
895 /* nothing to convert */
896 break;
897 case GN_SMS_VP_RelativeFormat:
898 rawsms->validity[0] = pdu[offset++];
899 break;
900 case GN_SMS_VP_EnhancedFormat:
901 /* FALL THROUGH */
902 case GN_SMS_VP_AbsoluteFormat:
903 memcpy(rawsms->validity, pdu + offset, 7);
904 offset += 7;
905 break;
906 default:
907 dprintf("Unknown validity_indicator 0x%02x\n", rawsms->validity_indicator);
908 return GN_ERR_INTERNALERROR;
909 }
910 /* TP-UDL TP-User-Data-Length */
911 /* TP-UD TP-User-Data */
912 COPY_USER_DATA(pdu, offset);
913 break;
914 case GN_SMS_MT_StatusReport:
915 dprintf("SMS-STATUS-REPORT found\n");
916 /* TP-MMS TP-More-Messages-to-Send */
917 rawsms->more_messages = pdu[offset] & 0x04;
918 /* TP-SRQ TP-Status-Report-Qualifier */
919 rawsms->report = pdu[offset] & 0x10;
920 /* TP-UDHI TP-User-Data-Header-Indication */
921 rawsms->udh_indicator = pdu[offset] & 0x40;
922 offset++;
923 /* TP-MR TP-Message-Reference */
924 rawsms->reference = pdu[offset++];
925 /* TP-RA TP-Recipient-Address */
926 extraoffset = 0;
927 COPY_REMOTE_NUMBER(pdu, offset);
928 /* TP-SCTS TP-Service-Centre-Time-Stamp */
929 memcpy(rawsms->smsc_time, pdu + offset, 7);
930 offset += 7;
931 /* TP-DT TP-Discharge-Time */
932 memcpy(rawsms->time, pdu + offset, 7);
933 offset += 7;
934 /* TP-ST TP-Status */
935 rawsms->report_status = pdu[offset++];
936 /* TP-PI TP-Parameter-Indicator */
937 parameter_indicator = pdu[offset];
938 /* handle the "extension bit" skipping the following octects, if any (see 9.2.3.27 TP-Parameter-Indicator):
939 * The most significant bit in octet 1 and any other TP-PI octets which may be added later is reserved as an extension bit
940 * which when set to a 1 shall indicate that another TP-PI octet follows immediately afterwards.
941 */
942 while ((offset < pdu_len) && (pdu[offset++] & 0x80))
943 ;
944 if ((offset < pdu_len) && (parameter_indicator & 0x01)) {
945 /* TP-PID TP-Protocol-Identifier */
946 rawsms->pid = pdu[offset++];
947 }
948 if ((offset < pdu_len) && (parameter_indicator & 0x02)) {
949 /* TP-DCS TP-Data-Coding-Scheme */
950 rawsms->dcs = pdu[offset++];
951 }
952 if ((offset < pdu_len) && (parameter_indicator & 0x04)) {
953 /* TP-UDL TP-User-Data-Length */
954 /* TP-UD TP-User-Data */
955 COPY_USER_DATA(pdu, offset);
956 }
957 break;
958 case GN_SMS_MT_Command:
959 #if 0
960 dprintf("SMS-COMMAND found\n");
961 /* TP-Message-Type-Indicator */
962 /** decoded above **/
963 offset = 5;
964 /* TP-User-Data-Header-Indication */
965 rawsms->udh_indicator = pdu[offset] & 0x40;
966 /* TP-Status-Report-Request */
967 rawsms->report_status = pdu[offset] & 0x08;
968 offset++;
969 /* TP-Message Reference */
970 rawsms->reference = pdu[offset++];
971 /* TP-Protocol-Identifier */
972 rawsms->pid = pdu[offset++];
973 /* TP-Command-Type */
974 dprintf("TP-Command-Type 0x%02x\n", pdu[offset++]);
975 /* TP-Message-Number */
976 dprintf("TP-Message-Number 0x%02x\n", pdu[offset++]);
977 /* TP-Destination-Address */
978 l = (pdu[offset] % 2) ? pdu[offset] + 1 : pdu[offset];
979 l = l / 2 + 2;
980 memcpy(rawsms->remote_number, pdu + offset, l);
981 offset += l;
982 /* TP-Command-Data-Length */
983 dprintf("TP-Command-Data-Length 0x%02x\n", pdu[offset++]);
984 /* TP-Command-Data */
985 dprintf("TP-Command-Data 0x%02x ...\n", pdu[offset++]);
986 goto out;
987 break;
988 #endif
989 /* FALL THROUGH */
990 default:
991 dprintf("Unknown PDU type %d\n", rawsms->type);
992 return GN_ERR_INTERNALERROR;
993 }
994
995 /*
996 * Unicode message has length in octets. We need length in characters: see comment in sms_pdu_decode().
997 */
998 if ((rawsms->dcs & 0x08) == 0x08)
999 rawsms->length /= 2;
1000 out:
1001 return ret;
1002 #undef COPY_USER_DATA
1003 #undef COPY_REMOTE_NUMBER
1004 }
1005
1006 /**
1007 * gn_sms_request - High-level function for the explicit SMS reading from the phone
1008 * @data: GSM data for the phone driver
1009 * @state: current statemachine state
1010 *
1011 * This is the function for explicit requesting the SMS from the
1012 * phone driver. Note that raw_sms field in the gn_data structure must
1013 * be initialized
1014 */
gn_sms_request(gn_data * data,struct gn_statemachine * state)1015 gn_error gn_sms_request(gn_data *data, struct gn_statemachine *state)
1016 {
1017 if (!data->raw_sms) return GN_ERR_INTERNALERROR;
1018 return gn_sm_functions(GN_OP_GetSMS, data, state);
1019 }
1020
1021 /**
1022 * gn_sms_get- High-level function for reading SMS
1023 * @data: GSM data for the phone driver
1024 * @state: current statemachine state
1025 *
1026 * This function is the frontend for reading SMS. Note that SMS field
1027 * in the gn_data structure must be initialized.
1028 */
gn_sms_get(gn_data * data,struct gn_statemachine * state)1029 GNOKII_API gn_error gn_sms_get(gn_data *data, struct gn_statemachine *state)
1030 {
1031 gn_error error;
1032 gn_sms_raw rawsms;
1033
1034 if (!data->sms)
1035 return GN_ERR_INTERNALERROR;
1036 if (data->sms->number < 0)
1037 return GN_ERR_EMPTYLOCATION;
1038 if (data->sms->memory_type > GN_MT_LAST)
1039 return GN_ERR_INVALIDMEMORYTYPE;
1040 memset(&rawsms, 0, sizeof(gn_sms_raw));
1041 rawsms.number = data->sms->number;
1042 rawsms.memory_type = data->sms->memory_type;
1043 data->raw_sms = &rawsms;
1044 error = gn_sms_request(data, state);
1045 ERROR();
1046 data->sms->status = rawsms.status;
1047 return gn_sms_parse(data);
1048 }
1049
1050 /**
1051 * gn_sms_delete - High-level function for deleting SMS
1052 * @data: GSM data for the phone driver
1053 * @state: current statemachine state
1054 *
1055 * This function is the frontend for deleting SMS. Note that SMS field
1056 * in the gn_data structure must be initialized.
1057 */
gn_sms_delete(gn_data * data,struct gn_statemachine * state)1058 GNOKII_API gn_error gn_sms_delete(gn_data *data, struct gn_statemachine *state)
1059 {
1060 gn_sms_raw rawsms;
1061
1062 if (!data->sms) return GN_ERR_INTERNALERROR;
1063 memset(&rawsms, 0, sizeof(gn_sms_raw));
1064 rawsms.number = data->sms->number;
1065 rawsms.memory_type = data->sms->memory_type;
1066 data->raw_sms = &rawsms;
1067 return gn_sm_functions(GN_OP_DeleteSMS, data, state);
1068 }
1069
1070 /***
1071 *** OTHER FUNCTIONS
1072 ***/
1073
sms_request_no_validate(gn_data * data,struct gn_statemachine * state)1074 static gn_error sms_request_no_validate(gn_data *data, struct gn_statemachine *state)
1075 {
1076 return gn_sm_functions(GN_OP_GetSMSnoValidate, data, state);
1077 }
1078
gn_sms_get_no_validate(gn_data * data,struct gn_statemachine * state)1079 GNOKII_API gn_error gn_sms_get_no_validate(gn_data *data, struct gn_statemachine *state)
1080 {
1081 gn_error error;
1082 gn_sms_raw rawsms;
1083
1084 if (!data->sms) return GN_ERR_INTERNALERROR;
1085 memset(&rawsms, 0, sizeof(gn_sms_raw));
1086 rawsms.number = data->sms->number;
1087 rawsms.memory_type = data->sms->memory_type;
1088 data->raw_sms = &rawsms;
1089 error = sms_request_no_validate(data, state);
1090 ERROR();
1091 data->sms->status = rawsms.status;
1092 return gn_sms_parse(data);
1093 }
1094
gn_sms_delete_no_validate(gn_data * data,struct gn_statemachine * state)1095 GNOKII_API gn_error gn_sms_delete_no_validate(gn_data *data, struct gn_statemachine *state)
1096 {
1097 gn_sms_raw rawsms;
1098
1099 if (!data->sms) return GN_ERR_INTERNALERROR;
1100 memset(&rawsms, 0, sizeof(gn_sms_raw));
1101 rawsms.number = data->sms->number;
1102 rawsms.memory_type = data->sms->memory_type;
1103 data->raw_sms = &rawsms;
1104 return gn_sm_functions(GN_OP_DeleteSMSnoValidate, data, state);
1105 }
1106
sms_free_deleted(gn_data * data,int folder)1107 static gn_error sms_free_deleted(gn_data *data, int folder)
1108 {
1109 int i, j;
1110
1111 if (!data->sms_status) return GN_ERR_INTERNALERROR;
1112
1113 for (i = 0; i < data->folder_stats[folder]->used; i++) { /* for all previously found locations */
1114 if (data->message_list[i][folder]->status == GN_SMS_FLD_ToBeRemoved) { /* previously deleted and read message */
1115 dprintf("Found deleted message, which will now be freed! %i , %i\n",
1116 i, data->message_list[i][folder]->location);
1117 for (j = i; j < data->folder_stats[folder]->used; j++) {
1118 memcpy(data->message_list[j][folder], data->message_list[j + 1][folder],
1119 sizeof(gn_sms_message_list));
1120 }
1121 data->folder_stats[folder]->used--;
1122 i--;
1123 }
1124 }
1125 return GN_ERR_NONE;
1126 }
1127
sms_get_read(gn_data * data)1128 static gn_error sms_get_read(gn_data *data)
1129 {
1130 int i, j, found;
1131
1132 if (!data->message_list || !data->folder_stats || !data->sms_folder) return GN_ERR_INTERNALERROR;
1133
1134 for (i = 0; i < data->sms_folder->number; i++) { /* cycle through all messages in phone */
1135 found = 0;
1136 for (j = 0; j < data->folder_stats[data->sms_folder->folder_id]->used; j++) { /* and compare them to those alread in list */
1137 if (data->sms_folder->locations[i] == data->message_list[j][data->sms_folder->folder_id]->location) found = 1;
1138 }
1139 if (data->folder_stats[data->sms_folder->folder_id]->used >= GN_SMS_MESSAGE_MAX_NUMBER) {
1140 dprintf("Max messages number in folder exceeded (%d)\n", GN_SMS_MESSAGE_MAX_NUMBER);
1141 return GN_ERR_MEMORYFULL;
1142 }
1143 if (!found) {
1144 dprintf("Found new (read) message. Will store it at #%i!\n", data->folder_stats[data->sms_folder->folder_id]->used);
1145 dprintf("%i\n", data->sms_folder->locations[i]);
1146 data->message_list[data->folder_stats[data->sms_folder->folder_id]->used][data->sms_folder->folder_id]->location =
1147 data->sms_folder->locations[i];
1148 data->message_list[data->folder_stats[data->sms_folder->folder_id]->used][data->sms_folder->folder_id]->status = GN_SMS_FLD_New;
1149 data->folder_stats[data->sms_folder->folder_id]->used++;
1150 data->folder_stats[data->sms_folder->folder_id]->changed++;
1151 data->sms_status->changed++;
1152 }
1153 }
1154 return GN_ERR_NONE;
1155 }
1156
sms_get_deleted(gn_data * data)1157 static gn_error sms_get_deleted(gn_data *data)
1158 {
1159 int i, j, found = 0;
1160
1161 for (i = 0; i < data->folder_stats[data->sms_folder->folder_id]->used; i++) { /* for all previously found locations in folder */
1162 found = 0;
1163
1164 for (j = 0; j < data->sms_folder->number; j++) { /* see if there is a corresponding message in phone */
1165 if (data->message_list[i][data->sms_folder->folder_id]->location == data->sms_folder->locations[j]) found = 1;
1166 }
1167 if ((found == 0) && (data->message_list[i][data->sms_folder->folder_id]->status == GN_SMS_FLD_Old)) { /* we have found a deleted message */
1168 dprintf("found a deleted message!!!! i: %i, loc: %i, MT: %i \n",
1169 i, data->message_list[i][data->sms_folder->folder_id]->location, data->sms_folder->folder_id);
1170
1171 data->message_list[i][data->sms_folder->folder_id]->status = GN_SMS_FLD_Deleted;
1172 data->sms_status->changed++;
1173 data->folder_stats[data->sms_folder->folder_id]->changed++;
1174 }
1175 }
1176 return GN_ERR_NONE;
1177 }
1178
sms_verify_status(gn_data * data)1179 static gn_error sms_verify_status(gn_data *data)
1180 {
1181 int i, j;
1182
1183 for (i = 0; i < data->folder_stats[data->sms_folder->folder_id]->used; i++) { /* Cycle through all messages we know of */
1184 if ((data->message_list[i][data->sms_folder->folder_id]->status == GN_SMS_FLD_NotRead) || /* if it is a unread one, i.e. not in folderstatus */
1185 (data->message_list[i][data->sms_folder->folder_id]->status == GN_SMS_FLD_NotReadHandled)) {
1186 for (j = 0; j < data->sms_folder->number; j++) {
1187 if (data->message_list[i][data->sms_folder->folder_id]->location == data->sms_folder->locations[j]) {
1188 /* We have found a formerly unread message which has been read in the meantime */
1189 dprintf("Found a formerly unread message which has been read in the meantime: loc: %i\n",
1190 data->message_list[i][data->sms_folder->folder_id]->location);
1191 data->message_list[i][data->sms_folder->folder_id]->status = GN_SMS_FLD_Changed;
1192 data->sms_status->changed++;
1193 data->folder_stats[data->sms_folder->folder_id]->changed++;
1194 }
1195 }
1196 }
1197 }
1198 return GN_ERR_NONE;
1199 }
1200
1201
gn_sms_get_folder_changes(gn_data * data,struct gn_statemachine * state,int has_folders)1202 GNOKII_API gn_error gn_sms_get_folder_changes(gn_data *data, struct gn_statemachine *state, int has_folders)
1203 {
1204 gn_error error;
1205 gn_sms_folder sms_folder;
1206 gn_sms_folder_list sms_folder_list;
1207 int i, previous_unread, previous_total;
1208
1209 previous_total = data->sms_status->number;
1210 previous_unread = data->sms_status->unread;
1211 dprintf("GetFolderChanges: Old status: %d %d\n", data->sms_status->number, data->sms_status->unread);
1212
1213 error = gn_sm_functions(GN_OP_GetSMSStatus, data, state); /* Check overall SMS Status */
1214 ERROR();
1215 dprintf("GetFolderChanges: Status: %d %d\n", data->sms_status->number, data->sms_status->unread);
1216
1217 if (!has_folders) {
1218 if ((previous_total == data->sms_status->number) && (previous_unread == data->sms_status->unread))
1219 data->sms_status->changed = 0;
1220 else
1221 data->sms_status->changed = 1;
1222 return GN_ERR_NONE;
1223 }
1224
1225 data->sms_folder_list = &sms_folder_list;
1226 error = gn_sm_functions(GN_OP_GetSMSFolders, data, state);
1227 ERROR();
1228
1229 data->sms_status->folders_count = data->sms_folder_list->number;
1230
1231 for (i = 0; i < data->sms_status->folders_count; i++) {
1232 dprintf("GetFolderChanges: Freeing deleted messages for folder #%i\n", i);
1233 error = sms_free_deleted(data, i);
1234 ERROR();
1235
1236 data->sms_folder = &sms_folder;
1237 data->sms_folder->folder_id = (gn_memory_type) i + 12;
1238 dprintf("GetFolderChanges: Getting folder status for folder #%i\n", i);
1239 error = gn_sm_functions(GN_OP_GetSMSFolderStatus, data, state);
1240 ERROR();
1241
1242 data->sms_folder->folder_id = i; /* so we don't need to do a modulo 8 each time */
1243
1244 dprintf("GetFolderChanges: Reading read messages (%i) for folder #%i\n", data->sms_folder->number, i);
1245 error = sms_get_read(data);
1246 ERROR();
1247
1248 dprintf("GetFolderChanges: Getting deleted messages for folder #%i\n", i);
1249 error = sms_get_deleted(data);
1250 ERROR();
1251
1252 dprintf("GetFolderChanges: Verifying messages for folder #%i\n", i);
1253 error = sms_verify_status(data);
1254 ERROR();
1255 }
1256 return GN_ERR_NONE;
1257 }
1258
1259 /***
1260 *** ENCODING SMS
1261 ***/
1262
1263
1264 /**
1265 * sms_udh_encode - encodes User Data Header
1266 * @sms: SMS structure with the data source
1267 * @type:
1268 *
1269 * returns pointer to data it added;
1270 *
1271 * This function encodes the UserDataHeader as described in:
1272 * o GSM 03.40 version 6.1.0 Release 1997, section 9.2.3.24
1273 * o Smart Messaging Specification, Revision 1.0.0, September 15, 1997
1274 * o Smart Messaging Specification, Revision 3.0.0
1275 */
sms_udh_encode(gn_sms_raw * rawsms,int type)1276 static char *sms_udh_encode(gn_sms_raw *rawsms, int type)
1277 {
1278 unsigned char pos;
1279 char *udh = rawsms->user_data;
1280 char *res = NULL;
1281
1282 pos = udh[0];
1283
1284 switch (type) {
1285 case GN_SMS_UDH_None:
1286 break;
1287 case GN_SMS_UDH_VoiceMessage:
1288 case GN_SMS_UDH_FaxMessage:
1289 case GN_SMS_UDH_EmailMessage:
1290 return NULL;
1291 #if 0
1292 udh[pos+4] = udhi.u.SpecialSMSMessageIndication.MessageCount;
1293 if (udhi.u.SpecialSMSMessageIndication.Store) udh[pos+3] |= 0x80;
1294 #endif
1295 case GN_SMS_UDH_ConcatenatedMessages:
1296 dprintf("Adding ConcatMsg header\n");
1297 case GN_SMS_UDH_OpLogo:
1298 case GN_SMS_UDH_CallerIDLogo:
1299 case GN_SMS_UDH_Ringtone:
1300 case GN_SMS_UDH_MultipartMessage:
1301 case GN_SMS_UDH_WAPPush:
1302 udh[0] += headers[type].length;
1303 res = udh+pos+1;
1304 memcpy(res, headers[type].header, headers[type].length);
1305 rawsms->user_data_length += headers[type].length;
1306 rawsms->length += headers[type].length;
1307 break;
1308 default:
1309 dprintf("User Data Header type 0x%02x isn't supported\n", type);
1310 break;
1311 }
1312 if (!rawsms->udh_indicator) {
1313 rawsms->udh_indicator = 1;
1314 rawsms->length++; /* Length takes one byte, too */
1315 rawsms->user_data_length++;
1316 }
1317 return res;
1318 }
1319
1320 /**
1321 * sms_concat_header_encode - Adds concatenated messages header
1322 * @rawsms: processed SMS
1323 * @curr: current part number
1324 * @total: total parts number
1325 *
1326 * This function adds sequent part of the concatenated messages header. Note
1327 * that this header should be the first of all headers.
1328 */
sms_concat_header_encode(gn_sms_raw * rawsms,int curr,int total)1329 static gn_error sms_concat_header_encode(gn_sms_raw *rawsms, int curr, int total)
1330 {
1331 char *header = sms_udh_encode(rawsms, GN_SMS_UDH_ConcatenatedMessages);
1332 if (!header) return GN_ERR_NOTSUPPORTED;
1333 header[2] = 0xce; /* Message serial number. Is 0xce value somehow special? -- pkot */
1334 header[3] = total;
1335 header[4] = curr;
1336 return GN_ERR_NONE;
1337 }
1338
1339 /**
1340 * sms_data_encode - Encodes the data from the SMS structure to the phone frame
1341 *
1342 * @sms: #gn_sms structure holding user provided data
1343 * @rawsms: #gn_sms_raw structure to be filled with encoded data
1344 *
1345 * This function converts values from libgnokii format to a format more similar
1346 * to the GSM standard.
1347 * This function is capable of creating only one frame at a time.
1348 */
sms_data_encode(gn_sms * sms,gn_sms_raw * rawsms)1349 static gn_error sms_data_encode(gn_sms *sms, gn_sms_raw *rawsms)
1350 {
1351 gn_sms_dcs_alphabet_type al = GN_SMS_DCS_DefaultAlphabet;
1352 unsigned int i, size = 0;
1353 gn_error error;
1354
1355 /* Additional Headers */
1356 switch (sms->dcs.type) {
1357 case GN_SMS_DCS_GeneralDataCoding:
1358 dprintf("General Data Coding\n");
1359 switch (sms->dcs.u.general.m_class) {
1360 case 0: break;
1361 case 1: rawsms->dcs |= 0xf0; break; /* Class 0 */
1362 case 2: rawsms->dcs |= 0xf1; break; /* Class 1 */
1363 case 3: rawsms->dcs |= 0xf2; break; /* Class 2 */
1364 case 4: rawsms->dcs |= 0xf3; break; /* Class 3 */
1365 default: dprintf("General Data Coding class 0x%02x isn't supported\n", sms->dcs.u.general.m_class); break;
1366 }
1367 if (sms->dcs.u.general.compressed) {
1368 /* Compression not supported yet */
1369 dprintf("SMS message compression isn't supported\n");
1370 /* dcs[0] |= 0x20; */
1371 }
1372 al = sms->dcs.u.general.alphabet;
1373 break;
1374 case GN_SMS_DCS_MessageWaiting:
1375 al = sms->dcs.u.message_waiting.alphabet;
1376 if (sms->dcs.u.message_waiting.discard) rawsms->dcs |= 0xc0;
1377 else if (sms->dcs.u.message_waiting.alphabet == GN_SMS_DCS_UCS2) rawsms->dcs |= 0xe0;
1378 else rawsms->dcs |= 0xd0;
1379
1380 if (sms->dcs.u.message_waiting.active) rawsms->dcs |= 0x08;
1381 rawsms->dcs |= (sms->dcs.u.message_waiting.type & 0x03);
1382
1383 break;
1384 default:
1385 dprintf("Data Coding Scheme type 0x%02x isn't supported\n", sms->dcs.type);
1386 return GN_ERR_WRONGDATAFORMAT;
1387 }
1388
1389 for (i = 0; i < GN_SMS_PART_MAX_NUMBER; i++) {
1390 switch (sms->user_data[i].type) {
1391 case GN_SMS_DATA_Bitmap:
1392 switch (sms->user_data[i].u.bitmap.type) {
1393 case GN_BMP_PictureMessage:
1394 size = sms_nokia_bitmap_encode(&(sms->user_data[i].u.bitmap),
1395 rawsms->user_data + rawsms->user_data_length,
1396 (i == 0));
1397 break;
1398 case GN_BMP_OperatorLogo:
1399 if (!sms_udh_encode(rawsms, GN_SMS_UDH_OpLogo)) return GN_ERR_NOTSUPPORTED;
1400 size = gn_bmp_sms_encode(&(sms->user_data[i].u.bitmap),
1401 rawsms->user_data + rawsms->user_data_length);
1402 break;
1403 case GN_BMP_CallerLogo:
1404 if (!sms_udh_encode(rawsms, GN_SMS_UDH_CallerIDLogo)) return GN_ERR_NOTSUPPORTED;
1405 size = gn_bmp_sms_encode(&(sms->user_data[i].u.bitmap),
1406 rawsms->user_data + rawsms->user_data_length);
1407 break;
1408 default:
1409 size = gn_bmp_sms_encode(&(sms->user_data[i].u.bitmap),
1410 rawsms->user_data + rawsms->user_data_length);
1411 break;
1412 }
1413 rawsms->length += size;
1414 rawsms->user_data_length += size;
1415 rawsms->dcs = 0xf5;
1416 rawsms->udh_indicator = 1;
1417 break;
1418
1419 case GN_SMS_DATA_Animation: {
1420 int j;
1421
1422 for (j = 0; j < 4; j++) {
1423 size = gn_bmp_sms_encode(&(sms->user_data[i].u.animation[j]), rawsms->user_data + rawsms->user_data_length);
1424 rawsms->length += size;
1425 rawsms->user_data_length += size;
1426 }
1427 rawsms->dcs = 0xf5;
1428 rawsms->udh_indicator = 1;
1429 break;
1430 }
1431
1432 case GN_SMS_DATA_Text: {
1433 unsigned int length, udh_length, offset = rawsms->user_data_length;
1434
1435 length = sms->user_data[i].length;
1436 if (sms->udh.length)
1437 udh_length = sms->udh.length + 1;
1438 else
1439 udh_length = 0;
1440 switch (al) {
1441 case GN_SMS_DCS_DefaultAlphabet:
1442 dprintf("Default Alphabet\n");
1443 size = char_7bit_pack((7 - (udh_length % 7)) % 7,
1444 sms->user_data[i].u.text,
1445 rawsms->user_data + offset,
1446 &length);
1447 rawsms->length = length + (udh_length * 8 + 6) / 7;
1448 rawsms->user_data_length = size + offset;
1449 dprintf("\tsize: %d\n\toffset: %d\n", size, offset);
1450 dprintf("\tencoded size: %d\n\trawsms length: %d\n\trawsms user data length: %d\n", size, rawsms->length, rawsms->user_data_length);
1451 break;
1452 case GN_SMS_DCS_8bit:
1453 dprintf("8bit\n");
1454 rawsms->dcs |= 0xf4;
1455 memcpy(rawsms->user_data + offset, sms->user_data[i].u.text, sms->user_data[i].u.text[0]);
1456 rawsms->user_data_length = rawsms->length = length + udh_length;
1457 break;
1458 case GN_SMS_DCS_UCS2:
1459 dprintf("UCS-2\n");
1460 rawsms->dcs |= 0x08;
1461 length = ucs2_encode(rawsms->user_data + offset, GN_SMS_LONG_MAX_LENGTH, sms->user_data[i].u.text, length);
1462 if (length < 0) {
1463 dprintf("Failed to encode UCS2 message\n");
1464 return GN_ERR_WRONGDATAFORMAT;
1465 }
1466 rawsms->user_data_length = rawsms->length = length + udh_length;
1467 break;
1468 default:
1469 return GN_ERR_WRONGDATAFORMAT;
1470 }
1471 break;
1472 }
1473
1474 case GN_SMS_DATA_NokiaText:
1475 size = sms_nokia_text_encode(sms->user_data[i].u.text,
1476 rawsms->user_data + rawsms->user_data_length,
1477 (i == 0));
1478 rawsms->length += size;
1479 rawsms->user_data_length += size;
1480 break;
1481
1482 case GN_SMS_DATA_iMelody:
1483 size = imelody_sms_encode(sms->user_data[i].u.text, rawsms->user_data + rawsms->user_data_length);
1484 dprintf("Imelody, size %d\n", size);
1485 rawsms->length += size;
1486 rawsms->user_data_length += size;
1487 rawsms->dcs = 0xf5;
1488 rawsms->udh_indicator = 1;
1489 break;
1490
1491 case GN_SMS_DATA_Multi:
1492 size = sms->user_data[0].length;
1493 if (!sms_udh_encode(rawsms, GN_SMS_UDH_MultipartMessage)) return GN_ERR_NOTSUPPORTED;
1494 error = sms_concat_header_encode(rawsms, sms->user_data[i].u.multi.curr, sms->user_data[i].u.multi.total);
1495 ERROR();
1496 memcpy(rawsms->user_data + rawsms->user_data_length, sms->user_data[i].u.multi.binary, MAX_SMS_PART - 6);
1497 rawsms->length += size;
1498 rawsms->user_data_length += size;
1499 rawsms->dcs = 0xf5;
1500 break;
1501
1502 case GN_SMS_DATA_Ringtone:
1503 if (!sms_udh_encode(rawsms, GN_SMS_UDH_Ringtone)) return GN_ERR_NOTSUPPORTED;
1504 size = ringtone_sms_encode(rawsms->user_data + rawsms->length, &sms->user_data[i].u.ringtone);
1505 rawsms->length += size;
1506 rawsms->user_data_length += size;
1507 rawsms->dcs = 0xf5;
1508 break;
1509
1510 case GN_SMS_DATA_WAPPush:
1511 if (!sms_udh_encode(rawsms, GN_SMS_UDH_WAPPush)) return GN_ERR_NOTSUPPORTED;
1512 size = sms->user_data[i].length;
1513 memcpy(rawsms->user_data + rawsms->user_data_length, sms->user_data[i].u.text, size );
1514 rawsms->length += size;
1515 rawsms->user_data_length += size;
1516 rawsms->dcs = 0xf5;
1517 break;
1518
1519 case GN_SMS_DATA_Concat:
1520 al = GN_SMS_DCS_8bit;
1521 rawsms->dcs = 0xf5;
1522 sms_concat_header_encode(rawsms, sms->user_data[i].u.concat.curr, sms->user_data[i].u.concat.total);
1523 break;
1524
1525 case GN_SMS_DATA_None:
1526 return GN_ERR_NONE;
1527
1528 default:
1529 dprintf("User Data type 0x%02x isn't supported\n", sms->user_data[i].type);
1530 break;
1531 }
1532 }
1533 return GN_ERR_NONE;
1534 }
1535
sms_prepare(gn_sms * sms,gn_sms_raw * rawsms)1536 gn_error sms_prepare(gn_sms *sms, gn_sms_raw *rawsms)
1537 {
1538 int i;
1539
1540 switch (rawsms->type = sms->type) {
1541 case GN_SMS_MT_Submit:
1542 case GN_SMS_MT_Deliver:
1543 case GN_SMS_MT_Picture:
1544 break;
1545 case GN_SMS_MT_DeliveryReport:
1546 default:
1547 dprintf("Raw SMS message type 0x%02x isn't supported\n", rawsms->type);
1548 return GN_ERR_NOTSUPPORTED;
1549 }
1550 /* Encoding the header */
1551 rawsms->report = sms->delivery_report;
1552 rawsms->remote_number[0] = char_semi_octet_pack(sms->remote.number, rawsms->remote_number + 1, sms->remote.type);
1553 if (rawsms->remote_number[0] > GN_SMS_NUMBER_MAX_LENGTH) {
1554 dprintf("Remote number length %d > %d\n", rawsms->remote_number[0], GN_SMS_NUMBER_MAX_LENGTH);
1555 return GN_ERR_ENTRYTOOLONG;
1556 }
1557 rawsms->validity_indicator = GN_SMS_VP_RelativeFormat;
1558 rawsms->validity[0] = GN_SMS_VP_72H;
1559
1560 for (i = 0; i < sms->udh.number; i++)
1561 if (sms->udh.udh[i].type == GN_SMS_UDH_ConcatenatedMessages)
1562 sms_concat_header_encode(rawsms, sms->udh.udh[i].u.concatenated_short_message.current_number,
1563 sms->udh.udh[i].u.concatenated_short_message.maximum_number);
1564
1565 return sms_data_encode(sms, rawsms);
1566 }
1567
sms_dump_raw(gn_sms_raw * rawsms)1568 static void sms_dump_raw(gn_sms_raw *rawsms)
1569 {
1570 #ifdef DEBUG
1571 char buf[10240];
1572
1573 memset(buf, 0, 10240);
1574
1575 dprintf("dcs: 0x%02x\n", rawsms->dcs);
1576 dprintf("Length: 0x%02x\n", rawsms->length);
1577 dprintf("user_data_length: 0x%02x\n", rawsms->user_data_length);
1578 dprintf("ValidityIndicator: %d\n", rawsms->validity_indicator);
1579 bin2hex(buf, rawsms->user_data, rawsms->user_data_length);
1580 dprintf("user_data: %s\n", buf);
1581 #endif
1582 }
1583
1584
gn_sms_udh_add(gn_sms * sms,gn_sms_udh_type type)1585 GNOKII_API int gn_sms_udh_add(gn_sms *sms, gn_sms_udh_type type)
1586 {
1587 sms->udh.length += headers[type].length;
1588 sms->udh.udh[sms->udh.number].type = type;
1589 sms->udh.number++;
1590 return sms->udh.number - 1;
1591 }
1592
1593 static gn_error sms_send_single(gn_data *data, struct gn_statemachine *state);
1594 static gn_error sms_send_long(gn_data *data, struct gn_statemachine *state, int octets);
1595
1596 /**
1597 * gn_sms_send - The main function for the SMS sending
1598 * @data: GSM data for the phone driver
1599 * @state: current statemachine state
1600 *
1601 * The high level function to send SMS. You need to fill in data->sms
1602 * (gn_sms) in the higher level. This is converted to raw_sms here,
1603 * and then phone driver takes the fields it needs and sends it in the
1604 * phone specific way to the phone.
1605 *
1606 * In case of multipart message, sending is interrupted on the first
1607 * failure. It means that if message got split into 3 messages, and 2nd
1608 * part fails to send, the 3rd part is not attepted to get sent. There is
1609 * no interface to retry multipart message from the failed part.
1610 *
1611 * data->sms->parts indicates how many parts were (or supposed to be) sent.
1612 *
1613 * data->sms->reference contains data->sms->parts reference numbers from the
1614 * sent messages. They might be used to relate the delivery reports. If
1615 * given element has reference 0, sending this part, falied.
1616 */
gn_sms_send(gn_data * data,struct gn_statemachine * state)1617 GNOKII_API gn_error gn_sms_send(gn_data *data, struct gn_statemachine *state)
1618 {
1619 int i, total;
1620 int enc_chars, ext_chars;
1621 gn_error retval;
1622 gn_sms sms;
1623 gn_sms *orig_sms;
1624
1625 dprintf("=====> ENTER gn_sms_send()\n");
1626 /*
1627 * Make sure that we will not get any corruption if it was not
1628 * initialized properly by the app. Issue a warning that
1629 * application may leak the memory
1630 */
1631 if (data->sms->reference) {
1632 dprintf("data->sms->reference was not set to NULL. The app may not initialize it\nproperly or leak memory.\n");
1633 data->sms->reference = NULL;
1634 }
1635
1636 /*
1637 * We need to work on data->sms->user_data; let's have a copy of the
1638 * original request so message could be resubmitted.
1639 * FIXME: use for that only additional user_data structure
1640 */
1641 orig_sms = data->sms;
1642 memcpy(&sms, data->sms, sizeof(gn_sms));
1643 data->sms = &sms;
1644
1645 /* Convert all the input strings to UTF-8 */
1646 if (data->sms->dcs.u.general.alphabet != GN_SMS_DCS_8bit) {
1647 i = 0;
1648 data->sms->dcs.u.general.alphabet = GN_SMS_DCS_DefaultAlphabet;
1649 while (data->sms->user_data[i].type != GN_SMS_DATA_None) {
1650 gchar *str;
1651 gsize inlen, outlen;
1652 gn_sms_dcs_alphabet_type enc;
1653
1654 if (data->sms->user_data[i].type == GN_SMS_DATA_Text ||
1655 data->sms->user_data[i].type == GN_SMS_DATA_NokiaText) {
1656 str = g_locale_to_utf8(data->sms->user_data[i].u.text, -1, &inlen, &outlen, NULL);
1657 data->sms->user_data[i].chars = g_utf8_strlen(str, outlen);
1658 memset(data->sms->user_data[i].u.text, 0, sizeof(data->sms->user_data[i].u.text));
1659 g_utf8_strncpy(data->sms->user_data[i].u.text, str, data->sms->user_data[i].chars);
1660 g_free(str);
1661 /* Let's make sure the encoding is correct */
1662 enc = char_def_alphabet_string_stats(data->sms->user_data[i].u.text, &enc_chars, &ext_chars);
1663 if (enc == GN_SMS_DCS_UCS2)
1664 data->sms->dcs.u.general.alphabet = enc;
1665 data->sms->user_data[i].chars = enc_chars;
1666 } else {
1667 /* FIXME: that's wrong for Nokia Multipart Picture Message, does anybody still use it? */
1668 data->sms->dcs.u.general.alphabet = GN_SMS_DCS_8bit;
1669 }
1670 i++;
1671 }
1672 }
1673
1674 dprintf("enc_chars: %d\next_chars: %d\n", enc_chars, ext_chars);
1675 /* Count the total length of the message text octets to be sent */
1676 total = 0;
1677 i = 0;
1678 while (data->sms->user_data[i].type != GN_SMS_DATA_None) {
1679 switch (data->sms->dcs.u.general.alphabet) {
1680 case GN_SMS_DCS_DefaultAlphabet:
1681 /*
1682 * Extended alphabet chars are doubled on the input.
1683 */
1684 total += ((enc_chars + ext_chars) * 7 + 7) / 8;
1685 break;
1686 case GN_SMS_DCS_UCS2:
1687 total += (enc_chars * 2);
1688 break;
1689 default:
1690 total += data->sms->user_data[i].length;
1691 break;
1692 }
1693 i++;
1694 }
1695
1696 dprintf("total: %d\n", total);
1697
1698 /* FIXME: from now on we handle only the user_data[0] */
1699 dprintf("size of the input string: %d bytes\n", data->sms->user_data[0].length);
1700 dprintf("number of characters in the input string: %d chars\n", data->sms->user_data[0].chars);
1701
1702 /* It will eventually get overwritten in sms_send_long() */
1703 data->sms->parts = 1;
1704
1705 if (total > MAX_SMS_PART)
1706 retval = sms_send_long(data, state, total);
1707 else
1708 retval = sms_send_single(data, state);
1709
1710 data->sms = orig_sms;
1711 /* Let's put back information about sent messages */
1712 data->sms->reference = sms.reference;
1713 data->sms->parts = sms.parts;
1714
1715 return retval;
1716 }
1717
sms_send_single(gn_data * data,struct gn_statemachine * state)1718 static gn_error sms_send_single(gn_data *data, struct gn_statemachine *state)
1719 {
1720 int i = 0;
1721 gn_error error = GN_ERR_NONE;
1722 gn_sms_raw rawsms;
1723
1724 data->raw_sms = &rawsms;
1725 memset(&rawsms, 0, sizeof(rawsms));
1726
1727 if (!data->sms)
1728 return GN_ERR_INTERNALERROR;
1729
1730 if (data->sms->remote.number[0] == 0) {
1731 dprintf("Recipient number cannot be NULL.\n");
1732 return GN_ERR_WRONGDATAFORMAT;
1733 }
1734
1735 dprintf("=====> ENTER sms_send_single()\n");
1736 data->raw_sms->status = GN_SMS_Sent;
1737
1738 data->raw_sms->message_center[0] = char_semi_octet_pack(data->sms->smsc.number, data->raw_sms->message_center + 1, data->sms->smsc.type);
1739 if (data->raw_sms->message_center[0] % 2)
1740 data->raw_sms->message_center[0]++;
1741 if (data->raw_sms->message_center[0])
1742 data->raw_sms->message_center[0] = data->raw_sms->message_center[0] / 2 + 1;
1743
1744 error = sms_prepare(data->sms, data->raw_sms);
1745 if (error != GN_ERR_NONE)
1746 return error;
1747
1748 sms_dump_raw(data->raw_sms);
1749 dprintf("Input is %d bytes long\n", data->sms->user_data[0].length);
1750 dprintf("SMS is %d octets long\n", data->raw_sms->user_data_length);
1751
1752 dprintf("Sending\n");
1753 error = gn_sm_functions(GN_OP_SendSMS, data, state);
1754
1755 /* If there was an error, let's not put the reference into data->sms */
1756 if (error == GN_ERR_NONE) {
1757 /* In case of multipart message it is already allocated */
1758 if (!data->sms->reference)
1759 data->sms->reference = calloc(data->sms->parts, sizeof(unsigned int));
1760
1761 /* We send SMS parts from the first part to last. */
1762 i = 0;
1763 while (i < data->sms->parts-1 && data->sms->reference[i] != 0)
1764 i++;
1765 data->sms->reference[i] = data->raw_sms->reference;
1766 }
1767
1768 return error;
1769 }
1770
1771 /*
1772 * FIXME: we stop sending multipart message on first error. We may end up
1773 * with few messages send, and few failed. We're not able to resend only
1774 * failed ones.
1775 */
sms_send_long(gn_data * data,struct gn_statemachine * state,int octets)1776 static gn_error sms_send_long(gn_data *data, struct gn_statemachine *state, int octets)
1777 {
1778 int i, j, k, count, size, start, copied, refnum, is_concat = -1, max_sms_len = MAX_SMS_PART;
1779 gn_sms_user_data ud[GN_SMS_PART_MAX_NUMBER];
1780 gn_error error = GN_ERR_NONE;
1781 time_t t;
1782
1783 start = 0;
1784 copied = 0;
1785 count = data->sms->parts;
1786
1787 dprintf("=====> ENTER sms_send_long()\n");
1788
1789 dprintf("count: %d\n", count);
1790
1791 /* If there's no concat header we need to add one */
1792 for (i = 0; i < data->sms->number; i++) {
1793 if (data->sms->udh.udh[i].type == GN_SMS_UDH_ConcatenatedMessages)
1794 is_concat = i;
1795 }
1796 if (is_concat == -1)
1797 is_concat = gn_sms_udh_add(data->sms, GN_SMS_UDH_ConcatenatedMessages);
1798
1799 /* We need to attach user data header to each part */
1800 max_sms_len -= (data->sms->udh.length + 1);
1801
1802 /* Count number of SMS to be sent */
1803 count = (octets + max_sms_len - 1) / max_sms_len;
1804 dprintf("Will need %d sms-es\n", count);
1805 dprintf("SMS is %d octects long but we can only send %d octects in a single SMS after adding %d octects for udh\n", octets, max_sms_len, data->sms->udh.length + 1);
1806
1807 data->sms->parts = count;
1808 data->sms->reference = calloc(count, sizeof(unsigned int));
1809
1810 /* Generate reference number */
1811 time(&t);
1812 srand(t);
1813 refnum = (int)(255.0*rand()/(RAND_MAX+1.0));
1814
1815 /* have a copy of the original data */
1816 i = 0;
1817 while (data->sms->user_data[i].type != GN_SMS_DATA_None) {
1818 memcpy(&ud[i], &data->sms->user_data[i], sizeof(gn_sms_user_data));
1819 i++;
1820 }
1821
1822 for (i = 0; i < count; i++) {
1823 dprintf("Sending sms #%d (refnum: %d)\n", i+1, refnum);
1824 data->sms->udh.udh[is_concat].u.concatenated_short_message.reference_number = refnum;
1825 data->sms->udh.udh[is_concat].u.concatenated_short_message.maximum_number = count;
1826 data->sms->udh.udh[is_concat].u.concatenated_short_message.current_number = i+1;
1827 switch (data->sms->dcs.u.general.alphabet) {
1828 case GN_SMS_DCS_DefaultAlphabet:
1829 start += copied;
1830 memset(&data->sms->user_data[0], 0, sizeof(gn_sms_user_data));
1831 data->sms->user_data[0].type = ud[0].type;
1832 dprintf("%d %d %d\n", start, copied, max_sms_len);
1833 copied = char_def_alphabet_string_copy(data->sms->user_data[0].u.text, ud[0].u.text, max_sms_len * 8 / 7, start);
1834 dprintf("\tnumber of processed characters: %d\n", copied);
1835 break;
1836 case GN_SMS_DCS_UCS2:
1837 /* We need to copy precisely not to cut character in the middle */
1838 start += copied;
1839 memset(&data->sms->user_data[0], 0, sizeof(gn_sms_user_data));
1840 data->sms->user_data[0].type = ud[0].type;
1841 /* FIXME: We assume UTF8 input */
1842 size = 1;
1843 #define C ud[0].u.text[start + j]
1844 for (j = 0, k = 0; start + j < ud[0].length && k < max_sms_len / 2; j++) {
1845 size--;
1846 if (!size) {
1847 /* Let's detect size of UTF8 char */
1848 if (C >= 0 && C < 128)
1849 size = 1;
1850 else if (C >= 192 && C < 224)
1851 size = 2;
1852 else if (C >= 224 && C < 240)
1853 size = 3;
1854 else if (C >= 240 && C < 248)
1855 size = 4;
1856 else if (C >= 248 && C < 252)
1857 size = 5;
1858 else if (C >= 252 && C < 254)
1859 size = 6;
1860 else
1861 /* FIXME: handle it somehow */
1862 dprintf("CHARACTER ENCODING ERROR\n");
1863 k++;
1864 }
1865 /* Avoid cutting a character */
1866 if (k < max_sms_len / 2)
1867 data->sms->user_data[0].u.text[j] = ud[0].u.text[start + j];
1868 else
1869 j--;
1870 }
1871 #undef C
1872 data->sms->user_data[0].length = copied = j;
1873 dprintf("DEBUG: copied: %d\n", copied);
1874 break;
1875 default:
1876 start += copied;
1877 if (ud[0].length - start >= max_sms_len) {
1878 copied = max_sms_len;
1879 } else {
1880 copied = (ud[0].length - start) % (max_sms_len);
1881 }
1882 memset(&data->sms->user_data[0], 0, sizeof(gn_sms_user_data));
1883 data->sms->user_data[0].type = ud[0].type;
1884 data->sms->user_data[0].length = copied;
1885 memcpy(data->sms->user_data[0].u.text, ud[0].u.text+start, copied);
1886 switch (ud[0].type) {
1887 case GN_SMS_DATA_Bitmap:
1888 break;
1889 case GN_SMS_DATA_Ringtone:
1890 break;
1891 default:
1892 break;
1893 }
1894 break;
1895 }
1896 dprintf("Text to be sent in this part: %s\n", data->sms->user_data[0].u.text);
1897 error = sms_send_single(data, state);
1898 ERROR();
1899 }
1900 return GN_ERR_NONE;
1901 }
1902
gn_sms_save(gn_data * data,struct gn_statemachine * state)1903 GNOKII_API gn_error gn_sms_save(gn_data *data, struct gn_statemachine *state)
1904 {
1905 gn_error error = GN_ERR_NONE;
1906 gn_sms_raw rawsms;
1907
1908 data->raw_sms = &rawsms;
1909 memset(&rawsms, 0, sizeof(rawsms));
1910
1911 data->raw_sms->number = data->sms->number;
1912 data->raw_sms->status = data->sms->status;
1913 data->raw_sms->memory_type = data->sms->memory_type;
1914
1915 sms_timestamp_pack(&data->sms->smsc_time, data->raw_sms->smsc_time);
1916 dprintf("\tDate: %s\n", sms_timestamp_print(data->raw_sms->smsc_time));
1917
1918 if (data->sms->smsc.number[0] != '\0') {
1919 data->raw_sms->message_center[0] =
1920 char_semi_octet_pack(data->sms->smsc.number, data->raw_sms->message_center + 1, data->sms->smsc.type);
1921 if (data->raw_sms->message_center[0] % 2) data->raw_sms->message_center[0]++;
1922 if (data->raw_sms->message_center[0])
1923 data->raw_sms->message_center[0] = data->raw_sms->message_center[0] / 2 + 1;
1924 }
1925 error = sms_prepare(data->sms, data->raw_sms);
1926 ERROR();
1927
1928 if (data->raw_sms->length > GN_SMS_MAX_LENGTH) {
1929 dprintf("SMS is too long? %d\n", data->raw_sms->length);
1930 goto cleanup;
1931 }
1932
1933 error = gn_sm_functions(GN_OP_SaveSMS, data, state);
1934
1935 /* the message was perhaps not stored at the specified location,
1936 but the phone driver probably knows where, so copy it over */
1937 data->sms->number = data->raw_sms->number;
1938
1939 cleanup:
1940 data->raw_sms = NULL;
1941 return error;
1942 }
1943
1944 /* WAPPush functions */
1945
encode_attr_inline_string(char token,char * string,int * data_len)1946 char *encode_attr_inline_string(char token, char *string, int *data_len)
1947 {
1948 char *data = NULL;
1949
1950 /* we need 3 extra bytes for tags */
1951 *data_len = strlen(string) + 3;
1952 data = malloc(*data_len);
1953
1954 if (!data) {
1955 return NULL;
1956 }
1957
1958 data[0] = token;
1959 data[1] = TAG_INLINE;
1960 memcpy(data + 2, string, strlen(string));
1961 data[*data_len - 1] = 0x00;
1962
1963 return data;
1964 }
1965
encode_indication(gn_wap_push * wp,int * data_len)1966 char *encode_indication(gn_wap_push *wp, int *data_len)
1967 {
1968 char *data = NULL;
1969 char *attr = NULL;
1970 int attr_len = 0;
1971 int offset = 0;
1972
1973 /* encode tag attribute */
1974 attr = encode_attr_inline_string(ATTR_HREF, wp->url, &attr_len);
1975
1976 if (!attr || !attr_len) {
1977 return NULL;
1978 }
1979
1980 /* need 5 extra bytes for indication token & attributes */
1981 *data_len = attr_len + strlen(wp->text) + 5;
1982 data = malloc(*data_len);
1983
1984 if (!data) {
1985 free(attr);
1986 return NULL;
1987 }
1988
1989 /* indication tag token */
1990 data[offset++] = TOKEN_KNOWN_AC | TAG_INDICATION;
1991
1992 /* attribute */
1993 memcpy(data + offset, attr, attr_len);
1994 offset += attr_len;
1995 data[offset++] = TAG_END;
1996
1997 /* wappush text */
1998 data[offset++] = TAG_INLINE;
1999 memcpy(data + offset, wp->text, strlen(wp->text));
2000 offset += strlen(wp->text);
2001 data[offset++] = 0x00;
2002
2003 /* tag end */
2004 data[offset++] = TAG_END;
2005
2006 free(attr);
2007
2008 return data;
2009 }
2010
encode_si(gn_wap_push * wp,int * data_len)2011 char *encode_si(gn_wap_push *wp, int *data_len)
2012 {
2013 char *data = NULL;
2014 char *child = NULL;
2015 int child_len = 0;
2016
2017 child = encode_indication(wp, &child_len);
2018
2019 if (!child || !data_len) {
2020 return NULL;
2021 }
2022
2023 /* we need extra 2 bytes for si token */
2024 *data_len = child_len + 2;
2025 data = malloc(*data_len);
2026
2027 if (!data) {
2028 free(child);
2029 return NULL;
2030 }
2031
2032 data[0] = TOKEN_KNOWN_C | TAG_SI;
2033 memcpy(data + 1, child, child_len);
2034 data[*data_len - 1] = TAG_END;
2035
2036 free(child);
2037
2038 return data;
2039 }
2040
gn_wap_push_encode(gn_wap_push * wp)2041 GNOKII_API gn_error gn_wap_push_encode(gn_wap_push *wp)
2042 {
2043
2044 char *data = NULL;
2045 int data_len = 0;
2046
2047 data = encode_si(wp, &data_len);
2048
2049 if (!data || !data_len) {
2050 return GN_ERR_FAILED;
2051 }
2052
2053 wp->data = malloc(data_len + sizeof(gn_wap_push_header));
2054
2055 if (!wp->data) {
2056 return GN_ERR_FAILED;
2057 }
2058
2059 memcpy(wp->data, &wp->header, sizeof(gn_wap_push_header));
2060 memcpy(wp->data + sizeof(gn_wap_push_header), data, data_len);
2061
2062 wp->data_len = data_len + sizeof(gn_wap_push_header);
2063
2064 return GN_ERR_NONE;
2065 }
2066
gn_wap_push_init(gn_wap_push * wp)2067 GNOKII_API void gn_wap_push_init(gn_wap_push *wp)
2068 {
2069
2070 if (!wp) {
2071 return;
2072 }
2073
2074 memset(wp, 0, sizeof(gn_wap_push));
2075
2076 wp->header.wsp_tid = 0x00;
2077 wp->header.wsp_pdu = PDU_TYPE_Push;
2078 wp->header.wsp_hlen = 0x01;
2079 wp->header.wsp_content_type = CONTENT_TYPE;
2080
2081 wp->header.version = WBXML_VERSION;
2082 wp->header.public_id = TAG_SI;
2083 wp->header.charset = WAPPush_CHARSET;
2084 wp->header.stl = 0x00; /* string table length */
2085 }
2086
status2str(gn_sms_message_status status)2087 static char *status2str(gn_sms_message_status status)
2088 {
2089 switch (status) {
2090 case GN_SMS_Unread:
2091 return "Unread";
2092 case GN_SMS_Sent:
2093 return "Sent";
2094 case GN_SMS_Unsent:
2095 return "Unsent";
2096 default:
2097 return "Read";
2098 }
2099 }
2100
2101 #define MAX_TEXT_SUMMARY 20
2102 #define MAX_SUBJECT_LENGTH 25
2103 #define MAX_DATE_LENGTH 255
2104
2105 /* From snprintf(3) manual */
allocate(char * fmt,...)2106 static char *allocate(char *fmt, ...)
2107 {
2108 char *str, *nstr;
2109 int len, size = 100;
2110 va_list ap;
2111
2112 str = calloc(100, sizeof(char));
2113 if (!str)
2114 return NULL;
2115
2116 while (1) {
2117 va_start(ap, fmt);
2118 len = vsnprintf(str, size, fmt, ap);
2119 va_end(ap);
2120 if (len >= size) /* too small buffer */
2121 size = len + 1;
2122 else if (len > -1) /* buffer OK */
2123 return str;
2124 else /* let's try with larger buffer */
2125 size *= 2;
2126 nstr = realloc(str, size);
2127 if (!nstr) {
2128 free(str);
2129 return NULL;
2130 }
2131 str = nstr;
2132 }
2133 }
2134
2135 /* Here we allocate place for the line to append and we append it.
2136 * We free() the appended line. */
2137 #define APPEND(dst, src, size) \
2138 do { \
2139 char *ndst; \
2140 int old = size; \
2141 size += strlen(src); \
2142 ndst = realloc(dst, size + 1); \
2143 if (!ndst) { \
2144 free(dst); \
2145 goto error; \
2146 } \
2147 dst = ndst; \
2148 dst[old] = 0; \
2149 strcat(dst, src); \
2150 free(src); \
2151 } while (0)
2152
2153 #define CONCAT(dst, src, size, pattern, ...) \
2154 do { \
2155 src = allocate(pattern, __VA_ARGS__); \
2156 if (!src) \
2157 goto error; \
2158 APPEND(dst, src, size); \
2159 } while (0);
2160
2161 /* Returns allocated space for mbox-compatible formatted SMS */
2162 /* TODO:
2163 * o add encoding information
2164 * o support non-text SMS (MMS, EMS, delivery report, ...)
2165 */
gn_sms2mbox(gn_sms * sms,char * from)2166 GNOKII_API char *gn_sms2mbox(gn_sms *sms, char *from)
2167 {
2168 struct tm t, *loctime;
2169 time_t caltime;
2170 int size = 0;
2171 char *tmp;
2172 #ifdef ENABLE_NLS
2173 char *loc;
2174 #endif
2175 char *buf = NULL, *aux = NULL;
2176
2177 t.tm_sec = sms->smsc_time.second;
2178 t.tm_min = sms->smsc_time.minute;
2179 t.tm_hour = sms->smsc_time.hour;
2180 t.tm_mday = sms->smsc_time.day;
2181 t.tm_mon = sms->smsc_time.month - 1;
2182 t.tm_year = sms->smsc_time.year - 1900;
2183 #ifdef HAVE_TM_GMTON
2184 if (sms->smsc_time.timezone)
2185 t.tm_gmtoff = sms->smsc_time.timezone * 3600;
2186 #endif
2187 caltime = mktime(&t);
2188 loctime = localtime(&caltime);
2189
2190 #ifdef ENABLE_NLS
2191 loc = setlocale(LC_ALL, "C");
2192 #endif
2193 switch (sms->status) {
2194 case GN_SMS_Sent:
2195 case GN_SMS_Unsent:
2196 CONCAT(buf, tmp, size, "From %s@%s %s", "+0", from, asctime(loctime));
2197 break;
2198 case GN_SMS_Read:
2199 case GN_SMS_Unread:
2200 default:
2201 CONCAT(buf, tmp, size, "From %s@%s %s", sms->remote.number, from, asctime(loctime));
2202 break;
2203 }
2204
2205 tmp = calloc(MAX_DATE_LENGTH, sizeof(char));
2206 if (!tmp)
2207 goto error;
2208 strftime(tmp, MAX_DATE_LENGTH - 1, "Date: %a, %d %b %Y %H:%M:%S %z (%Z)\n", loctime);
2209 #ifdef ENABLE_NLS
2210 setlocale(LC_ALL, loc);
2211 #endif
2212 APPEND(buf, tmp, size);
2213
2214 switch (sms->status) {
2215 case GN_SMS_Sent:
2216 case GN_SMS_Unsent:
2217 CONCAT(buf, tmp, size, "To: %s@%s\n", sms->remote.number, from);
2218 break;
2219 case GN_SMS_Read:
2220 case GN_SMS_Unread:
2221 default:
2222 CONCAT(buf, tmp, size, "From: %s@%s\n", sms->remote.number, from);
2223 break;
2224 }
2225
2226 CONCAT(buf, tmp, size, "X-GSM-SMSC: %s\n", sms->smsc.number);
2227 CONCAT(buf, tmp, size, "X-GSM-Status: %s\n", status2str(sms->status));
2228 CONCAT(buf, tmp, size, "X-GSM-Memory: %s\n", gn_memory_type2str(sms->memory_type));
2229
2230 aux = calloc(16, sizeof(char)); /* assuming location will never have more than 15 digits */
2231 if (!aux)
2232 goto error;
2233 snprintf(aux, 16, "%d", sms->number);
2234 CONCAT(buf, tmp, size, "X-GSM-Location: %s\n", aux);
2235 free(aux);
2236
2237 if (strlen(sms->user_data[0].u.text) < MAX_SUBJECT_LENGTH) {
2238 CONCAT(buf, tmp, size, "Subject: %s\n\n", sms->user_data[0].u.text);
2239 } else {
2240 aux = calloc(MAX_TEXT_SUMMARY + 1, sizeof(char));
2241 if (!aux)
2242 goto error;
2243 snprintf(aux, MAX_TEXT_SUMMARY, "%s", sms->user_data[0].u.text);
2244 CONCAT(buf, tmp, size, "Subject: %s...\n\n", aux);
2245 free(aux);
2246 }
2247
2248 CONCAT(buf, tmp, size, "%s\n\n", sms->user_data[0].u.text);
2249
2250 return buf;
2251 error:
2252 free(buf);
2253 free(aux);
2254 return NULL;
2255 }
2256