Kannel: Open Source WAP and SMS gateway  svn-r5335
mtbatch.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  * mtbatch.c - an MT batch run utility for bearerbox
59  *
60  * This utility reads in a content file which has the SMS text message and
61  * a receivers file, which has receiver numbers in each line. It connects
62  * to bearerbox as if it would be a smsbox and issues the SMS sequentially
63  * to bearerbox.
64  *
65  * Stipe Tolj <stolj@kannel.org>
66  * Vincent Chavanis <v.chavanis@telemaque.fr>
67  *
68  * XXX Add UDH capabilities.
69  */
70 
71 #include <string.h>
72 #include <unistd.h>
73 #include <signal.h>
74 #include <ctype.h>
75 
76 #include "gwlib/gwlib.h"
77 
78 #include "msg.h"
79 #include "sms.h"
80 #include "dlr.h"
81 #include "bb.h"
82 #include "shared.h"
83 #include "heartbeat.h"
84 
85 static char *pid_file;
86 static Octstr *smsbox_id = NULL;
87 static Octstr *content = NULL;
88 static List *lines = NULL;
89 static Octstr *bb_host;
90 static long bb_port;
91 static int bb_ssl;
92 static Counter *counter;
93 static Octstr *service = NULL;
94 static Octstr *account = NULL;
95 static Octstr *from = NULL;
96 static int dlr_mask = 0;
97 static Octstr *dlr_url = NULL;
98 static Octstr *smsc_id = NULL;
99 static double delay = 0;
100 static int no_smsbox_id = 0;
101 static Octstr *meta_data = NULL;
102 static int coding = DC_7BIT;
103 static Octstr *charset = NULL;
104 static Octstr *payload = NULL;
105 static int msg_log = 0;
106 
107 static void write_pid_file(void) {
108  FILE *f;
109 
110  if (pid_file != NULL) {
111  f = fopen(pid_file, "w");
112  fprintf(f, "%d\n", (int)getpid());
113  fclose(f);
114  }
115 }
116 
117 /***********************************************************************
118  * Communication with the bearerbox.
119  */
120 
121 
122 /*
123  * Identify ourself to bearerbox for smsbox-specific routing inside bearerbox.
124  * Do this even while no smsbox-id is given to unlock the sender thread in
125  * bearerbox.
126  */
127 static void identify_to_bearerbox(void)
128 {
129  Msg *msg;
130 
131  msg = msg_create(admin);
132  msg->admin.command = cmd_identify;
133  msg->admin.boxc_id = octstr_duplicate(smsbox_id);
135 }
136 
137 
138 /*
139  * Read an Msg from the bearerbox and send it to the proper receiver
140  * via a List. At the moment all messages are sent to the smsbox_requests
141  * List.
142  */
143 static void read_messages_from_bearerbox(void *arg)
144 {
145  time_t start, t;
146  unsigned long secs;
147  unsigned long total_s, total_f, total_ft, total_b, total_o;
148  Msg *msg;
149 
150  total_s = total_f = total_ft = total_b = total_o = 0;
151  start = t = time(NULL);
152  while (program_status != shutting_down) {
153  int ret;
154 
155  /* block infinite for reading messages */
156  ret = read_from_bearerbox(&msg, 0.5);
157  if (ret == -1)
158  break;
159  else if (ret == 1) /* timeout */
160  continue;
161  else if (msg == NULL) /* just to be sure, may not happens */
162  break;
163 
164  if (msg_log) {
165  debug("msg", 0, "Received message from bearerbox:");
166  msg_dump(msg, 0);
167  }
168 
169  if (msg_type(msg) == admin) {
170  if (msg->admin.command == cmd_shutdown ||
171  msg->admin.command == cmd_restart) {
172  info(0, "Bearerbox told us to die");
174  }
175  /*
176  * XXXX here should be suspend/resume, add RSN
177  */
178  msg_destroy(msg);
179  } else if (msg_type(msg) == ack) {
181  switch (msg->ack.nack) {
182  case ack_success:
183  total_s++;
184  break;
185  case ack_failed:
186  total_f++;
187  break;
188  case ack_failed_tmp:
189  total_ft++;
190  break;
191  case ack_buffered:
192  total_b++;
193  break;
194  }
195  msg_destroy(msg);
196  } else {
197  warning(0, "Received other message than ack/admin, ignoring!");
198  msg_destroy(msg);
199  total_o++;
200  }
201  }
202  secs = difftime(time(NULL), start);
203  info(0, "Received ACKs: %ld success, %ld failed, %ld failed temporarly, %ld queued, %ld other in %ld seconds "
204  "(%.2f per second)", total_s, total_f, total_ft, total_b, total_o, secs,
205  (float)(total_s+total_f+total_ft+total_b) / secs);
206 }
207 
208 /*
209  * Send a message to the bearerbox for delivery to a phone.
210  * Return 0 on success, and destroys the message,
211  * otherwise -1 for failure, without destroying the message.
212  */
213 static int send_message(Msg *msg)
214 {
215  gw_assert(msg != NULL);
216  gw_assert(msg_type(msg) == sms);
217 
218  if (delay > 0)
220 
221  if (msg_log) {
222  debug("msg", 0, "Sending message to bearerbox:");
223  msg_dump(msg, 0);
224  }
225 
226  /* pass message to bearerbox */
227  return deliver_to_bearerbox(msg);
228 }
229 
230 
231 static void help(void)
232 {
233  info(0, "Usage: mtbatch [options] content-file receivers-file ...");
234  info(0, "where options are:");
235  info(0, "-v number");
236  info(0, " set log level for stderr logging");
237  info(0, "-b host");
238  info(0, " defines the host of bearerbox (default: localhost)");
239  info(0, "-p port");
240  info(0, " the smsbox port to connect to (default: 13001)");
241  info(0, "-s");
242  info(0, " inidicator to use SSL for bearerbox connection (default: no)");
243  info(0, "-i smsbox-id");
244  info(0, " defines the smsbox-id to be used for bearerbox connection (default: none)");
245  info(0, "-x");
246  info(0, " indicator to not use smsbox-id in messages send to bearerbox (default: yes)");
247  info(0, "-f sender");
248  info(0, " which sender address should be used");
249  info(0, "-D dlr-mask");
250  info(0, " defines the dlr-mask");
251  info(0, "-u dlr-url");
252  info(0, " defines the dlr-url");
253  info(0, "-n service");
254  info(0, " defines which service name should be logged (default: none)");
255  info(0, "-a account");
256  info(0, " defines which account name should be logged (default: none)");
257  info(0, "-d seconds");
258  info(0, " delay between message sending to bearerbox (default: 0)");
259  info(0, "-r smsc-id");
260  info(0, " use a specific route for the MT traffic");
261  info(0, "-M meta-data");
262  info(0, " defines the meta-data");
263  info(0, "-c coding (0: UTF-8, 1: binary, 2: UCS-2; default: 0)");
264  info(0, " defines the coding");
265  info(0, "-C charset (iconv name; default: UTF-8");
266  info(0, " defines which character encoding is used in content-file");
267  info(0, "-m");
268  info(0, " indicator to dump messages exchanged with bearebrox (default: no)");
269 }
270 
271 static void init_batch(Octstr *cfilename, Octstr *rfilename)
272 {
273  Octstr *receivers;
274  long lineno = 0;
275 
276  /* read content file */
279  if (content == NULL)
280  panic(0,"Can not read content file `%s'.",
281  octstr_get_cstr(cfilename));
282 
283  /* handle transcoding */
284  switch (coding) {
285  case DC_8BIT: {
287  info(0, "SMS message (binary), coding 1 (DC_8BIT):");
289  }
290  break;
291  case DC_7BIT: {
294 
295  /* convert to UTF-8 if given in other charset */
296  if (charset != NULL) {
297  if (charset_convert(payload, octstr_get_cstr(charset), "UTF-8") != 0) {
298  error(0, "Failed to convert content from %s to UTF-8, will leave as is.",
300  }
301  info(0, "Content (%s):", octstr_get_cstr(charset));
303  }
304  info(0, "SMS message (UTF-8), coding 0 (DC_7BIT):");
306  }
307  break;
308  case DC_UCS2: {
311 
312  /* convert to UTF-16BE (Unicode) */
313  if (charset == NULL)
314  charset = octstr_imm("UTF-8");
315  if (charset_convert(payload, octstr_get_cstr(charset), "UTF-16BE") != 0) {
316  error(0, "Failed to convert content from %s to UTF-16BE (Unicode), will leave as is.",
318  }
319  info(0, "Content (%s):", octstr_get_cstr(charset));
321  info(0, "SMS message (UTF-16BE, Unicode), coding 2 (DCS_UCS2):");
323  }
324  break;
325  default:
326  panic(0, "Coding value %d out of range!", coding);
327  break;
328  }
329 
330  /* read receivers */
331  info(0,"Loading receiver list. This may take a while...");
332  receivers = octstr_read_file(octstr_get_cstr(rfilename));
333  if (receivers == NULL)
334  panic(0,"Can not read receivers file `%s'.",
335  octstr_get_cstr(rfilename));
336 
337  lines = octstr_split(receivers, octstr_imm("\n"));
338  lineno = gwlist_len(lines);
339  if (lineno <= 0)
340  panic(0,"Receiver file seems empty!");
341 
342  info(0,"Receivers file `%s' contains %ld destination address(es).",
343  octstr_get_cstr(rfilename), lineno);
344 
346 }
347 
348 static int gw_ismsisdnchar(int c)
349 {
350  return (isdigit(c) || c == '+');
351 }
352 
353 static unsigned long run_batch(void)
354 {
355  Octstr *no;
356  unsigned long linerr = 0;
357  unsigned long lineno = 0;
358  Msg *tmsg;
359  unsigned long msg_count;
360  List *list;
361 
362  /*
363  * Create message template.
364  * Receiver is set in the duplicate that is send.
365  */
366  tmsg = msg_create(sms);
367  tmsg->sms.smsc_id = smsc_id ? octstr_duplicate(smsc_id) : NULL;
368  tmsg->sms.service = service ? octstr_duplicate(service) : NULL;
369  tmsg->sms.sms_type = mt_push;
370  tmsg->sms.sender = octstr_duplicate(from);
371  tmsg->sms.account = account ? octstr_duplicate(account) : NULL;
372  tmsg->sms.msgdata = payload ? octstr_duplicate(payload) : octstr_create("");
373  tmsg->sms.dlr_mask = dlr_mask;
374  tmsg->sms.dlr_url = octstr_duplicate(dlr_url);
375  tmsg->sms.udhdata = octstr_create("");
376  tmsg->sms.coding = coding;
377  tmsg->sms.meta_data = octstr_duplicate(meta_data);
378 
379  /*
380  * Encode our smsbox-id to the msg structure.
381  * This will allow bearerbox to return specific answers to the
382  * same smsbox, mainly for DLRs and SMS proxy modes.
383  *
384  * In addition the -x flag can be used to identify the mtbatch
385  * instance with an own smsbox-id, but let the normal smsbox
386  * daemons handle the DLRs coming back, as the mtbatch shuts down
387  * after all MTs have been injected. It's not meant to process
388  * the DLR messages.
389  */
390  if (no_smsbox_id == 0 && smsbox_id != NULL) {
391  tmsg->sms.boxc_id = octstr_duplicate(smsbox_id);
392  }
393 
394  list = sms_split(tmsg, NULL, NULL, NULL, NULL, 1, 0, 100, MAX_SMS_OCTETS);
395  msg_count = gwlist_len(list);
397 
398  if (msg_count > 1) {
399  debug("sms", 0, "Message length %ld octets, will send %ld concat parts for each message.",
400  octstr_len(tmsg->sms.msgdata), msg_count);
401  }
402 
403  /*
404  * Send loop
405  */
406  while ((no = gwlist_consume(lines)) != NULL) {
407  if (octstr_check_range(no, 0, 256, gw_ismsisdnchar)) {
408  Msg *msg;
409 
410  lineno++;
411 
412  msg = msg_duplicate(tmsg);
413  msg->sms.receiver = octstr_duplicate(no);
414 
415  if (send_message(msg) != 0) {
416  linerr++;
417  info(0,"Failed to send message at line <%ld> for receiver `%s' to bearerbox.",
418  lineno, octstr_get_cstr(no));
419  msg_destroy(msg);
420  }
421  }
422  else {
423  linerr++;
424  error(0, "Receiver `%s' at line <%ld> contains non-MSISDN characters, discarded!",
425  octstr_get_cstr(no), lineno);
426  }
427  octstr_destroy(no);
428  }
429  info(0, "Processed batch of %ld messages with %ld send errors.", lineno, linerr);
430  msg_destroy(tmsg);
431  return lineno;
432 }
433 
434 int main(int argc, char **argv)
435 {
436  int opt;
437  unsigned long sended = 0;
438  Octstr *cf, *rf;
439 
440  gwlib_init();
441 
442  bb_host = octstr_create("localhost");
443  bb_port = 13001;
444  bb_ssl = 0;
445 
446  while ((opt = getopt(argc, argv, "hv:b:p:si:xn:a:f:D:u:d:r:M:c:C:m")) != EOF) {
447  switch (opt) {
448  case 'v':
450  break;
451  case 'b':
454  break;
455  case 'p':
456  bb_port = atoi(optarg);
457  break;
458  case 's':
459  bb_ssl = 1;
460  break;
461  case 'i':
463  break;
464  case 'x':
465  no_smsbox_id = 1;
466  break;
467  case 'n':
469  break;
470  case 'a':
472  break;
473  case 'f':
475  break;
476  case 'D':
477  dlr_mask = atoi(optarg);
478  break;
479  case 'u':
481  break;
482  case 'd':
483  delay = atof(optarg);
484  break;
485  case 'r':
487  break;
488  case 'M':
490  break;
491  case 'c':
492  coding = atoi(optarg);
493  break;
494  case 'C':
496  break;
497  case 'm':
498  msg_log = 1;
499  break;
500  case '?':
501  default:
502  error(0, "Invalid option %c", opt);
503  help();
504  panic(0, "Stopping.");
505  }
506  }
507 
508  if (optind == argc || argc-optind < 2) {
509  help();
510  exit(1);
511  }
512 
513  /* check some mandatory elements */
514  if (from == NULL)
515  panic(0,"Sender address not specified. Use option -f to specify sender address.");
516 
517  if ((DLR_IS_ENABLED(dlr_mask) && dlr_url == NULL) || (!DLR_IS_ENABLED(dlr_mask) && dlr_url != NULL))
518  panic(0,"dlr-url address OR dlr-mask not specified. Use option -D or -u to specify dlr values");
519 
520  rf = octstr_create(argv[argc-1]);
521  cf = octstr_create(argv[argc-2]);
522 
523  report_versions("mtbatch");
524  write_pid_file();
525 
526  init_batch(cf, rf);
527 
528  connect_to_bearerbox(bb_host, bb_port, bb_ssl, NULL /* bb_our_host */);
531 
532  sended = run_batch();
533 
534  /* avoid exiting before receiving all ACK msgs */
535  while (sended > counter_value(counter)) {
536  gwthread_sleep(0.1);
537  }
538 
541 
554 
555  gwlib_shutdown();
556 
557  return 0;
558 }
void msg_dump(Msg *msg, int level)
Definition: msg.c:152
void error(int err, const char *fmt,...)
Definition: log.c:648
void info(int err, const char *fmt,...)
Definition: log.c:672
#define MAX_SMS_OCTETS
Definition: sms.h:129
Msg * msg_duplicate(Msg *msg)
Definition: msg.c:111
void gwthread_join_all(void)
static unsigned long run_batch(void)
Definition: mtbatch.c:353
static int coding
Definition: mtbatch.c:102
static Octstr * dlr_url
Definition: mtbatch.c:97
gw_assert(wtls_machine->packet_to_send !=NULL)
void counter_destroy(Counter *counter)
Definition: counter.c:110
static void write_pid_file(void)
Definition: mtbatch.c:107
int octstr_check_range(Octstr *ostr, long pos, long len, octstr_func_t filter)
Definition: octstr.c:814
static int bb_ssl
Definition: mtbatch.c:91
long gwlist_len(List *list)
Definition: list.c:166
program_status
Definition: shared.h:79
int read_from_bearerbox(Msg **msg, double seconds)
Definition: shared.c:220
static void help(void)
Definition: mtbatch.c:231
msg_type
Definition: msg.h:73
int optind
Definition: attgetopt.c:80
static void identify_to_bearerbox(void)
Definition: mtbatch.c:127
static Octstr * service
Definition: mtbatch.c:93
#define DC_8BIT
Definition: sms.h:111
#define msg_create(type)
Definition: msg.h:136
#define octstr_get_cstr(ostr)
Definition: octstr.h:233
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
unsigned long counter_increase(Counter *counter)
Definition: counter.c:123
static List * lines
Definition: mtbatch.c:88
static void read_messages_from_bearerbox(void *arg)
Definition: mtbatch.c:143
static Octstr * charset
Definition: mtbatch.c:103
int getopt(int argc, char **argv, char *opts)
Definition: attgetopt.c:84
static Octstr * from
Definition: mtbatch.c:95
static Octstr * payload
Definition: mtbatch.c:104
Definition: msg.h:108
static int send_message(Msg *msg)
Definition: mtbatch.c:213
void msg_destroy_item(void *msg)
Definition: msg.c:147
Octstr * octstr_imm(const char *cstr)
Definition: octstr.c:283
static Octstr * smsbox_id
Definition: mtbatch.c:86
Definition: msg.h:79
Counter * counter_create(void)
Definition: counter.c:94
void log_set_output_level(enum output_level level)
Definition: log.c:253
int main(int argc, char **argv)
Definition: mtbatch.c:434
void connect_to_bearerbox(Octstr *host, int port, int ssl, Octstr *our_host)
Definition: shared.c:108
#define octstr_duplicate(ostr)
Definition: octstr.h:187
#define octstr_dump(ostr, level,...)
Definition: octstr.h:564
static Octstr * smsc_id
Definition: mtbatch.c:98
static Octstr * bb_host
Definition: mtbatch.c:89
void msg_destroy(Msg *msg)
Definition: msg.c:132
static double delay
Definition: mtbatch.c:99
void warning(int err, const char *fmt,...)
Definition: log.c:660
void octstr_destroy(Octstr *ostr)
Definition: octstr.c:324
#define gwthread_create(func, arg)
Definition: gwthread.h:90
#define octstr_create(cstr)
Definition: octstr.h:125
void octstr_destroy_item(void *os)
Definition: octstr.c:336
unsigned long counter_value(Counter *counter)
Definition: counter.c:145
void gwthread_sleep(double seconds)
static void init_batch(Octstr *cfilename, Octstr *rfilename)
Definition: mtbatch.c:271
int deliver_to_bearerbox(Msg *msg)
Definition: shared.c:166
static long bb_port
Definition: mtbatch.c:90
static char * pid_file
Definition: mtbatch.c:85
Octstr * octstr_read_file(const char *filename)
Definition: octstr.c:1548
void octstr_strip_crlfs(Octstr *text)
Definition: octstr.c:1378
void report_versions(const char *boxname)
Definition: utils.c:539
long octstr_len(const Octstr *ostr)
Definition: octstr.c:342
static int dlr_mask
Definition: mtbatch.c:96
void write_to_bearerbox(Msg *pmsg)
Definition: shared.c:142
Definition: octstr.c:118
void * gwlist_consume(List *list)
Definition: list.c:427
Definition: log.h:69
void debug(const char *place, int err, const char *fmt,...)
Definition: log.c:726
char * optarg
Definition: attgetopt.c:82
#define panic
Definition: log.h:87
void gwlib_shutdown(void)
Definition: gwlib.c:94
void gwlib_init(void)
Definition: gwlib.c:78
static int msg_log
Definition: mtbatch.c:105
static int no_smsbox_id
Definition: mtbatch.c:100
#define DLR_IS_ENABLED(dlr)
Definition: dlr.h:81
static Counter * counter
Definition: mtbatch.c:92
List * octstr_split(const Octstr *os, const Octstr *sep)
Definition: octstr.c:1640
Definition: list.c:102
static Octstr * account
Definition: mtbatch.c:94
static int gw_ismsisdnchar(int c)
Definition: mtbatch.c:348
static XMLRPCDocument * msg
Definition: test_xmlrpc.c:86
static int start
#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 gwlist_destroy(List *list, gwlist_item_destructor_t *destructor)
Definition: list.c:145
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.