Kannel: Open Source WAP and SMS gateway  $Revision: 5037 $
wsp_push_client.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  * wsp_push_client.c: Client WSP Push implementation, for testing purposes
59  *
60  * Aarno Syvšnen for Wapit Ltd
61  */
62 
63 #include "wsp_push_client.h"
64 #include "wsp.h"
65 #include "wtp.h"
66 #include "wsp_pdu.h"
67 #include "wsp_headers.h"
68 #include "wap.h"
69 
70 /**************************************************************************
71  *
72  * Internal data structures:
73  *
74  * List of client WSP push machines.
75  */
76 static List *push_client_machines = NULL;
77 
78 /*
79  * Counter for client push machine id numbers, to make sure that they are
80  * unique.
81  */
83 
84 /*
85  * Give the status of push client:
86  *
87  * limbo
88  * not running at all
89  * running
90  * operating normally
91  * terminating
92  * waiting for operations to terminate, returning to limbo
93  */
95 
96 /*
97  * Queue of events to be handled by the push client.
98  */
99 static List *push_client_queue = NULL;
100 
103 
104 /*****************************************************************************
105  *
106  * Prototypes of internal functions:
107  *
108  * Create and destroy an uniniatilised push client state machine.
109  */
111 static void push_client_machine_destroy(void *a);
112 
113 /*
114  * Checks whether the client push machines list includes a specific machine.
115  * Creates it, if the event is TR-Invoke.ind
116  */
118 
119 /*
120  * Feed an event to the client push state machine. Do not report errors to
121  * caller.
122  */
124 
125 /*
126  * Print WSP client push machine state as a string.
127  */
128 static unsigned char *name_push_client_state(int name);
129 static void main_thread(void *);
131  Octstr *push_body);
133  long abort_reason);
135 static WAPEvent *send_abort_to_responder(WSPPushClientMachine *cpm, long reason);
137 
138 
139 /**************************************************************************
140  *
141  * EXTERNAL FUNCTIONS:
142  *
143  */
144 
146  wap_dispatch_func_t *dispatch_wtp_resp)
147 {
148  push_client_machines = gwlist_create();
149  push_client_machine_id_counter = counter_create();
150 
151  push_client_queue = gwlist_create();
152  gwlist_add_producer(push_client_queue);
153 
154  dispatch_to_self = dispatch_self;
155  dispatch_to_wtp_resp = dispatch_wtp_resp;
156 
160 }
161 
163 {
166  gwlist_remove_producer(push_client_queue);
168 
169  debug("wap.wsp", 0, "wsp_push_client_shutdown: %ld push client machines"
170  "left", gwlist_len(push_client_machines));
171  gwlist_destroy(push_client_machines, push_client_machine_destroy);
172  gwlist_destroy(push_client_queue, wap_event_destroy_item);
173 
174  counter_destroy(push_client_machine_id_counter);
175 }
176 
178 {
179  gwlist_produce(push_client_queue, e);
180 }
181 
182 /***************************************************************************
183  *
184  * INTERNAL FUNCTIONS:
185  *
186  */
187 
188 static void main_thread(void *arg)
189 {
191  WAPEvent *e;
192 
193  while (push_client_run_status == running &&
194  (e = gwlist_consume(push_client_queue)) != NULL) {
196  if (cpm == NULL)
198  else
199  push_client_event_handle(cpm, e);
200  }
201 }
202 
203 /*
204  * Give the name of a push client machine state in a readable form
205  */
206 static unsigned char *name_push_client_state(int n) {
207 
208  switch (n) {
209  #define PUSH_CLIENT_STATE_NAME(state) case state : return (unsigned char *)#state;
210  #define ROW(state, event, condition, action, new_state)
211  #include "wsp_push_client_states.def"
212  default:
213  return (unsigned char *)"unknown state";
214  }
215 }
216 
217 /*
218  * Feed an event to a WSP push client state machine. Do not report errors to
219  * the caller.
220  */
222  WAPEvent *e)
223 {
224  WAPEvent *wtp_event;
225  WSP_PDU *pdu = NULL;
226 
227  wap_event_assert(e);
228  gw_assert(cpm);
229 
230  if (e->type == TR_Invoke_Ind) {
231  pdu = wsp_pdu_unpack(e->u.TR_Invoke_Ind.user_data);
232 /*
233  * Class 1 tests here
234  * Case 4, no session matching address quadruplet, handled by the session mach-
235  * ine.
236  * Tests from table WSP, page 45. Case 5, a PDU state tables cannot handle.
237  */
238  if (pdu == NULL || pdu->type != ConfirmedPush) {
240  wtp_event = send_abort_to_responder(cpm, PROTOERR);
241  wtp_resp_dispatch_event(wtp_event);
242  return;
243  }
244  }
245 
246  debug("wap.wsp", 0, "WSP_PUSH: WSPPushClientMachine %ld, state %s,"
247  " event %s",
248  cpm->client_push_id,
249  name_push_client_state(cpm->state),
250  wap_event_name(e->type));
251  #define PUSH_CLIENT_STATE_NAME(state)
252  #define ROW(push_state, event_type, condition, action, next_state) \
253  if (cpm->state == push_state && \
254  e->type == event_type && \
255  (condition)) { \
256  action \
257  cpm->state = next_state; \
258  debug("wap.wsp", 0, "WSP_PUSH %ld: new state %s", \
259  cpm->client_push_id, #next_state); \
260  } else
261  #include "wsp_push_client_states.def"
262  {
263  error(0, "WSP_PUSH: handle_event: unhandled event!");
264  debug("wap.wsp", 0, "Unhandled event was:");
265  wap_event_dump(e);
267  return;
268  }
269 
270  wsp_pdu_destroy(pdu);
272 
273  if (cpm->state == PUSH_CLIENT_NULL_STATE)
275 }
276 
277 static int push_client_machine_has_transid(void *a, void *b)
278 {
279  long transid;
281 
282  m = a;
283  transid = *(long *)b;
284  return m->transaction_id == transid;
285 }
286 
288  long transid)
289 {
291 
292  m = gwlist_search(push_client_machines, &transid,
294  return m;
295 }
296 
297 /*
298  * Checks client push machines list for a specific machine. Creates it, if the
299  * event is TR-Invoke.ind.
300  * Client push machine is identified (when searching) by transcation identifi-
301  * er.
302  * Note that only WTP responder send its class 1 messages to client push state
303  * machine. So, it is no need to specify WTP machine type.
304  */
306 {
308  long transid;
309 
310  cpm = NULL;
311  transid = -1;
312 
313  switch (e->type) {
314  case TR_Invoke_Ind:
315  transid = e->u.TR_Invoke_Ind.handle;
316  break;
317 
318  case S_ConfirmedPush_Res:
319  transid = e->u.S_ConfirmedPush_Res.client_push_id;
320  break;
321 
322  case S_PushAbort_Req:
323  transid = e->u.S_PushAbort_Req.push_id;
324  break;
325 
326  case Abort_Event:
327  break;
328 
329  case TR_Abort_Ind:
330  transid = e->u.TR_Abort_Ind.handle;
331  break;
332 
333  default:
334  debug("wap.wsp", 0, "WSP PUSH: push_client_find_or_create: unhandled"
335  " event");
336  wap_event_dump(e);
338  return NULL;
339  }
340 
341  gw_assert(transid != -1);
342 
344 
345  if (cpm == NULL) {
346  switch (e->type) {
347  case TR_Invoke_Ind:
348  cpm = push_client_machine_create(transid);
349  break;
350 
351  case S_ConfirmedPush_Res:
352  case S_PushAbort_Req:
353  error(0, "WSP_PUSH_CLIENT: POT primitive to a nonexisting"
354  " push client machine");
355  break;
356 
357  case Abort_Event:
358  error(0, "WSP_PUSH_CLIENT: internal abort to a nonexisting"
359  " push client machine");
360  break;
361 
362  case TR_Abort_Ind:
363  error(0, "WSP_PUSH_CLIENT: WTP abort to a nonexisting push client"
364  " machine");
365  break;
366 
367  default:
368  error(0, "WSP_PUSH_CLIENT: Cannot handle event type %s",
369  wap_event_name(e->type));
370  break;
371  }
372  }
373 
374  return cpm;
375 }
376 
378 {
380 
381  m = gw_malloc(sizeof(WSPPushClientMachine));
382  debug("wap.wsp", 0, "WSP_PUSH_CLIENT: Created WSPPushClientMachine %p",
383  (void *) m);
384 
385  #define INTEGER(name) m->name = 0;
386  #define HTTPHEADERS(name) m->name = NULL;
387  #define MACHINE(fields) fields
388  #include "wsp_push_client_machine.def"
389 
390  m->state = PUSH_CLIENT_NULL_STATE;
391  m->transaction_id = transid;
392  m->client_push_id = counter_increase(push_client_machine_id_counter);
393 
394  gwlist_append(push_client_machines, m);
395 
396  return m;
397 }
398 
399 static void push_client_machine_destroy(void *a)
400 {
402 
403  m = a;
404  debug("wap.wsp", 0, "Destroying WSPPushClientMachine %p", (void *) m);
405  gwlist_delete_equal(push_client_machines, m);
406 
407  #define INTEGER(name) m->name = 0;
408  #define HTTPHEADERS(name) http_destroy_headers(m->name);
409  #define MACHINE(fields) fields;
410  #include "wsp_push_client_machine.def"
411 
412  gw_free(m);
413 }
414 
415 
417  Octstr *push_body)
418 {
419  WAPEvent *e;
420 
421  e = wap_event_create(S_ConfirmedPush_Ind);
422  e->u.S_ConfirmedPush_Ind.client_push_id = cpm->client_push_id;
423  e->u.S_ConfirmedPush_Ind.push_headers =
424  http_header_duplicate(cpm->push_headers);
425  e->u.S_ConfirmedPush_Ind.push_body = octstr_duplicate(push_body);
426 
427  return e;
428 }
429 
431  long abort_reason)
432 {
433  WAPEvent *e;
434 
435  e = wap_event_create(S_PushAbort_Ind);
436  e->u.S_PushAbort_Ind.push_id = cpm->client_push_id;
437  e->u.S_PushAbort_Ind.reason = abort_reason;
438 
439  return e;
440 }
441 
442 
443 /*
444  * For debugging: create S-ConfirmedPush.res by ourselves.
445  */
447 {
448  WAPEvent *e;
449 
450  e = wap_event_create(S_ConfirmedPush_Res);
451  e->u.S_ConfirmedPush_Res.client_push_id = cpm->client_push_id;
452 
453  return e;
454 }
455 
457  long reason)
458 {
459  WAPEvent *e;
460 
461  e = wap_event_create(TR_Abort_Req);
462  e->u.TR_Abort_Req.abort_type = USER;
463  e->u.TR_Abort_Req.abort_reason = reason;
464  e->u.TR_Abort_Req.handle = cpm->client_push_id;
465 
466  return e;
467 }
468 
469 
471 {
472  WAPEvent *e;
473 
474  e = wap_event_create(TR_Invoke_Res);
475  e->u.TR_Invoke_Res.handle = cpm->transaction_id;
476 
477  return e;
478 }
479 
480 
481 
482 
void error(int err, const char *fmt,...)
Definition: log.c:612
Definition: wtp.h:125
void * gwlist_search(List *list, void *pattern, int(*cmp)(void *, void *))
Definition: list.c:486
void wsp_push_client_shutdown(void)
static int push_client_machine_has_transid(void *a, void *b)
static WAPEvent * indicate_confirmedpush(WSPPushClientMachine *cpm, Octstr *push_body)
void counter_destroy(Counter *counter)
Definition: counter.c:110
void gwlist_append(List *list, void *item)
Definition: list.c:179
void gwlist_produce(List *list, void *item)
Definition: list.c:411
long gwlist_len(List *list)
Definition: list.c:166
static void push_client_machine_destroy(void *a)
static Counter * push_client_machine_id_counter
static WAPEvent * response_responder_invoke(WSPPushClientMachine *cpm)
static WSPPushClientMachine * push_client_machine_create(long cpid)
void wap_event_dump(WAPEvent *event)
Definition: wap_events.c:181
void gwthread_join_every(gwthread_func_t *func)
static unsigned char * name_push_client_state(int name)
unsigned long counter_increase(Counter *counter)
Definition: counter.c:123
void wsp_push_client_dispatch_event(WAPEvent *e)
static WAPEvent * send_abort_to_responder(WSPPushClientMachine *cpm, long reason)
Counter * counter_create(void)
Definition: counter.c:94
void wap_event_assert(WAPEvent *event)
Definition: wap_events.c:220
void gwlist_remove_producer(List *list)
Definition: list.c:401
void wap_event_destroy_item(void *event)
Definition: wap_events.c:130
static WAPEvent * indicate_pushabort(WSPPushClientMachine *cpm, long abort_reason)
#define octstr_duplicate(ostr)
Definition: octstr.h:187
const char * wap_event_name(WAPEventName type)
Definition: wap_events.c:169
long gwlist_delete_equal(List *list, void *item)
Definition: list.c:266
static WSPPushClientMachine * push_client_machine_find_using_transid(long transid)
char * name
Definition: smsc_cimd2.c:212
#define wap_event_create(type)
Definition: wap_events.h:107
#define gwthread_create(func, arg)
Definition: gwthread.h:90
static List * push_client_machines
gw_assert(wtls_machine->packet_to_send!=NULL)
void wsp_pdu_destroy(WSP_PDU *pdu)
Definition: wsp_pdu.c:102
static void main_thread(void *)
static List * push_client_queue
void wtp_resp_dispatch_event(WAPEvent *event)
Definition: wtp_resp.c:253
Definition: octstr.c:118
void * gwlist_consume(List *list)
Definition: list.c:427
wap_dispatch_func_t * dispatch_to_self
void debug(const char *place, int err, const char *fmt,...)
Definition: log.c:690
int type
Definition: wsp_pdu.h:87
WAPEventName type
Definition: wap_events.h:88
static void push_client_event_handle(WSPPushClientMachine *cpm, WAPEvent *e)
List * http_header_duplicate(List *headers)
Definition: http.c:2946
static WSPPushClientMachine * push_client_machine_find_or_create(WAPEvent *e)
wap_dispatch_func_t * dispatch_to_wtp_resp
#define gwlist_create()
Definition: list.h:136
static WAPEvent * response_confirmedpush(WSPPushClientMachine *cpm)
void gwlist_add_producer(List *list)
Definition: list.c:383
union WAPEvent::@87 u
Definition: list.c:102
static enum @90 push_client_run_status
WSP_PDU * wsp_pdu_unpack(Octstr *data)
Definition: wsp_pdu.c:178
void wap_event_destroy(WAPEvent *event)
Definition: wap_events.c:102
void wap_dispatch_func_t(WAPEvent *event)
Definition: wap.h:85
Definition: wtp.h:133
void wsp_push_client_init(wap_dispatch_func_t *dispatch_self, wap_dispatch_func_t *dispatch_wtp_resp)
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.