From 80a41ed11b8ba40a54cecf044c2957a70a249b4d Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 17 Jan 2012 13:13:09 -0600 Subject: gsm: add SMS PDU creation function Only for basic SMS-SUBMIT PDUs at the moment, and doesn't support large SMSs yet. --- src/mm-charsets.c | 178 +++++++++++++++++++++++++++++- src/mm-charsets.h | 5 + src/mm-sms-utils.c | 303 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/mm-sms-utils.h | 15 +++ src/tests/test-sms.c | 173 +++++++++++++++++++++++++++++ 5 files changed, 671 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/mm-charsets.c b/src/mm-charsets.c index 708dd3e1..0956e4e1 100644 --- a/src/mm-charsets.c +++ b/src/mm-charsets.c @@ -36,8 +36,8 @@ static CharsetEntry charset_map[] = { { "IRA", "ASCII", "ASCII", "ASCII//TRANSLIT", MM_MODEM_CHARSET_IRA }, { "GSM", NULL, NULL, NULL, MM_MODEM_CHARSET_GSM }, { "8859-1", NULL, "ISO8859-1", "ISO8859-1//TRANSLIT", MM_MODEM_CHARSET_8859_1 }, - { "PCCP437", NULL, NULL, NULL, MM_MODEM_CHARSET_PCCP437 }, - { "PCDN", NULL, NULL, NULL, MM_MODEM_CHARSET_PCDN }, + { "PCCP437", "CP437", "CP437", "CP437//TRANSLIT", MM_MODEM_CHARSET_PCCP437 }, + { "PCDN", "CP850", "CP850", "CP850//TRANSLIT", MM_MODEM_CHARSET_PCDN }, { "HEX", NULL, NULL, NULL, MM_MODEM_CHARSET_HEX }, { NULL, NULL, NULL, NULL, MM_MODEM_CHARSET_UNKNOWN } }; @@ -456,6 +456,180 @@ mm_charset_utf8_to_unpacked_gsm (const char *utf8, guint32 *out_len) return g_byte_array_free (gsm, FALSE); } +static gboolean +gsm_is_subset (gunichar c, const char *utf8, gsize ulen, guint *out_clen) +{ + guint8 gsm; + + *out_clen = 1; + if (utf8_to_gsm_def_char (utf8, ulen, &gsm)) + return TRUE; + if (utf8_to_gsm_ext_char (utf8, ulen, &gsm)) { + *out_clen = 2; + return TRUE; + } + return FALSE; +} + +static gboolean +ira_is_subset (gunichar c, const char *utf8, gsize ulen, guint *out_clen) +{ + *out_clen = 1; + return (ulen == 1); +} + +static gboolean +ucs2_is_subset (gunichar c, const char *utf8, gsize ulen, guint *out_clen) +{ + *out_clen = 2; + return (c <= 0xFFFF); +} + +static gboolean +iso88591_is_subset (gunichar c, const char *utf8, gsize ulen, guint *out_clen) +{ + *out_clen = 1; + return (c <= 0xFF); +} + +static gboolean +pccp437_is_subset (gunichar c, const char *utf8, gsize ulen, guint *out_clen) +{ + static const gunichar t[] = { + 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, + 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6, + 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, + 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, + 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, + 0x00a1, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, + 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, + 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, + 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, 0x2568, + 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, + 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, 0x03b1, 0x00df, 0x0393, + 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, 0x03b4, + 0x221e, 0x03c6, 0x03b5, 0x2229, 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, + 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, + 0x25a0, 0x00a0 + }; + int i; + + *out_clen = 1; + + if (c <= 0x7F) + return TRUE; + for (i = 0; i < sizeof (t) / sizeof (t[0]); i++) { + if (c == t[i]) + return TRUE; + } + return FALSE; +} + +static gboolean +pcdn_is_subset (gunichar c, const char *utf8, gsize ulen, guint *out_clen) +{ + static const gunichar t[] = { + 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, + 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6, + 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, + 0x00f8, 0x00a3, 0x00d8, 0x00d7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, + 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x00ae, 0x00ac, 0x00bd, 0x00bc, + 0x00a1, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00c1, + 0x00c2, 0x00c0, 0x00a9, 0x2563, 0x2551, 0x2557, 0x255d, 0x00a2, 0x00a5, + 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x00e3, 0x00c3, + 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x00a4, 0x00f0, + 0x00d0, 0x00ca, 0x00cb, 0x00c8, 0x0131, 0x00cd, 0x00ce, 0x00cf, 0x2518, + 0x250c, 0x2588, 0x2584, 0x00a6, 0x00cc, 0x2580, 0x00d3, 0x00df, 0x00d4, + 0x00d2, 0x00f5, 0x00d5, 0x00b5, 0x00fe, 0x00de, 0x00da, 0x00db, 0x00d9, + 0x00fd, 0x00dd, 0x00af, 0x00b4, 0x00ad, 0x00b1, 0x2017, 0x00be, 0x00b6, + 0x00a7, 0x00f7, 0x00b8, 0x00b0, 0x00a8, 0x00b7, 0x00b9, 0x00b3, 0x00b2, + 0x25a0, 0x00a0 + }; + int i; + + *out_clen = 1; + + if (c <= 0x7F) + return TRUE; + for (i = 0; i < sizeof (t) / sizeof (t[0]); i++) { + if (c == t[i]) + return TRUE; + } + return FALSE; +} + +typedef struct { + MMModemCharset cs; + gboolean (*func) (gunichar c, const char *utf8, gsize ulen, guint *out_clen); + guint charsize; +} SubsetEntry; + +SubsetEntry subset_table[] = { + { MM_MODEM_CHARSET_GSM, gsm_is_subset }, + { MM_MODEM_CHARSET_IRA, ira_is_subset }, + { MM_MODEM_CHARSET_UCS2, ucs2_is_subset }, + { MM_MODEM_CHARSET_8859_1, iso88591_is_subset }, + { MM_MODEM_CHARSET_PCCP437, pccp437_is_subset }, + { MM_MODEM_CHARSET_PCDN, pcdn_is_subset }, + { MM_MODEM_CHARSET_UNKNOWN, NULL }, +}; + +/** + * mm_charset_get_encoded_len: + * + * @utf8: UTF-8 valid string + * @charset: the #MMModemCharset to check the length of @utf8 in + * @out_unsupported: on return, number of characters of @utf8 that are not fully + * representable in @charset + * + * Returns: the size in bytes of the string if converted from UTF-8 into @charset. + **/ +guint +mm_charset_get_encoded_len (const char *utf8, + MMModemCharset charset, + guint *out_unsupported) +{ + const char *p = utf8, *next; + guint len = 0, unsupported = 0; + SubsetEntry *e; + + g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, 0); + g_return_val_if_fail (utf8 != NULL, 0); + + if (charset == MM_MODEM_CHARSET_UTF8) + return strlen (utf8); + + /* Find the charset in our subset table */ + for (e = &subset_table[0]; + e->cs != charset && e->cs != MM_MODEM_CHARSET_UNKNOWN; + e++); + g_return_val_if_fail (e->cs != MM_MODEM_CHARSET_UNKNOWN, 0); + + while (*p) { + gunichar c; + const char *end; + guint clen = 0; + + c = g_utf8_get_char_validated (p, -1); + g_return_val_if_fail (c != (gunichar) -1, 0); + end = next = g_utf8_find_next_char (p, NULL); + if (end == NULL) { + /* Find the end... */ + end = p; + while (*end++); + } + + if (!e->func (c, p, (end - p), &clen)) + unsupported++; + len += clen; + p = next; + } + + if (out_unsupported) + *out_unsupported = unsupported; + return len; +} + guint8 * gsm_unpack (const guint8 *gsm, guint32 num_septets, diff --git a/src/mm-charsets.h b/src/mm-charsets.h index d620b157..7eb35ab8 100644 --- a/src/mm-charsets.h +++ b/src/mm-charsets.h @@ -57,6 +57,11 @@ guint8 *mm_charset_utf8_to_unpacked_gsm (const char *utf8, guint32 *out_len); guint8 *mm_charset_gsm_unpacked_to_utf8 (const guint8 *gsm, guint32 len); +/* Returns the size in bytes required to hold the UTF-8 string in the given charset */ +guint mm_charset_get_encoded_len (const char *utf8, + MMModemCharset charset, + guint *out_unsupported); + guint8 *gsm_unpack (const guint8 *gsm, guint32 num_septets, guint8 start_offset, /* in bits */ diff --git a/src/mm-sms-utils.c b/src/mm-sms-utils.c index f3e3b759..fc493dc4 100644 --- a/src/mm-sms-utils.c +++ b/src/mm-sms-utils.c @@ -14,7 +14,7 @@ */ #include -#include +#include #include @@ -75,6 +75,93 @@ sms_semi_octets_to_bcd_string (char *dest, const guint8 *octets, int num_octets) *dest++ = '\0'; } +static gboolean +char_to_bcd (char in, guint8 *out) +{ + guint32 z; + + if (isdigit (in)) { + *out = in - 0x30; + return TRUE; + } + + for (z = 10; z < 16; z++) { + if (in == sms_bcd_chars[z]) { + *out = z; + return TRUE; + } + } + return FALSE; +} + +static gsize +sms_string_to_bcd_semi_octets (guint8 *buf, gsize buflen, const char *string) +{ + guint i; + guint8 bcd; + gsize addrlen, slen; + + addrlen = slen = strlen (string); + if (addrlen % 2) + addrlen++; + g_return_val_if_fail (buflen >= addrlen, 0); + + for (i = 0; i < addrlen; i += 2) { + if (!char_to_bcd (string[i], &bcd)) + return 0; + buf[i / 2] = bcd & 0xF; + + if (i >= slen - 1) { + /* PDU address gets padded with 0xF if string is odd length */ + bcd = 0xF; + } else if (!char_to_bcd (string[i + 1], &bcd)) + return 0; + buf[i / 2] |= bcd << 4; + } + return addrlen / 2; +} + +/** + * sms_encode_address: + * + * @address: the phone number to encode + * @buf: the buffer to encode @address in + * @buflen: the size of @buf + * @is_smsc: if %TRUE encode size as number of octets of address infromation, + * otherwise if %FALSE encode size as number of digits of @address + * + * Returns: the size in bytes of the data added to @buf + **/ +guint +sms_encode_address (const char *address, + guint8 *buf, + size_t buflen, + gboolean is_smsc) +{ + gsize len; + + g_return_val_if_fail (address != NULL, 0); + g_return_val_if_fail (buf != NULL, 0); + g_return_val_if_fail (buflen >= 2, 0); + + /* Handle number type & plan */ + buf[1] = 0x80; /* Bit 7 always 1 */ + if (address[0] == '+') { + buf[1] |= SMS_NUMBER_TYPE_INTL; + address++; + } + buf[1] |= SMS_NUMBER_PLAN_TELEPHONE; + + len = sms_string_to_bcd_semi_octets (&buf[2], buflen, address); + + if (is_smsc) + buf[0] = len + 1; /* addr length + size byte */ + else + buf[0] = strlen (address); /* number of digits in address */ + + return len ? len + 2 : 0; /* addr length + size byte + number type/plan */ +} + /* len is in semi-octets */ static char * sms_decode_address (const guint8 *address, int len) @@ -457,3 +544,217 @@ sms_parse_pdu (const char *hexpdu, GError **error) return properties; } + +static guint8 +validity_to_relative (guint validity) +{ + if (validity == 0) + return 167; /* 24 hours */ + + if (validity <= 144) { + /* 5 minute units up to 12 hours */ + return validity - 1; + } + + if (validity > 144 && validity <= 288) { + /* 12 hours + 30 minute units up to 1 day */ + if (validity % 6) + validity += 6; /* round up to next 30 minutes */ + validity = MIN (validity, 288); + return 143 + ((validity - 144) / 6); /* 6 = 30 minutes / 5 minutes */ + } + + if (validity > 288 && validity <= 8640) { + /* 2 days up to 1 month */ + if (validity % 288) + validity += 288; /* round up to next day */ + validity = MIN (validity, 8640); + return 167 + ((validity - 288) / 288); /* 288 = 1 day / 5 minutes */ + } + + /* 8640 = 30 days in 5-minute units + * 2016 = 7 days in 5-minute units + * 127008 = 63 weeks in 5-minute units + * 8064 = 4 weeks in 5-minute units + */ + if (validity > 8640 && validity <= 127008) { + /* 5 weeks up to 63 weeks */ + if (validity % 2016) + validity += 2016; /* round up to next week */ + validity = MIN (validity, 127008); + return 196 + ((validity - 8064) / 2016); + } + + return 255; /* 63 weeks */ +} + +#define PDU_SIZE 200 + +/** + * sms_create_submit_pdu: + * + * @number: the subscriber number to send this message to + * @text: the body of this SMS + * @smsc: if given, the SMSC address + * @validity: validity period of this SMS in 5-minute increments, or 0 for a + * suitable default + * @class: unused + * @out_pdulen: on success, the size of the returned PDU in bytes + * @error: on error, filled with the error that occurred + * + * Constructs a single-part SMS message with the given details, preferring to + * use the UCS2 character set when the message will fit, otherwise falling back + * to the GSM character set. + * + * Returns: the constructed PDU data on success, or %NULL on error + **/ +guint8 * +sms_create_submit_pdu (const char *number, + const char *text, + const char *smsc, + guint validity, + guint class, + guint *out_pdulen, + GError **error) +{ + guint8 *pdu; + guint len, offset = 0; + MMModemCharset best_cs = MM_MODEM_CHARSET_GSM; + guint ucs2len = 0, gsm_unsupported = 0; + guint textlen = 0; + + g_return_val_if_fail (number != NULL, NULL); + g_return_val_if_fail (text != NULL, NULL); + + /* FIXME: support multiple fragments */ + + textlen = mm_charset_get_encoded_len (text, MM_MODEM_CHARSET_GSM, &gsm_unsupported); + if (textlen > 160) { + g_set_error_literal (error, + MM_MODEM_ERROR, + MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED, + "Cannot encode message to fit into an SMS."); + return NULL; + } + + /* If there are characters that are unsupported in the GSM charset, try + * UCS2. If the UCS2 encoded string is too long to fit in an SMS, then + * just use GSM and suck up the unconverted chars. + */ + if (gsm_unsupported > 0) { + ucs2len = mm_charset_get_encoded_len (text, MM_MODEM_CHARSET_UCS2, NULL); + if (ucs2len <= 140) { + best_cs = MM_MODEM_CHARSET_UCS2; + textlen = ucs2len; + } + } + + /* Build up the PDU */ + pdu = g_malloc0 (PDU_SIZE); + g_return_val_if_fail (pdu != NULL, NULL); + + if (smsc) { + len = sms_encode_address (smsc, pdu, PDU_SIZE, TRUE); + if (len == 0) { + g_set_error (error, + MM_MSG_ERROR, + MM_MSG_ERROR_INVALID_PDU_PARAMETER, + "Invalid SMSC address '%s'", smsc); + goto error; + } + offset += len; + } else { + /* No SMSC, use default */ + pdu[offset++] = 0x00; + } + + if (validity > 0) + pdu[offset] = 1 << 4; /* TP-VP present; format RELATIVE */ + else + pdu[offset] = 0; /* TP-VP not present */ + pdu[offset++] |= 0x01; /* TP-MTI = SMS-SUBMIT */ + + pdu[offset++] = 0x00; /* TP-Message-Reference: filled by device */ + + len = sms_encode_address (number, &pdu[offset], PDU_SIZE - offset, FALSE); + if (len == 0) { + g_set_error (error, + MM_MSG_ERROR, + MM_MSG_ERROR_INVALID_PDU_PARAMETER, + "Invalid send-to number '%s'", number); + goto error; + } + offset += len; + + /* TP-PID */ + pdu[offset++] = 0x00; + + /* TP-DCS */ + if (best_cs == MM_MODEM_CHARSET_UCS2) + pdu[offset++] = 0x08; + else + pdu[offset++] = 0x00; /* GSM */ + + /* TP-Validity-Period: 4 days */ + if (validity > 0) + pdu[offset++] = validity_to_relative (validity); + + /* TP-User-Data-Length */ + pdu[offset++] = textlen; + + if (best_cs == MM_MODEM_CHARSET_GSM) { + guint8 *unpacked, *packed; + guint32 unlen = 0, packlen = 0; + + unpacked = mm_charset_utf8_to_unpacked_gsm (text, &unlen); + if (!unpacked || unlen == 0) { + g_free (unpacked); + g_set_error_literal (error, + MM_MSG_ERROR, + MM_MSG_ERROR_INVALID_PDU_PARAMETER, + "Failed to convert message text to GSM."); + goto error; + } + + packed = gsm_pack (unpacked, unlen, 0, &packlen); + g_free (unpacked); + if (!packed || packlen == 0) { + g_free (packed); + g_set_error_literal (error, + MM_MSG_ERROR, + MM_MSG_ERROR_INVALID_PDU_PARAMETER, + "Failed to pack message text to GSM."); + goto error; + } + + memcpy (&pdu[offset], packed, packlen); + g_free (packed); + offset += packlen; + } else if (best_cs == MM_MODEM_CHARSET_UCS2) { + GByteArray *array; + + array = g_byte_array_sized_new (textlen / 2); + if (!mm_modem_charset_byte_array_append (array, text, FALSE, best_cs)) { + g_byte_array_free (array, TRUE); + g_set_error_literal (error, + MM_MSG_ERROR, + MM_MSG_ERROR_INVALID_PDU_PARAMETER, + "Failed to convert message text to UCS2."); + goto error; + } + + memcpy (&pdu[offset], array->data, array->len); + offset += array->len; + g_byte_array_free (array, TRUE); + } else + g_assert_not_reached (); + + if (out_pdulen) + *out_pdulen = offset; + return pdu; + +error: + g_free (pdu); + return NULL; +} + diff --git a/src/mm-sms-utils.h b/src/mm-sms-utils.h index 26d9829b..8455df9a 100644 --- a/src/mm-sms-utils.h +++ b/src/mm-sms-utils.h @@ -22,4 +22,19 @@ GHashTable *sms_parse_pdu (const char *hexpdu, GError **error); +guint8 *sms_create_submit_pdu (const char *number, + const char *text, + const char *smsc, + guint validity, + guint class, + guint *out_pdulen, + GError **error); + +/* For testcases only */ +guint sms_encode_address (const char *address, + guint8 *buf, + size_t buflen, + gboolean is_smsc); + + #endif /* MM_SMS_UTILS_H */ diff --git a/src/tests/test-sms.c b/src/tests/test-sms.c index aa234e63..2f3f3781 100644 --- a/src/tests/test-sms.c +++ b/src/tests/test-sms.c @@ -420,7 +420,170 @@ test_pduX (void *f, gpointer d) } #endif +static void +test_encode_sms_addr_encode_smsc_intl (void *f, gpointer d) +{ + static const char *addr = "+19037029920"; + static const guint8 expected[] = { 0x07, 0x91, 0x91, 0x30, 0x07, 0x92, 0x29, 0xF0 }; + guint enclen; + guint8 buf[20]; + + enclen = sms_encode_address (addr, buf, sizeof (buf), TRUE); + g_assert_cmpint (enclen, ==, sizeof (expected)); + g_assert_cmpint (memcmp (buf, expected, sizeof (expected)), ==, 0); +} + +static void +test_encode_sms_addr_encode_smsc_unknown (void *f, gpointer d) +{ + static const char *addr = "9037029920"; + static const guint8 expected[] = { 0x06, 0x81, 0x09, 0x73, 0x20, 0x99, 0x02 }; + guint enclen; + guint8 buf[20]; + + enclen = sms_encode_address (addr, buf, sizeof (buf), TRUE); + g_assert_cmpint (enclen, ==, sizeof (expected)); + g_assert_cmpint (memcmp (buf, expected, sizeof (expected)), ==, 0); +} + +static void +test_encode_sms_addr_encode_intl (void *f, gpointer d) +{ + static const char *addr = "+19037029920"; + static const guint8 expected[] = { 0x0B, 0x91, 0x91, 0x30, 0x07, 0x92, 0x29, 0xF0 }; + guint enclen; + guint8 buf[20]; + + enclen = sms_encode_address (addr, buf, sizeof (buf), FALSE); + g_assert_cmpint (enclen, ==, sizeof (expected)); + g_assert_cmpint (memcmp (buf, expected, sizeof (expected)), ==, 0); +} + +static void +test_encode_sms_addr_encode_unknown (void *f, gpointer d) +{ + static const char *addr = "9037029920"; + static const guint8 expected[] = { 0x0A, 0x81, 0x09, 0x73, 0x20, 0x99, 0x02 }; + guint enclen; + guint8 buf[20]; + + enclen = sms_encode_address (addr, buf, sizeof (buf), FALSE); + g_assert_cmpint (enclen, ==, sizeof (expected)); + g_assert_cmpint (memcmp (buf, expected, sizeof (expected)), ==, 0); +} + +static void +test_create_pdu_ucs2_with_smsc (void *f, gpointer d) +{ + static const char *smsc = "+19037029920"; + static const char *number = "+15555551234"; + static const char *text = "Да здравствует король, детка!"; + static const guint8 expected[] = { + 0x07, 0x91, 0x91, 0x30, 0x07, 0x92, 0x29, 0xF0, 0x11, 0x00, 0x0B, 0x91, + 0x51, 0x55, 0x55, 0x15, 0x32, 0xF4, 0x00, 0x08, 0x00, 0x3A, 0x04, 0x14, + 0x04, 0x30, 0x00, 0x20, 0x04, 0x37, 0x04, 0x34, 0x04, 0x40, 0x04, 0x30, + 0x04, 0x32, 0x04, 0x41, 0x04, 0x42, 0x04, 0x32, 0x04, 0x43, 0x04, 0x35, + 0x04, 0x42, 0x00, 0x20, 0x04, 0x3A, 0x04, 0x3E, 0x04, 0x40, 0x04, 0x3E, + 0x04, 0x3B, 0x04, 0x4C, 0x00, 0x2C, 0x00, 0x20, 0x04, 0x34, 0x04, 0x35, + 0x04, 0x42, 0x04, 0x3A, 0x04, 0x30, 0x00, 0x21 + }; + guint8 *pdu; + guint len = 0; + GError *error = NULL; + + pdu = sms_create_submit_pdu (number, text, smsc, 1, 0, &len, &error); + g_assert_no_error (error); + g_assert (pdu); + g_assert_cmpint (len, ==, sizeof (expected)); + g_assert_cmpint (memcmp (pdu, expected, len), ==, 0); +} +static void +test_create_pdu_ucs2_no_smsc (void *f, gpointer d) +{ + static const char *number = "+15555551234"; + static const char *text = "Да здравствует король, детка!"; + static const guint8 expected[] = { + 0x00, 0x11, 0x00, 0x0B, 0x91, 0x51, 0x55, 0x55, 0x15, 0x32, 0xF4, 0x00, + 0x08, 0x00, 0x3A, 0x04, 0x14, 0x04, 0x30, 0x00, 0x20, 0x04, 0x37, 0x04, + 0x34, 0x04, 0x40, 0x04, 0x30, 0x04, 0x32, 0x04, 0x41, 0x04, 0x42, 0x04, + 0x32, 0x04, 0x43, 0x04, 0x35, 0x04, 0x42, 0x00, 0x20, 0x04, 0x3A, 0x04, + 0x3E, 0x04, 0x40, 0x04, 0x3E, 0x04, 0x3B, 0x04, 0x4C, 0x00, 0x2C, 0x00, + 0x20, 0x04, 0x34, 0x04, 0x35, 0x04, 0x42, 0x04, 0x3A, 0x04, 0x30, 0x00, + 0x21 + }; + guint8 *pdu; + guint len = 0; + GError *error = NULL; + + pdu = sms_create_submit_pdu (number, text, NULL, 1, 0, &len, &error); + g_assert_no_error (error); + g_assert (pdu); + g_assert_cmpint (len, ==, sizeof (expected)); + g_assert_cmpint (memcmp (pdu, expected, len), ==, 0); +} + +static void +test_create_pdu_gsm_with_smsc (void *f, gpointer d) +{ + static const char *smsc = "+19037029920"; + static const char *number = "+15555551234"; + static const char *text = "Hi there...Tue 17th Jan 2012 05:30.18 pm (GMT+1) ΔΔΔΔΔ"; + static const guint8 expected[] = { + 0x07, 0x91, 0x91, 0x30, 0x07, 0x92, 0x29, 0xF0, 0x11, 0x00, 0x0B, 0x91, + 0x51, 0x55, 0x55, 0x15, 0x32, 0xF4, 0x00, 0x00, 0x00, 0x36, 0xC8, 0x34, + 0x88, 0x8E, 0x2E, 0xCB, 0xCB, 0x2E, 0x97, 0x8B, 0x5A, 0x2F, 0x83, 0x62, + 0x37, 0x3A, 0x1A, 0xA4, 0x0C, 0xBB, 0x41, 0x32, 0x58, 0x4C, 0x06, 0x82, + 0xD5, 0x74, 0x33, 0x98, 0x2B, 0x86, 0x03, 0xC1, 0xDB, 0x20, 0xD4, 0xB1, + 0x49, 0x5D, 0xC5, 0x52, 0x20, 0x08, 0x04, 0x02, 0x81, 0x00 + }; + guint8 *pdu; + guint len = 0; + GError *error = NULL; + + pdu = sms_create_submit_pdu (number, text, smsc, 1, 0, &len, &error); + g_assert_no_error (error); + g_assert (pdu); + g_assert_cmpint (len, ==, sizeof (expected)); + g_assert_cmpint (memcmp (pdu, expected, len), ==, 0); +} + +static void +test_create_pdu_gsm_no_smsc (void *f, gpointer d) +{ + static const char *number = "+15555551234"; + static const char *text = "Hi there...Tue 17th Jan 2012 05:30.18 pm (GMT+1) ΔΔΔΔΔ"; + static const guint8 expected[] = { + 0x00, 0x11, 0x00, 0x0B, 0x91, 0x51, 0x55, 0x55, 0x15, 0x32, 0xF4, 0x00, + 0x00, 0x00, 0x36, 0xC8, 0x34, 0x88, 0x8E, 0x2E, 0xCB, 0xCB, 0x2E, 0x97, + 0x8B, 0x5A, 0x2F, 0x83, 0x62, 0x37, 0x3A, 0x1A, 0xA4, 0x0C, 0xBB, 0x41, + 0x32, 0x58, 0x4C, 0x06, 0x82, 0xD5, 0x74, 0x33, 0x98, 0x2B, 0x86, 0x03, + 0xC1, 0xDB, 0x20, 0xD4, 0xB1, 0x49, 0x5D, 0xC5, 0x52, 0x20, 0x08, 0x04, + 0x02, 0x81, 0x00 + }; + guint8 *pdu; + guint len = 0; + GError *error = NULL; + + pdu = sms_create_submit_pdu (number, text, NULL, 1, 0, &len, &error); + g_assert_no_error (error); + g_assert (pdu); + g_assert_cmpint (len, ==, sizeof (expected)); + g_assert_cmpint (memcmp (pdu, expected, len), ==, 0); +} + +#if 0 +{ +int i; +g_print ("\n "); +for (i = 0; i < len; i++) { + g_print (" 0x%02X", pdu[i]); + if (((i + 1) % 12) == 0) + g_print ("\n "); +} +g_print ("\n"); +} +#endif #if GLIB_CHECK_VERSION(2,25,12) typedef GTestFixtureFunc TCFunc; @@ -453,6 +616,16 @@ int main (int argc, char **argv) g_test_suite_add (suite, TESTCASE (test_pdu_insufficient_data, NULL)); g_test_suite_add (suite, TESTCASE (test_pdu_udhi, NULL)); + g_test_suite_add (suite, TESTCASE (test_encode_sms_addr_encode_smsc_intl, NULL)); + g_test_suite_add (suite, TESTCASE (test_encode_sms_addr_encode_smsc_unknown, NULL)); + g_test_suite_add (suite, TESTCASE (test_encode_sms_addr_encode_intl, NULL)); + g_test_suite_add (suite, TESTCASE (test_encode_sms_addr_encode_unknown, NULL)); + + g_test_suite_add (suite, TESTCASE (test_create_pdu_ucs2_with_smsc, NULL)); + g_test_suite_add (suite, TESTCASE (test_create_pdu_ucs2_no_smsc, NULL)); + g_test_suite_add (suite, TESTCASE (test_create_pdu_gsm_with_smsc, NULL)); + g_test_suite_add (suite, TESTCASE (test_create_pdu_gsm_no_smsc, NULL)); + result = g_test_run (); return result; -- cgit v1.2.3-70-g09d2