Kannel: Open Source WAP and SMS gateway  $Revision: 5037 $
bb_alog.c
Go to the documentation of this file.
1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2016 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  * gw/bb_alog.c -- encapsulate custom access log logic and escape code parsing
59  *
60  * Stipe Tolj <stolj at kannel dot org>
61  * Alexander Malysh <amalysh at kannel dot org>
62  */
63 
64 #include "gwlib/gwlib.h"
65 #include "msg.h"
66 #include "sms.h"
67 #include "bearerbox.h"
68 #include "smscconn.h"
69 
70 static Octstr *custom_log_format = NULL;
71 
72 
73 /********************************************************************
74  * Routine to escape the values into the custom log format.
75  *
76  * The following escape code values are acceptable within the
77  * 'access-log-format' config directive of bearerbox:
78  *
79  * %l - log message
80  * %i - smsc-id
81  * %n - service-name (for MO) or sendsms-user (for MT)
82  * %A - account
83  * %B - billing identifier/information
84  * %p - sender (from)
85  * %P - receiver (to)
86  * %m - message class (mclass)
87  * %c - coding
88  * %M - message waiting indicator (mwi)
89  * %C - compress indicator
90  * %d - dlr_mask
91  * %R - dlr_url
92  * %D - meta-data
93  * %a - the original SMS message, spaces squeezed
94  * %u - UDH data (in escaped form)
95  * %U - length of UDH data
96  * %k - the keyword in the SMS request (the first word in the SMS message)
97  * %s - next word from the SMS message, starting with the second one
98  * %S - same as %s, but '*' is converted to '~'
99  * %r - words not yet used by %s
100  * %b - the original SMS message
101  * %N - the DLR notification message
102  * %L - length of SMS message
103  * %t - the time of the message, formatted as "YYYY-MM-DD HH:MM:SS"
104  * %T - the time of the message, in UNIX epoch timestamp format
105  * %I - the internal message ID
106  * %F - the foreign (smsc-provided) message ID
107  * %x - smsbox-id, identifying the smsbox connection
108  *
109  * Most escape codes should be compatible with escape codes used in
110  * sms-service groups.
111  *
112  * The default access-log-format would look like this (if access-log-clean is true):
113  * "%t %l [SMSC:%i] [SVC:%n] [ACT:%A] [BINF:%B] [FID:%F] [META:%D] [from:%p] [to:%P] \
114  * [flags:%m:%c:%M:%C:%d] [msg:%L:%b] [udh:%U:%u]"
115  */
116 
117 static Octstr *get_pattern(SMSCConn *conn, Msg *msg, const char *message)
118 {
119  int nextarg, j;
120  struct tm tm;
121  int num_words;
122  List *word_list;
123  Octstr *result;
124  const char *pattern;
125  Octstr *temp, *text, *udh;
126  size_t n;
127  long i;
128 
129  text = msg->sms.msgdata ? octstr_duplicate(msg->sms.msgdata) : octstr_create("");
130  udh = msg->sms.udhdata ? octstr_duplicate(msg->sms.udhdata) : octstr_create("");
131  if ((msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2))
132  octstr_binary_to_hex(text, 1);
133  else
135  octstr_binary_to_hex(udh, 1);
136 
137  if (octstr_len(text)) {
138  word_list = octstr_split_words(text);
139  num_words = gwlist_len(word_list);
140  } else {
141  word_list = gwlist_create();
142  num_words = 0;
143  }
144 
145  result = octstr_create("");
146  pattern = octstr_get_cstr(custom_log_format);
147 
148  nextarg = 1;
149 
150  while (*pattern != '\0') {
151  n = strcspn(pattern, "%");
152  octstr_append_data(result, pattern, n);
153  pattern += n;
154  gw_assert(*pattern == '%' || *pattern == '\0');
155  if (*pattern == '\0')
156  break;
157 
158  pattern++;
159 
160  switch (*pattern) {
161  case 'k':
162  if (num_words <= 0)
163  break;
164  octstr_append(result, gwlist_get(word_list, 0));
165  break;
166 
167  case 's':
168  if (nextarg >= num_words)
169  break;
170  octstr_append(result, gwlist_get(word_list, nextarg));
171  ++nextarg;
172  break;
173 
174  case 'S':
175  if (nextarg >= num_words)
176  break;
177  temp = gwlist_get(word_list, nextarg);
178  for (i = 0; i < octstr_len(temp); ++i) {
179  if (octstr_get_char(temp, i) == '*')
180  octstr_append_char(result, '~');
181  else
182  octstr_append_char(result, octstr_get_char(temp, i));
183  }
184  ++nextarg;
185  break;
186 
187  case 'r':
188  for (j = nextarg; j < num_words; ++j) {
189  if (j != nextarg)
190  octstr_append_char(result, '+');
191  octstr_append(result, gwlist_get(word_list, j));
192  }
193  break;
194 
195  case 'l':
196  if (message)
197  octstr_append_cstr(result, message);
198  break;
199 
200  case 'P':
201  if (msg->sms.receiver)
202  octstr_append(result, msg->sms.receiver);
203  break;
204 
205  case 'p':
206  if (msg->sms.sender)
207  octstr_append(result, msg->sms.sender);
208  break;
209 
210  case 'a':
211  for (j = 0; j < num_words; ++j) {
212  if (j > 0)
213  octstr_append_char(result, ' ');
214  octstr_append(result, gwlist_get(word_list, j));
215  }
216  break;
217 
218  case 'b':
219  if (text)
220  octstr_append(result, text);
221  break;
222 
223  case 'L':
224  octstr_append_decimal(result, octstr_len(msg->sms.msgdata));
225  break;
226 
227  case 't':
228  tm = gw_gmtime(msg->sms.time);
229  octstr_format_append(result, "%04d-%02d-%02d %02d:%02d:%02d",
230  tm.tm_year + 1900,
231  tm.tm_mon + 1,
232  tm.tm_mday,
233  tm.tm_hour,
234  tm.tm_min,
235  tm.tm_sec);
236  break;
237 
238  case 'T':
239  if (msg->sms.time != MSG_PARAM_UNDEFINED)
240  octstr_format_append(result, "%ld", msg->sms.time);
241  break;
242 
243  case 'i':
244  if (conn && smscconn_id(conn))
245  octstr_append(result, smscconn_id(conn));
246  else if (conn && smscconn_name(conn))
247  octstr_append(result, smscconn_name(conn));
248  else if (msg->sms.smsc_id)
249  octstr_append(result, msg->sms.smsc_id);
250  break;
251 
252  case 'I':
253  if (!uuid_is_null(msg->sms.id)) {
254  char id[UUID_STR_LEN + 1];
255  uuid_unparse(msg->sms.id, id);
256  octstr_append_cstr(result, id);
257  }
258  break;
259 
260  case 'n':
261  if (msg->sms.service != NULL)
262  octstr_append(result, msg->sms.service);
263  break;
264 
265  case 'd':
266  octstr_append_decimal(result, msg->sms.dlr_mask);
267  break;
268 
269  case 'R':
270  if (msg->sms.dlr_url != NULL)
271  octstr_append(result, msg->sms.dlr_url);
272  break;
273 
274  case 'N':
275  if (msg->sms.sms_type == report_mo && text != NULL)
276  octstr_append(result, text);
277  break;
278 
279  case 'D': /* meta_data */
280  if (msg->sms.meta_data != NULL)
281  octstr_append(result, msg->sms.meta_data);
282  break;
283 
284  case 'c':
285  octstr_append_decimal(result, msg->sms.coding);
286  break;
287 
288  case 'm':
289  octstr_append_decimal(result, msg->sms.mclass);
290  break;
291 
292  case 'C':
293  octstr_append_decimal(result, msg->sms.compress);
294  break;
295 
296  case 'M':
297  octstr_append_decimal(result, msg->sms.mwi);
298  break;
299 
300  case 'u':
301  if (octstr_len(udh)) {
302  octstr_append(result, udh);
303  }
304  break;
305 
306  case 'U':
307  octstr_append_decimal(result, octstr_len(msg->sms.udhdata));
308  break;
309 
310  case 'B': /* billing identifier/information */
311  if (octstr_len(msg->sms.binfo)) {
312  octstr_append(result, msg->sms.binfo);
313  }
314  break;
315 
316  case 'A': /* account */
317  if (octstr_len(msg->sms.account)) {
318  octstr_append(result, msg->sms.account);
319  }
320  break;
321 
322  case 'F': /* the foreign (smsc-provided) message ID */
323  if (msg->sms.foreign_id != NULL)
324  octstr_append(result, msg->sms.foreign_id);
325  break;
326 
327  case 'x': /* the boxc_id, hence the smsbox-id of the message */
328  if (msg->sms.boxc_id != NULL)
329  octstr_append(result, msg->sms.boxc_id);
330  break;
331 
332  /* XXX add more here if needed */
333 
334  case '%':
335  octstr_format_append(result, "%%");
336  break;
337 
338  default:
339  warning(0, "Unknown escape code (%%%c) within custom-log-format, skipping!", *pattern);
340  octstr_format_append(result, "%%%c", *pattern);
341  break;
342  } /* switch(...) */
343 
344  pattern++;
345  } /* for ... */
346 
348  octstr_destroy(text);
349  octstr_destroy(udh);
350 
351  return result;
352 }
353 
354 
355 /********************************************************************
356  *
357  */
358 
360 {
361  gw_assert(format != NULL);
362 
363  custom_log_format = octstr_duplicate(format);
364 }
365 
366 
368 {
369  octstr_destroy(custom_log_format);
370  custom_log_format = NULL;
371 }
372 
373 
374 void bb_alog_sms(SMSCConn *conn, Msg *msg, const char *message)
375 {
376  Octstr *text = NULL;
377 
378  gw_assert(msg_type(msg) == sms);
379 
380  /* if we don't have any custom log, then use our "default" one */
381 
382  if (custom_log_format == NULL) {
383  Octstr *udh;
384  const Octstr *cid;
385 
386  text = msg->sms.msgdata ? octstr_duplicate(msg->sms.msgdata) : octstr_create("");
387  udh = msg->sms.udhdata ? octstr_duplicate(msg->sms.udhdata) : octstr_create("");
388 
389  if (conn && smscconn_id(conn))
390  cid = smscconn_id(conn);
391  else if (conn && smscconn_name(conn))
392  cid = smscconn_name(conn);
393  else if (msg->sms.smsc_id)
394  cid = msg->sms.smsc_id;
395  else
396  cid = octstr_imm("");
397 
398  if ((msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2))
399  octstr_binary_to_hex(text, 1);
400  else
402  octstr_binary_to_hex(udh, 1);
403 
404  alog("%s [SMSC:%s] [SVC:%s] [ACT:%s] [BINF:%s] [FID:%s] [META:%s] [from:%s] [to:%s] [flags:%ld:%ld:%ld:%ld:%ld] "
405  "[msg:%ld:%s] [udh:%ld:%s]",
406  message,
407  octstr_get_cstr(cid),
408  msg->sms.service ? octstr_get_cstr(msg->sms.service) : "",
409  msg->sms.account ? octstr_get_cstr(msg->sms.account) : "",
410  msg->sms.binfo ? octstr_get_cstr(msg->sms.binfo) : "",
411  msg->sms.foreign_id ? octstr_get_cstr(msg->sms.foreign_id) : "",
412  msg->sms.meta_data ? octstr_get_cstr(msg->sms.meta_data) : "",
413  msg->sms.sender ? octstr_get_cstr(msg->sms.sender) : "",
414  msg->sms.receiver ? octstr_get_cstr(msg->sms.receiver) : "",
415  msg->sms.mclass, msg->sms.coding, msg->sms.mwi, msg->sms.compress,
416  msg->sms.dlr_mask,
417  octstr_len(msg->sms.msgdata), octstr_get_cstr(text),
418  octstr_len(msg->sms.udhdata), octstr_get_cstr(udh)
419  );
420 
421  octstr_destroy(udh);
422  } else {
423  text = get_pattern(conn, msg, message);
424  alog("%s", octstr_get_cstr(text));
425  }
426 
427  octstr_destroy(text);
428 }
429 
430 
void bb_alog_sms(SMSCConn *conn, Msg *msg, const char *message)
Definition: bb_alog.c:374
void octstr_append_data(Octstr *ostr, const char *data, long len)
Definition: octstr.c:1495
void octstr_append(Octstr *ostr1, const Octstr *ostr2)
Definition: octstr.c:1502
struct tm gw_gmtime(time_t t)
Definition: protected.c:137
Definition: msg.h:109
long gwlist_len(List *list)
Definition: list.c:166
void * gwlist_get(List *list, long pos)
Definition: list.c:292
void octstr_append_char(Octstr *ostr, int ch)
Definition: octstr.c:1515
msg_type
Definition: msg.h:73
const Octstr * smscconn_name(SMSCConn *conn)
Definition: smscconn.c:493
#define DC_8BIT
Definition: sms.h:111
void uuid_unparse(const uuid_t uu, char *out)
Definition: gw_uuid.c:561
void octstr_append_cstr(Octstr *ostr, const char *cstr)
Definition: octstr.c:1509
static Octstr * get_pattern(SMSCConn *conn, Msg *msg, const char *message)
Definition: bb_alog.c:117
#define octstr_get_cstr(ostr)
Definition: octstr.h:233
void octstr_binary_to_hex(Octstr *ostr, int uppercase)
Definition: octstr.c:463
void bb_alog_init(const Octstr *format)
Definition: bb_alog.c:359
void octstr_convert_printable(Octstr *ostr)
Definition: octstr.c:862
void bb_alog_shutdown(void)
Definition: bb_alog.c:367
Octstr * octstr_imm(const char *cstr)
Definition: octstr.c:281
Definition: msg.h:79
char * text
Definition: smsc_cimd2.c:921
int uuid_is_null(const uuid_t uu)
Definition: gw_uuid.c:412
const Octstr * smscconn_id(SMSCConn *conn)
Definition: smscconn.c:500
#define octstr_duplicate(ostr)
Definition: octstr.h:187
void warning(int err, const char *fmt,...)
Definition: log.c:624
List * octstr_split_words(const Octstr *ostr)
Definition: octstr.c:1600
void octstr_destroy(Octstr *ostr)
Definition: octstr.c:322
#define octstr_create(cstr)
Definition: octstr.h:125
void octstr_destroy_item(void *os)
Definition: octstr.c:334
gw_assert(wtls_machine->packet_to_send!=NULL)
#define UUID_STR_LEN
Definition: gw_uuid.h:19
long octstr_len(const Octstr *ostr)
Definition: octstr.c:340
void octstr_append_decimal(Octstr *ostr, long value)
Definition: octstr.c:1974
Definition: octstr.c:118
void alog(const char *fmt,...)
Definition: accesslog.c:206
#define MSG_PARAM_UNDEFINED
Definition: msg.h:71
void octstr_format_append(Octstr *os, const char *fmt,...)
Definition: octstr.c:2505
#define gwlist_create()
Definition: list.h:136
int octstr_get_char(const Octstr *ostr, long pos)
Definition: octstr.c:404
Definition: list.c:102
static XMLRPCDocument * msg
Definition: test_xmlrpc.c:86
static Octstr * custom_log_format
Definition: bb_alog.c:70
#define DC_UCS2
Definition: sms.h:112
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.