wsp_session.c
资源名称:gateway-1.2.1 [点击查看]
上传用户:gzpyjq
上传日期:2013-01-31
资源大小:1852k
文件大小:37k
源码类别:
手机WAP编程
开发平台:
WINDOWS
- /*
- * wsp_session.c - Implement WSP session oriented service
- *
- * Lars Wirzenius
- */
- #include <string.h>
- #include <limits.h>
- #include "gwlib/gwlib.h"
- #include "wsp.h"
- #include "wsp_pdu.h"
- #include "wsp_headers.h"
- #include "wsp_caps.h"
- #include "wsp_strings.h"
- #include "cookies.h"
- #include "wap.h"
- #include "wtp.h"
- typedef enum {
- #define STATE_NAME(name) name,
- #define ROW(state, event, condition, action, next_state)
- #include "wsp_server_session_states.def"
- #define STATE_NAME(name) name,
- #define ROW(state, event, condition, action, next_state)
- #include "wsp_server_method_states.def"
- #define STATE_NAME(name) name,
- #define ROW(state, event, condition, action, next_state)
- #include "wsp_server_push_states.def"
- WSPState_count
- } WSPState;
- /*
- * Give the status the module:
- *
- * limbo
- * not running at all
- * running
- * operating normally
- * terminating
- * waiting for operations to terminate, returning to limbo
- */
- static enum { limbo, running, terminating } run_status = limbo;
- static wap_dispatch_func_t *dispatch_to_wtp_resp;
- static wap_dispatch_func_t *dispatch_to_wtp_init;
- static wap_dispatch_func_t *dispatch_to_appl;
- static wap_dispatch_func_t *dispatch_to_ota;
- /*
- * True iff "Session resume facility" is enabled. This means we are
- * willing to let sessions go to SUSPENDED state, and later resume them.
- * Currently we always support it, but this may become configurable
- * at some point.
- */
- static int resume_enabled = 1;
- static List *queue = NULL;
- static List *session_machines = NULL;
- static Counter *session_id_counter = NULL;
- static WSPMachine *find_session_machine(WAPEvent *event, WSP_PDU *pdu);
- static void handle_session_event(WSPMachine *machine, WAPEvent *event,
- WSP_PDU *pdu);
- static WSPMachine *machine_create(void);
- static void machine_destroy(void *p);
- static void handle_method_event(WSPMachine *session, WSPMethodMachine *machine, WAPEvent *event, WSP_PDU *pdu);
- static void cant_handle_event(WSPMachine *sm, WAPEvent *event);
- static WSPMethodMachine *method_machine_create(WSPMachine *, long);
- static void method_machine_destroy(void *msm);
- static void handle_push_event(WSPMachine *session, WSPPushMachine *machine,
- WAPEvent *e);
- static WSPPushMachine *push_machine_create(WSPMachine *session, long id);
- static void push_machine_destroy(void *p);
- static char *state_name(WSPState state);
- static unsigned long next_wsp_session_id(void);
- static List *make_capabilities_reply(WSPMachine *m);
- static Octstr *make_connectreply_pdu(WSPMachine *m);
- static Octstr *make_resume_reply_pdu(WSPMachine *m, List *headers);
- static WSP_PDU *make_confirmedpush_pdu(WAPEvent *e);
- static WSP_PDU *make_push_pdu(WAPEvent *e);
- static int transaction_belongs_to_session(void *session, void *tuple);
- static int find_by_session_id(void *session, void *idp);
- static int same_client(void *sm1, void *sm2);
- static WSPMethodMachine *find_method_machine(WSPMachine *, long id);
- static WSPPushMachine *find_push_machine(WSPMachine *m, long id);
- static List *unpack_new_headers(WSPMachine *sm, Octstr *hdrs);
- static void disconnect_other_sessions(WSPMachine *sm);
- static void send_abort(long reason, long handle);
- static void indicate_disconnect(WSPMachine *sm, long reason);
- static void indicate_suspend(WSPMachine *sm, long reason);
- static void indicate_resume(WSPMachine *sm, WAPAddrTuple *tuple,
- List *client_headers);
- static void release_holding_methods(WSPMachine *sm);
- static void abort_methods(WSPMachine *sm, long reason);
- static void abort_pushes(WSPMachine *sm, long reason);
- static void method_abort(WSPMethodMachine *msm, long reason);
- static void indicate_method_abort(WSPMethodMachine *msm, long reason);
- static WAPEvent *make_abort(long reason, long handle);
- static void send_invoke(WSPMachine *session, WSP_PDU *pdu, WAPEvent *e,
- long class);
- static void send_abort_to_initiator(long reason, long handle);
- static void indicate_pushabort(WSPPushMachine *machine, long reason);
- static void confirm_push(WSPPushMachine *machine);
- static void main_thread(void *);
- static int id_belongs_to_session (void *, void *);
- /***********************************************************************
- * Public functions.
- */
- void wsp_session_init(wap_dispatch_func_t *responder_dispatch,
- wap_dispatch_func_t *initiator_dispatch,
- wap_dispatch_func_t *application_dispatch,
- wap_dispatch_func_t *push_ota_dispatch) {
- queue = list_create();
- list_add_producer(queue);
- session_machines = list_create();
- session_id_counter = counter_create();
- dispatch_to_wtp_resp = responder_dispatch;
- dispatch_to_wtp_init = initiator_dispatch;
- dispatch_to_appl = application_dispatch;
- dispatch_to_ota = push_ota_dispatch;
- wsp_strings_init();
- run_status = running;
- gwthread_create(main_thread, NULL);
- }
- void wsp_session_shutdown(void) {
- gw_assert(run_status == running);
- run_status = terminating;
- list_remove_producer(queue);
- gwthread_join_every(main_thread);
- list_destroy(queue, wap_event_destroy_item);
- debug("wap.wsp", 0, "WSP: %ld session machines left.",
- list_len(session_machines));
- list_destroy(session_machines, machine_destroy);
- counter_destroy(session_id_counter);
- wsp_strings_shutdown();
- }
- void wsp_session_dispatch_event(WAPEvent *event) {
- wap_event_assert(event);
- list_produce(queue, event);
- }
- /***********************************************************************
- * Local functions
- */
- static void main_thread(void *arg) {
- WAPEvent *e;
- WSPMachine *sm;
- WSP_PDU *pdu;
- while (run_status == running && (e = list_consume(queue)) != NULL) {
- wap_event_assert(e);
- switch (e->type) {
- case TR_Invoke_Ind:
- pdu = wsp_pdu_unpack(e->u.TR_Invoke_Ind.user_data);
- if (pdu == NULL) {
- warning(0, "WSP: Broken PDU ignored.");
- wap_event_destroy(e);
- continue;
- }
- break;
- default:
- pdu = NULL;
- break;
- }
- sm = find_session_machine(e, pdu);
- if (sm == NULL) {
- wap_event_destroy(e);
- } else {
- handle_session_event(sm, e, pdu);
- }
- wsp_pdu_destroy(pdu);
- }
- }
- static WSPMachine *find_session_machine(WAPEvent *event, WSP_PDU *pdu) {
- WSPMachine *sm;
- long session_id;
- WAPAddrTuple *tuple;
- tuple = NULL;
- session_id = -1;
- switch (event->type) {
- case TR_Invoke_Ind:
- tuple = wap_addr_tuple_duplicate(
- event->u.TR_Invoke_Ind.addr_tuple);
- break;
- case TR_Invoke_Cnf:
- tuple = wap_addr_tuple_duplicate(
- event->u.TR_Invoke_Cnf.addr_tuple);
- break;
- case TR_Result_Cnf:
- tuple = wap_addr_tuple_duplicate(
- event->u.TR_Result_Cnf.addr_tuple);
- break;
- case TR_Abort_Ind:
- tuple = wap_addr_tuple_duplicate(
- event->u.TR_Abort_Ind.addr_tuple);
- break;
- case S_Connect_Res:
- session_id = event->u.S_Connect_Res.session_id;
- break;
- case S_Resume_Res:
- session_id = event->u.S_Resume_Res.session_id;
- break;
- case Disconnect_Event:
- session_id = event->u.Disconnect_Event.session_handle;
- break;
- case Suspend_Event:
- session_id = event->u.Suspend_Event.session_handle;
- break;
- case S_MethodInvoke_Res:
- session_id = event->u.S_MethodInvoke_Res.session_id;
- break;
- case S_MethodResult_Req:
- session_id = event->u.S_MethodResult_Req.session_id;
- break;
- case S_ConfirmedPush_Req:
- session_id = event->u.S_ConfirmedPush_Req.session_id;
- break;
- case S_Push_Req:
- session_id = event->u.S_Push_Req.session_id;
- break;
- default:
- error(0, "WSP: Cannot find machine for %s event",
- wap_event_name(event->type));
- }
- gw_assert(tuple != NULL || session_id != -1);
- /* Pre-state-machine tests, according to 7.1.5. After the tests,
- * caller will pass the event to sm if sm is not NULL. */
- sm = NULL;
- /* First test is for MRUEXCEEDED, and we don't have a MRU */
- /* Second test is for class 2 TR-Invoke.ind with Connect PDU */
- if (event->type == TR_Invoke_Ind &&
- event->u.TR_Invoke_Ind.tcl == 2 &&
- pdu->type == Connect) {
- /* Create a new session, even if there is already
- * a session open for this address. The new session
- * will take care of killing the old ones. */
- sm = machine_create();
- gw_assert(tuple != NULL);
- sm->addr_tuple = wap_addr_tuple_duplicate(tuple);
- sm->connect_handle = event->u.TR_Invoke_Ind.handle;
- /* Third test is for class 2 TR-Invoke.ind with Resume PDU */
- } else if (event->type == TR_Invoke_Ind &&
- event->u.TR_Invoke_Ind.tcl == 2 &&
- pdu->type == Resume) {
- /* Pass to session identified by session id, not
- * the address tuple. */
- session_id = pdu->u.Resume.sessionid;
- sm = list_search(session_machines, &session_id,
- find_by_session_id);
- if (sm == NULL) {
- /* No session; TR-Abort.req(DISCONNECT) */
- send_abort(WSP_ABORT_DISCONNECT,
- event->u.TR_Invoke_Ind.handle);
- }
- /* Fourth test is for a class 1 or 2 TR-Invoke.Ind with no
- * session for that address tuple. We also handle class 0
- * TR-Invoke.ind here by ignoring them; this seems to be
- * an omission in the spec table. */
- } else if (event->type == TR_Invoke_Ind) {
- sm = list_search(session_machines, tuple,
- transaction_belongs_to_session);
- if (sm == NULL && (event->u.TR_Invoke_Ind.tcl == 1 ||
- event->u.TR_Invoke_Ind.tcl == 2)) {
- send_abort(WSP_ABORT_DISCONNECT,
- event->u.TR_Invoke_Ind.handle);
- }
- /* Other tests are for events not handled by the state tables;
- * do those later, after we've tried to handle them. */
- } else {
- if (session_id != -1) {
- sm = list_search(session_machines, &session_id,
- find_by_session_id);
- } else {
- sm = list_search(session_machines, tuple,
- transaction_belongs_to_session);
- }
- /* The table doesn't really say what we should do with
- * non-Invoke events for which there is no session. But
- * such a situation means there is an error _somewhere_
- * in the gateway. */
- if (sm == NULL) {
- error(0, "WSP: Cannot find session machine for event.");
- wap_event_dump(event);
- }
- }
- wap_addr_tuple_destroy(tuple);
- return sm;
- }
- static void handle_session_event(WSPMachine *sm, WAPEvent *current_event,
- WSP_PDU *pdu) {
- debug("wap.wsp", 0, "WSP: machine %p, state %s, event %s",
- (void *) sm,
- state_name(sm->state),
- wap_event_name(current_event->type));
- #define STATE_NAME(name)
- #define ROW(state_name, event, condition, action, next_state)
- {
- struct event *e;
- e = ¤t_event->u.event;
- if (sm->state == state_name &&
- current_event->type == event &&
- (condition)) {
- action
- sm->state = next_state;
- debug("wap.wsp", 0, "WSP %ld: New state %s",
- sm->session_id, #next_state);
- goto end;
- }
- }
- #include "wsp_server_session_states.def"
- cant_handle_event(sm, current_event);
- end:
- wap_event_destroy(current_event);
- if (sm->state == NULL_SESSION)
- machine_destroy(sm);
- }
- static void cant_handle_event(WSPMachine *sm, WAPEvent *event) {
- /* We do the rest of the pre-state-machine tests here. The first
- * four were done in find_session_machine(). The fifth is a
- * class 1 or 2 TR-Invoke.ind not handled by the state tables. */
- if (event->type == TR_Invoke_Ind &&
- (event->u.TR_Invoke_Ind.tcl == 1 ||
- event->u.TR_Invoke_Ind.tcl == 2)) {
- warning(0, "WSP: Can't handle TR-Invoke.ind, aborting transaction.");
- debug("wap.wsp", 0, "WSP: The unhandled event:");
- wap_event_dump(event);
- send_abort(WSP_ABORT_PROTOERR,
- event->u.TR_Invoke_Ind.handle);
- /* The sixth is a class 0 TR-Invoke.ind not handled by state tables. */
- } else if (event->type == TR_Invoke_Ind) {
- warning(0, "WSP: Can't handle TR-Invoke.ind, ignoring.");
- debug("wap.wsp", 0, "WSP: The ignored event:");
- wap_event_dump(event);
- /* The seventh is any other event not handled by state tables. */
- } else {
- error(0, "WSP: Can't handle event. Aborting session.");
- debug("wap.wsp", 0, "WSP: The unhandled event:");
- wap_event_dump(event);
- /* TR-Abort.req(PROTOERR) if it is some other transaction
- * event than abort. */
- /* Currently that means TR-Result.cnf, because we already
- * tested for Invoke. */
- /* FIXME We need a better way to get at event values than
- * by hardcoding the types. */
- if (event->type == TR_Result_Cnf) {
- send_abort(WSP_ABORT_PROTOERR,
- event->u.TR_Result_Cnf.handle);
- }
- /* Abort(PROTOERR) all method and push transactions */
- abort_methods(sm, WSP_ABORT_PROTOERR);
- abort_pushes(sm, WSP_ABORT_PROTOERR);
- /* S-Disconnect.ind(PROTOERR) */
- indicate_disconnect(sm, WSP_ABORT_PROTOERR);
- }
- }
- static WSPMachine *machine_create(void) {
- WSPMachine *p;
- p = gw_malloc(sizeof(WSPMachine));
- debug("wap.wsp", 0, "WSP: Created WSPMachine %p", (void *) p);
- #define INTEGER(name) p->name = 0;
- #define OCTSTR(name) p->name = NULL;
- #define HTTPHEADERS(name) p->name = NULL;
- #define ADDRTUPLE(name) p->name = NULL;
- #define MACHINESLIST(name) p->name = list_create();
- #define CAPABILITIES(name) p->name = NULL;
- #define COOKIES(name) p->name = NULL;
- #define REFERER(name) p->name = NULL;
- #define MACHINE(fields) fields
- #include "wsp_server_session_machine.def"
- p->state = NULL_SESSION;
- /* set capabilities to default values (defined in 1.1) */
- p->client_SDU_size = 1400;
- p->MOR_push = 1;
- /* Insert new machine at the _front_, because 1) it's more likely
- * to get events than old machines are, so this speeds up the linear
- * search, and 2) we want the newest machine to get any method
- * invokes that come through before the Connect is established. */
- list_insert(session_machines, 0, p);
- return p;
- }
- static void destroy_methodmachines(List *machines) {
- if (list_len(machines) > 0) {
- warning(0, "Destroying WSP session with %ld active methodsn",
- list_len(machines));
- }
- list_destroy(machines, method_machine_destroy);
- }
- static void destroy_pushmachines(List *machines) {
- if (list_len(machines) > 0) {
- warning(0, "Destroying WSP session with %ld active pushesn",
- list_len(machines));
- }
- list_destroy(machines, push_machine_destroy);
- }
- static void machine_destroy(void *pp) {
- WSPMachine *p;
- p = pp;
- debug("wap.wsp", 0, "Destroying WSPMachine %p", pp);
- list_delete_equal(session_machines, p);
- #define INTEGER(name) p->name = 0;
- #define OCTSTR(name) octstr_destroy(p->name);
- #define HTTPHEADERS(name) http_destroy_headers(p->name);
- #define ADDRTUPLE(name) wap_addr_tuple_destroy(p->name);
- #define MACHINESLIST(name) destroy_##name(p->name);
- #define CAPABILITIES(name) wsp_cap_destroy_list(p->name);
- #define COOKIES(name) cookies_destroy(p->name);
- #define REFERER(name) octstr_destroy(p->name);
- #define MACHINE(fields) fields
- #include "wsp_server_session_machine.def"
- gw_free(p);
- }
- struct msm_pattern {
- WAPAddrTuple *addr_tuple;
- long msmid, tid;
- };
- /* This function does NOT consume its event; it leaves that task up
- * to the parent session */
- static void handle_method_event(WSPMachine *sm, WSPMethodMachine *msm,
- WAPEvent *current_event, WSP_PDU *pdu) {
- if (msm == NULL) {
- warning(0, "No method machine for event.");
- wap_event_dump(current_event);
- return;
- }
- debug("wap.wsp", 0, "WSP: method %ld, state %s, event %s",
- msm->transaction_id, state_name(msm->state),
- wap_event_name(current_event->type));
- gw_assert(sm->session_id == msm->session_id);
- #define STATE_NAME(name)
- #define ROW(state_name, event, condition, action, next_state)
- {
- struct event *e;
- e = ¤t_event->u.event;
- if (msm->state == state_name &&
- current_event->type == event &&
- (condition)) {
- action
- msm->state = next_state;
- debug("wap.wsp", 0, "WSP %ld/%ld: New method state %s",
- msm->session_id, msm->transaction_id, #next_state);
- goto end;
- }
- }
- #include "wsp_server_method_states.def"
- cant_handle_event(sm, current_event);
- end:
- if (msm->state == NULL_METHOD) {
- method_machine_destroy(msm);
- list_delete_equal(sm->methodmachines, msm);
- }
- }
- static WSPMethodMachine *method_machine_create(WSPMachine *sm,
- long wtp_handle) {
- WSPMethodMachine *msm;
- msm = gw_malloc(sizeof(*msm));
- #define INTEGER(name) msm->name = 0;
- #define ADDRTUPLE(name) msm->name = NULL;
- #define EVENT(name) msm->name = NULL;
- #define MACHINE(fields) fields
- #include "wsp_server_method_machine.def"
- msm->transaction_id = wtp_handle;
- msm->state = NULL_METHOD;
- msm->addr_tuple = wap_addr_tuple_duplicate(sm->addr_tuple);
- msm->session_id = sm->session_id;
- list_append(sm->methodmachines, msm);
- return msm;
- }
- static void method_machine_destroy(void *p) {
- WSPMethodMachine *msm;
- if (p == NULL)
- return;
- msm = p;
- debug("wap.wsp", 0, "Destroying WSPMethodMachine %ld",
- msm->transaction_id);
- #define INTEGER(name)
- #define ADDRTUPLE(name) wap_addr_tuple_destroy(msm->name);
- #define EVENT(name) wap_event_destroy(msm->name);
- #define MACHINE(fields) fields
- #include "wsp_server_method_machine.def"
- gw_free(msm);
- }
- static void handle_push_event(WSPMachine *sm, WSPPushMachine *pm,
- WAPEvent *current_event)
- {
- if (pm == NULL) {
- warning(0, "No push machine for event.");
- wap_event_dump(current_event);
- return;
- }
- debug("wap.wsp", 0, "WSP(tid/pid): push %ld/%ld, state %s, event %s",
- pm->transaction_id, pm->server_push_id, state_name(pm->state),
- wap_event_name(current_event->type));
- gw_assert(sm->session_id == pm->session_id);
- #define STATE_NAME(name)
- #define ROW(state_name, event, condition, action, next_state)
- {
- if (pm->state == state_name &&
- current_event->type == event &&
- (condition)) {
- action
- pm->state = next_state;
- debug("wap.wsp", 0, "WSP %ld/%ld: New push state %s",
- pm->session_id, pm->transaction_id, #next_state);
- goto end;
- }
- }
- #include "wsp_server_push_states.def"
- cant_handle_event(sm, current_event);
- end:
- if (pm->state == SERVER_PUSH_NULL_STATE) {
- push_machine_destroy(pm);
- list_delete_equal(sm->pushmachines, pm);
- }
- }
- static WSPPushMachine *push_machine_create(WSPMachine *sm,
- long pid)
- {
- WSPPushMachine *m;
- m = gw_malloc(sizeof(WSPPushMachine));
- #define INTEGER(name) m->name = 0;
- #define ADDRTUPLE(name) m->name = NULL;
- #define HTTPHEADER(name) m->name = http_create_empty_headers();
- #define MACHINE(fields) fields
- #include "wsp_server_push_machine.def"
- m->server_push_id = pid;
- m->transaction_id = pid;
- m->state = SERVER_PUSH_NULL_STATE;
- m->addr_tuple = wap_addr_tuple_duplicate(sm->addr_tuple);
- m->session_id = sm->session_id;
- list_append(sm->pushmachines, m);
- return m;
- }
- static void push_machine_destroy(void *p)
- {
- WSPPushMachine *m = NULL;
- if (p == NULL)
- return;
- m = p;
- debug("wap.wsp", 0, "Destroying WSPPushMachine %ld",
- m->transaction_id);
- #define INTEGER(name)
- #define ADDRTUPLE(name) wap_addr_tuple_destroy(m->name);
- #define HTTPHEADER(name) http_destroy_headers(m->name);
- #define MACHINE(fields) fields
- #include "wsp_server_push_machine.def"
- gw_free(m);
- }
- static char *state_name(WSPState state) {
- switch (state) {
- #define STATE_NAME(name) case name: return #name;
- #define ROW(state, event, cond, stmt, next_state)
- #include "wsp_server_session_states.def"
- #define STATE_NAME(name) case name: return #name;
- #define ROW(state, event, cond, stmt, next_state)
- #include "wsp_server_method_states.def"
- #define STATE_NAME(name) case name: return #name;
- #define ROW(state, event, cond, stmt, next_state)
- #include "wsp_server_push_states.def"
- default:
- return "unknown wsp state";
- }
- }
- static unsigned long next_wsp_session_id(void) {
- return counter_increase(session_id_counter);
- }
- static void sanitize_capabilities(List *caps, WSPMachine *m) {
- long i;
- Capability *cap;
- unsigned long ui;
- for (i = 0; i < list_len(caps); i++) {
- cap = list_get(caps, i);
- /* We only know numbered capabilities. Let the application
- * layer negotiate whatever it wants for unknown ones. */
- if (cap->name != NULL)
- continue;
- switch (cap->id) {
- case WSP_CAPS_CLIENT_SDU_SIZE:
- /* Check if it's a valid uintvar. The value is the
- * max SDU size we will send, and there's no
- * internal limit to that, so accept any value. */
- if (cap->data != NULL &&
- octstr_extract_uintvar(cap->data, &ui, 0) < 0)
- goto bad_cap;
- else
- m->client_SDU_size = ui;
- break;
- case WSP_CAPS_SERVER_SDU_SIZE:
- /* Check if it's a valid uintvar */
- if (cap->data != NULL &&
- (octstr_extract_uintvar(cap->data, &ui, 0) < 0))
- goto bad_cap;
- /* XXX Our MRU is not quite unlimited, since we
- * use signed longs in the library functions --
- * should we make sure we limit the reply value
- * to LONG_MAX? (That's already a 2GB packet) */
- break;
- case WSP_CAPS_PROTOCOL_OPTIONS:
- /* Currently we don't support any Push, nor
- * session resume, nor acknowledgement headers,
- * so make sure those bits are not set. */
- if (cap->data != NULL && octstr_len(cap->data) > 0
- && (octstr_get_char(cap->data, 0) & 0xf0) != 0) {
- warning(0, "WSP: Application layer tried to "
- "negotiate protocol options.");
- octstr_set_bits(cap->data, 0, 4, 0);
- }
- break;
- case WSP_CAPS_EXTENDED_METHODS:
- /* XXX Check format here */
- break;
- case WSP_CAPS_HEADER_CODE_PAGES:
- /* We don't support any yet, so don't let this
- * be negotiated. */
- if (cap->data)
- goto bad_cap;
- break;
- }
- continue;
- bad_cap:
- error(0, "WSP: Found illegal value in capabilities reply.");
- wsp_cap_dump(cap);
- list_delete(caps, i, 1);
- i--;
- wsp_cap_destroy(cap);
- continue;
- }
- }
- static void reply_known_capabilities(List *caps, List *req, WSPMachine *m) {
- unsigned long ui;
- Capability *cap;
- Octstr *data;
- if (wsp_cap_count(caps, WSP_CAPS_CLIENT_SDU_SIZE, NULL) == 0) {
- if (wsp_cap_get_client_sdu(req, &ui) > 0) {
- /* Accept value if it is not silly. */
- if ((ui >= 256 && ui < LONG_MAX) || ui == 0) {
- m->client_SDU_size = ui;
- }
- }
- /* Reply with the client SDU we decided on */
- data = octstr_create("");
- octstr_append_uintvar(data, m->client_SDU_size);
- cap = wsp_cap_create(WSP_CAPS_CLIENT_SDU_SIZE,
- NULL, data);
- list_append(caps, cap);
- }
- if (wsp_cap_count(caps, WSP_CAPS_SERVER_SDU_SIZE, NULL) == 0) {
- /* Accept whatever size the client is willing
- * to send. If the client did not specify anything,
- * then use the default. */
- if (wsp_cap_get_server_sdu(req, &ui) <= 0) {
- ui = 1400;
- }
- data = octstr_create("");
- octstr_append_uintvar(data, ui);
- cap = wsp_cap_create(WSP_CAPS_SERVER_SDU_SIZE, NULL, data);
- list_append(caps, cap);
- }
- /* Currently we cannot handle any protocol options */
- if (wsp_cap_count(caps, WSP_CAPS_PROTOCOL_OPTIONS, NULL) == 0) {
- data = octstr_create("");
- octstr_append_char(data, 0);
- cap = wsp_cap_create(WSP_CAPS_PROTOCOL_OPTIONS, NULL, data);
- list_append(caps, cap);
- }
- /* Accept any Method-MOR the client sent; if it sent none,
- * use the default. */
- if (wsp_cap_count(caps, WSP_CAPS_METHOD_MOR, NULL) == 0) {
- if (wsp_cap_get_method_mor(req, &ui) <= 0) {
- ui = 1;
- }
- data = octstr_create("");
- octstr_append_char(data, ui);
- cap = wsp_cap_create(WSP_CAPS_METHOD_MOR, NULL, data);
- list_append(caps, cap);
- }
- /* We will never send any Push requests because we don't support
- * that yet. But we already specified that in protocol options;
- * so, pretend we do, and handle the value that way. */
- if (wsp_cap_count(caps, WSP_CAPS_PUSH_MOR, NULL) == 0) {
- if (wsp_cap_get_push_mor(req, &ui) > 0) {
- m->MOR_push = ui;
- }
- data = octstr_create("");
- octstr_append_char(data, m->MOR_push);
- cap = wsp_cap_create(WSP_CAPS_PUSH_MOR, NULL, data);
- list_append(caps, cap);
- }
- /* Supporting extended methods is up to the application layer,
- * not up to us. If the application layer didn't specify any,
- * then we refuse whatever the client requested. The default
- * is to support none, so we don't really have to add anything here. */
- /* We do not support any header code pages. sanitize_capabilities
- * must have already deleted any reply that indicates otherwise.
- * Again, not adding anything here is the same as refusing support. */
- /* Listing aliases is something the application layer can do if
- * it wants to. We don't care. */
- }
- /* Generate a refusal for all requested capabilities that are not
- * replied to. */
- static void refuse_unreplied_capabilities(List *caps, List *req) {
- long i, len;
- Capability *cap;
- len = list_len(req);
- for (i = 0; i < len; i++) {
- cap = list_get(req, i);
- if (wsp_cap_count(caps, cap->id, cap->name) == 0) {
- cap = wsp_cap_create(cap->id, cap->name, NULL);
- list_append(caps, cap);
- }
- }
- }
- static int is_default_cap(Capability *cap) {
- unsigned long ui;
- /* All unknown values are empty by default */
- if (cap->name != NULL || cap->id < 0 || cap->id >= WSP_NUM_CAPS)
- return cap->data == NULL || octstr_len(cap->data) == 0;
- switch (cap->id) {
- case WSP_CAPS_CLIENT_SDU_SIZE:
- case WSP_CAPS_SERVER_SDU_SIZE:
- return (cap->data != NULL &&
- octstr_extract_uintvar(cap->data, &ui, 0) >= 0 &&
- ui == 1400);
- case WSP_CAPS_PROTOCOL_OPTIONS:
- return cap->data != NULL && octstr_get_char(cap->data, 0) == 0;
- case WSP_CAPS_METHOD_MOR:
- case WSP_CAPS_PUSH_MOR:
- return cap->data != NULL && octstr_get_char(cap->data, 0) == 1;
- case WSP_CAPS_EXTENDED_METHODS:
- case WSP_CAPS_HEADER_CODE_PAGES:
- case WSP_CAPS_ALIASES:
- return cap->data == NULL || octstr_len(cap->data) == 0;
- default:
- return 0;
- }
- }
- /* Remove any replies that have no corresponding request and that
- * are equal to the default. */
- static void strip_default_capabilities(List *caps, List *req) {
- long i;
- Capability *cap;
- int count;
- /* Hmm, this is an O(N*N) operation, which may be bad. */
- i = 0;
- while (i < list_len(caps)) {
- cap = list_get(caps, i);
- count = wsp_cap_count(req, cap->id, cap->name);
- if (count == 0 && is_default_cap(cap)) {
- list_delete(caps, i, 1);
- wsp_cap_destroy(cap);
- } else {
- i++;
- }
- }
- }
- static List *make_capabilities_reply(WSPMachine *m) {
- List *caps;
- /* In principle, copy the application layer's capabilities
- * response, add refusals for all unknown requested capabilities,
- * and add responses for all known capabilities that are
- * not already responded to. Then eliminate any replies that
- * would have no effect because they are equal to the default. */
- caps = wsp_cap_duplicate_list(m->reply_caps);
- /* Don't let the application layer negotiate anything we
- * cannot handle. Also parse the values it set if we're
- * interested. */
- sanitize_capabilities(caps, m);
- /* Add capability records for all capabilities we know about
- * that are not already in the reply list. */
- reply_known_capabilities(caps, m->request_caps, m);
- /* All remaining capabilities in the request list that are
- * not in the reply list at this point must be unknown ones
- * that we want to refuse. */
- refuse_unreplied_capabilities(caps, m->request_caps);
- /* Now eliminate replies that would be equal to the requested
- * value, or (if there was none) to the default value. */
- strip_default_capabilities(caps, m->request_caps);
- return caps;
- }
- static Octstr *make_connectreply_pdu(WSPMachine *m) {
- WSP_PDU *pdu;
- Octstr *os;
- List *caps;
- pdu = wsp_pdu_create(ConnectReply);
- pdu->u.ConnectReply.sessionid = m->session_id;
- caps = make_capabilities_reply(m);
- pdu->u.ConnectReply.capabilities = wsp_cap_pack_list(caps);
- wsp_cap_destroy_list(caps);
- pdu->u.ConnectReply.headers = NULL;
- os = wsp_pdu_pack(pdu);
- wsp_pdu_destroy(pdu);
- return os;
- }
- static Octstr *make_resume_reply_pdu(WSPMachine *m, List *headers) {
- WSP_PDU *pdu;
- Octstr *os;
- pdu = wsp_pdu_create(Reply);
- /* Not specified for Resume replies */
- pdu->u.Reply.status = wsp_convert_http_status_to_wsp_status(HTTP_OK);
- if (headers == NULL) {
- headers = http_create_empty_headers();
- pdu->u.Reply.headers = wsp_headers_pack(headers, 1);
- http_destroy_headers(headers);
- } else {
- pdu->u.Reply.headers = wsp_headers_pack(headers, 1);
- }
- pdu->u.Reply.data = octstr_create("");
- os = wsp_pdu_pack(pdu);
- wsp_pdu_destroy(pdu);
- return os;
- }
- static WSP_PDU *make_confirmedpush_pdu(WAPEvent *e)
- {
- WSP_PDU *pdu;
- List *headers;
- pdu = wsp_pdu_create(ConfirmedPush);
- /*
- * Both push headers and push body are optional.
- */
- if (e->u.S_ConfirmedPush_Req.push_headers == NULL) {
- headers = http_create_empty_headers();
- pdu->u.ConfirmedPush.headers = wsp_headers_pack(headers, 1);
- http_destroy_headers(headers);
- } else
- pdu->u.ConfirmedPush.headers =
- wsp_headers_pack(e->u.S_ConfirmedPush_Req.push_headers, 1);
- if (e->u.S_ConfirmedPush_Req.push_body == NULL)
- pdu->u.ConfirmedPush.data = octstr_create("");
- else
- pdu->u.ConfirmedPush.data =
- octstr_duplicate(e->u.S_ConfirmedPush_Req.push_body);
- return pdu;
- }
- static WSP_PDU *make_push_pdu(WAPEvent *e)
- {
- WSP_PDU *pdu;
- List *headers;
- pdu = wsp_pdu_create(Push);
- /*
- * Both push headers and push body are optional
- */
- if (e->u.S_Push_Req.push_headers == NULL) {
- headers = http_create_empty_headers();
- pdu->u.Push.headers = wsp_headers_pack(headers, 1);
- http_destroy_headers(headers);
- } else
- pdu->u.Push.headers =
- wsp_headers_pack(e->u.S_Push_Req.push_headers, 1);
- if (e->u.S_Push_Req.push_body == NULL)
- pdu->u.Push.data = octstr_create("");
- else
- pdu->u.Push.data =
- octstr_duplicate(e->u.S_Push_Req.push_body);
- return pdu;
- }
- static int transaction_belongs_to_session(void *wsp_ptr, void *tuple_ptr) {
- WSPMachine *wsp;
- WAPAddrTuple *tuple;
- wsp = wsp_ptr;
- tuple = tuple_ptr;
- return wap_addr_tuple_same(wsp->addr_tuple, tuple);
- }
- static int find_by_session_id(void *wsp_ptr, void *id_ptr) {
- WSPMachine *wsp = wsp_ptr;
- long *idp = id_ptr;
- return wsp->session_id == *idp;
- }
- static int find_by_method_id(void *wspm_ptr, void *id_ptr) {
- WSPMethodMachine *msm = wspm_ptr;
- long *idp = id_ptr;
- return msm->transaction_id == *idp;
- }
- static int find_by_push_id(void *m_ptr, void *id_ptr) {
- WSPPushMachine *m = m_ptr;
- long *idp = id_ptr;
- return m->transaction_id == *idp;
- }
- static WSPMethodMachine *find_method_machine(WSPMachine *sm, long id) {
- return list_search(sm->methodmachines, &id, find_by_method_id);
- }
- static WSPPushMachine *find_push_machine(WSPMachine *m, long id)
- {
- return list_search(m->pushmachines, &id, find_by_push_id);
- }
- static int same_client(void *a, void *b) {
- WSPMachine *sm1, *sm2;
- sm1 = a;
- sm2 = b;
- return wap_addr_tuple_same(sm1->addr_tuple, sm2->addr_tuple);
- }
- static void disconnect_other_sessions(WSPMachine *sm) {
- List *old_sessions;
- WAPEvent *disconnect;
- WSPMachine *sm2;
- long i;
- old_sessions = list_search_all(session_machines, sm, same_client);
- if (old_sessions == NULL)
- return;
- for (i = 0; i < list_len(old_sessions); i++) {
- sm2 = list_get(old_sessions, i);
- if (sm2 != sm) {
- disconnect = wap_event_create(Disconnect_Event);
- handle_session_event(sm2, disconnect, NULL);
- }
- }
- list_destroy(old_sessions, NULL);
- }
- static List *unpack_new_headers(WSPMachine *sm, Octstr *hdrs) {
- List *new_headers;
- if (hdrs && octstr_len(hdrs) > 0) {
- new_headers = wsp_headers_unpack(hdrs, 0);
- if (sm->http_headers == NULL)
- sm->http_headers = http_create_empty_headers();
- http_header_combine(sm->http_headers, new_headers);
- return new_headers;
- }
- return NULL;
- }
- static WAPEvent *make_abort(long reason, long handle)
- {
- WAPEvent *wtp_event;
- wtp_event = wap_event_create(TR_Abort_Req);
- wtp_event->u.TR_Abort_Req.abort_type = 0x01;
- wtp_event->u.TR_Abort_Req.abort_reason = reason;
- wtp_event->u.TR_Abort_Req.handle = handle;
- return wtp_event;
- }
- static void send_abort(long reason, long handle) {
- WAPEvent *wtp_event;
- wtp_event = make_abort(reason, handle);
- dispatch_to_wtp_resp(wtp_event);
- }
- static void send_abort_to_initiator(long reason, long handle)
- {
- WAPEvent *wtp_event;
- wtp_event = make_abort(reason, handle);
- dispatch_to_wtp_init(wtp_event);
- }
- /*
- * The server sends invoke (to be exact, makes TR-Invoke.req) only when it is
- * pushing. (Only the client disconnects sessions.)
- */
- static void send_invoke(WSPMachine *m, WSP_PDU *pdu, WAPEvent *e, long class)
- {
- WAPEvent *wtp_event;
- wtp_event = wap_event_create(TR_Invoke_Req);
- wtp_event->u.TR_Invoke_Req.addr_tuple =
- wap_addr_tuple_duplicate(m->addr_tuple);
- /*
- * There is no mention of acknowledgement type in the specs.燘ut because
- * confirmed push is confirmed after response from OTA, provider acknowledge-
- * ments seem redundant.
- */
- wtp_event->u.TR_Invoke_Req.up_flag = USER_ACKNOWLEDGEMENT;
- wtp_event->u.TR_Invoke_Req.tcl = class;
- if (e->type == S_ConfirmedPush_Req)
- wtp_event->u.TR_Invoke_Req.handle =
- e->u.S_ConfirmedPush_Req.server_push_id;
- wtp_event->u.TR_Invoke_Req.user_data = wsp_pdu_pack(pdu);
- wsp_pdu_destroy(pdu);
- dispatch_to_wtp_init(wtp_event);
- }
- static void indicate_disconnect(WSPMachine *sm, long reason) {
- WAPEvent *new_event;
- new_event = wap_event_create(S_Disconnect_Ind);
- new_event->u.S_Disconnect_Ind.reason_code = reason;
- new_event->u.S_Disconnect_Ind.redirect_security = 0;
- new_event->u.S_Disconnect_Ind.redirect_addresses = 0;
- new_event->u.S_Disconnect_Ind.error_headers = NULL;
- new_event->u.S_Disconnect_Ind.error_body = NULL;
- new_event->u.S_Disconnect_Ind.session_handle = sm->session_id;
- dispatch_to_appl(new_event);
- }
- static void indicate_suspend(WSPMachine *sm, long reason) {
- WAPEvent *new_event;
- new_event = wap_event_create(S_Suspend_Ind);
- new_event->u.S_Suspend_Ind.reason = reason;
- new_event->u.S_Suspend_Ind.session_id = sm->session_id;
- dispatch_to_appl(new_event);
- }
- static void indicate_resume(WSPMachine *sm,
- WAPAddrTuple *tuple, List *headers) {
- WAPEvent *new_event;
- new_event = wap_event_create(S_Resume_Ind);
- new_event->u.S_Resume_Ind.addr_tuple = wap_addr_tuple_duplicate(tuple);
- new_event->u.S_Resume_Ind.client_headers = http_header_duplicate(headers);
- new_event->u.S_Resume_Ind.session_id = sm->session_id;
- dispatch_to_appl(new_event);
- }
- static void indicate_pushabort(WSPPushMachine *spm, long reason)
- {
- WAPEvent *ota_event;
- ota_event = wap_event_create(S_PushAbort_Ind);
- ota_event->u.S_PushAbort_Ind.push_id = spm->server_push_id;
- ota_event->u.S_PushAbort_Ind.reason = reason;
- ota_event->u.S_PushAbort_Ind.session_id = spm->session_id;
- dispatch_to_appl(ota_event);
- }
- static void confirm_push(WSPPushMachine *m)
- {
- WAPEvent *ota_event;
- ota_event = wap_event_create(S_ConfirmedPush_Cnf);
- ota_event->u.S_ConfirmedPush_Cnf.server_push_id = m->server_push_id;
- ota_event->u.S_ConfirmedPush_Cnf.session_id = m->session_id;
- dispatch_to_appl(ota_event);
- }
- static void method_abort(WSPMethodMachine *msm, long reason) {
- WAPEvent *wtp_event;
- /* Send TR-Abort.req(reason) */
- wtp_event = wap_event_create(TR_Abort_Req);
- /* FIXME: Specs are unclear about this; we may indeed have to
- * guess abort whether this is a WSP or WTP level abort code */
- if (reason < WSP_ABORT_PROTOERR) {
- wtp_event->u.TR_Abort_Req.abort_type = 0x00;
- } else {
- wtp_event->u.TR_Abort_Req.abort_type = 0x01;
- }
- wtp_event->u.TR_Abort_Req.abort_reason = reason;
- wtp_event->u.TR_Abort_Req.handle = msm->transaction_id;
- dispatch_to_wtp_resp(wtp_event);
- }
- static void indicate_method_abort(WSPMethodMachine *msm, long reason) {
- WAPEvent *new_event;
- /* Send S-MethodAbort.ind(reason) */
- new_event = wap_event_create(S_MethodAbort_Ind);
- new_event->u.S_MethodAbort_Ind.transaction_id = msm->transaction_id;
- new_event->u.S_MethodAbort_Ind.reason = reason;
- new_event->u.S_MethodAbort_Ind.session_handle = msm->session_id;
- dispatch_to_appl(new_event);
- }
- static int method_is_holding(void *item, void *pattern) {
- WSPMethodMachine *msm = item;
- return msm->state == HOLDING;
- }
- static void release_holding_methods(WSPMachine *sm) {
- WAPEvent *release;
- WSPMethodMachine *msm;
- List *holding;
- long i, len;
- holding = list_search_all(sm->methodmachines, NULL, method_is_holding);
- if (holding == NULL)
- return;
- /* We can re-use this because wsp_handle_method_event does not
- * destroy its event */
- release = wap_event_create(Release_Event);
- len = list_len(holding);
- for (i = 0; i < len; i++) {
- msm = list_get(holding, i);
- handle_method_event(sm, msm, release, NULL);
- }
- list_destroy(holding, NULL);
- wap_event_destroy(release);
- }
- static void abort_methods(WSPMachine *sm, long reason) {
- WAPEvent *ab;
- WSPMethodMachine *msm;
- long i, len;
- ab = wap_event_create(Abort_Event);
- ab->u.Abort_Event.reason = reason;
- /* This loop goes backward because it has to deal with the
- * possibility of method machines disappearing after their event. */
- len = list_len(sm->methodmachines);
- for (i = len - 1; i >= 0; i--) {
- msm = list_get(sm->methodmachines, i);
- handle_method_event(sm, msm, ab, NULL);
- }
- wap_event_destroy(ab);
- }
- static void abort_pushes(WSPMachine *sm, long reason)
- {
- WAPEvent *ab;
- WSPPushMachine *psm;
- long i, len;
- ab = wap_event_create(Abort_Event);
- ab->u.Abort_Event.reason = reason;
- len = list_len(sm->pushmachines);
- for (i = len - 1; i >= 0; i--) {
- psm = list_get(sm->pushmachines, i);
- handle_push_event(sm, psm, ab);
- }
- wap_event_destroy(ab);
- }
- WSPMachine *find_session_machine_by_id (int id) {
- return list_search(session_machines, &id, id_belongs_to_session);
- }
- static int id_belongs_to_session (void *wsp_ptr, void *pid) {
- WSPMachine *wsp;
- int *id;
- wsp = wsp_ptr;
- id = (int *) pid;
- if (*id == wsp->session_id) return 1;
- return 0;
- }