Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

wsp_session.c

Go to the documentation of this file.
00001 /* ==================================================================== 
00002  * The Kannel Software License, Version 1.0 
00003  * 
00004  * Copyright (c) 2001-2008 Kannel Group  
00005  * Copyright (c) 1998-2001 WapIT Ltd.   
00006  * All rights reserved. 
00007  * 
00008  * Redistribution and use in source and binary forms, with or without 
00009  * modification, are permitted provided that the following conditions 
00010  * are met: 
00011  * 
00012  * 1. Redistributions of source code must retain the above copyright 
00013  *    notice, this list of conditions and the following disclaimer. 
00014  * 
00015  * 2. Redistributions in binary form must reproduce the above copyright 
00016  *    notice, this list of conditions and the following disclaimer in 
00017  *    the documentation and/or other materials provided with the 
00018  *    distribution. 
00019  * 
00020  * 3. The end-user documentation included with the redistribution, 
00021  *    if any, must include the following acknowledgment: 
00022  *       "This product includes software developed by the 
00023  *        Kannel Group (http://www.kannel.org/)." 
00024  *    Alternately, this acknowledgment may appear in the software itself, 
00025  *    if and wherever such third-party acknowledgments normally appear. 
00026  * 
00027  * 4. The names "Kannel" and "Kannel Group" must not be used to 
00028  *    endorse or promote products derived from this software without 
00029  *    prior written permission. For written permission, please  
00030  *    contact org@kannel.org. 
00031  * 
00032  * 5. Products derived from this software may not be called "Kannel", 
00033  *    nor may "Kannel" appear in their name, without prior written 
00034  *    permission of the Kannel Group. 
00035  * 
00036  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 
00037  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
00038  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
00039  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 
00040  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,  
00041  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
00042  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
00043  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
00044  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
00045  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  
00046  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
00047  * ==================================================================== 
00048  * 
00049  * This software consists of voluntary contributions made by many 
00050  * individuals on behalf of the Kannel Group.  For more information on  
00051  * the Kannel Group, please see <http://www.kannel.org/>. 
00052  * 
00053  * Portions of this software are based upon software originally written at  
00054  * WapIT Ltd., Helsinki, Finland for the Kannel project.  
00055  */ 
00056 
00057 /*
00058  * wsp_session.c - Implement WSP session oriented service
00059  *
00060  * Lars Wirzenius
00061  * Stipe Tolj
00062  */
00063 
00064 
00065 #include <string.h>
00066 #include <limits.h>
00067 
00068 #include "gwlib/gwlib.h"
00069 #include "wsp.h"
00070 #include "wsp_pdu.h"
00071 #include "wsp_headers.h"
00072 #include "wsp_caps.h"
00073 #include "wsp_strings.h"
00074 #include "cookies.h"
00075 #include "wap.h"
00076 #include "wtp.h"
00077 
00078 
00079 typedef enum {
00080     #define STATE_NAME(name) name,
00081     #define ROW(state, event, condition, action, next_state)
00082     #include "wsp_server_session_states.def"
00083 
00084     #define STATE_NAME(name) name,
00085     #define ROW(state, event, condition, action, next_state)
00086     #include "wsp_server_method_states.def"
00087 
00088         #define STATE_NAME(name) name,
00089         #define ROW(state, event, condition, action, next_state)
00090         #include "wsp_server_push_states.def"
00091 
00092     WSPState_count
00093 } WSPState;
00094 
00095 
00096 /*
00097  * Give the status the module:
00098  *
00099  *  limbo
00100  *      not running at all
00101  *  running
00102  *      operating normally
00103  *  terminating
00104  *      waiting for operations to terminate, returning to limbo
00105  */
00106 static enum { limbo, running, terminating } run_status = limbo;
00107 
00108 static wap_dispatch_func_t *dispatch_to_wtp_resp;
00109 static wap_dispatch_func_t *dispatch_to_wtp_init;
00110 static wap_dispatch_func_t *dispatch_to_appl;
00111 static wap_dispatch_func_t *dispatch_to_ota;
00112 
00113 /*
00114  * True iff "Session resume facility" is enabled.  This means we are
00115  * willing to let sessions go to SUSPENDED state, and later resume them.
00116  * Currently we always support it, but this may become configurable
00117  * at some point.
00118  */
00119 
00120 static int resume_enabled = 1;
00121 
00122 static List *queue = NULL;
00123 static List *session_machines = NULL;
00124 static Counter *session_id_counter = NULL;
00125 
00126 
00127 static WSPMachine *find_session_machine(WAPEvent *event, WSP_PDU *pdu);
00128 static void handle_session_event(WSPMachine *machine, WAPEvent *event, 
00129                  WSP_PDU *pdu);
00130 static WSPMachine *machine_create(void);
00131 static void machine_destroy(void *p);
00132 
00133 static void handle_method_event(WSPMachine *session, WSPMethodMachine *machine, WAPEvent *event, WSP_PDU *pdu);
00134 static void cant_handle_event(WSPMachine *sm, WAPEvent *event);
00135 static WSPMethodMachine *method_machine_create(WSPMachine *, long);
00136 static void method_machine_destroy(void *msm);
00137 
00138 static void handle_push_event(WSPMachine *session, WSPPushMachine *machine,
00139                               WAPEvent *e);
00140 static WSPPushMachine *push_machine_create(WSPMachine *session, long id);
00141 static void push_machine_destroy(void *p);
00142 
00143 static char *state_name(WSPState state);
00144 static unsigned long next_wsp_session_id(void);
00145 
00146 static List *make_capabilities_reply(WSPMachine *m);
00147 static List *make_reply_headers(WSPMachine *m);
00148 static Octstr *make_connectreply_pdu(WSPMachine *m);
00149 static Octstr *make_resume_reply_pdu(WSPMachine *m, List *headers);
00150 static WSP_PDU *make_confirmedpush_pdu(WAPEvent *e);
00151 static WSP_PDU *make_push_pdu(WAPEvent *e);
00152 
00153 static int transaction_belongs_to_session(void *session, void *tuple);
00154 static int find_by_session_id(void *session, void *idp);
00155 static int same_client(void *sm1, void *sm2);
00156 static WSPMethodMachine *find_method_machine(WSPMachine *, long id);
00157 static WSPPushMachine *find_push_machine(WSPMachine *m, long id);
00158 
00159 static List *unpack_new_headers(WSPMachine *sm, Octstr *hdrs);
00160 
00161 static void disconnect_other_sessions(WSPMachine *sm);
00162 static void send_abort(long reason, long handle);
00163 static void indicate_disconnect(WSPMachine *sm, long reason);
00164 static void indicate_suspend(WSPMachine *sm, long reason);
00165 static void indicate_resume(WSPMachine *sm, WAPAddrTuple *tuple, 
00166                             List *client_headers);
00167 
00168 static void release_holding_methods(WSPMachine *sm);
00169 static void abort_methods(WSPMachine *sm, long reason);
00170 static void abort_pushes(WSPMachine *sm, long reason);
00171 
00172 static void method_abort(WSPMethodMachine *msm, long reason);
00173 static void indicate_method_abort(WSPMethodMachine *msm, long reason);
00174 
00175 static WAPEvent *make_abort(long reason, long handle);
00176 static void send_invoke(WSPMachine *session, WSP_PDU *pdu, WAPEvent *e,
00177                         long class);
00178 static void send_abort_to_initiator(long reason, long handle);
00179 static void indicate_pushabort(WSPPushMachine *machine, long reason);
00180 static void confirm_push(WSPPushMachine *machine);
00181 
00182 static void main_thread(void *);
00183 static int id_belongs_to_session (void *, void *);
00184 static int wsp_encoding_string_to_version(Octstr *enc);
00185 static Octstr *wsp_encoding_version_to_string(int version);
00186 
00187 
00188 /***********************************************************************
00189  * Public functions.
00190  */
00191 
00192 
00193 void wsp_session_init(wap_dispatch_func_t *responder_dispatch,
00194                       wap_dispatch_func_t *initiator_dispatch,
00195                       wap_dispatch_func_t *application_dispatch,
00196                       wap_dispatch_func_t *push_ota_dispatch) {
00197     queue = gwlist_create();
00198     gwlist_add_producer(queue);
00199     session_machines = gwlist_create();
00200     session_id_counter = counter_create();
00201     dispatch_to_wtp_resp = responder_dispatch;
00202     dispatch_to_wtp_init = initiator_dispatch;
00203     dispatch_to_appl = application_dispatch;
00204         dispatch_to_ota = push_ota_dispatch;
00205         wsp_strings_init();
00206     run_status = running;
00207     gwthread_create(main_thread, NULL);
00208 }
00209 
00210 
00211 void wsp_session_shutdown(void) {
00212     gw_assert(run_status == running);
00213     run_status = terminating;
00214     gwlist_remove_producer(queue);
00215     gwthread_join_every(main_thread);
00216 
00217     gwlist_destroy(queue, wap_event_destroy_item);
00218 
00219     debug("wap.wsp", 0, "WSP: %ld session machines left.",
00220         gwlist_len(session_machines));
00221     gwlist_destroy(session_machines, machine_destroy);
00222 
00223     counter_destroy(session_id_counter);
00224         wsp_strings_shutdown();
00225 }
00226 
00227 
00228 void wsp_session_dispatch_event(WAPEvent *event) {
00229     wap_event_assert(event);
00230     gwlist_produce(queue, event);
00231 }
00232 
00233 
00234 /***********************************************************************
00235  * Local functions
00236  */
00237 
00238 
00239 static void main_thread(void *arg) {
00240     WAPEvent *e;
00241     WSPMachine *sm;
00242     WSP_PDU *pdu;
00243     
00244     while (run_status == running && (e = gwlist_consume(queue)) != NULL) {
00245         wap_event_assert(e);
00246         switch (e->type) {
00247         case TR_Invoke_Ind:
00248             pdu = wsp_pdu_unpack(e->u.TR_Invoke_Ind.user_data);
00249             if (pdu == NULL) {
00250                 warning(0, "WSP: Broken PDU ignored.");
00251                 wap_event_destroy(e);
00252                 continue;
00253             }
00254             break;
00255     
00256         default:
00257             pdu = NULL;
00258             break;
00259         }
00260     
00261         sm = find_session_machine(e, pdu);
00262         if (sm == NULL) {
00263             wap_event_destroy(e);
00264         } else {
00265             handle_session_event(sm, e, pdu);
00266         }
00267         
00268         wsp_pdu_destroy(pdu);
00269     }
00270 }
00271 
00272 
00273 static WSPMachine *find_session_machine(WAPEvent *event, WSP_PDU *pdu) {
00274     WSPMachine *sm;
00275     long session_id;
00276     WAPAddrTuple *tuple;
00277     
00278     tuple = NULL;
00279     session_id = -1;
00280     
00281     switch (event->type) {
00282     case TR_Invoke_Ind:
00283         tuple = wap_addr_tuple_duplicate(
00284                 event->u.TR_Invoke_Ind.addr_tuple);
00285         break;
00286 
00287         case TR_Invoke_Cnf:
00288                 tuple = wap_addr_tuple_duplicate(
00289                 event->u.TR_Invoke_Cnf.addr_tuple);
00290             break;
00291 
00292     case TR_Result_Cnf:
00293         tuple = wap_addr_tuple_duplicate(
00294                 event->u.TR_Result_Cnf.addr_tuple);
00295         break;
00296 
00297     case TR_Abort_Ind:
00298         tuple = wap_addr_tuple_duplicate(
00299                 event->u.TR_Abort_Ind.addr_tuple);
00300         break;
00301 
00302     case S_Connect_Res:
00303         session_id = event->u.S_Connect_Res.session_id;
00304         break;
00305 
00306     case S_Resume_Res:
00307         session_id = event->u.S_Resume_Res.session_id;
00308         break;
00309 
00310     case Disconnect_Event:
00311         session_id = event->u.Disconnect_Event.session_handle;
00312         break;
00313 
00314     case Suspend_Event:
00315         session_id = event->u.Suspend_Event.session_handle;
00316         break;
00317 
00318     case S_MethodInvoke_Res:
00319         session_id = event->u.S_MethodInvoke_Res.session_id;
00320         break;
00321 
00322     case S_MethodResult_Req:
00323         session_id = event->u.S_MethodResult_Req.session_id;
00324         break;
00325 
00326     case S_ConfirmedPush_Req:
00327                 session_id = event->u.S_ConfirmedPush_Req.session_id;
00328             break;
00329 
00330         case S_Push_Req:
00331                 session_id = event->u.S_Push_Req.session_id;
00332             break;
00333 
00334     default:
00335         error(0, "WSP: Cannot find machine for %s event",
00336             wap_event_name(event->type));
00337     }
00338     
00339     gw_assert(tuple != NULL || session_id != -1);
00340 
00341     /* Pre-state-machine tests, according to 7.1.5.  After the tests,
00342      * caller will pass the event to sm if sm is not NULL. */
00343     sm = NULL;
00344     /* First test is for MRUEXCEEDED, and we don't have a MRU */
00345 
00346     /* Second test is for class 2 TR-Invoke.ind with Connect PDU */
00347     if (event->type == TR_Invoke_Ind &&
00348         event->u.TR_Invoke_Ind.tcl == 2 &&
00349         pdu->type == Connect) {
00350             /* Create a new session, even if there is already
00351              * a session open for this address.  The new session
00352              * will take care of killing the old ones. */
00353             sm = machine_create();
00354             gw_assert(tuple != NULL);
00355             sm->addr_tuple = wap_addr_tuple_duplicate(tuple);
00356             sm->connect_handle = event->u.TR_Invoke_Ind.handle;
00357     /* Third test is for class 2 TR-Invoke.ind with Resume PDU */
00358     } else if (event->type == TR_Invoke_Ind &&
00359            event->u.TR_Invoke_Ind.tcl == 2 &&
00360            pdu->type == Resume) {
00361         /* Pass to session identified by session id, not
00362          * the address tuple. */
00363         session_id = pdu->u.Resume.sessionid;
00364         sm = gwlist_search(session_machines, &session_id,
00365                 find_by_session_id);
00366         if (sm == NULL) {
00367             /* No session; TR-Abort.req(DISCONNECT) */
00368             send_abort(WSP_ABORT_DISCONNECT,
00369                 event->u.TR_Invoke_Ind.handle);
00370         }
00371     /* Fourth test is for a class 1 or 2 TR-Invoke.Ind with no
00372      * session for that address tuple.  We also handle class 0
00373      * TR-Invoke.ind here by ignoring them; this seems to be
00374      * an omission in the spec table. */
00375     } else if (event->type == TR_Invoke_Ind) {
00376         sm = gwlist_search(session_machines, tuple,
00377                  transaction_belongs_to_session);
00378         if (sm == NULL && (event->u.TR_Invoke_Ind.tcl == 1 ||
00379                 event->u.TR_Invoke_Ind.tcl == 2)) {
00380             send_abort(WSP_ABORT_DISCONNECT,
00381                 event->u.TR_Invoke_Ind.handle);
00382         }
00383     /* Other tests are for events not handled by the state tables;
00384      * do those later, after we've tried to handle them. */
00385     } else {
00386         if (session_id != -1) {
00387             sm = gwlist_search(session_machines, &session_id,
00388                 find_by_session_id);
00389         } else {
00390             sm = gwlist_search(session_machines, tuple,
00391                 transaction_belongs_to_session);
00392         }
00393         /* The table doesn't really say what we should do with
00394          * non-Invoke events for which there is no session.  But
00395          * such a situation means there is an error _somewhere_
00396          * in the gateway. */
00397         if (sm == NULL) {
00398             error(0, "WSP: Cannot find session machine for event.");
00399             wap_event_dump(event);
00400         }
00401     }
00402 
00403     wap_addr_tuple_destroy(tuple);
00404     return sm;
00405 }
00406 
00407 
00408 static void handle_session_event(WSPMachine *sm, WAPEvent *current_event, 
00409 WSP_PDU *pdu) {
00410     debug("wap.wsp", 0, "WSP: machine %p, state %s, event %s",
00411         (void *) sm,
00412         state_name(sm->state), 
00413         wap_event_name(current_event->type));
00414 
00415     #define STATE_NAME(name)
00416     #define ROW(state_name, event, condition, action, next_state) \
00417         { \
00418             struct event *e; \
00419             e = &current_event->u.event; \
00420             if (sm->state == state_name && \
00421                current_event->type == event && \
00422                (condition)) { \
00423                 action \
00424                 sm->state = next_state; \
00425                 debug("wap.wsp", 0, "WSP %ld: New state %s", \
00426                     sm->session_id, #next_state); \
00427                 goto end; \
00428             } \
00429         }
00430     #include "wsp_server_session_states.def"
00431     
00432     cant_handle_event(sm, current_event);
00433 
00434 end:
00435     wap_event_destroy(current_event);
00436 
00437     if (sm->state == NULL_SESSION)
00438         machine_destroy(sm);
00439 }
00440 
00441 
00442 static void cant_handle_event(WSPMachine *sm, WAPEvent *event) {
00443     /* We do the rest of the pre-state-machine tests here.  The first
00444      * four were done in find_session_machine().  The fifth is a
00445      * class 1 or 2 TR-Invoke.ind not handled by the state tables. */
00446     if (event->type == TR_Invoke_Ind &&
00447         (event->u.TR_Invoke_Ind.tcl == 1 ||
00448          event->u.TR_Invoke_Ind.tcl == 2)) {
00449         warning(0, "WSP: Can't handle TR-Invoke.ind, aborting transaction.");
00450         debug("wap.wsp", 0, "WSP: The unhandled event:");
00451         wap_event_dump(event);
00452         send_abort(WSP_ABORT_PROTOERR,
00453             event->u.TR_Invoke_Ind.handle);
00454     /* The sixth is a class 0 TR-Invoke.ind not handled by state tables. */
00455     } else if (event->type == TR_Invoke_Ind) {
00456         warning(0, "WSP: Can't handle TR-Invoke.ind, ignoring.");
00457         debug("wap.wsp", 0, "WSP: The ignored event:");
00458         wap_event_dump(event);
00459     /* The seventh is any other event not handled by state tables. */
00460     } else {
00461         error(0, "WSP: Can't handle event. Aborting session.");
00462         debug("wap.wsp", 0, "WSP: The unhandled event:");
00463         wap_event_dump(event);
00464         /* TR-Abort.req(PROTOERR) if it is some other transaction
00465          * event than abort. */
00466         /* Currently that means TR-Result.cnf, because we already
00467          * tested for Invoke. */
00468         /* FIXME We need a better way to get at event values than
00469          * by hardcoding the types. */
00470         if (event->type == TR_Result_Cnf) {
00471             send_abort(WSP_ABORT_PROTOERR,
00472                 event->u.TR_Result_Cnf.handle);
00473         }
00474         /* Abort(PROTOERR) all method and push transactions */
00475         abort_methods(sm, WSP_ABORT_PROTOERR);
00476                 abort_pushes(sm, WSP_ABORT_PROTOERR);
00477         /* S-Disconnect.ind(PROTOERR) */
00478         indicate_disconnect(sm, WSP_ABORT_PROTOERR);
00479     }
00480 }
00481 
00482 
00483 static WSPMachine *machine_create(void) {
00484     WSPMachine *p;
00485     
00486     p = gw_malloc(sizeof(WSPMachine));
00487     debug("wap.wsp", 0, "WSP: Created WSPMachine %p", (void *) p);
00488     
00489     #define INTEGER(name) p->name = 0;
00490     #define OCTSTR(name) p->name = NULL;
00491     #define HTTPHEADERS(name) p->name = NULL;
00492     #define ADDRTUPLE(name) p->name = NULL;
00493     #define MACHINESLIST(name) p->name = gwlist_create();
00494     #define CAPABILITIES(name) p->name = NULL;
00495     #define COOKIES(name) p->name = gwlist_create();
00496     #define REFERER(name) p->name = NULL;
00497     #define MACHINE(fields) fields
00498     #include "wsp_server_session_machine.def"
00499     
00500     p->state = NULL_SESSION;
00501 
00502     /* set capabilities to default values (defined in 1.1) */
00503 
00504     p->client_SDU_size = 1400;
00505     p->MOR_push = 1;
00506     
00507     /* Insert new machine at the _front_, because 1) it's more likely
00508      * to get events than old machines are, so this speeds up the linear
00509      * search, and 2) we want the newest machine to get any method
00510      * invokes that come through before the Connect is established. */
00511     gwlist_insert(session_machines, 0, p);
00512 
00513     return p;
00514 }
00515 
00516 
00517 static void destroy_methodmachines(List *machines) {
00518     if (gwlist_len(machines) > 0) {
00519         warning(0, "Destroying WSP session with %ld active methods\n",
00520             gwlist_len(machines));
00521     }
00522 
00523     gwlist_destroy(machines, method_machine_destroy);
00524 }
00525 
00526 static void destroy_pushmachines(List *machines) {
00527     if (gwlist_len(machines) > 0) {
00528         warning(0, "Destroying WSP session with %ld active pushes\n",
00529             gwlist_len(machines));
00530     }
00531 
00532     gwlist_destroy(machines, push_machine_destroy);
00533 }
00534 
00535 static void machine_destroy(void *pp) {
00536     WSPMachine *p;
00537     
00538     p = pp;
00539     debug("wap.wsp", 0, "Destroying WSPMachine %p", pp);
00540     gwlist_delete_equal(session_machines, p);
00541 
00542     #define INTEGER(name) p->name = 0;
00543     #define OCTSTR(name) octstr_destroy(p->name);
00544     #define HTTPHEADERS(name) http_destroy_headers(p->name);
00545     #define ADDRTUPLE(name) wap_addr_tuple_destroy(p->name);
00546     #define MACHINESLIST(name) destroy_##name(p->name);
00547     #define CAPABILITIES(name) wsp_cap_destroy_list(p->name);
00548     #define COOKIES(name) cookies_destroy(p->name);
00549     #define REFERER(name) octstr_destroy(p->name);
00550     #define MACHINE(fields) fields
00551     #include "wsp_server_session_machine.def"
00552     gw_free(p);
00553 }
00554 
00555 
00556 struct msm_pattern {
00557     WAPAddrTuple *addr_tuple;
00558     long msmid, tid;
00559 };
00560 
00561 
00562 /* This function does NOT consume its event; it leaves that task up
00563  * to the parent session */
00564 static void handle_method_event(WSPMachine *sm, WSPMethodMachine *msm, 
00565 WAPEvent *current_event, WSP_PDU *pdu) {
00566 
00567     if (msm == NULL) {
00568         warning(0, "No method machine for event.");
00569         wap_event_dump(current_event);
00570         return;
00571     }
00572         
00573     debug("wap.wsp", 0, "WSP: method %ld, state %s, event %s",
00574         msm->transaction_id, state_name(msm->state), 
00575         wap_event_name(current_event->type));
00576 
00577     gw_assert(sm->session_id == msm->session_id);
00578 
00579     #define STATE_NAME(name)
00580     #define ROW(state_name, event, condition, action, next_state) \
00581         { \
00582             struct event *e; \
00583             e = &current_event->u.event; \
00584             if (msm->state == state_name && \
00585                current_event->type == event && \
00586                (condition)) { \
00587                 action \
00588                 msm->state = next_state; \
00589                 debug("wap.wsp", 0, "WSP %ld/%ld: New method state %s", \
00590                     msm->session_id, msm->transaction_id, #next_state); \
00591                 goto end; \
00592             } \
00593         }
00594     #include "wsp_server_method_states.def"
00595     
00596     cant_handle_event(sm, current_event);
00597 
00598 end:
00599     if (msm->state == NULL_METHOD) {
00600         method_machine_destroy(msm);
00601         gwlist_delete_equal(sm->methodmachines, msm);
00602     }
00603 }
00604 
00605 
00606 static WSPMethodMachine *method_machine_create(WSPMachine *sm,
00607             long wtp_handle) {
00608     WSPMethodMachine *msm;
00609     
00610     msm = gw_malloc(sizeof(*msm));
00611     
00612     #define INTEGER(name) msm->name = 0;
00613     #define ADDRTUPLE(name) msm->name = NULL;
00614     #define EVENT(name) msm->name = NULL;
00615     #define MACHINE(fields) fields
00616     #include "wsp_server_method_machine.def"
00617     
00618     msm->transaction_id = wtp_handle;
00619     msm->state = NULL_METHOD;
00620     msm->addr_tuple = wap_addr_tuple_duplicate(sm->addr_tuple);
00621     msm->session_id = sm->session_id;
00622 
00623     gwlist_append(sm->methodmachines, msm);
00624 
00625     return msm;
00626 }
00627 
00628 
00629 
00630 static void method_machine_destroy(void *p) {
00631     WSPMethodMachine *msm;
00632 
00633     if (p == NULL)
00634         return;
00635 
00636     msm = p;
00637 
00638     debug("wap.wsp", 0, "Destroying WSPMethodMachine %ld",
00639             msm->transaction_id);
00640 
00641     #define INTEGER(name)
00642     #define ADDRTUPLE(name) wap_addr_tuple_destroy(msm->name);
00643     #define EVENT(name) wap_event_destroy(msm->name);
00644     #define MACHINE(fields) fields
00645     #include "wsp_server_method_machine.def"
00646 
00647     gw_free(msm);
00648 }
00649 
00650 static void handle_push_event(WSPMachine *sm, WSPPushMachine *pm, 
00651                               WAPEvent *current_event)
00652 {
00653         if (pm == NULL) {
00654         warning(0, "No push machine for event.");
00655         wap_event_dump(current_event);
00656         return;
00657     }
00658 
00659         debug("wap.wsp", 0, "WSP(tid/pid): push %ld/%ld, state %s, event %s",
00660         pm->transaction_id, pm->server_push_id, state_name(pm->state), 
00661         wap_event_name(current_event->type));
00662     gw_assert(sm->session_id == pm->session_id);
00663 
00664         #define STATE_NAME(name)
00665     #define ROW(state_name, event, condition, action, next_state) \
00666         { \
00667             if (pm->state == state_name && \
00668             current_event->type == event && \
00669             (condition)) { \
00670                  action \
00671                  pm->state = next_state; \
00672                  debug("wap.wsp", 0, "WSP %ld/%ld: New push state %s", \
00673                        pm->session_id, pm->transaction_id, #next_state); \
00674                 goto end; \
00675             } \
00676         }
00677     #include "wsp_server_push_states.def"
00678 
00679         cant_handle_event(sm, current_event);
00680 end:
00681         if (pm->state == SERVER_PUSH_NULL_STATE) {
00682         push_machine_destroy(pm);
00683         gwlist_delete_equal(sm->pushmachines, pm);
00684     }
00685 }
00686 
00687 static WSPPushMachine *push_machine_create(WSPMachine *sm, 
00688         long pid)
00689 {
00690         WSPPushMachine *m;
00691 
00692         m = gw_malloc(sizeof(WSPPushMachine));
00693 
00694         #define INTEGER(name) m->name = 0;
00695         #define ADDRTUPLE(name) m->name = NULL;
00696         #define HTTPHEADER(name) m->name = http_create_empty_headers();
00697         #define MACHINE(fields) fields
00698         #include "wsp_server_push_machine.def"
00699 
00700         m->server_push_id = pid;
00701         m->transaction_id = pid;
00702     m->state = SERVER_PUSH_NULL_STATE;
00703     m->addr_tuple = wap_addr_tuple_duplicate(sm->addr_tuple);
00704     m->session_id = sm->session_id;
00705 
00706     gwlist_append(sm->pushmachines, m);
00707 
00708     return m;      
00709 }
00710 
00711 static void push_machine_destroy(void *p)
00712 {
00713         WSPPushMachine *m = NULL;   
00714 
00715     if (p == NULL)
00716            return;  
00717         m = p;
00718         debug("wap.wsp", 0, "Destroying WSPPushMachine %ld",
00719             m->transaction_id);
00720         #define INTEGER(name) 
00721         #define ADDRTUPLE(name) wap_addr_tuple_destroy(m->name);
00722         #define HTTPHEADER(name) http_destroy_headers(m->name);
00723         #define MACHINE(fields) fields
00724         #include "wsp_server_push_machine.def"
00725 
00726         gw_free(m);
00727 }
00728 
00729 static char *state_name(WSPState state) {
00730     switch (state) {
00731     #define STATE_NAME(name) case name: return #name;
00732     #define ROW(state, event, cond, stmt, next_state)
00733     #include "wsp_server_session_states.def"
00734 
00735     #define STATE_NAME(name) case name: return #name;
00736     #define ROW(state, event, cond, stmt, next_state)
00737     #include "wsp_server_method_states.def"
00738 
00739         #define STATE_NAME(name) case name: return #name;
00740         #define ROW(state, event, cond, stmt, next_state)
00741         #include "wsp_server_push_states.def"
00742 
00743     default:
00744         return "unknown wsp state";
00745     }
00746 }
00747 
00748 
00749 static unsigned long next_wsp_session_id(void) {
00750     return counter_increase(session_id_counter);
00751 }
00752 
00753 
00754 static void sanitize_capabilities(List *caps, WSPMachine *m) {
00755     long i;
00756     Capability *cap;
00757     unsigned long ui;
00758 
00759     for (i = 0; i < gwlist_len(caps); i++) {
00760         cap = gwlist_get(caps, i);
00761 
00762         /* We only know numbered capabilities.  Let the application
00763          * layer negotiate whatever it wants for unknown ones. */
00764         if (cap->name != NULL)
00765             continue;
00766 
00767         switch (cap->id) {
00768         case WSP_CAPS_CLIENT_SDU_SIZE:
00769             /* Check if it's a valid uintvar.  The value is the
00770              * max SDU size we will send, and there's no
00771              * internal limit to that, so accept any value. */
00772             if (cap->data != NULL &&
00773                 octstr_extract_uintvar(cap->data, &ui, 0) < 0)
00774                 goto bad_cap;
00775             else
00776                 m->client_SDU_size = ui;
00777             break;
00778 
00779         case WSP_CAPS_SERVER_SDU_SIZE:
00780             /* Check if it's a valid uintvar */
00781             if (cap->data != NULL &&
00782                 (octstr_extract_uintvar(cap->data, &ui, 0) < 0))
00783                 goto bad_cap;
00784             /* XXX Our MRU is not quite unlimited, since we
00785              * use signed longs in the library functions --
00786              * should we make sure we limit the reply value
00787              * to LONG_MAX?  (That's already a 2GB packet) */
00788             break;
00789 
00790         case WSP_CAPS_PROTOCOL_OPTIONS:
00791             /* Currently we don't support any Push, nor
00792              * session resume, nor acknowledgement headers,
00793              * so make sure those bits are not set. */
00794             if (cap->data != NULL && octstr_len(cap->data) > 0
00795                && (octstr_get_char(cap->data, 0) & 0xf0) != 0) {
00796                 warning(0, "WSP: Application layer tried to "
00797                     "negotiate protocol options.");
00798                 octstr_set_bits(cap->data, 0, 4, 0);
00799             }
00800             break;
00801 
00802         case WSP_CAPS_EXTENDED_METHODS:
00803             /* XXX Check format here */
00804             break;
00805 
00806         
00807         case WSP_CAPS_HEADER_CODE_PAGES:
00808             /* We don't support any yet, so don't let this
00809              * be negotiated. */
00810             if (cap->data)
00811                 goto bad_cap;
00812             break;
00813         }
00814         continue;
00815 
00816     bad_cap:
00817         error(0, "WSP: Found illegal value in capabilities reply.");
00818         wsp_cap_dump(cap);
00819         gwlist_delete(caps, i, 1);
00820         i--;
00821         wsp_cap_destroy(cap);
00822         continue;
00823     }
00824 }
00825 
00826 
00827 static void reply_known_capabilities(List *caps, List *req, WSPMachine *m) {
00828     unsigned long ui;
00829     Capability *cap;
00830     Octstr *data;
00831 
00832     if (wsp_cap_count(caps, WSP_CAPS_CLIENT_SDU_SIZE, NULL) == 0) {
00833         if (wsp_cap_get_client_sdu(req, &ui) > 0) {
00834             /* Accept value if it is not silly. */
00835             if ((ui >= 256 && ui < LONG_MAX) || ui == 0) {
00836                 m->client_SDU_size = ui;
00837             }
00838         }
00839         /* Reply with the client SDU we decided on */
00840         data = octstr_create("");
00841         octstr_append_uintvar(data, m->client_SDU_size);
00842         cap = wsp_cap_create(WSP_CAPS_CLIENT_SDU_SIZE,
00843             NULL, data);
00844         gwlist_append(caps, cap);
00845     }
00846 
00847     if (wsp_cap_count(caps, WSP_CAPS_SERVER_SDU_SIZE, NULL) == 0) {
00848         /* Accept whatever size the client is willing
00849          * to send.  If the client did not specify anything,
00850          * then use the default. */
00851         if (wsp_cap_get_server_sdu(req, &ui) <= 0) {
00852             ui = 1400;
00853         }
00854         data = octstr_create("");
00855         octstr_append_uintvar(data, ui);
00856         cap = wsp_cap_create(WSP_CAPS_SERVER_SDU_SIZE, NULL, data);
00857         gwlist_append(caps, cap);
00858     }
00859 
00860     /* Currently we cannot handle any protocol options */
00861     if (wsp_cap_count(caps, WSP_CAPS_PROTOCOL_OPTIONS, NULL) == 0) {
00862         data = octstr_create("");
00863         octstr_append_char(data, 0);
00864         cap = wsp_cap_create(WSP_CAPS_PROTOCOL_OPTIONS, NULL, data);
00865         gwlist_append(caps, cap);
00866     }
00867 
00868     /* Accept any Method-MOR the client sent; if it sent none,
00869      * use the default. */
00870     if (wsp_cap_count(caps, WSP_CAPS_METHOD_MOR, NULL) == 0) {
00871         if (wsp_cap_get_method_mor(req, &ui) <= 0) {
00872             ui = 1;
00873         }
00874         data = octstr_create("");
00875         octstr_append_char(data, ui);
00876         cap = wsp_cap_create(WSP_CAPS_METHOD_MOR, NULL, data);
00877         gwlist_append(caps, cap);
00878     }
00879 
00880     /* We will never send any Push requests because we don't support
00881      * that yet.  But we already specified that in protocol options;
00882      * so, pretend we do, and handle the value that way. */
00883     if (wsp_cap_count(caps, WSP_CAPS_PUSH_MOR, NULL) == 0) {
00884         if (wsp_cap_get_push_mor(req, &ui) > 0) {
00885             m->MOR_push = ui;
00886         }
00887         data = octstr_create("");
00888         octstr_append_char(data, m->MOR_push);
00889         cap = wsp_cap_create(WSP_CAPS_PUSH_MOR, NULL, data);
00890         gwlist_append(caps, cap);
00891     }
00892 
00893     /* Supporting extended methods is up to the application layer,
00894      * not up to us.  If the application layer didn't specify any,
00895      * then we refuse whatever the client requested.  The default
00896      * is to support none, so we don't really have to add anything here. */
00897 
00898     /* We do not support any header code pages.  sanitize_capabilities
00899      * must have already deleted any reply that indicates otherwise.
00900      * Again, not adding anything here is the same as refusing support. */
00901 
00902     /* Listing aliases is something the application layer can do if
00903      * it wants to.  We don't care. */
00904 }
00905 
00906 
00907 /* Generate a refusal for all requested capabilities that are not
00908  * replied to. */
00909 static void refuse_unreplied_capabilities(List *caps, List *req) {
00910     long i, len;
00911     Capability *cap;
00912 
00913     len = gwlist_len(req);
00914     for (i = 0; i < len; i++) {
00915         cap = gwlist_get(req, i);
00916         if (wsp_cap_count(caps, cap->id, cap->name) == 0) {
00917             cap = wsp_cap_create(cap->id, cap->name, NULL);
00918             gwlist_append(caps, cap);
00919         }
00920     }
00921 }
00922 
00923 
00924 static int is_default_cap(Capability *cap) {
00925     unsigned long ui;
00926 
00927     /* All unknown values are empty by default */
00928     if (cap->name != NULL || cap->id < 0 || cap->id >= WSP_NUM_CAPS)
00929         return cap->data == NULL || octstr_len(cap->data) == 0;
00930 
00931     switch (cap->id) {
00932     case WSP_CAPS_CLIENT_SDU_SIZE:
00933     case WSP_CAPS_SERVER_SDU_SIZE:
00934         return (cap->data != NULL &&
00935             octstr_extract_uintvar(cap->data, &ui, 0) >= 0 &&
00936             ui == 1400);
00937     case WSP_CAPS_PROTOCOL_OPTIONS:
00938         return cap->data != NULL && octstr_get_char(cap->data, 0) == 0;
00939     case WSP_CAPS_METHOD_MOR:
00940     case WSP_CAPS_PUSH_MOR:
00941         return cap->data != NULL && octstr_get_char(cap->data, 0) == 1;
00942     case WSP_CAPS_EXTENDED_METHODS:
00943     case WSP_CAPS_HEADER_CODE_PAGES:
00944     case WSP_CAPS_ALIASES:
00945         return cap->data == NULL || octstr_len(cap->data) == 0;
00946     default:
00947         return 0;
00948     }
00949 }
00950 
00951 
00952 /* Remove any replies that have no corresponding request and that
00953  * are equal to the default. */
00954 static void strip_default_capabilities(List *caps, List *req) {
00955     long i;
00956     Capability *cap;
00957     int count;
00958 
00959     /* Hmm, this is an O(N*N) operation, which may be bad. */
00960 
00961     i = 0;
00962     while (i < gwlist_len(caps)) {
00963         cap = gwlist_get(caps, i);
00964 
00965         count = wsp_cap_count(req, cap->id, cap->name);
00966         if (count == 0 && is_default_cap(cap)) {
00967             gwlist_delete(caps, i, 1);
00968             wsp_cap_destroy(cap);
00969         } else {
00970             i++;
00971         }
00972     }
00973 }
00974 
00975 
00976 static List *make_capabilities_reply(WSPMachine *m) {
00977     List *caps;
00978 
00979     /* In principle, copy the application layer's capabilities
00980      * response, add refusals for all unknown requested capabilities,
00981      * and add responses for all known capabilities that are
00982      * not already responded to.  Then eliminate any replies that
00983      * would have no effect because they are equal to the default. */
00984 
00985     caps = wsp_cap_duplicate_list(m->reply_caps);
00986 
00987     /* Don't let the application layer negotiate anything we
00988      * cannot handle.  Also parse the values it set if we're
00989      * interested. */
00990     sanitize_capabilities(caps, m);
00991 
00992     /* Add capability records for all capabilities we know about
00993      * that are not already in the reply list. */
00994     reply_known_capabilities(caps, m->request_caps, m);
00995 
00996     /* All remaining capabilities in the request list that are
00997      * not in the reply list at this point must be unknown ones
00998      * that we want to refuse. */
00999     refuse_unreplied_capabilities(caps, m->request_caps);
01000 
01001     /* Now eliminate replies that would be equal to the requested
01002      * value, or (if there was none) to the default value. */
01003     strip_default_capabilities(caps, m->request_caps);
01004 
01005     return caps;
01006 }
01007 
01008 
01009 static List *make_reply_headers(WSPMachine *m)
01010 {
01011     List *headers;
01012     Octstr *encoding_version;
01013 
01014     /* Add all server wsp level hop-by-hop headers. Currently only 
01015      * Encoding-Version, as defined by wsp, chapter 8.4.2.70. 
01016      * What headers belong to which version is defined in appendix A,
01017      * table 39.. 
01018     encoding_version = request_version = NULL;
01019      * Essentially, if the client sends us an Encoding-Version
01020      * higher than ours (1.3) we send our version number to it,
01021      * if it is lower, we left version number intact. */
01022     /* First the case that we have no Encoding-Version header at all. 
01023      * This case we must assume that the client supports version 1.2
01024      * or lower. */
01025 
01026     headers = http_create_empty_headers();
01027     encoding_version = wsp_encoding_version_to_string(m->encoding_version);
01028     http_header_add(headers, "Encoding-Version", octstr_get_cstr(encoding_version));
01029     octstr_destroy(encoding_version);
01030 
01031     return headers;
01032 }
01033 
01034 static Octstr *make_connectreply_pdu(WSPMachine *m) 
01035 {
01036     WSP_PDU *pdu;
01037     Octstr *os;
01038     List *caps;
01039     List *reply_headers;
01040     
01041     pdu = wsp_pdu_create(ConnectReply);
01042 
01043     pdu->u.ConnectReply.sessionid = m->session_id;
01044 
01045     caps = make_capabilities_reply(m);
01046     pdu->u.ConnectReply.capabilities = wsp_cap_pack_list(caps);
01047     wsp_cap_destroy_list(caps);
01048 
01049     reply_headers = make_reply_headers(m);
01050     pdu->u.ConnectReply.headers = 
01051         wsp_headers_pack(reply_headers, 0, m->encoding_version);
01052     http_destroy_headers(reply_headers);
01053     
01054     os = wsp_pdu_pack(pdu);
01055     wsp_pdu_destroy(pdu);
01056 
01057     return os;
01058 }
01059 
01060 
01061 static Octstr *make_resume_reply_pdu(WSPMachine *m, List *headers) 
01062 {
01063     WSP_PDU *pdu;
01064     Octstr *os;
01065 
01066     pdu = wsp_pdu_create(Reply);
01067 
01068     /* Not specified for Resume replies */
01069     pdu->u.Reply.status = wsp_convert_http_status_to_wsp_status(HTTP_OK);
01070     if (headers == NULL) {
01071         headers = http_create_empty_headers();
01072         pdu->u.Reply.headers = wsp_headers_pack(headers, 1, m->encoding_version);
01073         http_destroy_headers(headers);
01074     } else {
01075         pdu->u.Reply.headers = wsp_headers_pack(headers, 1, m->encoding_version);
01076     }
01077     pdu->u.Reply.data = octstr_create("");
01078 
01079     os = wsp_pdu_pack(pdu);
01080     wsp_pdu_destroy(pdu);
01081 
01082     return os;
01083 }
01084 
01085 static WSP_PDU *make_confirmedpush_pdu(WAPEvent *e)
01086 {
01087         WSP_PDU *pdu;
01088         List *headers;
01089 
01090         pdu = wsp_pdu_create(ConfirmedPush);
01091 /*
01092  * Both push headers and push body are optional. 
01093  */
01094         if (e->u.S_ConfirmedPush_Req.push_headers == NULL) {
01095         headers = http_create_empty_headers();
01096             pdu->u.ConfirmedPush.headers = wsp_headers_pack(headers, 1, WSP_1_2);
01097             http_destroy_headers(headers);
01098         } else
01099             pdu->u.ConfirmedPush.headers = 
01100                 wsp_headers_pack(e->u.S_ConfirmedPush_Req.push_headers, 1, WSP_1_2);
01101    
01102         if (e->u.S_ConfirmedPush_Req.push_body == NULL)
01103         pdu->u.ConfirmedPush.data = octstr_create("");
01104         else
01105         pdu->u.ConfirmedPush.data = 
01106                 octstr_duplicate(e->u.S_ConfirmedPush_Req.push_body);        
01107 
01108         return pdu;
01109 }
01110 
01111 static WSP_PDU *make_push_pdu(WAPEvent *e)
01112 {
01113         WSP_PDU *pdu;
01114         List *headers;
01115 
01116         pdu = wsp_pdu_create(Push);
01117 /*
01118  * Both push headers and push body are optional
01119  */
01120         if (e->u.S_Push_Req.push_headers == NULL) {
01121         headers = http_create_empty_headers();
01122             pdu->u.Push.headers = wsp_headers_pack(headers, 1, WSP_1_2);
01123             http_destroy_headers(headers);
01124         } else
01125             pdu->u.Push.headers = 
01126                 wsp_headers_pack(e->u.S_Push_Req.push_headers, 1, WSP_1_2);
01127    
01128         if (e->u.S_Push_Req.push_body == NULL)
01129         pdu->u.Push.data = octstr_create("");
01130         else
01131         pdu->u.Push.data = 
01132                 octstr_duplicate(e->u.S_Push_Req.push_body);        
01133 
01134         return pdu;
01135 }
01136 
01137 static int transaction_belongs_to_session(void *wsp_ptr, void *tuple_ptr) {
01138     WSPMachine *wsp;
01139     WAPAddrTuple *tuple;
01140     
01141     wsp = wsp_ptr;
01142     tuple = tuple_ptr;
01143 
01144     return wap_addr_tuple_same(wsp->addr_tuple, tuple);
01145 }
01146 
01147 
01148 static int find_by_session_id(void *wsp_ptr, void *id_ptr) {
01149     WSPMachine *wsp = wsp_ptr;
01150     long *idp = id_ptr;
01151     
01152     return wsp->session_id == *idp;
01153 }
01154 
01155 
01156 static int find_by_method_id(void *wspm_ptr, void *id_ptr) {
01157     WSPMethodMachine *msm = wspm_ptr;
01158     long *idp = id_ptr;
01159 
01160     return msm->transaction_id == *idp;
01161 }
01162 
01163 static int find_by_push_id(void *m_ptr, void *id_ptr) {
01164     WSPPushMachine *m = m_ptr;
01165     long *idp = id_ptr;
01166 
01167     return m->transaction_id == *idp;
01168 }
01169 
01170 static WSPMethodMachine *find_method_machine(WSPMachine *sm, long id) {
01171     return gwlist_search(sm->methodmachines, &id, find_by_method_id);
01172 }
01173 
01174 static WSPPushMachine *find_push_machine(WSPMachine *m, long id)
01175 {
01176        return gwlist_search(m->pushmachines, &id, find_by_push_id);
01177 }
01178 
01179 static int same_client(void *a, void *b) {
01180     WSPMachine *sm1, *sm2;
01181     
01182     sm1 = a;
01183     sm2 = b;
01184     return wap_addr_tuple_same(sm1->addr_tuple, sm2->addr_tuple);
01185 }
01186 
01187 
01188 static void disconnect_other_sessions(WSPMachine *sm) {
01189     List *old_sessions;
01190     WAPEvent *disconnect;
01191     WSPMachine *sm2;
01192     long i;
01193 
01194     old_sessions = gwlist_search_all(session_machines, sm, same_client);
01195     if (old_sessions == NULL)
01196         return;
01197 
01198     for (i = 0; i < gwlist_len(old_sessions); i++) {
01199         sm2 = gwlist_get(old_sessions, i);
01200         if (sm2 != sm) {
01201             disconnect = wap_event_create(Disconnect_Event);
01202             handle_session_event(sm2, disconnect, NULL);
01203         }
01204     }
01205 
01206     gwlist_destroy(old_sessions, NULL);
01207 }
01208 
01209 
01210 static List *unpack_new_headers(WSPMachine *sm, Octstr *hdrs) {
01211     List *new_headers;
01212 
01213     if (hdrs && octstr_len(hdrs) > 0) {
01214         new_headers = wsp_headers_unpack(hdrs, 0);
01215         if (sm->http_headers == NULL)
01216             sm->http_headers = http_create_empty_headers();
01217         http_header_combine(sm->http_headers, new_headers);
01218         return new_headers;
01219     }
01220     return NULL;
01221 }
01222 
01223 static WAPEvent *make_abort(long reason, long handle)
01224 {
01225         WAPEvent *wtp_event;
01226 
01227         wtp_event = wap_event_create(TR_Abort_Req);
01228         wtp_event->u.TR_Abort_Req.abort_type = 0x01;
01229         wtp_event->u.TR_Abort_Req.abort_reason = reason;
01230         wtp_event->u.TR_Abort_Req.handle = handle;
01231 
01232         return wtp_event;
01233 }
01234 
01235 static void send_abort(long reason, long handle) {
01236         WAPEvent *wtp_event;
01237 
01238     wtp_event = make_abort(reason, handle);
01239     dispatch_to_wtp_resp(wtp_event);
01240 }
01241 
01242 static void send_abort_to_initiator(long reason, long handle)
01243 {
01244        WAPEvent *wtp_event;
01245 
01246        wtp_event = make_abort(reason, handle);
01247        dispatch_to_wtp_init(wtp_event);
01248 }
01249 
01250 /*
01251  * The server sends invoke (to be exact, makes TR-Invoke.req) only when it is 
01252  * pushing. (Only the client disconnects sessions.)
01253  */ 
01254 static void send_invoke(WSPMachine *m, WSP_PDU *pdu, WAPEvent *e, long class)
01255 {
01256         WAPEvent *wtp_event;
01257 
01258         wtp_event = wap_event_create(TR_Invoke_Req);
01259         wtp_event->u.TR_Invoke_Req.addr_tuple = 
01260         wap_addr_tuple_duplicate(m->addr_tuple);
01261 /*
01262  * There is no mention of acknowledgement type in the specs. But because 
01263  * confirmed push is confirmed after response from OTA, provider acknowledge-
01264  * ments seem redundant.
01265  */
01266     wtp_event->u.TR_Invoke_Req.up_flag = USER_ACKNOWLEDGEMENT;
01267         wtp_event->u.TR_Invoke_Req.tcl = class;
01268         if (e->type == S_ConfirmedPush_Req)
01269            wtp_event->u.TR_Invoke_Req.handle = 
01270                e->u.S_ConfirmedPush_Req.server_push_id;
01271     wtp_event->u.TR_Invoke_Req.user_data = wsp_pdu_pack(pdu);
01272 
01273         wsp_pdu_destroy(pdu);
01274         dispatch_to_wtp_init(wtp_event);
01275 }
01276 
01277 static void indicate_disconnect(WSPMachine *sm, long reason) {
01278     WAPEvent *new_event;
01279 
01280     new_event = wap_event_create(S_Disconnect_Ind);
01281     new_event->u.S_Disconnect_Ind.reason_code = reason;
01282     new_event->u.S_Disconnect_Ind.redirect_security = 0;
01283     new_event->u.S_Disconnect_Ind.redirect_addresses = 0;
01284     new_event->u.S_Disconnect_Ind.error_headers = NULL;
01285     new_event->u.S_Disconnect_Ind.error_body = NULL;
01286     new_event->u.S_Disconnect_Ind.session_handle = sm->session_id;
01287     dispatch_to_appl(new_event);
01288 }
01289 
01290 
01291 static void indicate_suspend(WSPMachine *sm, long reason) {
01292     WAPEvent *new_event;
01293 
01294     new_event = wap_event_create(S_Suspend_Ind);
01295     new_event->u.S_Suspend_Ind.reason = reason;
01296     new_event->u.S_Suspend_Ind.session_id = sm->session_id;
01297     dispatch_to_appl(new_event);
01298 }
01299 
01300 
01301 static void indicate_resume(WSPMachine *sm,
01302                                 WAPAddrTuple *tuple, List *headers) {
01303     WAPEvent *new_event;
01304 
01305     new_event = wap_event_create(S_Resume_Ind);
01306     new_event->u.S_Resume_Ind.addr_tuple = wap_addr_tuple_duplicate(tuple);
01307     new_event->u.S_Resume_Ind.client_headers = http_header_duplicate(headers);
01308     new_event->u.S_Resume_Ind.session_id = sm->session_id;
01309     dispatch_to_appl(new_event);
01310 }
01311 
01312 static void indicate_pushabort(WSPPushMachine *spm, long reason)
01313 {
01314        WAPEvent *ota_event;
01315  
01316        ota_event = wap_event_create(S_PushAbort_Ind);
01317        ota_event->u.S_PushAbort_Ind.push_id = spm->server_push_id;
01318        ota_event->u.S_PushAbort_Ind.reason = reason;
01319        ota_event->u.S_PushAbort_Ind.session_id = spm->session_id;
01320        dispatch_to_appl(ota_event);
01321 }
01322 
01323 static void confirm_push(WSPPushMachine *m)
01324 {
01325        WAPEvent *ota_event;
01326 
01327        ota_event = wap_event_create(S_ConfirmedPush_Cnf);
01328        ota_event->u.S_ConfirmedPush_Cnf.server_push_id = m->server_push_id;
01329        ota_event->u.S_ConfirmedPush_Cnf.session_id = m->session_id;
01330        dispatch_to_appl(ota_event);
01331 }
01332 
01333 static void method_abort(WSPMethodMachine *msm, long reason) {
01334     WAPEvent *wtp_event;
01335 
01336     /* Send TR-Abort.req(reason) */
01337     wtp_event = wap_event_create(TR_Abort_Req);
01338     /* FIXME: Specs are unclear about this; we may indeed have to
01339      * guess abort whether this is a WSP or WTP level abort code */
01340     if (reason < WSP_ABORT_PROTOERR) {
01341         wtp_event->u.TR_Abort_Req.abort_type = 0x00;
01342     } else {
01343         wtp_event->u.TR_Abort_Req.abort_type = 0x01;
01344     }
01345     wtp_event->u.TR_Abort_Req.abort_reason = reason;
01346     wtp_event->u.TR_Abort_Req.handle = msm->transaction_id;
01347 
01348     dispatch_to_wtp_resp(wtp_event);
01349 }
01350 
01351 
01352 static void indicate_method_abort(WSPMethodMachine *msm, long reason) {
01353     WAPEvent *new_event;
01354 
01355     /* Send S-MethodAbort.ind(reason) */
01356     new_event = wap_event_create(S_MethodAbort_Ind);
01357     new_event->u.S_MethodAbort_Ind.transaction_id = msm->transaction_id;
01358     new_event->u.S_MethodAbort_Ind.reason = reason;
01359     new_event->u.S_MethodAbort_Ind.session_handle = msm->session_id;
01360     dispatch_to_appl(new_event);
01361 }
01362 
01363     
01364 static int method_is_holding(void *item, void *pattern) {
01365     WSPMethodMachine *msm = item;
01366 
01367     return msm->state == HOLDING;
01368 }
01369 
01370 
01371 static void release_holding_methods(WSPMachine *sm) {
01372     WAPEvent *release;
01373     WSPMethodMachine *msm;
01374     List *holding;
01375     long i, len;
01376 
01377     holding = gwlist_search_all(sm->methodmachines, NULL, method_is_holding);
01378     if (holding == NULL)
01379         return;
01380 
01381     /* We can re-use this because wsp_handle_method_event does not
01382      * destroy its event */
01383     release = wap_event_create(Release_Event);
01384 
01385     len = gwlist_len(holding);
01386     for (i = 0; i < len; i++) {
01387         msm = gwlist_get(holding, i);
01388         handle_method_event(sm, msm, release, NULL);
01389     }
01390     gwlist_destroy(holding, NULL);
01391     wap_event_destroy(release);
01392 }
01393 
01394 
01395 static void abort_methods(WSPMachine *sm, long reason) {
01396     WAPEvent *ab;
01397     WSPMethodMachine *msm;
01398     long i, len;
01399 
01400     ab = wap_event_create(Abort_Event);
01401     ab->u.Abort_Event.reason = reason;
01402 
01403     /* This loop goes backward because it has to deal with the
01404      * possibility of method machines disappearing after their event. */
01405     len = gwlist_len(sm->methodmachines);
01406     for (i = len - 1; i >= 0; i--) {
01407         msm = gwlist_get(sm->methodmachines, i);
01408         handle_method_event(sm, msm, ab, NULL);
01409     }
01410 
01411     wap_event_destroy(ab);
01412 }
01413 
01414 
01415 static void abort_pushes(WSPMachine *sm, long reason)
01416 {
01417         WAPEvent *ab;
01418     WSPPushMachine *psm;
01419     long i, len;
01420 
01421         ab = wap_event_create(Abort_Event);
01422     ab->u.Abort_Event.reason = reason;
01423 
01424         len = gwlist_len(sm->pushmachines);
01425     for (i = len - 1; i >= 0; i--) {
01426         psm = gwlist_get(sm->pushmachines, i);
01427         handle_push_event(sm, psm, ab);
01428     }
01429 
01430         wap_event_destroy(ab);
01431 }
01432 
01433 
01434 WSPMachine *find_session_machine_by_id (int id) {
01435 
01436     return gwlist_search(session_machines, &id, id_belongs_to_session);
01437 }
01438 
01439 
01440 static int id_belongs_to_session (void *wsp_ptr, void *pid) {
01441     WSPMachine *wsp;
01442     int *id;
01443 
01444     wsp = wsp_ptr;
01445     id = (int *) pid;
01446 
01447     if (*id == wsp->session_id) return 1;
01448     return 0;
01449 }
01450 
01451 
01452 static int wsp_encoding_string_to_version(Octstr *enc) 
01453 {
01454     int v;
01455     
01456     /* default will be WSP 1.2, as defined by WAPWSP */
01457     v = WSP_1_2;    
01458 
01459     if (octstr_compare(enc, octstr_imm("1.1")) == 0) {
01460         v = WSP_1_1;
01461     }
01462     else if (octstr_compare(enc, octstr_imm("1.2")) == 0) {
01463         v = WSP_1_2;
01464     }
01465     else if (octstr_compare(enc, octstr_imm("1.3")) == 0) {
01466         v = WSP_1_3;
01467     }
01468     else if (octstr_compare(enc, octstr_imm("1.4")) == 0) {
01469         v = WSP_1_4;
01470     }
01471     else if (octstr_compare(enc, octstr_imm("1.5")) == 0) {
01472         v = WSP_1_5;
01473     }
01474 
01475     return v;
01476 }
01477 
01478 static Octstr *wsp_encoding_version_to_string(int version) 
01479 {
01480     Octstr *os;
01481     
01482     switch (version) {
01483         case WSP_1_1:
01484             os = octstr_create("1.1");
01485             break;
01486         case WSP_1_2:
01487             os = octstr_create("1.2");
01488             break;
01489         case WSP_1_3:
01490             os = octstr_create("1.3");
01491             break;
01492         case WSP_1_4:
01493             os = octstr_create("1.4");
01494             break;
01495         case WSP_1_5:
01496             os = octstr_create("1.5");
01497             break;
01498         default:
01499             os = octstr_create("1.2");
01500             break;
01501     }
01502     
01503     return os;
01504 }
01505 
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.