Kannel: Open Source WAP and SMS gateway  svn-r5335
sms.c
Go to the documentation of this file.
1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2018 Kannel Group
5  * Copyright (c) 1998-2001 WapIT Ltd.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Kannel Group (http://www.kannel.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Kannel" and "Kannel Group" must not be used to
28  * endorse or promote products derived from this software without
29  * prior written permission. For written permission, please
30  * contact org@kannel.org.
31  *
32  * 5. Products derived from this software may not be called "Kannel",
33  * nor may "Kannel" appear in their name, without prior written
34  * permission of the Kannel Group.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
40  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
41  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
42  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
45  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
46  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Kannel Group. For more information on
51  * the Kannel Group, please see <http://www.kannel.org/>.
52  *
53  * Portions of this software are based upon software originally written at
54  * WapIT Ltd., Helsinki, Finland for the Kannel project.
55  */
56 
57 /*
58  * sms.c - features specific to SMS but not particular to any SMSC protocol.
59  *
60  * This file current contains very little, but sms features that are
61  * currently implemented separately in each protocol should be extracted
62  * and placed here.
63  */
64 
65 #include "sms.h"
66 #include "dlr.h"
67 
68 /*
69  * Encode DCS using sms fields
70  * mode = 0= encode using 00xxx, 1= encode using Fx mode
71  *
72  */
73 int fields_to_dcs(Msg *msg, int mode)
74 {
75  int dcs=0;
76 
77  /* Coding defaults to 7BIT or to 8BIT if udh is set */
78  if (msg->sms.coding == DC_UNDEF) {
79  if (octstr_len(msg->sms.udhdata))
80  msg->sms.coding = DC_8BIT;
81  else
82  msg->sms.coding = DC_7BIT;
83  }
84 
85 
86  /* MWI */
87  if (msg->sms.mwi != MWI_UNDEF) {
88  dcs = msg->sms.mwi; /* sets bits 2, 1 and 0 */
89 
90  if (dcs & 0x04)
91  dcs = (dcs & 0x03) | 0xC0; /* MWI Inactive */
92  else {
93  dcs = (dcs & 0x03) | 0x08; /* MWI Active, sets bit 3 */
94 
95  if (! octstr_len(msg->sms.msgdata))
96  dcs |= 0xC0; /* Discard */
97  else
98  if (msg->sms.coding == DC_7BIT)
99  dcs |= 0xD0; /* 7bit */
100  else
101  dcs |= 0xE0; /* UCS-2 */
102  /* XXX Shouldn't happen to have mwi and dc=DC_8BIT! */
103  }
104  }
105 
106  /* Non-MWI */
107  else {
108  /* mode 0 or mode UNDEF */
109  if (mode == 0 || mode == SMS_PARAM_UNDEFINED || msg->sms.coding == DC_UCS2
110  || msg->sms.compress == COMPRESS_ON) {
111  /* bits 7,6 are 0 */
112  if (msg->sms.compress == COMPRESS_ON)
113  dcs |= 0x20; /* sets bit 5 */
114  if (msg->sms.mclass != MC_UNDEF)
115  dcs |= (0x10 | msg->sms.mclass); /* sets bit 4,1,0 */
116  if (msg->sms.coding != DC_UNDEF)
117  dcs |= (msg->sms.coding << 2); /* sets bit 3,2 */
118  }
119 
120  /* mode 1 */
121  else {
122  dcs |= 0xF0; /* sets bits 7-3 */
123  if(msg->sms.coding != DC_UNDEF)
124  dcs |= (msg->sms.coding << 2); /* only DC_7BIT or DC_8BIT, sets bit 2*/
125  if (msg->sms.mclass == MC_UNDEF)
126  dcs |= 1; /* default meaning: ME specific */
127  else
128  dcs |= msg->sms.mclass; /* sets bit 1,0 */
129  }
130  }
131 
132  return dcs;
133 }
134 
135 
136 /*
137  * Decode DCS to sms fields
138  */
139 int dcs_to_fields(Msg **msg, int dcs)
140 {
141  /* Non-MWI Mode 1 */
142  if ((dcs & 0xF0) == 0xF0) {
143  dcs &= 0x07;
144  (*msg)->sms.coding = (dcs & 0x04) ? DC_8BIT : DC_7BIT; /* grab bit 2 */
145  (*msg)->sms.mclass = dcs & 0x03; /* grab bits 1,0 */
146  (*msg)->sms.alt_dcs = 1; /* set 0xFX data coding */
147  }
148 
149  /* Non-MWI Mode 0 */
150  else if ((dcs & 0xC0) == 0x00) {
151  (*msg)->sms.alt_dcs = 0;
152  (*msg)->sms.compress = ((dcs & 0x20) == 0x20) ? 1 : 0; /* grab bit 5 */
153  (*msg)->sms.mclass = ((dcs & 0x10) == 0x10) ? dcs & 0x03 : MC_UNDEF;
154  /* grab bit 0,1 if bit 4 is on */
155  (*msg)->sms.coding = (dcs & 0x0C) >> 2; /* grab bit 3,2 */
156  }
157 
158  /* MWI */
159  else if ((dcs & 0xC0) == 0xC0) {
160  (*msg)->sms.alt_dcs = 0;
161  (*msg)->sms.coding = ((dcs & 0x30) == 0x30) ? DC_UCS2 : DC_7BIT;
162  if (!(dcs & 0x08))
163  dcs |= 0x04; /* if bit 3 is active, have mwi += 4 */
164  dcs &= 0x07;
165  (*msg)->sms.mwi = dcs ; /* grab bits 1,0 */
166  }
167 
168  else {
169  return 0;
170  }
171 
172  return 1;
173 }
174 
175 
176 /*
177  * Compute length of an Octstr after it will be converted to GSM 03.38
178  * 7 bit alphabet - escaped characters would be counted as two septets
179  */
181 {
182  int ret = 0;
183  Octstr* msgdata = NULL;
184 
185  /* got a bad input */
186  if (!msg || !msg->sms.msgdata)
187  return -1;
188 
189  if (msg->sms.coding == DC_7BIT) {
190  msgdata = octstr_duplicate(msg->sms.msgdata);
191  charset_utf8_to_gsm(msgdata);
192  ret = octstr_len(msgdata);
193  octstr_destroy(msgdata);
194  } else
195  ret = octstr_len(msg->sms.msgdata);
196 
197  return ret;
198 }
199 
200 
202 {
203  Octstr *sender = NULL;
204 
205  if (msg->sms.sender != NULL && msg->sms.receiver != NULL) {
206  sender = msg->sms.sender;
207  msg->sms.sender = msg->sms.receiver;
208  msg->sms.receiver = sender;
209 
210  return 1;
211  }
212 
213  return 0;
214 }
215 
216 
217 /*****************************************************************************
218  *
219  * Split an SMS message into smaller ones.
220  */
221 #define CATENATE_UDH_LEN 5
222 
223 
224 void prepend_catenation_udh(Msg *sms, int part_no, int num_messages,
225  int msg_sequence)
226 {
227  if (sms->sms.udhdata == NULL)
228  sms->sms.udhdata = octstr_create("");
229  if (octstr_len(sms->sms.udhdata) == 0)
230  octstr_append_char(sms->sms.udhdata, CATENATE_UDH_LEN);
231  octstr_format_append(sms->sms.udhdata, "%c\3%c%c%c",
232  0, msg_sequence, num_messages, part_no);
233 
234  /* Set the number of messages left, if any */
235  if (part_no < num_messages)
236  sms->sms.msg_left = num_messages - part_no;
237  else
238  sms->sms.msg_left = 0;
239  /*
240  * Now that we added the concatenation information the
241  * length is all wrong. we need to recalculate it.
242  */
243  octstr_set_char(sms->sms.udhdata, 0, octstr_len(sms->sms.udhdata) - 1 );
244 }
245 
246 
247 static Octstr *extract_msgdata_part(Octstr *msgdata, Octstr *split_chars,
248  int max_part_len)
249 {
250  long i, len;
251  Octstr *part;
252 
253  len = max_part_len;
254  if (max_part_len < octstr_len(msgdata) && split_chars != NULL)
255  for (i = max_part_len; i > 0; i--)
256  if (octstr_search_char(split_chars,
257  octstr_get_char(msgdata, i - 1), 0) != -1) {
258  len = i;
259  break;
260  }
261  part = octstr_copy(msgdata, 0, len);
262  octstr_delete(msgdata, 0, len);
263  return part;
264 }
265 
266 
268  int max_part_len)
269 {
270  Octstr *temp = NULL, *temp_utf;
271 
272  if (msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2) {
273  /* nothing to do here, just call the original extract_msgdata_part */
274  return extract_msgdata_part(msg->sms.msgdata, split_chars, max_part_len);
275  }
276 
277  /*
278  * XXX TODO
279  * Convert to and the from gsm, so we drop all non GSM chars.
280  * This means effectively that we can NOT use any encoding specific
281  * characters in the SMSC module scope that are NOT in the GSM 03.38
282  * alphabet, i.e. UTF-8 0xC2 0xAE is latin1 0xAE and maps to an unknown
283  * character due to this round-trip transcoding.
284  */
285  charset_utf8_to_gsm(msg->sms.msgdata);
286  charset_gsm_to_utf8(msg->sms.msgdata);
287 
288  /*
289  * else we need to do something special. I'll just get charset_gsm_truncate to
290  * cut the string to the required length and then count real characters.
291  */
292  temp = octstr_duplicate(msg->sms.msgdata);
293  charset_utf8_to_gsm(temp);
294  charset_gsm_truncate(temp, max_part_len);
295 
296  /* calculate utf-8 length */
297  temp_utf = octstr_duplicate(temp);
298  charset_gsm_to_utf8(temp_utf);
299  max_part_len = octstr_len(temp_utf);
300 
301  octstr_destroy(temp);
302  octstr_destroy(temp_utf);
303 
304  /* now just call the original extract_msgdata_part with the new length */
305  return extract_msgdata_part(msg->sms.msgdata, split_chars, max_part_len);
306 }
307 
308 
309 List *sms_split(Msg *orig, Octstr *header, Octstr *footer,
310  Octstr *nonlast_suffix, Octstr *split_chars,
311  int catenate, unsigned long msg_sequence,
312  int max_messages, int max_octets)
313 {
314  long max_part_len, udh_len, hf_len, nlsuf_len;
315  unsigned long total_messages, msgno;
316  long last;
317  List *list;
318  Msg *part, *temp;
319 
320  hf_len = octstr_len(header) + octstr_len(footer);
321  nlsuf_len = octstr_len(nonlast_suffix);
322  udh_len = octstr_len(orig->sms.udhdata);
323 
324  /* First check whether the message is under one-part maximum */
325  if (orig->sms.coding == DC_8BIT || orig->sms.coding == DC_UCS2)
326  max_part_len = max_octets - udh_len - hf_len;
327  else
328  max_part_len = (max_octets - udh_len) * 8 / 7 - hf_len;
329 
330  if (sms_msgdata_len(orig) > max_part_len && catenate) {
331  /* Change part length to take concatenation overhead into account */
332  if (udh_len == 0)
333  udh_len = 1; /* Add the udh total length octet */
334  udh_len += CATENATE_UDH_LEN;
335  if (orig->sms.coding == DC_8BIT || orig->sms.coding == DC_UCS2)
336  max_part_len = max_octets - udh_len - hf_len;
337  else
338  max_part_len = (max_octets - udh_len) * 8 / 7 - hf_len;
339  }
340 
341  /* ensure max_part_len is never negativ */
342  max_part_len = max_part_len > 0 ? max_part_len : 0;
343 
344  temp = msg_duplicate(orig);
345  msgno = 0;
346  list = gwlist_create();
347 
348  last = 0;
349  do {
350  msgno++;
351  part = msg_duplicate(orig);
352 
353  /*
354  * if its a DLR request message getting split,
355  * only ask DLR for the first one
356  */
357  if ((msgno > 1) && DLR_IS_ENABLED(part->sms.dlr_mask)) {
358  octstr_destroy(part->sms.dlr_url);
359  part->sms.dlr_url = NULL;
360  part->sms.dlr_mask = 0;
361  }
362  octstr_destroy(part->sms.msgdata);
363  if (sms_msgdata_len(temp) <= max_part_len || msgno == max_messages)
364  last = 1;
365 
366  part->sms.msgdata =
367  extract_msgdata_part_by_coding(temp, split_chars,
368  max_part_len - nlsuf_len);
369  /* create new id for every part, except last */
370  if (!last)
371  uuid_generate(part->sms.id);
372 
373  if (header)
374  octstr_insert(part->sms.msgdata, header, 0);
375  if (footer)
376  octstr_append(part->sms.msgdata, footer);
377  if (!last && nonlast_suffix)
378  octstr_append(part->sms.msgdata, nonlast_suffix);
379  gwlist_append(list, part);
380  } while (!last);
381 
382  total_messages = msgno;
383  msg_destroy(temp);
384  if (catenate && total_messages > 1) {
385  for (msgno = 1; msgno <= total_messages; msgno++) {
386  part = gwlist_get(list, msgno - 1);
387  prepend_catenation_udh(part, msgno, total_messages, msg_sequence);
388  }
389  }
390 
391  return list;
392 }
393 
394 
395 int sms_priority_compare(const void *a, const void *b)
396 {
397  int ret;
398  Msg *msg1 = (Msg*)a, *msg2 = (Msg*)b;
399  gw_assert(msg_type(msg1) == sms);
400  gw_assert(msg_type(msg2) == sms);
401 
402  if (msg1->sms.priority > msg2->sms.priority)
403  ret = 1;
404  else if (msg1->sms.priority < msg2->sms.priority)
405  ret = -1;
406  else {
407  if (msg1->sms.time > msg2->sms.time)
408  ret = 1;
409  else if (msg1->sms.time < msg2->sms.time)
410  ret = -1;
411  else
412  ret = 0;
413  }
414 
415  return ret;
416 }
417 
418 
420 {
421  int resultcode = 0;
422 
423  /*
424  debug("gw.sms", 0, "%s: enter, charset=%s, coding=%d, msgdata is:",
425  __func__, octstr_get_cstr(charset), coding);
426  octstr_dump(body, 0);
427  */
428 
429  if (octstr_len(charset)) {
430  if (coding == DC_7BIT) {
431  /*
432  * For 7 bit, convert to UTF-8
433  */
434  if (charset_convert(body, octstr_get_cstr(charset), "UTF-8") < 0) {
435  error(0, "Failed to convert msgdata from charset <%s> to <%s>, will leave as is.",
436  octstr_get_cstr(charset), "UTF-8");
437  resultcode = -1;
438  }
439  } else if (coding == DC_UCS2) {
440  /*
441  * For UCS-2, convert to UTF-16BE
442  */
443  if (charset_convert(body, octstr_get_cstr(charset), "UTF-16BE") < 0) {
444  error(0, "Failed to convert msgdata from charset <%s> to <%s>, will leave as is.",
445  octstr_get_cstr(charset), "UTF-16BE");
446  resultcode = -1;
447  }
448  }
449  }
450 
451  /*
452  debug("gw.sms", 0, "%s: exit, charset=%s, coding=%d, msgdata is:",
453  __func__, octstr_get_cstr(charset), coding);
454  octstr_dump(body, 0);
455  */
456 
457  return resultcode;
458 }
static long num_messages
Definition: test_smsc.c:90
void error(int err, const char *fmt,...)
Definition: log.c:648
#define MWI_UNDEF
Definition: sms.h:99
Msg * msg_duplicate(Msg *msg)
Definition: msg.c:111
static int coding
Definition: mtbatch.c:102
gw_assert(wtls_machine->packet_to_send !=NULL)
void gwlist_append(List *list, void *item)
Definition: list.c:179
void octstr_append(Octstr *ostr1, const Octstr *ostr2)
Definition: octstr.c:1504
void * gwlist_get(List *list, long pos)
Definition: list.c:292
void octstr_append_char(Octstr *ostr, int ch)
Definition: octstr.c:1517
int sms_msgdata_len(Msg *msg)
Definition: sms.c:180
void charset_utf8_to_gsm(Octstr *ostr)
Definition: charset.c:288
msg_type
Definition: msg.h:73
#define DC_8BIT
Definition: sms.h:111
void uuid_generate(uuid_t out)
Definition: gw_uuid.c:393
static Octstr * extract_msgdata_part(Octstr *msgdata, Octstr *split_chars, int max_part_len)
Definition: sms.c:247
#define octstr_get_cstr(ostr)
Definition: octstr.h:233
#define octstr_copy(ostr, from, len)
Definition: octstr.h:178
List * sms_split(Msg *orig, Octstr *header, Octstr *footer, Octstr *nonlast_suffix, Octstr *split_chars, int catenate, unsigned long msg_sequence, int max_messages, int max_octets)
Definition: sms.c:309
long octstr_search_char(const Octstr *ostr, int ch, long pos)
Definition: octstr.c:1012
Octstr * charset
Definition: test_ota.c:68
static Octstr * extract_msgdata_part_by_coding(Msg *msg, Octstr *split_chars, int max_part_len)
Definition: sms.c:267
Definition: msg.h:79
int sms_priority_compare(const void *a, const void *b)
Definition: sms.c:395
void octstr_insert(Octstr *ostr1, const Octstr *ostr2, long pos)
Definition: octstr.c:1303
void octstr_delete(Octstr *ostr1, long pos, long len)
Definition: octstr.c:1527
long max_messages
Definition: wapbox.c:116
#define octstr_duplicate(ostr)
Definition: octstr.h:187
void msg_destroy(Msg *msg)
Definition: msg.c:132
#define COMPRESS_ON
Definition: sms.h:116
void octstr_destroy(Octstr *ostr)
Definition: octstr.c:324
int sms_charset_processing(Octstr *charset, Octstr *body, int coding)
Definition: sms.c:419
#define octstr_create(cstr)
Definition: octstr.h:125
int fields_to_dcs(Msg *msg, int mode)
Definition: sms.c:73
#define SMS_PARAM_UNDEFINED
Definition: sms.h:91
long octstr_len(const Octstr *ostr)
Definition: octstr.c:342
#define MC_UNDEF
Definition: sms.h:93
Definition: octstr.c:118
#define CATENATE_UDH_LEN
Definition: sms.c:221
int sms_swap(Msg *msg)
Definition: sms.c:201
void octstr_format_append(Octstr *os, const char *fmt,...)
Definition: octstr.c:2507
#define gwlist_create()
Definition: list.h:136
int dcs_to_fields(Msg **msg, int dcs)
Definition: sms.c:139
#define DC_UNDEF
Definition: sms.h:109
void prepend_catenation_udh(Msg *sms, int part_no, int num_messages, int msg_sequence)
Definition: sms.c:224
int octstr_get_char(const Octstr *ostr, long pos)
Definition: octstr.c:406
#define DLR_IS_ENABLED(dlr)
Definition: dlr.h:81
void octstr_set_char(Octstr *ostr, long pos, int ch)
Definition: octstr.c:415
Definition: list.c:102
static XMLRPCDocument * msg
Definition: test_xmlrpc.c:86
int charset_gsm_truncate(Octstr *gsm, long max)
Definition: charset.c:512
#define DC_UCS2
Definition: sms.h:112
int charset_convert(Octstr *string, char *charset_from, char *charset_to)
Definition: charset.c:589
#define DC_7BIT
Definition: sms.h:110
void charset_gsm_to_utf8(Octstr *ostr)
Definition: charset.c:220
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.