wsp_session.c
上传用户:gzpyjq
上传日期:2013-01-31
资源大小:1852k
文件大小:37k
源码类别:

手机WAP编程

开发平台:

WINDOWS

  1. /*
  2.  * wsp_session.c - Implement WSP session oriented service
  3.  *
  4.  * Lars Wirzenius
  5.  */
  6. #include <string.h>
  7. #include <limits.h>
  8. #include "gwlib/gwlib.h"
  9. #include "wsp.h"
  10. #include "wsp_pdu.h"
  11. #include "wsp_headers.h"
  12. #include "wsp_caps.h"
  13. #include "wsp_strings.h"
  14. #include "cookies.h"
  15. #include "wap.h"
  16. #include "wtp.h"
  17. typedef enum {
  18. #define STATE_NAME(name) name,
  19. #define ROW(state, event, condition, action, next_state)
  20. #include "wsp_server_session_states.def"
  21. #define STATE_NAME(name) name,
  22. #define ROW(state, event, condition, action, next_state)
  23. #include "wsp_server_method_states.def"
  24.         #define STATE_NAME(name) name,
  25.         #define ROW(state, event, condition, action, next_state)
  26.         #include "wsp_server_push_states.def"
  27. WSPState_count
  28. } WSPState;
  29. /*
  30.  * Give the status the module:
  31.  *
  32.  * limbo
  33.  * not running at all
  34.  * running
  35.  * operating normally
  36.  * terminating
  37.  * waiting for operations to terminate, returning to limbo
  38.  */
  39. static enum { limbo, running, terminating } run_status = limbo;
  40. static wap_dispatch_func_t *dispatch_to_wtp_resp;
  41. static wap_dispatch_func_t *dispatch_to_wtp_init;
  42. static wap_dispatch_func_t *dispatch_to_appl;
  43. static wap_dispatch_func_t *dispatch_to_ota;
  44. /*
  45.  * True iff "Session resume facility" is enabled.  This means we are
  46.  * willing to let sessions go to SUSPENDED state, and later resume them.
  47.  * Currently we always support it, but this may become configurable
  48.  * at some point.
  49.  */
  50. static int resume_enabled = 1;
  51. static List *queue = NULL;
  52. static List *session_machines = NULL;
  53. static Counter *session_id_counter = NULL;
  54. static WSPMachine *find_session_machine(WAPEvent *event, WSP_PDU *pdu);
  55. static void handle_session_event(WSPMachine *machine, WAPEvent *event, 
  56.  WSP_PDU *pdu);
  57. static WSPMachine *machine_create(void);
  58. static void machine_destroy(void *p);
  59. static void handle_method_event(WSPMachine *session, WSPMethodMachine *machine, WAPEvent *event, WSP_PDU *pdu);
  60. static void cant_handle_event(WSPMachine *sm, WAPEvent *event);
  61. static WSPMethodMachine *method_machine_create(WSPMachine *, long);
  62. static void method_machine_destroy(void *msm);
  63. static void handle_push_event(WSPMachine *session, WSPPushMachine *machine,
  64.                               WAPEvent *e);
  65. static WSPPushMachine *push_machine_create(WSPMachine *session, long id);
  66. static void push_machine_destroy(void *p);
  67. static char *state_name(WSPState state);
  68. static unsigned long next_wsp_session_id(void);
  69. static List *make_capabilities_reply(WSPMachine *m);
  70. static Octstr *make_connectreply_pdu(WSPMachine *m);
  71. static Octstr *make_resume_reply_pdu(WSPMachine *m, List *headers);
  72. static WSP_PDU *make_confirmedpush_pdu(WAPEvent *e);
  73. static WSP_PDU *make_push_pdu(WAPEvent *e);
  74. static int transaction_belongs_to_session(void *session, void *tuple);
  75. static int find_by_session_id(void *session, void *idp);
  76. static int same_client(void *sm1, void *sm2);
  77. static WSPMethodMachine *find_method_machine(WSPMachine *, long id);
  78. static WSPPushMachine *find_push_machine(WSPMachine *m, long id);
  79. static List *unpack_new_headers(WSPMachine *sm, Octstr *hdrs);
  80. static void disconnect_other_sessions(WSPMachine *sm);
  81. static void send_abort(long reason, long handle);
  82. static void indicate_disconnect(WSPMachine *sm, long reason);
  83. static void indicate_suspend(WSPMachine *sm, long reason);
  84. static void indicate_resume(WSPMachine *sm, WAPAddrTuple *tuple, 
  85.                             List *client_headers);
  86. static void release_holding_methods(WSPMachine *sm);
  87. static void abort_methods(WSPMachine *sm, long reason);
  88. static void abort_pushes(WSPMachine *sm, long reason);
  89. static void method_abort(WSPMethodMachine *msm, long reason);
  90. static void indicate_method_abort(WSPMethodMachine *msm, long reason);
  91. static WAPEvent *make_abort(long reason, long handle);
  92. static void send_invoke(WSPMachine *session, WSP_PDU *pdu, WAPEvent *e,
  93.                         long class);
  94. static void send_abort_to_initiator(long reason, long handle);
  95. static void indicate_pushabort(WSPPushMachine *machine, long reason);
  96. static void confirm_push(WSPPushMachine *machine);
  97. static void main_thread(void *);
  98. static int id_belongs_to_session (void *, void *);
  99. /***********************************************************************
  100.  * Public functions.
  101.  */
  102. void wsp_session_init(wap_dispatch_func_t *responder_dispatch,
  103.                       wap_dispatch_func_t *initiator_dispatch,
  104.                       wap_dispatch_func_t *application_dispatch,
  105.                       wap_dispatch_func_t *push_ota_dispatch) {
  106. queue = list_create();
  107. list_add_producer(queue);
  108. session_machines = list_create();
  109. session_id_counter = counter_create();
  110. dispatch_to_wtp_resp = responder_dispatch;
  111. dispatch_to_wtp_init = initiator_dispatch;
  112. dispatch_to_appl = application_dispatch;
  113.         dispatch_to_ota = push_ota_dispatch;
  114.         wsp_strings_init();
  115. run_status = running;
  116. gwthread_create(main_thread, NULL);
  117. }
  118. void wsp_session_shutdown(void) {
  119. gw_assert(run_status == running);
  120. run_status = terminating;
  121. list_remove_producer(queue);
  122. gwthread_join_every(main_thread);
  123. list_destroy(queue, wap_event_destroy_item);
  124. debug("wap.wsp", 0, "WSP: %ld session machines left.",
  125. list_len(session_machines));
  126. list_destroy(session_machines, machine_destroy);
  127. counter_destroy(session_id_counter);
  128.         wsp_strings_shutdown();
  129. }
  130. void wsp_session_dispatch_event(WAPEvent *event) {
  131. wap_event_assert(event);
  132. list_produce(queue, event);
  133. }
  134. /***********************************************************************
  135.  * Local functions
  136.  */
  137. static void main_thread(void *arg) {
  138. WAPEvent *e;
  139. WSPMachine *sm;
  140. WSP_PDU *pdu;
  141. while (run_status == running && (e = list_consume(queue)) != NULL) {
  142. wap_event_assert(e);
  143. switch (e->type) {
  144. case TR_Invoke_Ind:
  145. pdu = wsp_pdu_unpack(e->u.TR_Invoke_Ind.user_data);
  146. if (pdu == NULL) {
  147. warning(0, "WSP: Broken PDU ignored.");
  148. wap_event_destroy(e);
  149. continue;
  150. }
  151. break;
  152. default:
  153. pdu = NULL;
  154. break;
  155. }
  156. sm = find_session_machine(e, pdu);
  157. if (sm == NULL) {
  158. wap_event_destroy(e);
  159. } else {
  160. handle_session_event(sm, e, pdu);
  161. }
  162. wsp_pdu_destroy(pdu);
  163. }
  164. }
  165. static WSPMachine *find_session_machine(WAPEvent *event, WSP_PDU *pdu) {
  166. WSPMachine *sm;
  167. long session_id;
  168. WAPAddrTuple *tuple;
  169. tuple = NULL;
  170. session_id = -1;
  171. switch (event->type) {
  172. case TR_Invoke_Ind:
  173. tuple = wap_addr_tuple_duplicate(
  174. event->u.TR_Invoke_Ind.addr_tuple);
  175. break;
  176.         case TR_Invoke_Cnf:
  177.                 tuple = wap_addr_tuple_duplicate(
  178. event->u.TR_Invoke_Cnf.addr_tuple);
  179.         break;
  180. case TR_Result_Cnf:
  181. tuple = wap_addr_tuple_duplicate(
  182. event->u.TR_Result_Cnf.addr_tuple);
  183. break;
  184. case TR_Abort_Ind:
  185. tuple = wap_addr_tuple_duplicate(
  186. event->u.TR_Abort_Ind.addr_tuple);
  187. break;
  188. case S_Connect_Res:
  189. session_id = event->u.S_Connect_Res.session_id;
  190. break;
  191. case S_Resume_Res:
  192. session_id = event->u.S_Resume_Res.session_id;
  193. break;
  194. case Disconnect_Event:
  195. session_id = event->u.Disconnect_Event.session_handle;
  196. break;
  197. case Suspend_Event:
  198. session_id = event->u.Suspend_Event.session_handle;
  199. break;
  200. case S_MethodInvoke_Res:
  201. session_id = event->u.S_MethodInvoke_Res.session_id;
  202. break;
  203. case S_MethodResult_Req:
  204. session_id = event->u.S_MethodResult_Req.session_id;
  205. break;
  206. case S_ConfirmedPush_Req:
  207.                 session_id = event->u.S_ConfirmedPush_Req.session_id;
  208.         break;
  209.         case S_Push_Req:
  210.                 session_id = event->u.S_Push_Req.session_id;
  211.         break;
  212. default:
  213. error(0, "WSP: Cannot find machine for %s event",
  214. wap_event_name(event->type));
  215. }
  216. gw_assert(tuple != NULL || session_id != -1);
  217. /* Pre-state-machine tests, according to 7.1.5.  After the tests,
  218.  * caller will pass the event to sm if sm is not NULL. */
  219. sm = NULL;
  220. /* First test is for MRUEXCEEDED, and we don't have a MRU */
  221. /* Second test is for class 2 TR-Invoke.ind with Connect PDU */
  222. if (event->type == TR_Invoke_Ind &&
  223.     event->u.TR_Invoke_Ind.tcl == 2 &&
  224.     pdu->type == Connect) {
  225. /* Create a new session, even if there is already
  226.  * a session open for this address.  The new session
  227.  * will take care of killing the old ones. */
  228. sm = machine_create();
  229. gw_assert(tuple != NULL);
  230. sm->addr_tuple = wap_addr_tuple_duplicate(tuple);
  231. sm->connect_handle = event->u.TR_Invoke_Ind.handle;
  232. /* Third test is for class 2 TR-Invoke.ind with Resume PDU */
  233. } else if (event->type == TR_Invoke_Ind &&
  234.    event->u.TR_Invoke_Ind.tcl == 2 &&
  235.       pdu->type == Resume) {
  236. /* Pass to session identified by session id, not
  237.  * the address tuple. */
  238. session_id = pdu->u.Resume.sessionid;
  239. sm = list_search(session_machines, &session_id,
  240. find_by_session_id);
  241. if (sm == NULL) {
  242. /* No session; TR-Abort.req(DISCONNECT) */
  243. send_abort(WSP_ABORT_DISCONNECT,
  244. event->u.TR_Invoke_Ind.handle);
  245. }
  246. /* Fourth test is for a class 1 or 2 TR-Invoke.Ind with no
  247.  * session for that address tuple.  We also handle class 0
  248.  * TR-Invoke.ind here by ignoring them; this seems to be
  249.  * an omission in the spec table. */
  250. } else if (event->type == TR_Invoke_Ind) {
  251. sm = list_search(session_machines, tuple,
  252.  transaction_belongs_to_session);
  253. if (sm == NULL && (event->u.TR_Invoke_Ind.tcl == 1 ||
  254. event->u.TR_Invoke_Ind.tcl == 2)) {
  255. send_abort(WSP_ABORT_DISCONNECT,
  256. event->u.TR_Invoke_Ind.handle);
  257. }
  258. /* Other tests are for events not handled by the state tables;
  259.  * do those later, after we've tried to handle them. */
  260. } else {
  261. if (session_id != -1) {
  262. sm = list_search(session_machines, &session_id,
  263. find_by_session_id);
  264. } else {
  265. sm = list_search(session_machines, tuple,
  266. transaction_belongs_to_session);
  267. }
  268. /* The table doesn't really say what we should do with
  269.  * non-Invoke events for which there is no session.  But
  270.  * such a situation means there is an error _somewhere_
  271.  * in the gateway. */
  272. if (sm == NULL) {
  273. error(0, "WSP: Cannot find session machine for event.");
  274. wap_event_dump(event);
  275. }
  276. }
  277. wap_addr_tuple_destroy(tuple);
  278. return sm;
  279. }
  280. static void handle_session_event(WSPMachine *sm, WAPEvent *current_event, 
  281. WSP_PDU *pdu) {
  282. debug("wap.wsp", 0, "WSP: machine %p, state %s, event %s",
  283. (void *) sm,
  284. state_name(sm->state), 
  285. wap_event_name(current_event->type));
  286. #define STATE_NAME(name)
  287. #define ROW(state_name, event, condition, action, next_state) 
  288. struct event *e; 
  289. e = &current_event->u.event; 
  290. if (sm->state == state_name && 
  291.    current_event->type == event && 
  292.    (condition)) { 
  293. action 
  294. sm->state = next_state; 
  295. debug("wap.wsp", 0, "WSP %ld: New state %s", 
  296. sm->session_id, #next_state); 
  297. goto end; 
  298. }
  299. #include "wsp_server_session_states.def"
  300. cant_handle_event(sm, current_event);
  301. end:
  302. wap_event_destroy(current_event);
  303. if (sm->state == NULL_SESSION)
  304. machine_destroy(sm);
  305. }
  306. static void cant_handle_event(WSPMachine *sm, WAPEvent *event) {
  307. /* We do the rest of the pre-state-machine tests here.  The first
  308.  * four were done in find_session_machine().  The fifth is a
  309.  * class 1 or 2 TR-Invoke.ind not handled by the state tables. */
  310. if (event->type == TR_Invoke_Ind &&
  311.     (event->u.TR_Invoke_Ind.tcl == 1 ||
  312.      event->u.TR_Invoke_Ind.tcl == 2)) {
  313. warning(0, "WSP: Can't handle TR-Invoke.ind, aborting transaction.");
  314. debug("wap.wsp", 0, "WSP: The unhandled event:");
  315. wap_event_dump(event);
  316. send_abort(WSP_ABORT_PROTOERR,
  317. event->u.TR_Invoke_Ind.handle);
  318. /* The sixth is a class 0 TR-Invoke.ind not handled by state tables. */
  319. } else if (event->type == TR_Invoke_Ind) {
  320. warning(0, "WSP: Can't handle TR-Invoke.ind, ignoring.");
  321. debug("wap.wsp", 0, "WSP: The ignored event:");
  322. wap_event_dump(event);
  323. /* The seventh is any other event not handled by state tables. */
  324. } else {
  325. error(0, "WSP: Can't handle event. Aborting session.");
  326. debug("wap.wsp", 0, "WSP: The unhandled event:");
  327. wap_event_dump(event);
  328. /* TR-Abort.req(PROTOERR) if it is some other transaction
  329.  * event than abort. */
  330. /* Currently that means TR-Result.cnf, because we already
  331.  * tested for Invoke. */
  332. /* FIXME We need a better way to get at event values than
  333.  * by hardcoding the types. */
  334. if (event->type == TR_Result_Cnf) {
  335. send_abort(WSP_ABORT_PROTOERR,
  336. event->u.TR_Result_Cnf.handle);
  337. }
  338. /* Abort(PROTOERR) all method and push transactions */
  339. abort_methods(sm, WSP_ABORT_PROTOERR);
  340.                 abort_pushes(sm, WSP_ABORT_PROTOERR);
  341. /* S-Disconnect.ind(PROTOERR) */
  342. indicate_disconnect(sm, WSP_ABORT_PROTOERR);
  343. }
  344. }
  345. static WSPMachine *machine_create(void) {
  346. WSPMachine *p;
  347. p = gw_malloc(sizeof(WSPMachine));
  348. debug("wap.wsp", 0, "WSP: Created WSPMachine %p", (void *) p);
  349. #define INTEGER(name) p->name = 0;
  350. #define OCTSTR(name) p->name = NULL;
  351. #define HTTPHEADERS(name) p->name = NULL;
  352. #define ADDRTUPLE(name) p->name = NULL;
  353. #define MACHINESLIST(name) p->name = list_create();
  354. #define CAPABILITIES(name) p->name = NULL;
  355. #define COOKIES(name) p->name = NULL;
  356. #define REFERER(name) p->name = NULL;
  357. #define MACHINE(fields) fields
  358. #include "wsp_server_session_machine.def"
  359. p->state = NULL_SESSION;
  360. /* set capabilities to default values (defined in 1.1) */
  361. p->client_SDU_size = 1400;
  362. p->MOR_push = 1;
  363. /* Insert new machine at the _front_, because 1) it's more likely
  364.  * to get events than old machines are, so this speeds up the linear
  365.  * search, and 2) we want the newest machine to get any method
  366.  * invokes that come through before the Connect is established. */
  367. list_insert(session_machines, 0, p);
  368. return p;
  369. }
  370. static void destroy_methodmachines(List *machines) {
  371. if (list_len(machines) > 0) {
  372. warning(0, "Destroying WSP session with %ld active methodsn",
  373. list_len(machines));
  374. }
  375. list_destroy(machines, method_machine_destroy);
  376. }
  377. static void destroy_pushmachines(List *machines) {
  378. if (list_len(machines) > 0) {
  379. warning(0, "Destroying WSP session with %ld active pushesn",
  380. list_len(machines));
  381. }
  382. list_destroy(machines, push_machine_destroy);
  383. }
  384. static void machine_destroy(void *pp) {
  385. WSPMachine *p;
  386. p = pp;
  387. debug("wap.wsp", 0, "Destroying WSPMachine %p", pp);
  388. list_delete_equal(session_machines, p);
  389. #define INTEGER(name) p->name = 0;
  390. #define OCTSTR(name) octstr_destroy(p->name);
  391. #define HTTPHEADERS(name) http_destroy_headers(p->name);
  392. #define ADDRTUPLE(name) wap_addr_tuple_destroy(p->name);
  393. #define MACHINESLIST(name) destroy_##name(p->name);
  394. #define CAPABILITIES(name) wsp_cap_destroy_list(p->name);
  395. #define COOKIES(name) cookies_destroy(p->name);
  396. #define REFERER(name) octstr_destroy(p->name);
  397. #define MACHINE(fields) fields
  398. #include "wsp_server_session_machine.def"
  399. gw_free(p);
  400. }
  401. struct msm_pattern {
  402. WAPAddrTuple *addr_tuple;
  403. long msmid, tid;
  404. };
  405. /* This function does NOT consume its event; it leaves that task up
  406.  * to the parent session */
  407. static void handle_method_event(WSPMachine *sm, WSPMethodMachine *msm, 
  408. WAPEvent *current_event, WSP_PDU *pdu) {
  409. if (msm == NULL) {
  410. warning(0, "No method machine for event.");
  411. wap_event_dump(current_event);
  412. return;
  413. }
  414. debug("wap.wsp", 0, "WSP: method %ld, state %s, event %s",
  415. msm->transaction_id, state_name(msm->state), 
  416. wap_event_name(current_event->type));
  417. gw_assert(sm->session_id == msm->session_id);
  418. #define STATE_NAME(name)
  419. #define ROW(state_name, event, condition, action, next_state) 
  420. struct event *e; 
  421. e = &current_event->u.event; 
  422. if (msm->state == state_name && 
  423.    current_event->type == event && 
  424.    (condition)) { 
  425. action 
  426. msm->state = next_state; 
  427. debug("wap.wsp", 0, "WSP %ld/%ld: New method state %s", 
  428. msm->session_id, msm->transaction_id, #next_state); 
  429. goto end; 
  430. }
  431. #include "wsp_server_method_states.def"
  432. cant_handle_event(sm, current_event);
  433. end:
  434. if (msm->state == NULL_METHOD) {
  435. method_machine_destroy(msm);
  436. list_delete_equal(sm->methodmachines, msm);
  437. }
  438. }
  439. static WSPMethodMachine *method_machine_create(WSPMachine *sm,
  440. long wtp_handle) {
  441. WSPMethodMachine *msm;
  442. msm = gw_malloc(sizeof(*msm));
  443. #define INTEGER(name) msm->name = 0;
  444. #define ADDRTUPLE(name) msm->name = NULL;
  445. #define EVENT(name) msm->name = NULL;
  446. #define MACHINE(fields) fields
  447. #include "wsp_server_method_machine.def"
  448. msm->transaction_id = wtp_handle;
  449. msm->state = NULL_METHOD;
  450. msm->addr_tuple = wap_addr_tuple_duplicate(sm->addr_tuple);
  451. msm->session_id = sm->session_id;
  452. list_append(sm->methodmachines, msm);
  453. return msm;
  454. }
  455. static void method_machine_destroy(void *p) {
  456. WSPMethodMachine *msm;
  457. if (p == NULL)
  458. return;
  459. msm = p;
  460. debug("wap.wsp", 0, "Destroying WSPMethodMachine %ld",
  461. msm->transaction_id);
  462. #define INTEGER(name)
  463. #define ADDRTUPLE(name) wap_addr_tuple_destroy(msm->name);
  464. #define EVENT(name) wap_event_destroy(msm->name);
  465. #define MACHINE(fields) fields
  466. #include "wsp_server_method_machine.def"
  467. gw_free(msm);
  468. }
  469. static void handle_push_event(WSPMachine *sm, WSPPushMachine *pm, 
  470.                               WAPEvent *current_event)
  471. {
  472.         if (pm == NULL) {
  473. warning(0, "No push machine for event.");
  474. wap_event_dump(current_event);
  475. return;
  476. }
  477.         debug("wap.wsp", 0, "WSP(tid/pid): push %ld/%ld, state %s, event %s",
  478. pm->transaction_id, pm->server_push_id, state_name(pm->state), 
  479. wap_event_name(current_event->type));
  480. gw_assert(sm->session_id == pm->session_id);
  481.         #define STATE_NAME(name)
  482. #define ROW(state_name, event, condition, action, next_state) 
  483.     if (pm->state == state_name && 
  484. current_event->type == event && 
  485. (condition)) { 
  486.      action 
  487.      pm->state = next_state; 
  488.      debug("wap.wsp", 0, "WSP %ld/%ld: New push state %s", 
  489.            pm->session_id, pm->transaction_id, #next_state); 
  490. goto end; 
  491. }
  492. #include "wsp_server_push_states.def"
  493.         cant_handle_event(sm, current_event);
  494. end:
  495.         if (pm->state == SERVER_PUSH_NULL_STATE) {
  496. push_machine_destroy(pm);
  497. list_delete_equal(sm->pushmachines, pm);
  498. }
  499. }
  500. static WSPPushMachine *push_machine_create(WSPMachine *sm, 
  501.         long pid)
  502. {
  503.         WSPPushMachine *m;
  504.         m = gw_malloc(sizeof(WSPPushMachine));
  505.         #define INTEGER(name) m->name = 0;
  506.         #define ADDRTUPLE(name) m->name = NULL;
  507.         #define HTTPHEADER(name) m->name = http_create_empty_headers();
  508.         #define MACHINE(fields) fields
  509.         #include "wsp_server_push_machine.def"
  510.         m->server_push_id = pid;
  511.         m->transaction_id = pid;
  512. m->state = SERVER_PUSH_NULL_STATE;
  513. m->addr_tuple = wap_addr_tuple_duplicate(sm->addr_tuple);
  514. m->session_id = sm->session_id;
  515. list_append(sm->pushmachines, m);
  516. return m;      
  517. }
  518. static void push_machine_destroy(void *p)
  519. {
  520.         WSPPushMachine *m = NULL;   
  521. if (p == NULL)
  522.        return;  
  523.         m = p;
  524.         debug("wap.wsp", 0, "Destroying WSPPushMachine %ld",
  525. m->transaction_id);
  526.         #define INTEGER(name) 
  527.         #define ADDRTUPLE(name) wap_addr_tuple_destroy(m->name);
  528.         #define HTTPHEADER(name) http_destroy_headers(m->name);
  529.         #define MACHINE(fields) fields
  530.         #include "wsp_server_push_machine.def"
  531.         gw_free(m);
  532. }
  533. static char *state_name(WSPState state) {
  534. switch (state) {
  535. #define STATE_NAME(name) case name: return #name;
  536. #define ROW(state, event, cond, stmt, next_state)
  537. #include "wsp_server_session_states.def"
  538. #define STATE_NAME(name) case name: return #name;
  539. #define ROW(state, event, cond, stmt, next_state)
  540. #include "wsp_server_method_states.def"
  541.         #define STATE_NAME(name) case name: return #name;
  542.         #define ROW(state, event, cond, stmt, next_state)
  543.         #include "wsp_server_push_states.def"
  544. default:
  545. return "unknown wsp state";
  546. }
  547. }
  548. static unsigned long next_wsp_session_id(void) {
  549. return counter_increase(session_id_counter);
  550. }
  551. static void sanitize_capabilities(List *caps, WSPMachine *m) {
  552. long i;
  553. Capability *cap;
  554. unsigned long ui;
  555. for (i = 0; i < list_len(caps); i++) {
  556. cap = list_get(caps, i);
  557. /* We only know numbered capabilities.  Let the application
  558.  * layer negotiate whatever it wants for unknown ones. */
  559. if (cap->name != NULL)
  560. continue;
  561. switch (cap->id) {
  562. case WSP_CAPS_CLIENT_SDU_SIZE:
  563. /* Check if it's a valid uintvar.  The value is the
  564.  * max SDU size we will send, and there's no
  565.  * internal limit to that, so accept any value. */
  566. if (cap->data != NULL &&
  567.     octstr_extract_uintvar(cap->data, &ui, 0) < 0)
  568. goto bad_cap;
  569. else
  570. m->client_SDU_size = ui;
  571. break;
  572. case WSP_CAPS_SERVER_SDU_SIZE:
  573. /* Check if it's a valid uintvar */
  574. if (cap->data != NULL &&
  575.     (octstr_extract_uintvar(cap->data, &ui, 0) < 0))
  576. goto bad_cap;
  577. /* XXX Our MRU is not quite unlimited, since we
  578.  * use signed longs in the library functions --
  579.  * should we make sure we limit the reply value
  580.  * to LONG_MAX?  (That's already a 2GB packet) */
  581. break;
  582. case WSP_CAPS_PROTOCOL_OPTIONS:
  583. /* Currently we don't support any Push, nor
  584.  * session resume, nor acknowledgement headers,
  585.  * so make sure those bits are not set. */
  586. if (cap->data != NULL && octstr_len(cap->data) > 0
  587.    && (octstr_get_char(cap->data, 0) & 0xf0) != 0) {
  588. warning(0, "WSP: Application layer tried to "
  589. "negotiate protocol options.");
  590. octstr_set_bits(cap->data, 0, 4, 0);
  591. }
  592. break;
  593. case WSP_CAPS_EXTENDED_METHODS:
  594. /* XXX Check format here */
  595. break;
  596. case WSP_CAPS_HEADER_CODE_PAGES:
  597. /* We don't support any yet, so don't let this
  598.  * be negotiated. */
  599. if (cap->data)
  600. goto bad_cap;
  601. break;
  602. }
  603. continue;
  604. bad_cap:
  605. error(0, "WSP: Found illegal value in capabilities reply.");
  606. wsp_cap_dump(cap);
  607. list_delete(caps, i, 1);
  608. i--;
  609. wsp_cap_destroy(cap);
  610. continue;
  611. }
  612. }
  613. static void reply_known_capabilities(List *caps, List *req, WSPMachine *m) {
  614. unsigned long ui;
  615. Capability *cap;
  616. Octstr *data;
  617. if (wsp_cap_count(caps, WSP_CAPS_CLIENT_SDU_SIZE, NULL) == 0) {
  618. if (wsp_cap_get_client_sdu(req, &ui) > 0) {
  619. /* Accept value if it is not silly. */
  620. if ((ui >= 256 && ui < LONG_MAX) || ui == 0) {
  621. m->client_SDU_size = ui;
  622. }
  623. }
  624. /* Reply with the client SDU we decided on */
  625. data = octstr_create("");
  626. octstr_append_uintvar(data, m->client_SDU_size);
  627. cap = wsp_cap_create(WSP_CAPS_CLIENT_SDU_SIZE,
  628. NULL, data);
  629. list_append(caps, cap);
  630. }
  631. if (wsp_cap_count(caps, WSP_CAPS_SERVER_SDU_SIZE, NULL) == 0) {
  632. /* Accept whatever size the client is willing
  633.  * to send.  If the client did not specify anything,
  634.  * then use the default. */
  635. if (wsp_cap_get_server_sdu(req, &ui) <= 0) {
  636. ui = 1400;
  637. }
  638. data = octstr_create("");
  639. octstr_append_uintvar(data, ui);
  640. cap = wsp_cap_create(WSP_CAPS_SERVER_SDU_SIZE, NULL, data);
  641. list_append(caps, cap);
  642. }
  643. /* Currently we cannot handle any protocol options */
  644. if (wsp_cap_count(caps, WSP_CAPS_PROTOCOL_OPTIONS, NULL) == 0) {
  645. data = octstr_create("");
  646. octstr_append_char(data, 0);
  647. cap = wsp_cap_create(WSP_CAPS_PROTOCOL_OPTIONS, NULL, data);
  648. list_append(caps, cap);
  649. }
  650. /* Accept any Method-MOR the client sent; if it sent none,
  651.  * use the default. */
  652. if (wsp_cap_count(caps, WSP_CAPS_METHOD_MOR, NULL) == 0) {
  653. if (wsp_cap_get_method_mor(req, &ui) <= 0) {
  654. ui = 1;
  655. }
  656. data = octstr_create("");
  657. octstr_append_char(data, ui);
  658. cap = wsp_cap_create(WSP_CAPS_METHOD_MOR, NULL, data);
  659. list_append(caps, cap);
  660. }
  661. /* We will never send any Push requests because we don't support
  662.  * that yet.  But we already specified that in protocol options;
  663.  * so, pretend we do, and handle the value that way. */
  664. if (wsp_cap_count(caps, WSP_CAPS_PUSH_MOR, NULL) == 0) {
  665. if (wsp_cap_get_push_mor(req, &ui) > 0) {
  666. m->MOR_push = ui;
  667. }
  668. data = octstr_create("");
  669. octstr_append_char(data, m->MOR_push);
  670. cap = wsp_cap_create(WSP_CAPS_PUSH_MOR, NULL, data);
  671. list_append(caps, cap);
  672. }
  673. /* Supporting extended methods is up to the application layer,
  674.  * not up to us.  If the application layer didn't specify any,
  675.  * then we refuse whatever the client requested.  The default
  676.  * is to support none, so we don't really have to add anything here. */
  677. /* We do not support any header code pages.  sanitize_capabilities
  678.  * must have already deleted any reply that indicates otherwise.
  679.  * Again, not adding anything here is the same as refusing support. */
  680. /* Listing aliases is something the application layer can do if
  681.  * it wants to.  We don't care. */
  682. }
  683. /* Generate a refusal for all requested capabilities that are not
  684.  * replied to. */
  685. static void refuse_unreplied_capabilities(List *caps, List *req) {
  686. long i, len;
  687. Capability *cap;
  688. len = list_len(req);
  689. for (i = 0; i < len; i++) {
  690. cap = list_get(req, i);
  691. if (wsp_cap_count(caps, cap->id, cap->name) == 0) {
  692. cap = wsp_cap_create(cap->id, cap->name, NULL);
  693. list_append(caps, cap);
  694. }
  695. }
  696. }
  697. static int is_default_cap(Capability *cap) {
  698. unsigned long ui;
  699. /* All unknown values are empty by default */
  700. if (cap->name != NULL || cap->id < 0 || cap->id >= WSP_NUM_CAPS)
  701. return cap->data == NULL || octstr_len(cap->data) == 0;
  702. switch (cap->id) {
  703. case WSP_CAPS_CLIENT_SDU_SIZE:
  704. case WSP_CAPS_SERVER_SDU_SIZE:
  705. return (cap->data != NULL &&
  706.     octstr_extract_uintvar(cap->data, &ui, 0) >= 0 &&
  707.     ui == 1400);
  708. case WSP_CAPS_PROTOCOL_OPTIONS:
  709. return cap->data != NULL && octstr_get_char(cap->data, 0) == 0;
  710. case WSP_CAPS_METHOD_MOR:
  711. case WSP_CAPS_PUSH_MOR:
  712. return cap->data != NULL && octstr_get_char(cap->data, 0) == 1;
  713. case WSP_CAPS_EXTENDED_METHODS:
  714. case WSP_CAPS_HEADER_CODE_PAGES:
  715. case WSP_CAPS_ALIASES:
  716. return cap->data == NULL || octstr_len(cap->data) == 0;
  717. default:
  718. return 0;
  719. }
  720. }
  721. /* Remove any replies that have no corresponding request and that
  722.  * are equal to the default. */
  723. static void strip_default_capabilities(List *caps, List *req) {
  724. long i;
  725. Capability *cap;
  726. int count;
  727. /* Hmm, this is an O(N*N) operation, which may be bad. */
  728. i = 0;
  729. while (i < list_len(caps)) {
  730. cap = list_get(caps, i);
  731. count = wsp_cap_count(req, cap->id, cap->name);
  732. if (count == 0 && is_default_cap(cap)) {
  733. list_delete(caps, i, 1);
  734. wsp_cap_destroy(cap);
  735. } else {
  736. i++;
  737. }
  738. }
  739. }
  740. static List *make_capabilities_reply(WSPMachine *m) {
  741. List *caps;
  742. /* In principle, copy the application layer's capabilities
  743.  * response, add refusals for all unknown requested capabilities,
  744.  * and add responses for all known capabilities that are
  745.  * not already responded to.  Then eliminate any replies that
  746.    * would have no effect because they are equal to the default. */
  747. caps = wsp_cap_duplicate_list(m->reply_caps);
  748. /* Don't let the application layer negotiate anything we
  749.  * cannot handle.  Also parse the values it set if we're
  750.  * interested. */
  751. sanitize_capabilities(caps, m);
  752. /* Add capability records for all capabilities we know about
  753.  * that are not already in the reply list. */
  754. reply_known_capabilities(caps, m->request_caps, m);
  755. /* All remaining capabilities in the request list that are
  756.  * not in the reply list at this point must be unknown ones
  757.  * that we want to refuse. */
  758. refuse_unreplied_capabilities(caps, m->request_caps);
  759. /* Now eliminate replies that would be equal to the requested
  760.  * value, or (if there was none) to the default value. */
  761. strip_default_capabilities(caps, m->request_caps);
  762. return caps;
  763. }
  764. static Octstr *make_connectreply_pdu(WSPMachine *m) {
  765. WSP_PDU *pdu;
  766. Octstr *os;
  767. List *caps;
  768. pdu = wsp_pdu_create(ConnectReply);
  769. pdu->u.ConnectReply.sessionid = m->session_id;
  770. caps = make_capabilities_reply(m);
  771. pdu->u.ConnectReply.capabilities = wsp_cap_pack_list(caps);
  772. wsp_cap_destroy_list(caps);
  773. pdu->u.ConnectReply.headers = NULL;
  774. os = wsp_pdu_pack(pdu);
  775. wsp_pdu_destroy(pdu);
  776. return os;
  777. }
  778. static Octstr *make_resume_reply_pdu(WSPMachine *m, List *headers) {
  779. WSP_PDU *pdu;
  780. Octstr *os;
  781. pdu = wsp_pdu_create(Reply);
  782. /* Not specified for Resume replies */
  783. pdu->u.Reply.status = wsp_convert_http_status_to_wsp_status(HTTP_OK);
  784. if (headers == NULL) {
  785. headers = http_create_empty_headers();
  786. pdu->u.Reply.headers = wsp_headers_pack(headers, 1);
  787. http_destroy_headers(headers);
  788. } else {
  789. pdu->u.Reply.headers = wsp_headers_pack(headers, 1);
  790. }
  791. pdu->u.Reply.data = octstr_create("");
  792. os = wsp_pdu_pack(pdu);
  793. wsp_pdu_destroy(pdu);
  794. return os;
  795. }
  796. static WSP_PDU *make_confirmedpush_pdu(WAPEvent *e)
  797. {
  798.         WSP_PDU *pdu;
  799.         List *headers;
  800.         pdu = wsp_pdu_create(ConfirmedPush);
  801. /*
  802.  * Both push headers and push body are optional. 
  803.  */
  804.         if (e->u.S_ConfirmedPush_Req.push_headers == NULL) {
  805.     headers = http_create_empty_headers();
  806.             pdu->u.ConfirmedPush.headers = wsp_headers_pack(headers, 1);
  807.             http_destroy_headers(headers);
  808.         } else
  809.             pdu->u.ConfirmedPush.headers = 
  810.                 wsp_headers_pack(e->u.S_ConfirmedPush_Req.push_headers, 1);
  811.    
  812.         if (e->u.S_ConfirmedPush_Req.push_body == NULL)
  813.     pdu->u.ConfirmedPush.data = octstr_create("");
  814.         else
  815.     pdu->u.ConfirmedPush.data = 
  816.                 octstr_duplicate(e->u.S_ConfirmedPush_Req.push_body);        
  817.         return pdu;
  818. }
  819. static WSP_PDU *make_push_pdu(WAPEvent *e)
  820. {
  821.         WSP_PDU *pdu;
  822.         List *headers;
  823.         pdu = wsp_pdu_create(Push);
  824. /*
  825.  * Both push headers and push body are optional
  826.  */
  827.         if (e->u.S_Push_Req.push_headers == NULL) {
  828.     headers = http_create_empty_headers();
  829.             pdu->u.Push.headers = wsp_headers_pack(headers, 1);
  830.             http_destroy_headers(headers);
  831.         } else
  832.             pdu->u.Push.headers = 
  833.                 wsp_headers_pack(e->u.S_Push_Req.push_headers, 1);
  834.    
  835.         if (e->u.S_Push_Req.push_body == NULL)
  836.     pdu->u.Push.data = octstr_create("");
  837.         else
  838.     pdu->u.Push.data = 
  839.                 octstr_duplicate(e->u.S_Push_Req.push_body);        
  840.         return pdu;
  841. }
  842. static int transaction_belongs_to_session(void *wsp_ptr, void *tuple_ptr) {
  843. WSPMachine *wsp;
  844. WAPAddrTuple *tuple;
  845. wsp = wsp_ptr;
  846. tuple = tuple_ptr;
  847. return wap_addr_tuple_same(wsp->addr_tuple, tuple);
  848. }
  849. static int find_by_session_id(void *wsp_ptr, void *id_ptr) {
  850. WSPMachine *wsp = wsp_ptr;
  851. long *idp = id_ptr;
  852. return wsp->session_id == *idp;
  853. }
  854. static int find_by_method_id(void *wspm_ptr, void *id_ptr) {
  855. WSPMethodMachine *msm = wspm_ptr;
  856. long *idp = id_ptr;
  857. return msm->transaction_id == *idp;
  858. }
  859. static int find_by_push_id(void *m_ptr, void *id_ptr) {
  860. WSPPushMachine *m = m_ptr;
  861. long *idp = id_ptr;
  862. return m->transaction_id == *idp;
  863. }
  864. static WSPMethodMachine *find_method_machine(WSPMachine *sm, long id) {
  865. return list_search(sm->methodmachines, &id, find_by_method_id);
  866. }
  867. static WSPPushMachine *find_push_machine(WSPMachine *m, long id)
  868. {
  869.        return list_search(m->pushmachines, &id, find_by_push_id);
  870. }
  871. static int same_client(void *a, void *b) {
  872. WSPMachine *sm1, *sm2;
  873. sm1 = a;
  874. sm2 = b;
  875. return wap_addr_tuple_same(sm1->addr_tuple, sm2->addr_tuple);
  876. }
  877. static void disconnect_other_sessions(WSPMachine *sm) {
  878. List *old_sessions;
  879. WAPEvent *disconnect;
  880. WSPMachine *sm2;
  881. long i;
  882. old_sessions = list_search_all(session_machines, sm, same_client);
  883. if (old_sessions == NULL)
  884. return;
  885. for (i = 0; i < list_len(old_sessions); i++) {
  886. sm2 = list_get(old_sessions, i);
  887. if (sm2 != sm) {
  888. disconnect = wap_event_create(Disconnect_Event);
  889. handle_session_event(sm2, disconnect, NULL);
  890. }
  891. }
  892. list_destroy(old_sessions, NULL);
  893. }
  894. static List *unpack_new_headers(WSPMachine *sm, Octstr *hdrs) {
  895. List *new_headers;
  896. if (hdrs && octstr_len(hdrs) > 0) {
  897. new_headers = wsp_headers_unpack(hdrs, 0);
  898. if (sm->http_headers == NULL)
  899. sm->http_headers = http_create_empty_headers();
  900. http_header_combine(sm->http_headers, new_headers);
  901. return new_headers;
  902. }
  903. return NULL;
  904. }
  905. static WAPEvent *make_abort(long reason, long handle)
  906. {
  907.         WAPEvent *wtp_event;
  908.         wtp_event = wap_event_create(TR_Abort_Req);
  909.         wtp_event->u.TR_Abort_Req.abort_type = 0x01;
  910.         wtp_event->u.TR_Abort_Req.abort_reason = reason;
  911.         wtp_event->u.TR_Abort_Req.handle = handle;
  912.         return wtp_event;
  913. }
  914. static void send_abort(long reason, long handle) {
  915.         WAPEvent *wtp_event;
  916. wtp_event = make_abort(reason, handle);
  917. dispatch_to_wtp_resp(wtp_event);
  918. }
  919. static void send_abort_to_initiator(long reason, long handle)
  920. {
  921.        WAPEvent *wtp_event;
  922.        wtp_event = make_abort(reason, handle);
  923.        dispatch_to_wtp_init(wtp_event);
  924. }
  925. /*
  926.  * The server sends invoke (to be exact, makes TR-Invoke.req) only when it is 
  927.  * pushing. (Only the client disconnects sessions.)
  928.  */ 
  929. static void send_invoke(WSPMachine *m, WSP_PDU *pdu, WAPEvent *e, long class)
  930. {
  931.         WAPEvent *wtp_event;
  932.         wtp_event = wap_event_create(TR_Invoke_Req);
  933.         wtp_event->u.TR_Invoke_Req.addr_tuple = 
  934.     wap_addr_tuple_duplicate(m->addr_tuple);
  935. /*
  936.  * There is no mention of acknowledgement type in the specs.燘ut because 
  937.  * confirmed push is confirmed after response from OTA, provider acknowledge-
  938.  * ments seem redundant.
  939.  */
  940. wtp_event->u.TR_Invoke_Req.up_flag = USER_ACKNOWLEDGEMENT;
  941.         wtp_event->u.TR_Invoke_Req.tcl = class;
  942.         if (e->type == S_ConfirmedPush_Req)
  943.            wtp_event->u.TR_Invoke_Req.handle = 
  944.                e->u.S_ConfirmedPush_Req.server_push_id;
  945. wtp_event->u.TR_Invoke_Req.user_data = wsp_pdu_pack(pdu);
  946.         wsp_pdu_destroy(pdu);
  947.         dispatch_to_wtp_init(wtp_event);
  948. }
  949. static void indicate_disconnect(WSPMachine *sm, long reason) {
  950. WAPEvent *new_event;
  951. new_event = wap_event_create(S_Disconnect_Ind);
  952. new_event->u.S_Disconnect_Ind.reason_code = reason;
  953. new_event->u.S_Disconnect_Ind.redirect_security = 0;
  954. new_event->u.S_Disconnect_Ind.redirect_addresses = 0;
  955. new_event->u.S_Disconnect_Ind.error_headers = NULL;
  956. new_event->u.S_Disconnect_Ind.error_body = NULL;
  957. new_event->u.S_Disconnect_Ind.session_handle = sm->session_id;
  958. dispatch_to_appl(new_event);
  959. }
  960. static void indicate_suspend(WSPMachine *sm, long reason) {
  961. WAPEvent *new_event;
  962. new_event = wap_event_create(S_Suspend_Ind);
  963. new_event->u.S_Suspend_Ind.reason = reason;
  964. new_event->u.S_Suspend_Ind.session_id = sm->session_id;
  965. dispatch_to_appl(new_event);
  966. }
  967. static void indicate_resume(WSPMachine *sm,
  968.                                 WAPAddrTuple *tuple, List *headers) {
  969. WAPEvent *new_event;
  970. new_event = wap_event_create(S_Resume_Ind);
  971. new_event->u.S_Resume_Ind.addr_tuple = wap_addr_tuple_duplicate(tuple);
  972. new_event->u.S_Resume_Ind.client_headers = http_header_duplicate(headers);
  973. new_event->u.S_Resume_Ind.session_id = sm->session_id;
  974. dispatch_to_appl(new_event);
  975. }
  976. static void indicate_pushabort(WSPPushMachine *spm, long reason)
  977. {
  978.        WAPEvent *ota_event;
  979.  
  980.        ota_event = wap_event_create(S_PushAbort_Ind);
  981.        ota_event->u.S_PushAbort_Ind.push_id = spm->server_push_id;
  982.        ota_event->u.S_PushAbort_Ind.reason = reason;
  983.        ota_event->u.S_PushAbort_Ind.session_id = spm->session_id;
  984.        dispatch_to_appl(ota_event);
  985. }
  986. static void confirm_push(WSPPushMachine *m)
  987. {
  988.        WAPEvent *ota_event;
  989.        ota_event = wap_event_create(S_ConfirmedPush_Cnf);
  990.        ota_event->u.S_ConfirmedPush_Cnf.server_push_id = m->server_push_id;
  991.        ota_event->u.S_ConfirmedPush_Cnf.session_id = m->session_id;
  992.        dispatch_to_appl(ota_event);
  993. }
  994. static void method_abort(WSPMethodMachine *msm, long reason) {
  995. WAPEvent *wtp_event;
  996. /* Send TR-Abort.req(reason) */
  997. wtp_event = wap_event_create(TR_Abort_Req);
  998. /* FIXME: Specs are unclear about this; we may indeed have to
  999.  * guess abort whether this is a WSP or WTP level abort code */
  1000. if (reason < WSP_ABORT_PROTOERR) {
  1001. wtp_event->u.TR_Abort_Req.abort_type = 0x00;
  1002. } else {
  1003. wtp_event->u.TR_Abort_Req.abort_type = 0x01;
  1004. }
  1005. wtp_event->u.TR_Abort_Req.abort_reason = reason;
  1006. wtp_event->u.TR_Abort_Req.handle = msm->transaction_id;
  1007. dispatch_to_wtp_resp(wtp_event);
  1008. }
  1009. static void indicate_method_abort(WSPMethodMachine *msm, long reason) {
  1010. WAPEvent *new_event;
  1011. /* Send S-MethodAbort.ind(reason) */
  1012. new_event = wap_event_create(S_MethodAbort_Ind);
  1013. new_event->u.S_MethodAbort_Ind.transaction_id = msm->transaction_id;
  1014. new_event->u.S_MethodAbort_Ind.reason = reason;
  1015. new_event->u.S_MethodAbort_Ind.session_handle = msm->session_id;
  1016. dispatch_to_appl(new_event);
  1017. }
  1018. static int method_is_holding(void *item, void *pattern) {
  1019. WSPMethodMachine *msm = item;
  1020. return msm->state == HOLDING;
  1021. }
  1022. static void release_holding_methods(WSPMachine *sm) {
  1023. WAPEvent *release;
  1024. WSPMethodMachine *msm;
  1025. List *holding;
  1026. long i, len;
  1027. holding = list_search_all(sm->methodmachines, NULL, method_is_holding);
  1028. if (holding == NULL)
  1029. return;
  1030. /* We can re-use this because wsp_handle_method_event does not
  1031.  * destroy its event */
  1032. release = wap_event_create(Release_Event);
  1033. len = list_len(holding);
  1034. for (i = 0; i < len; i++) {
  1035. msm = list_get(holding, i);
  1036. handle_method_event(sm, msm, release, NULL);
  1037. }
  1038. list_destroy(holding, NULL);
  1039. wap_event_destroy(release);
  1040. }
  1041. static void abort_methods(WSPMachine *sm, long reason) {
  1042. WAPEvent *ab;
  1043. WSPMethodMachine *msm;
  1044. long i, len;
  1045. ab = wap_event_create(Abort_Event);
  1046. ab->u.Abort_Event.reason = reason;
  1047. /* This loop goes backward because it has to deal with the
  1048.  * possibility of method machines disappearing after their event. */
  1049. len = list_len(sm->methodmachines);
  1050. for (i = len - 1; i >= 0; i--) {
  1051. msm = list_get(sm->methodmachines, i);
  1052. handle_method_event(sm, msm, ab, NULL);
  1053. }
  1054. wap_event_destroy(ab);
  1055. }
  1056. static void abort_pushes(WSPMachine *sm, long reason)
  1057. {
  1058.         WAPEvent *ab;
  1059. WSPPushMachine *psm;
  1060. long i, len;
  1061.         ab = wap_event_create(Abort_Event);
  1062. ab->u.Abort_Event.reason = reason;
  1063.         len = list_len(sm->pushmachines);
  1064. for (i = len - 1; i >= 0; i--) {
  1065. psm = list_get(sm->pushmachines, i);
  1066. handle_push_event(sm, psm, ab);
  1067. }
  1068.         wap_event_destroy(ab);
  1069. }
  1070. WSPMachine *find_session_machine_by_id (int id) {
  1071. return list_search(session_machines, &id, id_belongs_to_session);
  1072. }
  1073. static int id_belongs_to_session (void *wsp_ptr, void *pid) {
  1074. WSPMachine *wsp;
  1075. int *id;
  1076. wsp = wsp_ptr;
  1077. id = (int *) pid;
  1078. if (*id == wsp->session_id) return 1;
  1079. return 0;
  1080. }