3 * oFono - Open Source Telephony
5 * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
31 #include <sys/types.h>
41 #define uninitialized_var(x) x = x
43 #define SMS_BACKUP_MODE 0600
44 #define SMS_BACKUP_PATH STORAGEDIR "/%s/sms_assembly"
45 #define SMS_BACKUP_PATH_DIR SMS_BACKUP_PATH "/%s-%i-%i"
46 #define SMS_BACKUP_PATH_FILE SMS_BACKUP_PATH_DIR "/%03i"
48 #define SMS_SR_BACKUP_PATH STORAGEDIR "/%s/sms_sr"
49 #define SMS_SR_BACKUP_PATH_FILE SMS_SR_BACKUP_PATH "/%s-%s"
51 #define SMS_TX_BACKUP_PATH STORAGEDIR "/%s/tx_queue"
52 #define SMS_TX_BACKUP_PATH_DIR SMS_TX_BACKUP_PATH "/%lu-%lu-%s"
53 #define SMS_TX_BACKUP_PATH_FILE SMS_TX_BACKUP_PATH_DIR "/%03i"
55 #define SMS_ADDR_FMT "%24[0-9A-F]"
56 #define SMS_MSGID_FMT "%40[0-9A-F]"
59 * Time zone accounts for daylight saving time, and the two extreme time
60 * zones on earth are UTC-12 and UTC+14.
62 #define MAX_TIMEZONE 56
63 #define MIN_TIMEZONE -48
65 static GSList *sms_assembly_add_fragment_backup(struct sms_assembly *assembly,
66 const struct sms *sms, time_t ts,
67 const struct sms_address *addr,
68 guint16 ref, guint8 max, guint8 seq,
72 * This function uses the meanings of digits 10..15 according to the rules
73 * defined in 23.040 Section 9.1.2.3 and 24.008 Table 10.5.118
75 void extract_bcd_number(const unsigned char *buf, int len, char *out)
77 static const char digit_lut[] = "0123456789*#abc\0";
81 for (i = 0; i < len; i++) {
84 out[i*2] = digit_lut[oct & 0x0f];
85 out[i*2+1] = digit_lut[(oct & 0xf0) >> 4];
91 static inline int to_semi_oct(char in)
134 void encode_bcd_number(const char *number, unsigned char *out)
136 while (number[0] != '\0' && number[1] != '\0') {
137 *out = to_semi_oct(*number++);
138 *out++ |= to_semi_oct(*number++) << 4;
142 *out = to_semi_oct(*number) | 0xf0;
146 * Returns whether the DCS could be parsed successfully, e.g. no reserved
149 gboolean sms_dcs_decode(guint8 dcs, enum sms_class *cls,
150 enum sms_charset *charset,
151 gboolean *compressed, gboolean *autodelete)
153 guint8 upper = (dcs & 0xf0) >> 4;
159 /* MWI DCS types are handled in sms_mwi_dcs_decode */
160 if (upper >= 0x8 && upper <= 0xE)
163 upper = (dcs & 0xc0) >> 6;
169 comp = (dcs & 0x20) ? TRUE : FALSE;
172 cl = (enum sms_class) (dcs & 0x03);
174 cl = SMS_CLASS_UNSPECIFIED;
176 if (((dcs & 0x0c) >> 2) < 3)
177 ch = (enum sms_charset) ((dcs & 0x0c) >> 2);
187 ch = SMS_CHARSET_8BIT;
189 ch = SMS_CHARSET_7BIT;
191 cl = (enum sms_class) (dcs & 0x03);
202 *autodelete = autodel;
213 gboolean sms_mwi_dcs_decode(guint8 dcs, enum sms_mwi_type *type,
214 enum sms_charset *charset,
215 gboolean *active, gboolean *discard)
217 guint8 upper = (dcs & 0xf0) >> 4;
223 if (upper < 0xC || upper > 0xE)
226 upper = (dcs & 0x30) >> 4;
234 * As per 3GPP TS 23.038 specification, if bits 7..4 set to 1110,
235 * text included in the user data is coded in the uncompresssed
236 * UCS2 character set.
239 ch = SMS_CHARSET_UCS2;
241 ch = SMS_CHARSET_7BIT;
245 t = (enum sms_mwi_type) (dcs & 0x3);
262 int sms_udl_in_bytes(guint8 ud_len, guint8 dcs)
264 int len_7bit = (ud_len + 1) * 7 / 8;
265 int len_8bit = ud_len;
271 upper = (dcs & 0xc0) >> 6;
276 if (dcs & 0x20) /* compressed */
279 switch ((dcs & 0x0c) >> 2) {
292 switch ((dcs & 0x30) >> 4) {
313 static inline gboolean next_octet(const unsigned char *pdu, int len,
314 int *offset, unsigned char *oct)
321 *offset = *offset + 1;
326 static inline gboolean set_octet(unsigned char *pdu, int *offset,
330 *offset = *offset + 1;
335 gboolean sms_encode_scts(const struct sms_scts *in, unsigned char *pdu,
358 if ((in->timezone > MAX_TIMEZONE || in->timezone < MIN_TIMEZONE) &&
359 in->has_timezone == TRUE)
364 pdu[0] = ((in->year / 10) & 0x0f) | (((in->year % 10) & 0x0f) << 4);
365 pdu[1] = ((in->month / 10) & 0x0f) | (((in->month % 10) & 0x0f) << 4);
366 pdu[2] = ((in->day / 10) & 0x0f) | (((in->day % 10) & 0x0f) << 4);
367 pdu[3] = ((in->hour / 10) & 0x0f) | (((in->hour % 10) & 0x0f) << 4);
368 pdu[4] = ((in->minute / 10) & 0x0f) | (((in->minute % 10) & 0x0f) << 4);
369 pdu[5] = ((in->second / 10) & 0x0f) | (((in->second % 10) & 0x0f) << 4);
371 if (in->has_timezone == FALSE) {
376 timezone = abs(in->timezone);
378 pdu[6] = ((timezone / 10) & 0x07) | (((timezone % 10) & 0x0f) << 4);
380 if (in->timezone < 0)
389 guint8 sms_decode_semi_octet(guint8 in)
391 return (in & 0x0f) * 10 + (in >> 4);
394 gboolean sms_decode_scts(const unsigned char *pdu, int len,
395 int *offset, struct sms_scts *out)
397 unsigned char oct = 0;
399 if ((len - *offset) < 7)
402 next_octet(pdu, len, offset, &oct);
403 out->year = sms_decode_semi_octet(oct);
408 next_octet(pdu, len, offset, &oct);
409 out->month = sms_decode_semi_octet(oct);
414 next_octet(pdu, len, offset, &oct);
415 out->day = sms_decode_semi_octet(oct);
420 next_octet(pdu, len, offset, &oct);
421 out->hour = sms_decode_semi_octet(oct);
426 next_octet(pdu, len, offset, &oct);
427 out->minute = sms_decode_semi_octet(oct);
429 if (out->minute > 59)
432 next_octet(pdu, len, offset, &oct);
433 out->second = sms_decode_semi_octet(oct);
435 if (out->second > 59)
438 next_octet(pdu, len, offset, &oct);
441 * Time Zone indicates the difference, expressed in quarters
442 * of an hour, between the local time and GMT. In the first of the two
443 * semi‑octets, the first bit (bit 3 of the seventh octet of the
444 * TP‑Service‑Centre‑Time‑Stamp field) represents the algebraic
445 * sign of this difference (0: positive, 1:Â negative).
447 out->timezone = (oct & 0x07) * 10 + ((oct & 0xf0) >> 4);
450 out->timezone = out->timezone * -1;
452 if ((out->timezone > MAX_TIMEZONE) || (out->timezone < MIN_TIMEZONE))
455 out->has_timezone = TRUE;
460 static gboolean decode_validity_period(const unsigned char *pdu, int len,
462 enum sms_validity_period_format vpf,
463 struct sms_validity_period *vp)
466 case SMS_VALIDITY_PERIOD_FORMAT_ABSENT:
468 case SMS_VALIDITY_PERIOD_FORMAT_RELATIVE:
469 if (!next_octet(pdu, len, offset, &vp->relative))
473 case SMS_VALIDITY_PERIOD_FORMAT_ABSOLUTE:
474 if (!sms_decode_scts(pdu, len, offset, &vp->absolute))
478 case SMS_VALIDITY_PERIOD_FORMAT_ENHANCED:
480 * TODO: Parse out enhanced structure properly
481 * 23.040 Section 9.2.3.12.3
483 if ((len - *offset) < 7)
486 memcpy(vp->enhanced, pdu + *offset, 7);
488 *offset = *offset + 7;
498 static gboolean encode_validity_period(const struct sms_validity_period *vp,
499 enum sms_validity_period_format vpf,
500 unsigned char *pdu, int *offset)
503 case SMS_VALIDITY_PERIOD_FORMAT_ABSENT:
505 case SMS_VALIDITY_PERIOD_FORMAT_RELATIVE:
506 set_octet(pdu, offset, vp->relative);
508 case SMS_VALIDITY_PERIOD_FORMAT_ABSOLUTE:
509 return sms_encode_scts(&vp->absolute, pdu, offset);
510 case SMS_VALIDITY_PERIOD_FORMAT_ENHANCED:
511 /* TODO: Write out proper enhanced VP structure */
512 memcpy(pdu + *offset, vp->enhanced, 7);
514 *offset = *offset + 7;
524 gboolean sms_encode_address_field(const struct sms_address *in, gboolean sc,
525 unsigned char *pdu, int *offset)
527 size_t len = strlen(in->address);
528 unsigned char addr_len = 0;
533 if (len == 0 && sc) {
535 *offset = *offset + 1;
543 if (in->number_type == SMS_NUMBER_TYPE_ALPHANUMERIC) {
552 gsm = convert_utf8_to_gsm(in->address, len, NULL, &written, 0);
556 r = pack_7bit_own_buf(gsm, written, 0, FALSE, &packed, 0, p);
564 addr_len = packed + 1;
566 addr_len = (written * 7 + 3) / 4;
575 for (i = 0; in->address[i]; i++) {
576 c = to_semi_oct(in->address[i]);
602 pdu[1] = (in->number_type << 4) | in->numbering_plan | 0x80;
603 memcpy(pdu+2, p, (sc ? addr_len - 1 : (addr_len + 1) / 2));
605 *offset = *offset + 2 + (sc ? addr_len - 1 : (addr_len + 1) / 2);
610 gboolean sms_decode_address_field(const unsigned char *pdu, int len,
611 int *offset, gboolean sc,
612 struct sms_address *out)
614 unsigned char addr_len;
615 unsigned char addr_type;
618 if (!next_octet(pdu, len, offset, &addr_len))
621 if (sc && addr_len == 0) {
622 out->address[0] = '\0';
626 if (!next_octet(pdu, len, offset, &addr_type))
630 byte_len = addr_len - 1;
632 byte_len = (addr_len + 1) / 2;
634 if ((len - *offset) < byte_len)
637 out->number_type = bit_field(addr_type, 4, 3);
638 out->numbering_plan = bit_field(addr_type, 0, 4);
640 if (out->number_type != SMS_NUMBER_TYPE_ALPHANUMERIC) {
641 extract_bcd_number(pdu + *offset, byte_len, out->address);
650 chars = byte_len * 8 / 7;
652 chars = addr_len * 4 / 7;
655 * This cannot happen according to 24.011, however
656 * nothing is said in 23.040
659 out->address[0] = '\0';
663 res = unpack_7bit(pdu + *offset, byte_len, 0, FALSE, chars,
666 *offset = *offset + (addr_len + 1) / 2;
671 utf8 = convert_gsm_to_utf8(res, written, NULL, NULL, 0);
678 if (strlen(utf8) > 20) {
683 strcpy(out->address, utf8);
691 static gboolean encode_deliver(const struct sms_deliver *in, unsigned char *pdu,
711 set_octet(pdu, offset, oct);
713 if (sms_encode_address_field(&in->oaddr, FALSE, pdu, offset) == FALSE)
716 set_octet(pdu, offset, in->pid);
717 set_octet(pdu, offset, in->dcs);
719 if (sms_encode_scts(&in->scts, pdu, offset) == FALSE)
722 set_octet(pdu, offset, in->udl);
724 ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs);
726 memcpy(pdu + *offset, in->ud, ud_oct_len);
728 *offset = *offset + ud_oct_len;
733 static gboolean decode_deliver(const unsigned char *pdu, int len,
740 out->type = SMS_TYPE_DELIVER;
742 if (!next_octet(pdu, len, &offset, &octet))
745 out->deliver.mms = !is_bit_set(octet, 2);
746 out->deliver.sri = is_bit_set(octet, 5);
747 out->deliver.udhi = is_bit_set(octet, 6);
748 out->deliver.rp = is_bit_set(octet, 7);
750 if (!sms_decode_address_field(pdu, len, &offset,
751 FALSE, &out->deliver.oaddr))
754 if (!next_octet(pdu, len, &offset, &out->deliver.pid))
757 if (!next_octet(pdu, len, &offset, &out->deliver.dcs))
760 if (!sms_decode_scts(pdu, len, &offset, &out->deliver.scts))
763 if (!next_octet(pdu, len, &offset, &out->deliver.udl))
766 expected = sms_udl_in_bytes(out->deliver.udl, out->deliver.dcs);
768 if ((len - offset) < expected)
771 memcpy(out->deliver.ud, pdu+offset, expected);
776 static gboolean encode_submit_ack_report(const struct sms_submit_ack_report *in,
777 unsigned char *pdu, int *offset)
786 set_octet(pdu, offset, oct);
788 set_octet(pdu, offset, in->pi);
790 if (!sms_encode_scts(&in->scts, pdu, offset))
794 set_octet(pdu, offset, in->pid);
797 set_octet(pdu, offset, in->dcs);
800 int ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs);
802 set_octet(pdu, offset, in->udl);
803 memcpy(pdu + *offset, in->ud, ud_oct_len);
804 *offset = *offset + ud_oct_len;
810 static gboolean encode_submit_err_report(const struct sms_submit_err_report *in,
811 unsigned char *pdu, int *offset)
820 set_octet(pdu, offset, oct);
822 set_octet(pdu, offset, in->fcs);
824 set_octet(pdu, offset, in->pi);
826 if (!sms_encode_scts(&in->scts, pdu, offset))
830 set_octet(pdu, offset, in->pid);
833 set_octet(pdu, offset, in->dcs);
836 int ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs);
838 set_octet(pdu, offset, in->udl);
839 memcpy(pdu + *offset, in->ud, ud_oct_len);
840 *offset = *offset + ud_oct_len;
846 static gboolean decode_submit_report(const unsigned char *pdu, int len,
852 guint8 uninitialized_var(fcs);
854 struct sms_scts *scts;
859 if (!next_octet(pdu, len, &offset, &octet))
862 udhi = is_bit_set(octet, 6);
864 if (!next_octet(pdu, len, &offset, &octet))
868 * At this point we don't know whether this is an ACK or an ERROR.
869 * FCS can only have values 0x80 and above, as 0x00 - 0x7F are reserved
870 * according to 3GPP 23.040. For PI, the values can be only in
871 * bit 0, 1, 2 with the 7th bit reserved as an extension. Since
872 * bits 3-6 are not used, assume no extension is feasible, so if the
873 * value of this octet is >= 0x80, this is an FCS and thus an error
878 out->type = SMS_TYPE_SUBMIT_REPORT_ERROR;
881 if (!next_octet(pdu, len, &offset, &octet))
884 scts = &out->submit_err_report.scts;
886 scts = &out->submit_ack_report.scts;
887 out->type = SMS_TYPE_SUBMIT_REPORT_ACK;
892 if (!sms_decode_scts(pdu, len, &offset, scts))
896 if (!next_octet(pdu, len, &offset, &pid))
901 if (!next_octet(pdu, len, &offset, &dcs))
905 if (out->type == SMS_TYPE_SUBMIT_REPORT_ERROR) {
906 out->submit_err_report.udhi = udhi;
907 out->submit_err_report.fcs = fcs;
908 out->submit_err_report.pi = pi;
909 out->submit_err_report.pid = pid;
910 out->submit_err_report.dcs = dcs;
912 out->submit_ack_report.udhi = udhi;
913 out->submit_ack_report.pi = pi;
914 out->submit_ack_report.pid = pid;
915 out->submit_ack_report.dcs = dcs;
921 if (!next_octet(pdu, len, &offset, &udl))
924 expected = sms_udl_in_bytes(udl, dcs);
926 if ((len - offset) < expected)
929 if (out->type == SMS_TYPE_SUBMIT_REPORT_ERROR) {
930 out->submit_err_report.udl = udl;
931 memcpy(out->submit_err_report.ud,
932 pdu+offset, expected);
934 out->submit_ack_report.udl = udl;
935 memcpy(out->submit_ack_report.ud,
936 pdu+offset, expected);
943 static gboolean encode_status_report(const struct sms_status_report *in,
944 unsigned char *pdu, int *offset)
959 set_octet(pdu, offset, octet);
961 set_octet(pdu, offset, in->mr);
963 if (!sms_encode_address_field(&in->raddr, FALSE, pdu, offset))
966 if (!sms_encode_scts(&in->scts, pdu, offset))
969 if (!sms_encode_scts(&in->dt, pdu, offset))
973 set_octet(pdu, offset, octet);
978 set_octet(pdu, offset, in->pi);
981 set_octet(pdu, offset, in->pid);
984 set_octet(pdu, offset, in->dcs);
987 int ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs);
989 set_octet(pdu, offset, in->udl);
990 memcpy(pdu + *offset, in->ud, ud_oct_len);
991 *offset = *offset + ud_oct_len;
997 static gboolean decode_status_report(const unsigned char *pdu, int len,
1001 unsigned char octet;
1003 out->type = SMS_TYPE_STATUS_REPORT;
1005 if (!next_octet(pdu, len, &offset, &octet))
1008 out->status_report.mms = !is_bit_set(octet, 2);
1009 out->status_report.srq = is_bit_set(octet, 5);
1010 out->status_report.udhi = is_bit_set(octet, 6);
1012 if (!next_octet(pdu, len, &offset, &out->status_report.mr))
1015 if (!sms_decode_address_field(pdu, len, &offset, FALSE,
1016 &out->status_report.raddr))
1019 if (!sms_decode_scts(pdu, len, &offset, &out->status_report.scts))
1022 if (!sms_decode_scts(pdu, len, &offset, &out->status_report.dt))
1025 if (!next_octet(pdu, len, &offset, &octet))
1028 out->status_report.st = octet;
1031 * We have to be careful here, PI is labeled as Optional in 23.040
1032 * which is different from RP-ERR & RP-ACK for both Deliver & Submit
1036 if ((len - offset) == 0)
1039 if (!next_octet(pdu, len, &offset, &octet))
1042 out->status_report.pi = octet & 0x07;
1044 if (out->status_report.pi & 0x01) {
1045 if (!next_octet(pdu, len, &offset, &out->status_report.pid))
1049 if (out->status_report.pi & 0x02) {
1050 if (!next_octet(pdu, len, &offset, &out->status_report.dcs))
1054 if (out->status_report.pi & 0x04) {
1057 if (!next_octet(pdu, len, &offset, &out->status_report.udl))
1060 expected = sms_udl_in_bytes(out->status_report.udl,
1061 out->status_report.dcs);
1063 if ((len - offset) < expected)
1066 memcpy(out->status_report.ud, pdu+offset, expected);
1072 static gboolean encode_deliver_ack_report(const struct sms_deliver_ack_report *in,
1083 set_octet(pdu, offset, oct);
1085 set_octet(pdu, offset, in->pi);
1088 set_octet(pdu, offset, in->pid);
1091 set_octet(pdu, offset, in->dcs);
1094 int ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs);
1096 set_octet(pdu, offset, in->udl);
1097 memcpy(pdu + *offset, in->ud, ud_oct_len);
1098 *offset = *offset + ud_oct_len;
1104 static gboolean encode_deliver_err_report(const struct sms_deliver_err_report *in,
1115 set_octet(pdu, offset, oct);
1117 set_octet(pdu, offset, in->fcs);
1119 set_octet(pdu, offset, in->pi);
1122 set_octet(pdu, offset, in->pid);
1125 set_octet(pdu, offset, in->dcs);
1128 int ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs);
1130 set_octet(pdu, offset, in->udl);
1131 memcpy(pdu + *offset, in->ud, ud_oct_len);
1132 *offset = *offset + ud_oct_len;
1138 static gboolean decode_deliver_report(const unsigned char *pdu, int len,
1142 unsigned char octet;
1144 guint8 uninitialized_var(fcs);
1150 if (!next_octet(pdu, len, &offset, &octet))
1153 udhi = is_bit_set(octet, 6);
1155 if (!next_octet(pdu, len, &offset, &octet))
1159 * At this point we don't know whether this is an ACK or an ERROR.
1160 * FCS can only have values 0x80 and above, as 0x00 - 0x7F are reserved
1161 * according to 3GPP 23.040. For PI, the values can be only in
1162 * bit 0, 1, 2 with the 7th bit reserved as an extension. Since
1163 * bits 3-6 are not used, assume no extension is feasible, so if the
1164 * value of this octet is >= 0x80, this is an FCS and thus an error
1168 if (octet >= 0x80) {
1169 out->type = SMS_TYPE_DELIVER_REPORT_ERROR;
1172 if (!next_octet(pdu, len, &offset, &octet))
1175 out->type = SMS_TYPE_DELIVER_REPORT_ACK;
1181 if (!next_octet(pdu, len, &offset, &pid))
1186 if (!next_octet(pdu, len, &offset, &dcs))
1190 if (out->type == SMS_TYPE_DELIVER_REPORT_ERROR) {
1191 out->deliver_err_report.udhi = udhi;
1192 out->deliver_err_report.fcs = fcs;
1193 out->deliver_err_report.pi = pi;
1194 out->deliver_err_report.pid = pid;
1195 out->deliver_err_report.dcs = dcs;
1197 out->deliver_ack_report.udhi = udhi;
1198 out->deliver_ack_report.pi = pi;
1199 out->deliver_ack_report.pid = pid;
1200 out->deliver_ack_report.dcs = dcs;
1206 if (!next_octet(pdu, len, &offset, &udl))
1209 expected = sms_udl_in_bytes(udl, dcs);
1211 if ((len - offset) < expected)
1214 if (out->type == SMS_TYPE_DELIVER_REPORT_ERROR) {
1215 out->deliver_err_report.udl = udl;
1216 memcpy(out->deliver_err_report.ud,
1217 pdu+offset, expected);
1219 out->deliver_ack_report.udl = udl;
1220 memcpy(out->deliver_ack_report.ud,
1221 pdu+offset, expected);
1228 static gboolean encode_submit(const struct sms_submit *in,
1229 unsigned char *pdu, int *offset)
1231 unsigned char octet;
1243 octet |= in->vpf << 3;
1251 set_octet(pdu, offset, octet);
1253 set_octet(pdu, offset, in->mr);
1255 if (sms_encode_address_field(&in->daddr, FALSE, pdu, offset) == FALSE)
1258 set_octet(pdu, offset, in->pid);
1260 set_octet(pdu, offset, in->dcs);
1262 if (!encode_validity_period(&in->vp, in->vpf, pdu, offset))
1265 set_octet(pdu, offset, in->udl);
1267 ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs);
1269 memcpy(pdu + *offset, in->ud, ud_oct_len);
1271 *offset = *offset + ud_oct_len;
1276 gboolean sms_decode_unpacked_stk_pdu(const unsigned char *pdu, int len,
1279 unsigned char octet;
1282 if (!next_octet(pdu, len, &offset, &octet))
1285 if ((octet & 0x3) != 1)
1288 out->type = SMS_TYPE_SUBMIT;
1290 out->submit.rd = is_bit_set(octet, 2);
1291 out->submit.vpf = bit_field(octet, 3, 2);
1292 out->submit.rp = is_bit_set(octet, 7);
1293 out->submit.udhi = is_bit_set(octet, 6);
1294 out->submit.srr = is_bit_set(octet, 5);
1296 if (!next_octet(pdu, len, &offset, &out->submit.mr))
1299 if (!sms_decode_address_field(pdu, len, &offset,
1300 FALSE, &out->submit.daddr))
1303 if (!next_octet(pdu, len, &offset, &out->submit.pid))
1306 if (!next_octet(pdu, len, &offset, &out->submit.dcs))
1309 /* Now we override the DCS */
1310 out->submit.dcs = 0xF0;
1312 if (!decode_validity_period(pdu, len, &offset, out->submit.vpf,
1316 if (!next_octet(pdu, len, &offset, &out->submit.udl))
1319 if ((len - offset) < out->submit.udl)
1322 pack_7bit_own_buf(pdu + offset, out->submit.udl, 0, FALSE,
1323 NULL, 0, out->submit.ud);
1328 static gboolean decode_submit(const unsigned char *pdu, int len,
1331 unsigned char octet;
1335 out->type = SMS_TYPE_SUBMIT;
1337 if (!next_octet(pdu, len, &offset, &octet))
1340 out->submit.rd = is_bit_set(octet, 2);
1341 out->submit.vpf = bit_field(octet, 3, 2);
1342 out->submit.rp = is_bit_set(octet, 7);
1343 out->submit.udhi = is_bit_set(octet, 6);
1344 out->submit.srr = is_bit_set(octet, 5);
1346 if (!next_octet(pdu, len, &offset, &out->submit.mr))
1349 if (!sms_decode_address_field(pdu, len, &offset,
1350 FALSE, &out->submit.daddr))
1353 if (!next_octet(pdu, len, &offset, &out->submit.pid))
1356 if (!next_octet(pdu, len, &offset, &out->submit.dcs))
1359 if (!decode_validity_period(pdu, len, &offset, out->submit.vpf,
1363 if (!next_octet(pdu, len, &offset, &out->submit.udl))
1366 expected = sms_udl_in_bytes(out->submit.udl, out->submit.dcs);
1368 if ((len - offset) < expected)
1371 if (expected > (int) sizeof(out->submit.ud))
1374 memcpy(out->submit.ud, pdu+offset, expected);
1379 static gboolean encode_command(const struct sms_command *in,
1380 unsigned char *pdu, int *offset)
1382 unsigned char octet;
1392 set_octet(pdu, offset, octet);
1394 set_octet(pdu, offset, in->mr);
1396 set_octet(pdu, offset, in->pid);
1399 set_octet(pdu, offset, octet);
1401 set_octet(pdu, offset, in->mn);
1403 if (!sms_encode_address_field(&in->daddr, FALSE, pdu, offset))
1406 set_octet(pdu, offset, in->cdl);
1408 memcpy(pdu + *offset, in->cd, in->cdl);
1410 *offset = *offset + in->cdl;
1415 static gboolean decode_command(const unsigned char *pdu, int len,
1418 unsigned char octet;
1421 out->type = SMS_TYPE_COMMAND;
1423 if (!next_octet(pdu, len, &offset, &octet))
1426 out->command.udhi = is_bit_set(octet, 6);
1427 out->command.srr = is_bit_set(octet, 5);
1429 if (!next_octet(pdu, len, &offset, &out->command.mr))
1432 if (!next_octet(pdu, len, &offset, &out->command.pid))
1435 if (!next_octet(pdu, len, &offset, &octet))
1438 out->command.ct = octet;
1440 if (!next_octet(pdu, len, &offset, &out->command.mn))
1443 if (!sms_decode_address_field(pdu, len, &offset,
1444 FALSE, &out->command.daddr))
1447 if (!next_octet(pdu, len, &offset, &out->command.cdl))
1450 if ((len - offset) < out->command.cdl)
1453 memcpy(out->command.cd, pdu+offset, out->command.cdl);
1458 /* Buffer must be at least 164 (tpud) + 12 (SC address) bytes long */
1459 gboolean sms_encode(const struct sms *in, int *len, int *tpdu_len,
1465 if (in->type == SMS_TYPE_DELIVER || in->type == SMS_TYPE_SUBMIT ||
1466 in->type == SMS_TYPE_COMMAND ||
1467 in->type == SMS_TYPE_STATUS_REPORT)
1468 if (!sms_encode_address_field(&in->sc_addr, TRUE, pdu, &offset))
1471 tpdu_start = offset;
1474 case SMS_TYPE_DELIVER:
1475 if (encode_deliver(&in->deliver, pdu, &offset) == FALSE)
1478 case SMS_TYPE_DELIVER_REPORT_ACK:
1479 if (!encode_deliver_ack_report(&in->deliver_ack_report, pdu,
1483 case SMS_TYPE_DELIVER_REPORT_ERROR:
1484 if (!encode_deliver_err_report(&in->deliver_err_report, pdu,
1488 case SMS_TYPE_STATUS_REPORT:
1489 if (!encode_status_report(&in->status_report, pdu, &offset))
1492 case SMS_TYPE_SUBMIT:
1493 if (!encode_submit(&in->submit, pdu, &offset))
1496 case SMS_TYPE_SUBMIT_REPORT_ACK:
1497 if (!encode_submit_ack_report(&in->submit_ack_report, pdu,
1501 case SMS_TYPE_SUBMIT_REPORT_ERROR:
1502 if (!encode_submit_err_report(&in->submit_err_report, pdu,
1506 case SMS_TYPE_COMMAND:
1507 if (!encode_command(&in->command, pdu, &offset))
1515 *tpdu_len = offset - tpdu_start;
1523 gboolean sms_decode(const unsigned char *pdu, int len, gboolean outgoing,
1524 int tpdu_len, struct sms *out)
1535 memset(out, 0, sizeof(*out));
1537 if (tpdu_len < len) {
1538 if (!sms_decode_address_field(pdu, len, &offset,
1539 TRUE, &out->sc_addr))
1543 if ((len - offset) < tpdu_len)
1546 /* 23.040 9.2.3.1 */
1547 type = pdu[offset] & 0x3;
1556 return decode_deliver(pdu, tpdu_len, out);
1558 return decode_submit_report(pdu, tpdu_len, out);
1560 return decode_status_report(pdu, tpdu_len, out);
1562 /* According to 9.2.3.1, Reserved treated as deliver */
1563 return decode_deliver(pdu, tpdu_len, out);
1565 return decode_deliver_report(pdu, tpdu_len, out);
1567 return decode_submit(pdu, tpdu_len, out);
1569 return decode_command(pdu, tpdu_len, out);
1575 const guint8 *sms_extract_common(const struct sms *sms, gboolean *out_udhi,
1576 guint8 *out_dcs, guint8 *out_udl,
1579 const guint8 *ud = NULL;
1580 guint8 uninitialized_var(udl);
1581 guint8 uninitialized_var(max);
1582 gboolean uninitialized_var(udhi);
1583 guint8 uninitialized_var(dcs);
1585 switch (sms->type) {
1586 case SMS_TYPE_DELIVER:
1587 udhi = sms->deliver.udhi;
1588 ud = sms->deliver.ud;
1589 udl = sms->deliver.udl;
1590 dcs = sms->deliver.dcs;
1591 max = sizeof(sms->deliver.ud);
1593 case SMS_TYPE_DELIVER_REPORT_ACK:
1594 udhi = sms->deliver_ack_report.udhi;
1595 ud = sms->deliver_ack_report.ud;
1596 udl = sms->deliver_ack_report.udl;
1597 dcs = sms->deliver_ack_report.dcs;
1598 max = sizeof(sms->deliver_ack_report.ud);
1600 case SMS_TYPE_DELIVER_REPORT_ERROR:
1601 udhi = sms->deliver_err_report.udhi;
1602 ud = sms->deliver_err_report.ud;
1603 udl = sms->deliver_err_report.udl;
1604 dcs = sms->deliver_err_report.dcs;
1605 max = sizeof(sms->deliver_err_report.ud);
1607 case SMS_TYPE_STATUS_REPORT:
1608 udhi = sms->status_report.udhi;
1609 ud = sms->status_report.ud;
1610 udl = sms->status_report.udl;
1611 dcs = sms->status_report.dcs;
1612 max = sizeof(sms->status_report.ud);
1614 case SMS_TYPE_SUBMIT:
1615 udhi = sms->submit.udhi;
1616 ud = sms->submit.ud;
1617 udl = sms->submit.udl;
1618 dcs = sms->submit.dcs;
1619 max = sizeof(sms->submit.ud);
1621 case SMS_TYPE_SUBMIT_REPORT_ACK:
1622 udhi = sms->submit_ack_report.udhi;
1623 ud = sms->submit_ack_report.ud;
1624 udl = sms->submit_ack_report.udl;
1625 dcs = sms->submit_ack_report.dcs;
1626 max = sizeof(sms->submit_ack_report.ud);
1628 case SMS_TYPE_SUBMIT_REPORT_ERROR:
1629 udhi = sms->submit_err_report.udhi;
1630 ud = sms->submit_err_report.ud;
1631 udl = sms->submit_err_report.udl;
1632 dcs = sms->submit_err_report.dcs;
1633 max = sizeof(sms->submit_err_report.ud);
1635 case SMS_TYPE_COMMAND:
1636 udhi = sms->command.udhi;
1637 ud = sms->command.cd;
1638 udl = sms->command.cdl;
1640 max = sizeof(sms->command.cd);
1662 static gboolean verify_udh(const guint8 *hdr, guint8 max_len)
1667 /* Must have at least one information-element if udhi is true */
1671 if (hdr[0] >= max_len)
1675 * According to 23.040: If the length of the User Data Header is
1676 * such that there are too few or too many octets in the final
1677 * Information Element then the whole User Data Header shall be
1681 max_offset = hdr[0] + 1;
1684 if ((offset + 2) > max_offset)
1687 if ((offset + 2 + hdr[offset + 1]) > max_offset)
1690 offset = offset + 2 + hdr[offset + 1];
1691 } while (offset < max_offset);
1693 if (offset != max_offset)
1699 gboolean sms_udh_iter_init(const struct sms *sms, struct sms_udh_iter *iter)
1701 gboolean udhi = FALSE;
1708 hdr = sms_extract_common(sms, &udhi, &dcs, &udl, &max_ud_len);
1715 if (sms->type == SMS_TYPE_COMMAND)
1718 max_len = sms_udl_in_bytes(udl, dcs);
1720 /* Can't actually store the HDL + IEI / IEL */
1724 if (max_len > max_ud_len)
1727 if (!verify_udh(hdr, max_len))
1736 gboolean sms_udh_iter_init_from_cbs(const struct cbs *cbs,
1737 struct sms_udh_iter *iter)
1739 gboolean udhi = FALSE;
1743 cbs_dcs_decode(cbs->dcs, &udhi, NULL, NULL, NULL, NULL, NULL);
1751 /* Must have at least one information-element if udhi is true */
1755 if (hdr[0] >= max_ud_len)
1758 if (!verify_udh(hdr, max_ud_len))
1766 guint8 sms_udh_iter_get_udh_length(struct sms_udh_iter *iter)
1768 return iter->data[0];
1771 const guint8 *sms_udh_iter_get_ud_after_header(struct sms_udh_iter *iter)
1773 return iter->data + iter->data[0] + 1;
1776 enum sms_iei sms_udh_iter_get_ie_type(struct sms_udh_iter *iter)
1778 if (iter->offset > iter->data[0])
1779 return SMS_IEI_INVALID;
1781 return (enum sms_iei) iter->data[iter->offset];
1784 guint8 sms_udh_iter_get_ie_length(struct sms_udh_iter *iter)
1788 ie_len = iter->data[iter->offset + 1];
1793 void sms_udh_iter_get_ie_data(struct sms_udh_iter *iter, guint8 *data)
1797 ie_len = iter->data[iter->offset + 1];
1799 memcpy(data, &iter->data[iter->offset + 2], ie_len);
1802 gboolean sms_udh_iter_has_next(struct sms_udh_iter *iter)
1804 guint8 total_len = iter->data[0];
1805 guint8 cur_ie_len = iter->data[iter->offset + 1];
1807 if ((iter->offset + 2 + cur_ie_len) > total_len)
1813 gboolean sms_udh_iter_next(struct sms_udh_iter *iter)
1815 if (iter->offset > iter->data[0])
1818 iter->offset = iter->offset + 2 + iter->data[iter->offset + 1];
1820 if (iter->offset > iter->data[0])
1827 * Returns both forms of time. The time_t value returns the time in local
1828 * timezone. The struct tm is filled out with the remote time information
1830 time_t sms_scts_to_time(const struct sms_scts *scts, struct tm *remote)
1835 t.tm_sec = scts->second;
1836 t.tm_min = scts->minute;
1837 t.tm_hour = scts->hour;
1838 t.tm_mday = scts->day;
1839 t.tm_mon = scts->month - 1;
1842 if (scts->year > 80)
1843 t.tm_year = scts->year;
1845 t.tm_year = scts->year + 100;
1849 /* Adjust local time by the local timezone information */
1852 /* Set the proper timezone on the remote side */
1853 t.tm_gmtoff = scts->timezone * 15 * 60;
1855 /* Now adjust by the remote timezone information */
1859 memcpy(remote, &t, sizeof(struct tm));
1864 void sms_address_from_string(struct sms_address *addr, const char *str)
1866 addr->numbering_plan = SMS_NUMBERING_PLAN_ISDN;
1867 if (str[0] == '+') {
1868 addr->number_type = SMS_NUMBER_TYPE_INTERNATIONAL;
1869 strcpy(addr->address, str+1);
1871 addr->number_type = SMS_NUMBER_TYPE_UNKNOWN;
1872 strcpy(addr->address, str);
1876 const char *sms_address_to_string(const struct sms_address *addr)
1878 static char buffer[64];
1880 if (addr->number_type == SMS_NUMBER_TYPE_INTERNATIONAL &&
1881 (strlen(addr->address) > 0) &&
1882 addr->address[0] != '+') {
1884 strcpy(buffer + 1, addr->address);
1886 strcpy(buffer, addr->address);
1892 static gboolean extract_app_port_common(struct sms_udh_iter *iter, int *dst,
1893 int *src, gboolean *is_8bit)
1899 gboolean uninitialized_var(is_addr_8bit);
1902 * According to the specification, we have to use the last
1903 * useable header. Also, we have to ignore ports that are reserved:
1904 * A receiving entity shall ignore (i.e. skip over and commence
1905 * processing at the next information element) any information element
1906 * where the value of the Information-Element-Data is Reserved or not
1909 while ((iei = sms_udh_iter_get_ie_type(iter)) !=
1912 case SMS_IEI_APPLICATION_ADDRESS_8BIT:
1913 if (sms_udh_iter_get_ie_length(iter) != 2)
1916 sms_udh_iter_get_ie_data(iter, addr_hdr);
1918 if (addr_hdr[0] < 240)
1921 if (addr_hdr[1] < 240)
1924 dstport = addr_hdr[0];
1925 srcport = addr_hdr[1];
1926 is_addr_8bit = TRUE;
1929 case SMS_IEI_APPLICATION_ADDRESS_16BIT:
1930 if (sms_udh_iter_get_ie_length(iter) != 4)
1933 sms_udh_iter_get_ie_data(iter, addr_hdr);
1935 if (((addr_hdr[0] << 8) | addr_hdr[1]) > 49151)
1938 if (((addr_hdr[2] << 8) | addr_hdr[3]) > 49151)
1941 dstport = (addr_hdr[0] << 8) | addr_hdr[1];
1942 srcport = (addr_hdr[2] << 8) | addr_hdr[3];
1943 is_addr_8bit = FALSE;
1950 sms_udh_iter_next(iter);
1953 if (dstport == -1 || srcport == -1)
1963 *is_8bit = is_addr_8bit;
1969 gboolean sms_extract_app_port(const struct sms *sms, int *dst, int *src,
1972 struct sms_udh_iter iter;
1974 if (!sms_udh_iter_init(sms, &iter))
1977 return extract_app_port_common(&iter, dst, src, is_8bit);
1980 gboolean sms_extract_concatenation(const struct sms *sms, guint16 *ref_num,
1981 guint8 *max_msgs, guint8 *seq_num)
1983 struct sms_udh_iter iter;
1985 guint8 concat_hdr[4];
1986 guint16 uninitialized_var(rn);
1987 guint8 uninitialized_var(max), uninitialized_var(seq);
1988 gboolean concatenated = FALSE;
1991 * We must ignore the entire user_data header here:
1992 * If the length of the User Data Header is such that there
1993 * are too few or too many octets in the final Information
1994 * Element then the whole User Data Header shall be ignored.
1996 if (!sms_udh_iter_init(sms, &iter))
2000 * According to the specification, we have to use the last
2002 * In the event that IEs determined as not repeatable are
2003 * duplicated, the last occurrence of the IE shall be used.
2004 * In the event that two or more IEs occur which have mutually
2005 * exclusive meanings (e.g. an 8bit port address and a 16bit
2006 * port address), then the last occurring IE shall be used.
2008 while ((iei = sms_udh_iter_get_ie_type(&iter)) !=
2011 case SMS_IEI_CONCATENATED_8BIT:
2012 if (sms_udh_iter_get_ie_length(&iter) != 3)
2015 sms_udh_iter_get_ie_data(&iter, concat_hdr);
2017 if (concat_hdr[1] == 0)
2020 if (concat_hdr[2] == 0 || concat_hdr[2] > concat_hdr[1])
2024 max = concat_hdr[1];
2025 seq = concat_hdr[2];
2026 concatenated = TRUE;
2029 case SMS_IEI_CONCATENATED_16BIT:
2030 if (sms_udh_iter_get_ie_length(&iter) != 4)
2033 sms_udh_iter_get_ie_data(&iter, concat_hdr);
2035 if (concat_hdr[2] == 0)
2038 if (concat_hdr[3] == 0 ||
2039 concat_hdr[3] > concat_hdr[2])
2042 rn = (concat_hdr[0] << 8) | concat_hdr[1];
2043 max = concat_hdr[2];
2044 seq = concat_hdr[3];
2045 concatenated = TRUE;
2051 sms_udh_iter_next(&iter);
2069 gboolean sms_extract_language_variant(const struct sms *sms, guint8 *locking,
2072 struct sms_udh_iter iter;
2077 * We must ignore the entire user_data header here:
2078 * If the length of the User Data Header is such that there
2079 * are too few or too many octets in the final Information
2080 * Element then the whole User Data Header shall be ignored.
2082 if (!sms_udh_iter_init(sms, &iter))
2086 * According to the specification, we have to use the last
2088 * In the event that IEs determined as not repeatable are
2089 * duplicated, the last occurrence of the IE shall be used.
2090 * In the event that two or more IEs occur which have mutually
2091 * exclusive meanings (e.g. an 8bit port address and a 16bit
2092 * port address), then the last occurring IE shall be used.
2094 while ((iei = sms_udh_iter_get_ie_type(&iter)) !=
2097 case SMS_IEI_NATIONAL_LANGUAGE_SINGLE_SHIFT:
2098 if (sms_udh_iter_get_ie_length(&iter) != 1)
2101 sms_udh_iter_get_ie_data(&iter, &variant);
2106 case SMS_IEI_NATIONAL_LANGUAGE_LOCKING_SHIFT:
2107 if (sms_udh_iter_get_ie_length(&iter) != 1)
2110 sms_udh_iter_get_ie_data(&iter, &variant);
2118 sms_udh_iter_next(&iter);
2125 * Decodes a list of SMSes that contain a datagram. The list must be
2126 * sorted in order of the sequence number. This function assumes that
2127 * all fragments are coded using 8-bit character set.
2129 * Returns a pointer to a newly allocated array or NULL if the
2130 * conversion could not be performed
2132 unsigned char *sms_decode_datagram(GSList *sms_list, long *out_len)
2135 const struct sms *sms;
2139 for (l = sms_list; l; l = l->next) {
2143 struct sms_udh_iter iter;
2147 ud = sms_extract_common(sms, NULL, NULL, &udl, NULL);
2152 * Note we do this because we must check whether the UDH
2153 * is properly formatted. If not, the entire UDH is ignored
2155 if (sms_udh_iter_init(sms, &iter))
2156 taken = sms_udh_iter_get_udh_length(&iter) + 1;
2161 /* Data is probably in headers we can't understand */
2165 buf = g_try_new(unsigned char, len);
2170 for (l = sms_list; l; l = l->next) {
2174 struct sms_udh_iter iter;
2178 ud = sms_extract_common(sms, NULL, NULL, &udl, NULL);
2180 if (sms_udh_iter_init(sms, &iter))
2181 taken = sms_udh_iter_get_udh_length(&iter) + 1;
2183 memcpy(buf + len, ud + taken, udl - taken);
2193 static inline int sms_text_capacity_gsm(int max, int offset)
2195 return max - (offset * 8 + 6) / 7;
2199 * Decodes a list of SMSes that contain a text in either 7bit or UCS2 encoding.
2200 * The list must be sorted in order of the sequence number. This function
2201 * assumes that all fragments have a proper DCS.
2203 * Returns a pointer to a newly allocated string or NULL if the conversion
2206 char *sms_decode_text(GSList *sms_list)
2210 const struct sms *sms;
2211 int guess_size = g_slist_length(sms_list);
2214 if (guess_size == 1)
2217 guess_size = (guess_size - 1) * 160;
2219 str = g_string_sized_new(guess_size);
2221 for (l = sms_list; l; l = l->next) {
2225 enum sms_charset charset;
2228 struct sms_udh_iter iter;
2233 ud = sms_extract_common(sms, NULL, &dcs, &udl, NULL);
2235 if (!sms_mwi_dcs_decode(dcs, NULL, &charset, NULL, NULL) &&
2236 !sms_dcs_decode(dcs, NULL, &charset, NULL, NULL))
2239 if (charset == SMS_CHARSET_8BIT)
2242 if (sms_udh_iter_init(sms, &iter))
2243 taken = sms_udh_iter_get_udh_length(&iter) + 1;
2245 udl_in_bytes = sms_udl_in_bytes(udl, dcs);
2247 if (udl_in_bytes == taken)
2250 if (charset == SMS_CHARSET_7BIT) {
2251 unsigned char buf[160];
2253 guint8 locking_shift = 0;
2254 guint8 single_shift = 0;
2255 int max_chars = sms_text_capacity_gsm(udl, taken);
2257 if (unpack_7bit_own_buf(ud + taken,
2258 udl_in_bytes - taken,
2259 taken, FALSE, max_chars,
2260 &written, 0, buf) == NULL)
2263 /* Take care of improperly split fragments */
2264 if (buf[written-1] == 0x1b)
2265 written = written - 1;
2267 sms_extract_language_variant(sms, &locking_shift,
2271 * If language is not defined in 3GPP TS 23.038,
2272 * implementations are instructed to ignore it
2274 if (locking_shift > SMS_ALPHABET_PORTUGUESE)
2275 locking_shift = GSM_DIALECT_DEFAULT;
2277 if (single_shift > SMS_ALPHABET_PORTUGUESE)
2278 single_shift = GSM_DIALECT_DEFAULT;
2280 converted = convert_gsm_to_utf8_with_lang(buf, written,
2285 const gchar *from = (const gchar *) (ud + taken);
2287 * According to the spec: A UCS2 character shall not be
2288 * split in the middle; if the length of the User Data
2289 * Header is odd, the maximum length of the whole TP-UD
2290 * field is 139 octets
2292 gssize num_ucs2_chars = (udl_in_bytes - taken) >> 1;
2293 num_ucs2_chars = num_ucs2_chars << 1;
2295 converted = g_convert(from, num_ucs2_chars,
2296 "UTF-8//TRANSLIT", "UCS-2BE",
2301 g_string_append(str, converted);
2306 utf8 = g_string_free(str, FALSE);
2311 static int sms_serialize(unsigned char *buf, const struct sms *sms)
2315 sms_encode(sms, &len, &tpdu_len, buf + 1);
2321 static gboolean sms_deserialize(const unsigned char *buf,
2322 struct sms *sms, int len)
2327 return sms_decode(buf + 1, len - 1, FALSE, buf[0], sms);
2330 static gboolean sms_deserialize_outgoing(const unsigned char *buf,
2331 struct sms *sms, int len)
2336 return sms_decode(buf + 1, len - 1, TRUE, buf[0], sms);
2339 static gboolean sms_assembly_extract_address(const char *straddr,
2340 struct sms_address *out)
2342 unsigned char pdu[12];
2346 if (decode_hex_own_buf(straddr, -1, &len, 0, pdu) == NULL)
2349 return sms_decode_address_field(pdu, len, &offset, FALSE, out);
2352 gboolean sms_address_to_hex_string(const struct sms_address *in, char *straddr)
2354 unsigned char pdu[12];
2357 if (sms_encode_address_field(in, FALSE, pdu, &offset) == FALSE)
2360 if (encode_hex_own_buf(pdu, offset, 0, straddr) == NULL)
2363 straddr[offset * 2 + 1] = '\0';
2368 static void sms_assembly_load(struct sms_assembly *assembly,
2369 const struct dirent *dir)
2371 struct sms_address addr;
2372 DECLARE_SMS_ADDR_STR(straddr);
2378 struct stat segment_stat;
2379 struct dirent **segments;
2383 unsigned char buf[177];
2386 if (dir->d_type != DT_DIR)
2389 /* Max of SMS address size is 12 bytes, hex encoded */
2390 if (sscanf(dir->d_name, SMS_ADDR_FMT "-%hi-%hhi",
2391 straddr, &ref, &max) < 3)
2394 if (sms_assembly_extract_address(straddr, &addr) == FALSE)
2397 path = g_strdup_printf(SMS_BACKUP_PATH "/%s",
2398 assembly->imsi, dir->d_name);
2399 len = scandir(path, &segments, NULL, versionsort);
2405 for (i = 0; i < len; i++) {
2406 if (segments[i]->d_type != DT_REG)
2409 seq = strtol(segments[i]->d_name, &endp, 10);
2413 r = read_file(buf, sizeof(buf), SMS_BACKUP_PATH "/%s/%s",
2415 dir->d_name, segments[i]->d_name);
2419 if (!sms_deserialize(buf, &segment, r))
2422 path = g_strdup_printf(SMS_BACKUP_PATH "/%s/%s",
2424 dir->d_name, segments[i]->d_name);
2425 r = stat(path, &segment_stat);
2431 /* Errors cannot occur here */
2432 sms_assembly_add_fragment_backup(assembly, &segment,
2433 segment_stat.st_mtime,
2434 &addr, ref, max, seq, FALSE);
2437 for (i = 0; i < len; i++)
2443 static gboolean sms_assembly_store(struct sms_assembly *assembly,
2444 struct sms_assembly_node *node,
2445 const struct sms *sms, guint8 seq)
2447 unsigned char buf[177];
2449 DECLARE_SMS_ADDR_STR(straddr);
2451 if (assembly->imsi == NULL)
2454 if (sms_address_to_hex_string(&node->addr, straddr) == FALSE)
2457 len = sms_serialize(buf, sms);
2459 if (write_file(buf, len, SMS_BACKUP_MODE,
2460 SMS_BACKUP_PATH_FILE, assembly->imsi, straddr,
2461 node->ref, node->max_fragments, seq) != len)
2467 static void sms_assembly_backup_free(struct sms_assembly *assembly,
2468 struct sms_assembly_node *node)
2472 DECLARE_SMS_ADDR_STR(straddr);
2474 if (assembly->imsi == NULL)
2477 if (sms_address_to_hex_string(&node->addr, straddr) == FALSE)
2480 for (seq = 0; seq < node->max_fragments; seq++) {
2481 int offset = seq / 32;
2482 int bit = 1 << (seq % 32);
2484 if (node->bitmap[offset] & bit) {
2485 path = g_strdup_printf(SMS_BACKUP_PATH_FILE,
2486 assembly->imsi, straddr,
2487 node->ref, node->max_fragments, seq);
2493 path = g_strdup_printf(SMS_BACKUP_PATH_DIR, assembly->imsi, straddr,
2494 node->ref, node->max_fragments);
2499 struct sms_assembly *sms_assembly_new(const char *imsi)
2501 struct sms_assembly *ret = g_new0(struct sms_assembly, 1);
2503 struct dirent **entries;
2509 /* Restore state from backup */
2511 path = g_strdup_printf(SMS_BACKUP_PATH, imsi);
2512 len = scandir(path, &entries, NULL, alphasort);
2519 sms_assembly_load(ret, entries[len]);
2529 void sms_assembly_free(struct sms_assembly *assembly)
2533 for (l = assembly->assembly_list; l; l = l->next) {
2534 struct sms_assembly_node *node = l->data;
2536 g_slist_foreach(node->fragment_list, (GFunc) g_free, 0);
2537 g_slist_free(node->fragment_list);
2541 g_slist_free(assembly->assembly_list);
2545 GSList *sms_assembly_add_fragment(struct sms_assembly *assembly,
2546 const struct sms *sms, time_t ts,
2547 const struct sms_address *addr,
2548 guint16 ref, guint8 max, guint8 seq)
2550 return sms_assembly_add_fragment_backup(assembly, sms,
2551 ts, addr, ref, max, seq, TRUE);
2554 static GSList *sms_assembly_add_fragment_backup(struct sms_assembly *assembly,
2555 const struct sms *sms, time_t ts,
2556 const struct sms_address *addr,
2557 guint16 ref, guint8 max, guint8 seq,
2560 unsigned int offset = seq / 32;
2561 unsigned int bit = 1 << (seq % 32);
2565 struct sms_assembly_node *node;
2567 unsigned int position;
2573 for (l = assembly->assembly_list; l; prev = l, l = l->next) {
2576 if (node->addr.number_type != addr->number_type)
2579 if (node->addr.numbering_plan != addr->numbering_plan)
2582 if (strcmp(node->addr.address, addr->address))
2585 if (ref != node->ref)
2589 * Message Reference and address the same, but max is not
2590 * ignore the SMS completely
2592 if (max != node->max_fragments)
2595 /* Now check if we already have this seq number */
2596 if (node->bitmap[offset] & bit)
2600 * Iterate over the bitmap to find in which position
2601 * should the fragment be inserted -- basically we
2602 * walk each bit in the bitmap until the bit we care
2603 * about (offset:bit) and count which are stored --
2604 * that gives us in which position we have to insert.
2607 for (i = 0; i < offset; i++)
2608 for (j = 0; j < 32; j++)
2609 if (node->bitmap[i] & (1 << j))
2612 for (j = 1; j < bit; j = j << 1)
2613 if (node->bitmap[offset] & j)
2619 node = g_new0(struct sms_assembly_node, 1);
2620 memcpy(&node->addr, addr, sizeof(struct sms_address));
2623 node->max_fragments = max;
2625 assembly->assembly_list = g_slist_prepend(assembly->assembly_list,
2629 l = assembly->assembly_list;
2633 newsms = g_new(struct sms, 1);
2635 memcpy(newsms, sms, sizeof(struct sms));
2636 node->fragment_list = g_slist_insert(node->fragment_list,
2638 node->bitmap[offset] |= bit;
2639 node->num_fragments += 1;
2641 if (node->num_fragments < node->max_fragments) {
2643 sms_assembly_store(assembly, node, sms, seq);
2648 completed = node->fragment_list;
2650 sms_assembly_backup_free(assembly, node);
2653 prev->next = l->next;
2655 assembly->assembly_list = l->next;
2663 * Expires all incomplete messages that have been received at time prior
2664 * to one given by before argument. The fragment list is freed and the
2665 * SMSes are vaporized.
2667 void sms_assembly_expire(struct sms_assembly *assembly, time_t before)
2674 cur = assembly->assembly_list;
2677 struct sms_assembly_node *node = cur->data;
2679 if (node->ts > before) {
2685 sms_assembly_backup_free(assembly, node);
2687 g_slist_foreach(node->fragment_list, (GFunc) g_free, 0);
2688 g_slist_free(node->fragment_list);
2692 prev->next = cur->next;
2694 assembly->assembly_list = cur->next;
2698 g_slist_free_1(tmp);
2702 static gboolean sha1_equal(gconstpointer v1, gconstpointer v2)
2704 return memcmp(v1, v2, SMS_MSGID_LEN) == 0;
2707 static guint sha1_hash(gconstpointer v)
2711 memcpy(&h, v, sizeof(h));
2716 static void sr_assembly_load_backup(GHashTable *assembly_table,
2718 const struct dirent *addr_dir)
2720 struct sms_address addr;
2721 DECLARE_SMS_ADDR_STR(straddr);
2722 struct id_table_node *node;
2723 GHashTable *id_table;
2725 char *assembly_table_key;
2726 unsigned int *id_table_key;
2727 char msgid_str[SMS_MSGID_LEN * 2 + 1];
2728 unsigned char msgid[SMS_MSGID_LEN];
2731 if (addr_dir->d_type != DT_REG)
2735 * All SMS-messages under the same IMSI-code are
2736 * included in the same directory.
2737 * So, SMS-address and message ID are included in the same file name
2738 * Max of SMS address size is 12 bytes, hex encoded
2739 * Max of SMS SHA1 hash is 20 bytes, hex encoded
2741 if (sscanf(addr_dir->d_name, SMS_ADDR_FMT "-" SMS_MSGID_FMT "%c",
2742 straddr, msgid_str, &endc) != 2)
2745 if (sms_assembly_extract_address(straddr, &addr) == FALSE)
2748 if (strlen(msgid_str) != 2 * SMS_MSGID_LEN)
2751 if (decode_hex_own_buf(msgid_str, 2 * SMS_MSGID_LEN,
2752 NULL, 0, msgid) == NULL)
2755 node = g_new0(struct id_table_node, 1);
2757 r = read_file((unsigned char *) node,
2758 sizeof(struct id_table_node),
2759 SMS_SR_BACKUP_PATH "/%s",
2760 imsi, addr_dir->d_name);
2767 id_table = g_hash_table_lookup(assembly_table,
2768 sms_address_to_string(&addr));
2770 /* Create hashtable keyed by the to address if required */
2771 if (id_table == NULL) {
2772 id_table = g_hash_table_new_full(sha1_hash, sha1_equal,
2775 assembly_table_key = g_strdup(sms_address_to_string(&addr));
2776 g_hash_table_insert(assembly_table, assembly_table_key,
2780 /* Node ready, create key and add them to the table */
2781 id_table_key = g_memdup(msgid, SMS_MSGID_LEN);
2783 g_hash_table_insert(id_table, id_table_key, node);
2786 struct status_report_assembly *status_report_assembly_new(const char *imsi)
2790 struct dirent **addresses;
2791 struct status_report_assembly *ret =
2792 g_new0(struct status_report_assembly, 1);
2794 ret->assembly_table = g_hash_table_new_full(g_str_hash, g_str_equal,
2795 g_free, (GDestroyNotify) g_hash_table_destroy);
2800 /* Restore state from backup */
2801 path = g_strdup_printf(SMS_SR_BACKUP_PATH, imsi);
2802 len = scandir(path, &addresses, NULL, alphasort);
2810 * Go through different addresses. Each address can relate to
2815 sr_assembly_load_backup(ret->assembly_table, imsi,
2817 g_free(addresses[len]);
2826 static gboolean sr_assembly_add_fragment_backup(const char *imsi,
2827 const struct id_table_node *node,
2828 const struct sms_address *addr,
2829 const unsigned char *msgid)
2831 int len = sizeof(struct id_table_node);
2832 DECLARE_SMS_ADDR_STR(straddr);
2833 char msgid_str[SMS_MSGID_LEN * 2 + 1];
2838 if (sms_address_to_hex_string(addr, straddr) == FALSE)
2841 if (encode_hex_own_buf(msgid, SMS_MSGID_LEN, 0, msgid_str) == NULL)
2844 /* storagedir/%s/sms_sr/%s-%s */
2845 if (write_file((unsigned char *) node, len, SMS_BACKUP_MODE,
2846 SMS_SR_BACKUP_PATH_FILE, imsi,
2847 straddr, msgid_str) != len)
2853 static gboolean sr_assembly_remove_fragment_backup(const char *imsi,
2854 const struct sms_address *addr,
2855 const unsigned char *sha1)
2858 DECLARE_SMS_ADDR_STR(straddr);
2859 char msgid_str[SMS_MSGID_LEN * 2 + 1];
2864 if (sms_address_to_hex_string(addr, straddr) == FALSE)
2867 if (encode_hex_own_buf(sha1, SMS_MSGID_LEN, 0, msgid_str) == FALSE)
2870 path = g_strdup_printf(SMS_SR_BACKUP_PATH_FILE,
2871 imsi, straddr, msgid_str);
2879 void status_report_assembly_free(struct status_report_assembly *assembly)
2881 g_hash_table_destroy(assembly->assembly_table);
2885 static gboolean sr_st_to_delivered(enum sms_st st, gboolean *delivered)
2887 if (st >= SMS_ST_TEMPFINAL_CONGESTION && st <= SMS_ST_TEMPFINAL_LAST)
2890 if (st >= SMS_ST_TEMPORARY_CONGESTION && st <= SMS_ST_TEMPORARY_LAST)
2893 if (st <= SMS_ST_COMPLETED_LAST) {
2898 if (st >= SMS_ST_PERMANENT_RP_ERROR && st <= SMS_ST_PERMANENT_LAST) {
2906 static struct id_table_node *find_by_mr_and_mark(GHashTable *id_table,
2908 GHashTableIter *out_iter,
2909 unsigned char **out_id)
2911 unsigned int offset = mr / 32;
2912 unsigned int bit = 1 << (mr % 32);
2913 gpointer key, value;
2914 struct id_table_node *node;
2916 g_hash_table_iter_init(out_iter, id_table);
2917 while (g_hash_table_iter_next(out_iter, &key, &value)) {
2920 /* Address and MR matched */
2921 if (node->mrs[offset] & bit) {
2922 node->mrs[offset] ^= bit;
2933 * Key (receiver address) does not exist in assembly. Some networks can change
2934 * address to international format, although address is sent in the national
2935 * format. Handle also change from national to international format.
2936 * Notify these special cases by comparing only last six digits of the assembly
2937 * addresses and received address. If address contains less than six digits,
2938 * compare only existing digits.
2940 static struct id_table_node *fuzzy_lookup(struct status_report_assembly *assy,
2941 const struct sms *sr,
2942 const char **out_addr,
2943 GHashTableIter *out_iter,
2944 unsigned char **out_msgid)
2946 GHashTableIter iter_addr;
2947 gpointer key, value;
2950 r_addr = sms_address_to_string(&sr->status_report.raddr);
2951 g_hash_table_iter_init(&iter_addr, assy->assembly_table);
2953 while (g_hash_table_iter_next(&iter_addr, &key, &value)) {
2954 const char *s_addr = key;
2955 GHashTable *id_table = value;
2956 unsigned int len, r_len, s_len;
2958 struct id_table_node *node;
2960 if (r_addr[0] == '+' && s_addr[0] == '+')
2963 if (r_addr[0] != '+' && s_addr[0] != '+')
2966 r_len = strlen(r_addr);
2967 s_len = strlen(s_addr);
2969 len = MIN(6, MIN(r_len, s_len));
2971 for (i = 0; i < len; i++)
2972 if (s_addr[s_len - i - 1] != r_addr[r_len - i - 1])
2975 /* Not all digits matched. */
2979 /* Address matched. Check message reference. */
2980 node = find_by_mr_and_mark(id_table, sr->status_report.mr,
2981 out_iter, out_msgid);
2991 gboolean status_report_assembly_report(struct status_report_assembly *assembly,
2992 const struct sms *sr,
2993 unsigned char *out_msgid,
2994 gboolean *out_delivered)
2996 const char *straddr;
2997 GHashTable *id_table;
2998 GHashTableIter iter;
2999 struct sms_address addr;
3000 struct id_table_node *node;
3003 unsigned char *msgid;
3006 /* We ignore temporary or tempfinal status reports */
3007 if (sr_st_to_delivered(sr->status_report.st, &delivered) == FALSE)
3010 straddr = sms_address_to_string(&sr->status_report.raddr);
3011 id_table = g_hash_table_lookup(assembly->assembly_table, straddr);
3013 if (id_table != NULL)
3014 node = find_by_mr_and_mark(id_table, sr->status_report.mr,
3017 node = fuzzy_lookup(assembly, sr, &straddr, &iter, &msgid);
3019 /* Unable to find a message reference belonging to this address */
3023 node->deliverable = node->deliverable && delivered;
3025 /* If we haven't sent the entire message yet, wait until sent */
3026 if (node->sent_mrs < node->total_mrs)
3029 /* Figure out if we are expecting more status reports */
3030 for (i = 0, pending = FALSE; i < 8; i++) {
3031 /* There are still pending mr(s). */
3032 if (node->mrs[i] != 0) {
3038 sms_address_from_string(&addr, straddr);
3040 if (pending == TRUE && node->deliverable == TRUE) {
3042 * More status reports expected, and already received
3043 * reports completed. Update backup file.
3045 sr_assembly_add_fragment_backup(assembly->imsi, node,
3052 *out_delivered = node->deliverable;
3055 memcpy(out_msgid, msgid, SMS_MSGID_LEN);
3057 sr_assembly_remove_fragment_backup(assembly->imsi, &addr, msgid);
3058 id_table = g_hash_table_iter_get_hash_table(&iter);
3059 g_hash_table_iter_remove(&iter);
3061 if (g_hash_table_size(id_table) == 0)
3062 g_hash_table_remove(assembly->assembly_table, straddr);
3067 void status_report_assembly_add_fragment(
3068 struct status_report_assembly *assembly,
3069 const unsigned char *msgid,
3070 const struct sms_address *to,
3071 unsigned char mr, time_t expiration,
3072 unsigned char total_mrs)
3074 unsigned int offset = mr / 32;
3075 unsigned int bit = 1 << (mr % 32);
3076 GHashTable *id_table;
3077 struct id_table_node *node;
3078 unsigned char *id_table_key;
3080 id_table = g_hash_table_lookup(assembly->assembly_table,
3081 sms_address_to_string(to));
3083 /* Create hashtable keyed by the to address if required */
3084 if (id_table == NULL) {
3085 id_table = g_hash_table_new_full(sha1_hash, sha1_equal,
3087 g_hash_table_insert(assembly->assembly_table,
3088 g_strdup(sms_address_to_string(to)),
3092 node = g_hash_table_lookup(id_table, msgid);
3094 /* Create node in the message id hashtable if required */
3096 id_table_key = g_memdup(msgid, SMS_MSGID_LEN);
3098 node = g_new0(struct id_table_node, 1);
3099 node->total_mrs = total_mrs;
3100 node->deliverable = TRUE;
3102 g_hash_table_insert(id_table, id_table_key, node);
3105 /* id_table and node both exists */
3106 node->mrs[offset] |= bit;
3107 node->expiration = expiration;
3109 sr_assembly_add_fragment_backup(assembly->imsi, node, to, msgid);
3112 void status_report_assembly_expire(struct status_report_assembly *assembly,
3115 GHashTable *id_table;
3116 GHashTableIter iter_addr, iter_node;
3117 struct sms_address addr;
3120 struct id_table_node *node;
3122 g_hash_table_iter_init(&iter_addr, assembly->assembly_table);
3125 * Go through different addresses. Each address can relate to
3128 while (g_hash_table_iter_next(&iter_addr, (gpointer) &straddr,
3129 (gpointer) &id_table)) {
3131 sms_address_from_string(&addr, straddr);
3132 g_hash_table_iter_init(&iter_node, id_table);
3134 /* Go through different messages. */
3135 while (g_hash_table_iter_next(&iter_node, &key,
3136 (gpointer) &node)) {
3138 * If message is expired, removed it from the
3139 * hash-table and remove the backup-file
3141 if (node->expiration <= before) {
3142 g_hash_table_iter_remove(&iter_node);
3144 sr_assembly_remove_fragment_backup(
3152 * If all messages are removed, remove address
3153 * from the hash-table.
3155 if (g_hash_table_size(id_table) == 0)
3156 g_hash_table_iter_remove(&iter_addr);
3160 static int sms_tx_load_filter(const struct dirent *dent)
3163 guint8 seq __attribute__ ((unused));
3165 if (dent->d_type != DT_REG)
3168 seq = strtol(dent->d_name, &endp, 10);
3176 * Each directory contains a file per pdu.
3178 static GSList *sms_tx_load(const char *imsi, const struct dirent *dir)
3180 GSList *list = NULL;
3181 struct dirent **pdus;
3184 unsigned char buf[177];
3187 if (dir->d_type != DT_DIR)
3190 path = g_strdup_printf(SMS_TX_BACKUP_PATH "/%s", imsi, dir->d_name);
3191 len = scandir(path, &pdus, sms_tx_load_filter, versionsort);
3198 r = read_file(buf, sizeof(buf), SMS_TX_BACKUP_PATH "/%s/%s",
3199 imsi, dir->d_name, pdus[len]->d_name);
3204 if (sms_deserialize_outgoing(buf, &s, r) == FALSE)
3207 list = g_slist_prepend(list, g_memdup(&s, sizeof(s)));
3218 static int sms_tx_queue_filter(const struct dirent *dirent)
3220 if (dirent->d_type != DT_DIR)
3223 if (!strcmp(dirent->d_name, ".") || !strcmp(dirent->d_name, ".."))
3230 * populate the queue with tx_backup_entry from stored backup
3233 GQueue *sms_tx_queue_load(const char *imsi)
3237 struct dirent **entries;
3245 path = g_strdup_printf(SMS_TX_BACKUP_PATH, imsi);
3247 len = scandir(path, &entries, sms_tx_queue_filter, versionsort);
3251 retq = g_queue_new();
3253 for (i = 0, id = 0; i < len; i++) {
3254 char uuid[SMS_MSGID_LEN * 2 + 1];
3256 unsigned long oldid;
3257 unsigned long flags;
3258 char *oldpath, *newpath;
3259 struct txq_backup_entry *entry;
3260 struct dirent *dir = entries[i];
3263 if (sscanf(dir->d_name, "%lu-%lu-" SMS_MSGID_FMT "%c",
3264 &oldid, &flags, uuid, &endc) != 3)
3267 if (strlen(uuid) != 2 * SMS_MSGID_LEN)
3270 msg_list = sms_tx_load(imsi, dir);
3271 if (msg_list == NULL)
3274 entry = g_new0(struct txq_backup_entry, 1);
3275 entry->msg_list = msg_list;
3276 entry->flags = flags;
3277 decode_hex_own_buf(uuid, -1, NULL, 0, entry->uuid);
3279 g_queue_push_tail(retq, entry);
3281 /* Don't bother re-shuffling the ids if they are the same */
3287 oldpath = g_strdup_printf("%s/%s", path, dir->d_name);
3288 newpath = g_strdup_printf(SMS_TX_BACKUP_PATH_DIR,
3289 imsi, id++, flags, uuid);
3291 /* rename directory to reflect new position in queue */
3292 rename(oldpath, newpath);
3298 for (i = 0; i < len; i++)
3308 gboolean sms_tx_backup_store(const char *imsi, unsigned long id,
3309 unsigned long flags, const char *uuid,
3310 guint8 seq, const unsigned char *pdu,
3311 int pdu_len, int tpdu_len)
3313 unsigned char buf[177];
3319 memcpy(buf + 1, pdu, pdu_len);
3324 * file name is: imsi/tx_queue/order-flags-uuid/pdu
3326 if (write_file(buf, len, SMS_BACKUP_MODE, SMS_TX_BACKUP_PATH_FILE,
3327 imsi, id, flags, uuid, seq) != len)
3333 void sms_tx_backup_free(const char *imsi, unsigned long id,
3334 unsigned long flags, const char *uuid)
3337 struct dirent **entries;
3340 path = g_strdup_printf(SMS_TX_BACKUP_PATH_DIR,
3341 imsi, id, flags, uuid);
3343 len = scandir(path, &entries, NULL, versionsort);
3348 /* skip '..' and '.' entries */
3350 struct dirent *dir = entries[len];
3351 char *file = g_strdup_printf("%s/%s", path, dir->d_name);
3356 g_free(entries[len]);
3369 void sms_tx_backup_remove(const char *imsi, unsigned long id,
3370 unsigned long flags, const char *uuid,
3375 path = g_strdup_printf(SMS_TX_BACKUP_PATH_FILE,
3376 imsi, id, flags, uuid, seq);
3382 static inline GSList *sms_list_append(GSList *l, const struct sms *in)
3386 sms = g_new(struct sms, 1);
3387 memcpy(sms, in, sizeof(struct sms));
3388 l = g_slist_prepend(l, sms);
3394 * Prepares a datagram for transmission. Breaks up into fragments if
3395 * necessary using ref as the concatenated message reference number.
3396 * Returns a list of sms messages in order.
3398 * @use_delivery_reports: value for the Status-Report-Request field
3399 * (23.040 3.2.9, 9.2.2.2)
3401 GSList *sms_datagram_prepare(const char *to,
3402 const unsigned char *data, unsigned int len,
3403 guint16 ref, gboolean use_16bit_ref,
3404 unsigned short src, unsigned short dst,
3405 gboolean use_16bit_port,
3406 gboolean use_delivery_reports)
3408 struct sms template;
3409 unsigned int offset;
3410 unsigned int written;
3415 memset(&template, 0, sizeof(struct sms));
3416 template.type = SMS_TYPE_SUBMIT;
3417 template.submit.rd = FALSE;
3418 template.submit.vpf = SMS_VALIDITY_PERIOD_FORMAT_RELATIVE;
3419 template.submit.rp = FALSE;
3420 template.submit.srr = use_delivery_reports;
3421 template.submit.mr = 0;
3422 template.submit.vp.relative = 0xA7; /* 24 Hours */
3423 template.submit.dcs = 0x04; /* Class Unspecified, 8 Bit */
3424 template.submit.udhi = TRUE;
3425 sms_address_from_string(&template.submit.daddr, to);
3429 if (use_16bit_port) {
3430 template.submit.ud[0] += 6;
3431 template.submit.ud[offset] = SMS_IEI_APPLICATION_ADDRESS_16BIT;
3432 template.submit.ud[offset + 1] = 4;
3433 template.submit.ud[offset + 2] = (dst & 0xff00) >> 8;
3434 template.submit.ud[offset + 3] = dst & 0xff;
3435 template.submit.ud[offset + 4] = (src & 0xff00) >> 8;
3436 template.submit.ud[offset + 5] = src & 0xff;
3440 template.submit.ud[0] += 4;
3441 template.submit.ud[offset] = SMS_IEI_APPLICATION_ADDRESS_8BIT;
3442 template.submit.ud[offset + 1] = 2;
3443 template.submit.ud[offset + 2] = dst & 0xff;
3444 template.submit.ud[offset + 3] = src & 0xff;
3449 if (len <= (140 - offset)) {
3450 template.submit.udl = len + offset;
3451 memcpy(template.submit.ud + offset, data, len);
3453 return sms_list_append(NULL, &template);
3456 if (use_16bit_ref) {
3457 template.submit.ud[0] += 6;
3458 template.submit.ud[offset] = SMS_IEI_CONCATENATED_16BIT;
3459 template.submit.ud[offset + 1] = 4;
3460 template.submit.ud[offset + 2] = (ref & 0xff00) >> 8;
3461 template.submit.ud[offset + 3] = ref & 0xff;
3465 template.submit.ud[0] += 5;
3466 template.submit.ud[offset] = SMS_IEI_CONCATENATED_8BIT;
3467 template.submit.ud[offset + 1] = 3;
3468 template.submit.ud[offset + 2] = ref & 0xff;
3482 chunk = 140 - offset;
3486 template.submit.udl = chunk + offset;
3487 memcpy(template.submit.ud + offset, data + written, chunk);
3492 template.submit.ud[offset - 1] = seq;
3494 r = sms_list_append(r, &template);
3501 g_slist_foreach(r, (GFunc) g_free, NULL);
3508 for (l = r; l; l = l->next) {
3509 struct sms *sms = l->data;
3511 sms->submit.ud[offset - 2] = seq;
3515 r = g_slist_reverse(r);
3521 * Prepares the text for transmission. Breaks up into fragments if
3522 * necessary using ref as the concatenated message reference number.
3523 * Returns a list of sms messages in order.
3525 * @use_delivery_reports: value for the Status-Report-Request field
3526 * (23.040 3.2.9, 9.2.2.2)
3528 GSList *sms_text_prepare_with_alphabet(const char *to, const char *utf8,
3529 guint16 ref, gboolean use_16bit,
3530 gboolean use_delivery_reports,
3531 enum sms_alphabet alphabet)
3533 struct sms template;
3535 unsigned char *gsm_encoded = NULL;
3536 char *ucs2_encoded = NULL;
3541 enum gsm_dialect used_locking;
3542 enum gsm_dialect used_single;
3544 memset(&template, 0, sizeof(struct sms));
3545 template.type = SMS_TYPE_SUBMIT;
3546 template.submit.rd = FALSE;
3547 template.submit.vpf = SMS_VALIDITY_PERIOD_FORMAT_RELATIVE;
3548 template.submit.rp = FALSE;
3549 template.submit.srr = use_delivery_reports;
3550 template.submit.mr = 0;
3551 template.submit.vp.relative = 0xA7; /* 24 Hours */
3552 sms_address_from_string(&template.submit.daddr, to);
3555 * UDHI, UDL, UD and DCS actually depend on the contents of
3556 * the text, and also on the GSM dialect we use to encode it.
3558 gsm_encoded = convert_utf8_to_gsm_best_lang(utf8, -1, NULL, &written, 0,
3559 alphabet, &used_locking,
3561 if (gsm_encoded == NULL) {
3564 ucs2_encoded = g_convert(utf8, -1, "UCS-2BE//TRANSLIT", "UTF-8",
3565 NULL, &converted, NULL);
3566 written = converted;
3569 if (gsm_encoded == NULL && ucs2_encoded == NULL)
3572 if (gsm_encoded != NULL)
3573 template.submit.dcs = 0x00; /* Class Unspecified, 7 Bit */
3575 template.submit.dcs = 0x08; /* Class Unspecified, UCS2 */
3577 if (gsm_encoded != NULL && used_single != GSM_DIALECT_DEFAULT) {
3581 template.submit.ud[0] += 3;
3582 template.submit.ud[offset] = SMS_IEI_NATIONAL_LANGUAGE_SINGLE_SHIFT;
3583 template.submit.ud[offset + 1] = 1;
3584 template.submit.ud[offset + 2] = used_single;
3588 if (gsm_encoded != NULL && used_locking != GSM_DIALECT_DEFAULT) {
3592 template.submit.ud[0] += 3;
3593 template.submit.ud[offset] = SMS_IEI_NATIONAL_LANGUAGE_LOCKING_SHIFT;
3594 template.submit.ud[offset + 1] = 1;
3595 template.submit.ud[offset + 2] = used_locking;
3600 template.submit.udhi = TRUE;
3602 if (gsm_encoded && (written <= sms_text_capacity_gsm(160, offset))) {
3603 template.submit.udl = written + (offset * 8 + 6) / 7;
3604 pack_7bit_own_buf(gsm_encoded, written, offset, FALSE, NULL,
3605 0, template.submit.ud + offset);
3607 g_free(gsm_encoded);
3608 return sms_list_append(NULL, &template);
3611 if (ucs2_encoded && (written <= (140 - offset))) {
3612 template.submit.udl = written + offset;
3613 memcpy(template.submit.ud + offset, ucs2_encoded, written);
3615 g_free(ucs2_encoded);
3616 return sms_list_append(NULL, &template);
3619 template.submit.udhi = TRUE;
3625 template.submit.ud[0] += 6;
3626 template.submit.ud[offset] = SMS_IEI_CONCATENATED_16BIT;
3627 template.submit.ud[offset + 1] = 4;
3628 template.submit.ud[offset + 2] = (ref & 0xff00) >> 8;
3629 template.submit.ud[offset + 3] = ref & 0xff;
3633 template.submit.ud[0] += 5;
3634 template.submit.ud[offset] = SMS_IEI_CONCATENATED_8BIT;
3635 template.submit.ud[offset + 1] = 3;
3636 template.submit.ud[offset + 2] = ref & 0xff;
3651 chunk = sms_text_capacity_gsm(160, offset);
3656 if (gsm_encoded[written + chunk - 1] == 0x1b)
3659 template.submit.udl = chunk + (offset * 8 + 6) / 7;
3660 pack_7bit_own_buf(gsm_encoded + written, chunk,
3661 offset, FALSE, NULL, 0,
3662 template.submit.ud + offset);
3664 chunk = 140 - offset;
3670 template.submit.udl = chunk + offset;
3671 memcpy(template.submit.ud + offset,
3672 ucs2_encoded + written, chunk);
3678 template.submit.ud[offset - 1] = seq;
3680 r = sms_list_append(r, &template);
3687 g_free(gsm_encoded);
3690 g_free(ucs2_encoded);
3693 g_slist_foreach(r, (GFunc) g_free, NULL);
3700 for (l = r; l; l = l->next) {
3701 struct sms *sms = l->data;
3703 sms->submit.ud[offset - 2] = seq;
3707 r = g_slist_reverse(r);
3712 GSList *sms_text_prepare(const char *to, const char *utf8, guint16 ref,
3714 gboolean use_delivery_reports)
3716 return sms_text_prepare_with_alphabet(to, utf8, ref, use_16bit,
3717 use_delivery_reports,
3718 SMS_ALPHABET_DEFAULT);
3721 gboolean cbs_dcs_decode(guint8 dcs, gboolean *udhi, enum sms_class *cls,
3722 enum sms_charset *charset, gboolean *compressed,
3723 enum cbs_language *language, gboolean *iso639)
3725 guint8 upper = (dcs & 0xf0) >> 4;
3726 guint8 lower = dcs & 0xf;
3727 enum sms_charset ch;
3729 enum cbs_language lang = CBS_LANGUAGE_UNSPECIFIED;
3730 gboolean iso = FALSE;
3731 gboolean comp = FALSE;
3732 gboolean udh = FALSE;
3734 if (upper == 0x3 || upper == 0x8 || (upper >= 0xA && upper <= 0xE))
3739 ch = SMS_CHARSET_7BIT;
3740 cl = SMS_CLASS_UNSPECIFIED;
3741 lang = (enum cbs_language) lower;
3748 ch = SMS_CHARSET_7BIT;
3750 ch = SMS_CHARSET_UCS2;
3752 cl = SMS_CLASS_UNSPECIFIED;
3760 ch = SMS_CHARSET_7BIT;
3761 cl = SMS_CLASS_UNSPECIFIED;
3762 lang = (enum cbs_language) dcs;
3768 comp = (dcs & 0x20) ? TRUE : FALSE;
3771 cl = (enum sms_class) (dcs & 0x03);
3773 cl = SMS_CLASS_UNSPECIFIED;
3775 if (((dcs & 0x0c) >> 2) < 3)
3776 ch = (enum sms_charset) ((dcs & 0x0c) >> 2);
3783 cl = (enum sms_class) (dcs & 0x03);
3784 if (((dcs & 0x0c) >> 2) < 3)
3785 ch = (enum sms_charset) ((dcs & 0x0c) >> 2);
3795 ch = SMS_CHARSET_8BIT;
3797 ch = SMS_CHARSET_7BIT;
3800 cl = (enum sms_class) (lower & 0x3);
3802 cl = SMS_CLASS_UNSPECIFIED;
3830 gboolean cbs_decode(const unsigned char *pdu, int len, struct cbs *out)
3832 /* CBS is always a fixed length of 88 bytes */
3836 out->gs = (enum cbs_geo_scope) ((pdu[0] >> 6) & 0x03);
3837 out->message_code = ((pdu[0] & 0x3f) << 4) | ((pdu[1] >> 4) & 0xf);
3838 out->update_number = (pdu[1] & 0xf);
3839 out->message_identifier = (pdu[2] << 8) | pdu[3];
3841 out->max_pages = pdu[5] & 0xf;
3842 out->page = (pdu[5] >> 4) & 0xf;
3845 * If a mobile receives the code 0000 in either the first field or
3846 * the second field then it shall treat the CBS message exactly the
3847 * same as a CBS message with page parameter 0001 0001 (i.e. a single
3850 if (out->max_pages == 0 || out->page == 0) {
3855 memcpy(out->ud, pdu + 6, 82);
3860 gboolean cbs_encode(const struct cbs *cbs, int *len, unsigned char *pdu)
3862 pdu[0] = (cbs->gs << 6) | ((cbs->message_code >> 4) & 0x3f);
3863 pdu[1] = ((cbs->message_code & 0xf) << 4) | cbs->update_number;
3864 pdu[2] = cbs->message_identifier >> 8;
3865 pdu[3] = cbs->message_identifier & 0xff;
3867 pdu[5] = cbs->max_pages | (cbs->page << 4);
3869 memcpy(pdu + 6, cbs->ud, 82);
3877 gboolean cbs_extract_app_port(const struct cbs *cbs, int *dst, int *src,
3880 struct sms_udh_iter iter;
3882 if (!sms_udh_iter_init_from_cbs(cbs, &iter))
3885 return extract_app_port_common(&iter, dst, src, is_8bit);
3888 gboolean iso639_2_from_language(enum cbs_language lang, char *iso639)
3891 case CBS_LANGUAGE_GERMAN:
3896 case CBS_LANGUAGE_ENGLISH:
3901 case CBS_LANGUAGE_ITALIAN:
3906 case CBS_LANGUAGE_FRENCH:
3911 case CBS_LANGUAGE_SPANISH:
3916 case CBS_LANGUAGE_DUTCH:
3921 case CBS_LANGUAGE_SWEDISH:
3926 case CBS_LANGUAGE_DANISH:
3931 case CBS_LANGUAGE_PORTUGESE:
3936 case CBS_LANGUAGE_FINNISH:
3941 case CBS_LANGUAGE_NORWEGIAN:
3946 case CBS_LANGUAGE_GREEK:
3951 case CBS_LANGUAGE_TURKISH:
3956 case CBS_LANGUAGE_HUNGARIAN:
3961 case CBS_LANGUAGE_POLISH:
3966 case CBS_LANGUAGE_CZECH:
3971 case CBS_LANGUAGE_HEBREW:
3976 case CBS_LANGUAGE_ARABIC:
3981 case CBS_LANGUAGE_RUSSIAN:
3986 case CBS_LANGUAGE_ICELANDIC:
3999 char *cbs_decode_text(GSList *cbs_list, char *iso639_lang)
4002 const struct cbs *cbs;
4003 enum sms_charset uninitialized_var(charset);
4004 enum cbs_language lang;
4005 gboolean uninitialized_var(iso639);
4010 if (cbs_list == NULL)
4014 * CBS can only come from the network, so we're much less lenient
4015 * on what we support. Namely we require the same charset to be
4016 * used across all pages.
4018 for (l = cbs_list; l; l = l->next) {
4019 enum sms_charset curch;
4024 if (!cbs_dcs_decode(cbs->dcs, NULL, NULL,
4025 &curch, NULL, &lang, &curiso))
4028 if (l == cbs_list) {
4033 if (curch != charset)
4036 if (curiso != iso639)
4039 if (curch == SMS_CHARSET_8BIT)
4042 if (curch == SMS_CHARSET_7BIT) {
4043 bufsize += CBS_MAX_GSM_CHARS;
4056 cbs = cbs_list->data;
4059 struct sms_udh_iter iter;
4062 if (sms_udh_iter_init_from_cbs(cbs, &iter))
4063 taken = sms_udh_iter_get_udh_length(&iter) + 1;
4065 unpack_7bit_own_buf(cbs->ud + taken, 82 - taken,
4068 (unsigned char *)iso639_lang);
4069 iso639_lang[2] = '\0';
4071 iso639_2_from_language(lang, iso639_lang);
4075 buf = g_new(unsigned char, bufsize);
4078 for (l = cbs_list; l; l = l->next) {
4080 struct sms_udh_iter iter;
4086 if (sms_udh_iter_init_from_cbs(cbs, &iter))
4087 taken = sms_udh_iter_get_udh_length(&iter) + 1;
4089 if (charset == SMS_CHARSET_7BIT) {
4090 unsigned char unpacked[CBS_MAX_GSM_CHARS];
4096 sms_text_capacity_gsm(CBS_MAX_GSM_CHARS, taken);
4098 unpack_7bit_own_buf(ud + taken, 82 - taken,
4099 taken, FALSE, max_chars,
4100 &written, 0, unpacked);
4105 * CR is a padding character, which means we can
4106 * safely discard everything afterwards
4108 for (; i < written; i++, bufsize++) {
4109 if (unpacked[i] == '\r')
4112 buf[bufsize] = unpacked[i];
4116 * It isn't clear whether extension sequences
4117 * (2 septets) must be wholly present in the page
4118 * and not broken over multiple pages. The behavior
4119 * is probably the same as SMS, but we don't make
4120 * the check here since the specification isn't clear
4123 int num_ucs2_chars = (82 - taken) >> 1;
4125 int max_offset = taken + num_ucs2_chars * 2;
4128 * It is completely unclear how UCS2 chars are handled
4129 * especially across pages or when the UDH is present.
4130 * For now do the best we can.
4134 num_ucs2_chars -= 1;
4137 while (i < max_offset) {
4138 if (ud[i] == 0x00 && ud[i+1] == '\r')
4141 buf[bufsize] = ud[i];
4142 buf[bufsize + 1] = ud[i+1];
4150 if (charset == SMS_CHARSET_7BIT)
4151 utf8 = convert_gsm_to_utf8(buf, bufsize, NULL, NULL, 0);
4153 utf8 = g_convert((char *) buf, bufsize, "UTF-8//TRANSLIT",
4154 "UCS-2BE", NULL, NULL, NULL);
4160 static inline gboolean cbs_is_update_newer(unsigned int n, unsigned int o)
4162 unsigned int old_update = o & 0xf;
4163 unsigned int new_update = n & 0xf;
4165 if (new_update == old_update)
4169 * Any Update Number eight or less higher (modulo 16) than the last
4170 * received Update Number will be considered more recent, and shall be
4171 * treated as a new CBS message, provided the mobile has not been
4174 if (new_update <= ((old_update + 8) % 16))
4180 struct cbs_assembly *cbs_assembly_new(void)
4182 return g_new0(struct cbs_assembly, 1);
4185 void cbs_assembly_free(struct cbs_assembly *assembly)
4189 for (l = assembly->assembly_list; l; l = l->next) {
4190 struct cbs_assembly_node *node = l->data;
4192 g_slist_foreach(node->pages, (GFunc) g_free, 0);
4193 g_slist_free(node->pages);
4197 g_slist_free(assembly->assembly_list);
4198 g_slist_free(assembly->recv_plmn);
4199 g_slist_free(assembly->recv_loc);
4200 g_slist_free(assembly->recv_cell);
4205 static gint cbs_compare_node_by_gs(gconstpointer a, gconstpointer b)
4207 const struct cbs_assembly_node *node = a;
4208 unsigned int gs = GPOINTER_TO_UINT(b);
4210 if (((node->serial >> 14) & 0x3) == gs)
4216 static gint cbs_compare_node_by_update(gconstpointer a, gconstpointer b)
4218 const struct cbs_assembly_node *node = a;
4219 unsigned int serial = GPOINTER_TO_UINT(b);
4221 if ((serial & (~0xf)) != (node->serial & (~0xf)))
4224 if (cbs_is_update_newer(node->serial, serial))
4230 static gint cbs_compare_recv_by_serial(gconstpointer a, gconstpointer b)
4232 unsigned int old_serial = GPOINTER_TO_UINT(a);
4233 unsigned int new_serial = GPOINTER_TO_UINT(b);
4235 if ((old_serial & (~0xf)) == (new_serial & (~0xf)))
4241 static void cbs_assembly_expire(struct cbs_assembly *assembly,
4242 GCompareFunc func, gconstpointer *userdata)
4249 * Take care of the case where several updates are being
4250 * reassembled at the same time. If the newer one is assembled
4251 * first, then the subsequent old update is discarded, make
4252 * sure that we're also discarding the assembly node for the
4253 * partially assembled ones
4256 l = assembly->assembly_list;
4259 struct cbs_assembly_node *node = l->data;
4261 if (func(node, userdata) != 0) {
4268 prev->next = l->next;
4270 assembly->assembly_list = l->next;
4272 g_slist_foreach(node->pages, (GFunc) g_free, NULL);
4273 g_slist_free(node->pages);
4274 g_free(node->pages);
4277 g_slist_free_1(tmp);
4281 void cbs_assembly_location_changed(struct cbs_assembly *assembly, gboolean plmn,
4282 gboolean lac, gboolean ci)
4285 * Location Area wide (in GSM) (which means that a CBS message with the
4286 * same Message Code and Update Number may or may not be "new" in the
4287 * next cell according to whether the next cell is in the same Location
4288 * Area as the current cell), or
4290 * Service Area Wide (in UMTS) (which means that a CBS message with the
4291 * same Message Code and Update Number may or may not be "new" in the
4292 * next cell according to whether the next cell is in the same Service
4293 * Area as the current cell)
4295 * NOTEÂ 4: According to 3GPPÂ TSÂ 23.003Â [2] a Service Area consists of
4301 g_slist_free(assembly->recv_plmn);
4302 assembly->recv_plmn = NULL;
4304 cbs_assembly_expire(assembly, cbs_compare_node_by_gs,
4305 GUINT_TO_POINTER(CBS_GEO_SCOPE_PLMN));
4309 /* If LAC changed, then cell id has changed */
4311 g_slist_free(assembly->recv_loc);
4312 assembly->recv_loc = NULL;
4314 cbs_assembly_expire(assembly, cbs_compare_node_by_gs,
4315 GUINT_TO_POINTER(CBS_GEO_SCOPE_SERVICE_AREA));
4319 g_slist_free(assembly->recv_cell);
4320 assembly->recv_cell = NULL;
4321 cbs_assembly_expire(assembly, cbs_compare_node_by_gs,
4322 GUINT_TO_POINTER(CBS_GEO_SCOPE_CELL_IMMEDIATE));
4323 cbs_assembly_expire(assembly, cbs_compare_node_by_gs,
4324 GUINT_TO_POINTER(CBS_GEO_SCOPE_CELL_NORMAL));
4328 GSList *cbs_assembly_add_page(struct cbs_assembly *assembly,
4329 const struct cbs *cbs)
4332 struct cbs_assembly_node *node;
4334 unsigned int new_serial;
4340 new_serial = cbs->gs << 14;
4341 new_serial |= cbs->message_code << 4;
4342 new_serial |= cbs->update_number;
4343 new_serial |= cbs->message_identifier << 16;
4345 if (cbs->gs == CBS_GEO_SCOPE_PLMN)
4346 recv = &assembly->recv_plmn;
4347 else if (cbs->gs == CBS_GEO_SCOPE_SERVICE_AREA)
4348 recv = &assembly->recv_loc;
4350 recv = &assembly->recv_cell;
4352 /* Have we seen this message before? */
4353 l = g_slist_find_custom(*recv, GUINT_TO_POINTER(new_serial),
4354 cbs_compare_recv_by_serial);
4356 /* If we have, is the message newer? */
4357 if (l && !cbs_is_update_newer(new_serial, GPOINTER_TO_UINT(l->data)))
4360 /* Easy case first, page 1 of 1 */
4361 if (cbs->max_pages == 1 && cbs->page == 1) {
4363 l->data = GUINT_TO_POINTER(new_serial);
4365 *recv = g_slist_prepend(*recv,
4366 GUINT_TO_POINTER(new_serial));
4368 newcbs = g_new(struct cbs, 1);
4369 memcpy(newcbs, cbs, sizeof(struct cbs));
4370 completed = g_slist_append(NULL, newcbs);
4378 for (l = assembly->assembly_list; l; prev = l, l = l->next) {
4382 if (new_serial != node->serial)
4385 if (node->bitmap & (1 << cbs->page))
4388 for (j = 1; j < cbs->page; j++)
4389 if (node->bitmap & (1 << j))
4395 node = g_new0(struct cbs_assembly_node, 1);
4396 node->serial = new_serial;
4398 assembly->assembly_list = g_slist_prepend(assembly->assembly_list,
4402 l = assembly->assembly_list;
4406 newcbs = g_new(struct cbs, 1);
4407 memcpy(newcbs, cbs, sizeof(struct cbs));
4408 node->pages = g_slist_insert(node->pages, newcbs, position);
4409 node->bitmap |= 1 << cbs->page;
4411 if (g_slist_length(node->pages) < cbs->max_pages)
4414 completed = node->pages;
4417 prev->next = l->next;
4419 assembly->assembly_list = l->next;
4424 cbs_assembly_expire(assembly, cbs_compare_node_by_update,
4425 GUINT_TO_POINTER(new_serial));
4426 *recv = g_slist_prepend(*recv, GUINT_TO_POINTER(new_serial));
4431 static inline int skip_to_next_field(const char *str, int pos, int len)
4433 if (pos < len && str[pos] == ',')
4436 while (pos < len && str[pos] == ' ')
4442 static gboolean next_range(const char *str, int *offset, gint *min, gint *max)
4454 while (pos < len && str[pos] == ' ')
4459 while (str[end] >= '0' && str[end] <= '9') {
4460 low = low * 10 + (int)(str[end] - '0');
4467 if (str[end] != '-') {
4472 pos = end = end + 1;
4474 while (str[end] >= '0' && str[end] <= '9') {
4475 high = high * 10 + (int)(str[end] - '0');
4483 *offset = skip_to_next_field(str, end, len);
4494 GSList *cbs_optimize_ranges(GSList *ranges)
4496 struct cbs_topic_range *range;
4497 unsigned char bitmap[125];
4502 memset(bitmap, 0, sizeof(bitmap));
4504 for (l = ranges; l; l = l->next) {
4507 for (i = range->min; i <= range->max; i++) {
4508 int byte_offset = i / 8;
4511 bitmap[byte_offset] |= 1 << bit;
4517 for (i = 0; i <= 999; i++) {
4518 int byte_offset = i / 8;
4521 if (is_bit_set(bitmap[byte_offset], bit) == FALSE) {
4523 ret = g_slist_prepend(ret, range);
4535 range = g_new0(struct cbs_topic_range, 1);
4541 ret = g_slist_prepend(ret, range);
4543 ret = g_slist_reverse(ret);
4548 GSList *cbs_extract_topic_ranges(const char *ranges)
4556 while (next_range(ranges, &offset, &min, &max) == TRUE) {
4557 if (min < 0 || min > 999)
4560 if (max < 0 || max > 999)
4567 if (ranges[offset] != '\0')
4572 while (next_range(ranges, &offset, &min, &max) == TRUE) {
4573 struct cbs_topic_range *range = g_new0(struct cbs_topic_range, 1);
4578 ret = g_slist_prepend(ret, range);
4581 tmp = cbs_optimize_ranges(ret);
4582 g_slist_foreach(ret, (GFunc) g_free, NULL);
4588 static inline int element_length(unsigned short element)
4599 if (element <= 9999)
4605 static inline int range_length(struct cbs_topic_range *range)
4607 if (range->min == range->max)
4608 return element_length(range->min);
4610 return element_length(range->min) + element_length(range->max) + 1;
4613 char *cbs_topic_ranges_to_string(GSList *ranges)
4617 struct cbs_topic_range *range;
4622 return g_new0(char, 1);
4624 for (l = ranges; l; l = l->next) {
4627 len += range_length(range);
4631 /* Space for ranges, commas and terminator null */
4632 ret = g_new(char, len + nelem);
4636 for (l = ranges; l; l = l->next) {
4639 if (range->min != range->max)
4640 len += sprintf(ret + len, "%hu-%hu",
4641 range->min, range->max);
4643 len += sprintf(ret + len, "%hu", range->min);
4645 if (l->next != NULL)
4652 static gint cbs_topic_compare(gconstpointer a, gconstpointer b)
4654 const struct cbs_topic_range *range = a;
4655 unsigned short topic = GPOINTER_TO_UINT(b);
4657 if (topic >= range->min && topic <= range->max)
4663 gboolean cbs_topic_in_range(unsigned int topic, GSList *ranges)
4668 return g_slist_find_custom(ranges, GUINT_TO_POINTER(topic),
4669 cbs_topic_compare) != NULL;
4672 char *ussd_decode(int dcs, int len, const unsigned char *data)
4675 enum sms_charset charset;
4676 gboolean compressed;
4680 if (!cbs_dcs_decode(dcs, &udhi, NULL, &charset,
4681 &compressed, NULL, &iso639))
4684 if (udhi || compressed || iso639)
4688 case SMS_CHARSET_7BIT:
4691 unsigned char *unpacked = unpack_7bit(data, len, 0, TRUE, 0,
4693 if (unpacked == NULL)
4696 utf8 = convert_gsm_to_utf8(unpacked, written, NULL, NULL, 0);
4701 case SMS_CHARSET_8BIT:
4702 utf8 = convert_gsm_to_utf8(data, len, NULL, NULL, 0);
4704 case SMS_CHARSET_UCS2:
4705 utf8 = g_convert((const gchar *) data, len,
4706 "UTF-8//TRANSLIT", "UCS-2BE",
4716 gboolean ussd_encode(const char *str, long *items_written, unsigned char *pdu)
4718 unsigned char *converted = NULL;
4725 converted = convert_utf8_to_gsm(str, -1, NULL, &written, 0);
4726 if (converted == NULL || written > 182) {
4731 pack_7bit_own_buf(converted, written, 0, TRUE, &num_packed, 0, pdu);
4738 *items_written = num_packed;