gsm: add SMS PDU creation function
Only for basic SMS-SUBMIT PDUs at the moment, and doesn't support large SMSs yet.
This commit is contained in:
@@ -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,
|
||||
|
@@ -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 */
|
||||
|
@@ -14,7 +14,7 @@
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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 */
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user