/* ==================================================================== * The Kannel Software License, Version 1.0 * * Copyright (c) 2001-2005 Kannel Group * Copyright (c) 1998-2001 WapIT Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Kannel Group (http://www.kannel.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Kannel" and "Kannel Group" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please * contact org@kannel.org. * * 5. Products derived from this software may not be called "Kannel", * nor may "Kannel" appear in their name, without prior written * permission of the Kannel Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Kannel Group. For more information on * the Kannel Group, please see . * * Portions of this software are based upon software originally written at * WapIT Ltd., Helsinki, Finland for the Kannel project. */ /* * gw/wap-appl.c - wapbox application layer and push ota indication, response * and confirmation primitive implementation. * * This module implements various indication and confirmation primitives and * protocol mappings as defined in: * * WAP-189-PushOTA-20000217-a (hereafter called ota) * WAP-200-WDP-20001212-a (wdp) * WAP-248-UAProf-20011020-a (UAProf) * * Wapbox application layer itself is not a WAP Forum protocol. * * The application layer is reads events from its event queue, fetches the * corresponding URLs and feeds back events to the WSP layer (pull). * * In addition, the layer forwards WSP events related to push to the module * wap_push_ppg and wsp, implementing indications, responses and confirma- * tions of ota. * * Note that push header encoding and decoding are divided two parts: * first decoding and encoding numeric values and then packing these values * into WSP format and unpacking them from WSP format. This module contains * encoding part. * * Lars Wirzenius */ #include #include "gwlib/gwlib.h" #include "wmlscript/ws.h" #include "xml_shared.h" #include "wml_compiler.h" #include "mime_decompiler.h" #include "wap/wap.h" #include "wap-appl.h" #include "wap_push_ppg.h" #include "wap/wsp_strings.h" #include "wap/wsp_caps.h" #include "wap/wsp.h" #ifdef ENABLE_COOKIES #include "wap/cookies.h" #endif #include "radius/radius_acct.h" #include "wap-error.h" #include "wap-maps.h" #define ENABLE_NOT_ACCEPTED /* * 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; /* * The queue of incoming events. */ static List *queue = NULL; /* * HTTP caller identifier for application layer. */ static HTTPCaller *caller = NULL; /* * Number of currently running HTTP fetching threads. */ static Counter *fetches = NULL; /* * Charsets supported by WML compiler, queried from wml_compiler. */ static List *charsets = NULL; struct content { Octstr *body; Octstr *type; Octstr *charset; Octstr *url; Octstr *version; }; /* * A mapping from HTTP request identifiers to information about the request. */ struct request_data { long client_SDU_size; WAPEvent *event; long session_id; Octstr *method; Octstr *url; long x_wap_tod; List *request_headers; Octstr *msisdn; }; /* * WSP smart error messaging */ extern int wsp_smart_errors; extern Octstr *device_home; /* * Defines if PPG is running in wapbox instance */ static int have_ppg = 0; /* * Private functions. */ static void main_thread(void *); static void start_fetch(WAPEvent *); static void return_replies_thread(void *); static void dev_null(const char *data, size_t len, void *context); static Octstr *convert_wml_to_wmlc(struct content *content); static Octstr *convert_wmlscript_to_wmlscriptc(struct content *content); /* DAVI: To-Do static Octstr *convert_multipart_mixed(struct content *content); */ static Octstr *deconvert_multipart_formdata(struct content *content); /* DAVI: To-Do static Octstr *deconvert_mms_message(struct content *content); */ static List *negotiate_capabilities(List *req_caps); static struct { char *type; char *result_type; Octstr *(*convert)(struct content *); } converters[] = { { "text/vnd.wap.wml", "application/vnd.wap.wmlc", convert_wml_to_wmlc }, { "text/vnd.wap.wmlscript", "application/vnd.wap.wmlscriptc", convert_wmlscript_to_wmlscriptc }, /* DAVI: To-Do { "multipart/mixed", "application/vnd.wap.multipart.mixed", convert_multipart_mixed }, */ }; #define NUM_CONVERTERS ((long)(sizeof(converters) / sizeof(converters[0]))) static struct { char *type; char *result_type; Octstr *(*deconvert)(struct content *); } deconverters[] = { { "application/vnd.wap.multipart.form-data", "multipart/form-data; boundary=kannel_boundary", deconvert_multipart_formdata }, /* DAVI: To-Do { "application/vnd.wap.mms-message", "multipart/related; type=application/smil; boundary=kannel_boundary; start=", deconvert_mms_message }, */ }; #define NUM_DECONVERTERS ((long)(sizeof(deconverters) / sizeof(deconverters[0]))) /* * Following functions implement indications and conformations part of Push * OTA protocol. */ static void indicate_push_connection(WAPEvent *e); static void indicate_push_disconnect(WAPEvent *e); static void indicate_push_suspend(WAPEvent *e); static void indicate_push_resume(WAPEvent *e); static void confirm_push(WAPEvent *e); static void indicate_push_abort(WAPEvent *e); static void split_header_list(List **headers, List **new_headers, char *name); static void check_application_headers(List **headers, List **app_headers); static void decode_bearer_indication(List **headers, List **bearer_headers); static void response_push_connection(WAPEvent *e); /*********************************************************************** * The public interface to the application layer. */ void wap_appl_init(Cfg *cfg) { gw_assert(run_status == limbo); queue = gwlist_create(); fetches = counter_create(); gwlist_add_producer(queue); run_status = running; charsets = wml_charsets(); caller = http_caller_create(); gwthread_create(main_thread, NULL); gwthread_create(return_replies_thread, NULL); if (cfg != NULL) have_ppg = 1; else have_ppg = 0; } void wap_appl_shutdown(void) { gw_assert(run_status == running); run_status = terminating; gwlist_remove_producer(queue); gwthread_join_every(main_thread); http_caller_signal_shutdown(caller); gwthread_join_every(return_replies_thread); wap_map_destroy(); wap_map_user_destroy(); http_caller_destroy(caller); gwlist_destroy(queue, wap_event_destroy_item); gwlist_destroy(charsets, octstr_destroy_item); counter_destroy(fetches); } void wap_appl_dispatch(WAPEvent *event) { gw_assert(run_status == running); gwlist_produce(queue, event); } long wap_appl_get_load(void) { gw_assert(run_status == running); return counter_value(fetches) + gwlist_len(queue); } /*********************************************************************** * Private functions. */ /* * When we have a push event, create ota indication or confirmation and send * it to ppg module. * Because Accept-Application and Bearer-Indication are optional, we cannot * rely on them. We must ask ppg main module do we have an open push session * for this initiator. Push is identified by push id. * If there is no ppg configured, do not refer to ppg's sessions' list. */ static void main_thread(void *arg) { WAPEvent *ind, *res; long sid; WAPAddrTuple *tuple; while (run_status == running && (ind = gwlist_consume(queue)) != NULL) { switch (ind->type) { case S_MethodInvoke_Ind: res = wap_event_create(S_MethodInvoke_Res); res->u.S_MethodInvoke_Res.server_transaction_id = ind->u.S_MethodInvoke_Ind.server_transaction_id; res->u.S_MethodInvoke_Res.session_id = ind->u.S_MethodInvoke_Ind.session_id; wsp_session_dispatch_event(res); start_fetch(ind); break; case S_Unit_MethodInvoke_Ind: start_fetch(ind); break; case S_Connect_Ind: tuple = ind->u.S_Connect_Ind.addr_tuple; if (have_ppg && wap_push_ppg_have_push_session_for(tuple)) { indicate_push_connection(ind); } else { res = wap_event_create(S_Connect_Res); /* FIXME: Not yet used by WSP layer */ res->u.S_Connect_Res.server_headers = NULL; res->u.S_Connect_Res.negotiated_capabilities = negotiate_capabilities(ind->u.S_Connect_Ind.requested_capabilities); res->u.S_Connect_Res.session_id = ind->u.S_Connect_Ind.session_id; wsp_session_dispatch_event(res); } wap_event_destroy(ind); break; case S_Disconnect_Ind: sid = ind->u.S_Disconnect_Ind.session_handle; if (have_ppg && wap_push_ppg_have_push_session_for_sid(sid)) indicate_push_disconnect(ind); wap_event_destroy(ind); break; case S_Suspend_Ind: sid = ind->u.S_Suspend_Ind.session_id; if (have_ppg && wap_push_ppg_have_push_session_for_sid(sid)) indicate_push_suspend(ind); wap_event_destroy(ind); break; case S_Resume_Ind: sid = ind->u.S_Resume_Ind.session_id; if (have_ppg && wap_push_ppg_have_push_session_for_sid(sid)) { indicate_push_resume(ind); } else { res = wap_event_create(S_Resume_Res); res->u.S_Resume_Res.server_headers = NULL; res->u.S_Resume_Res.session_id = ind->u.S_Resume_Ind.session_id; wsp_session_dispatch_event(res); } wap_event_destroy(ind); break; case S_MethodResult_Cnf: wap_event_destroy(ind); break; case S_ConfirmedPush_Cnf: confirm_push(ind); wap_event_destroy(ind); break; case S_MethodAbort_Ind: /* XXX Interrupt the fetch thread somehow */ wap_event_destroy(ind); break; case S_PushAbort_Ind: indicate_push_abort(ind); wap_event_destroy(ind); break; case Pom_Connect_Res: response_push_connection(ind); wap_event_destroy(ind); break; default: panic(0, "WAP-APPL: Can't handle %s event", wap_event_name(ind->type)); break; } /* switch */ } /* while */ } /* * Tries to convert or compile a specific content-type to * it's complementing one. It does not convert if the client has explicitely * told us via Accept: header that a specific type is supported. * Returns 1 if an convertion has been successfull, * -1 if an convertion failed and 0 if no convertion routine * was maching this content-type */ static int convert_content(struct content *content, List *request_headers, int allow_empty) { Octstr *new_body; int failed = 0; int i; for (i = 0; i < NUM_CONVERTERS; i++) { if (octstr_str_compare(content->type, converters[i].type) == 0 && !http_type_accepted(request_headers, octstr_get_cstr(content->type))) { debug("wap.convert",0,"WSP: Converting from <%s> to <%s>", octstr_get_cstr(content->type), converters[i].result_type); /* * Note: if request is HEAD, body is empty and we still need to adapt * content-type but we don't need to convert a 0 bytes body */ if (allow_empty && octstr_len(content->body) == 0) return 1; new_body = converters[i].convert(content); if (new_body != NULL) { long s = octstr_len(content->body); octstr_destroy(content->body); octstr_destroy(content->type); content->body = new_body; content->type = octstr_create(converters[i].result_type); debug("wap.convert",0,"WSP: Content-type is " "now <%s>, size %ld bytes (before: %ld bytes), content body is:", converters[i].result_type, octstr_len(new_body), s); octstr_dump(new_body, 0); return 1; } debug("wap.convert",0,"WSP: Content convertion failed!"); failed = 1; } } return (failed ? -1 : 0); } /* * Tries to deconvert or decompile a specific content-type to * it's complementing one. * Returns 1 if an deconvertion has been successfull, * -1 if an deconvertion failed and 0 if no deconvertion routine * was maching this content-type */ static int deconvert_content(struct content *content) { Octstr *new_body; int failed = 0; int i; debug("wap.deconvert",0,"WSP deconvert: Trying to deconvert:"); octstr_dump(content->body, 0); for (i = 0; i < NUM_DECONVERTERS; i++) { if (octstr_str_compare(content->type, deconverters[i].type) == 0) { debug("wap.deconvert",0,"WSP: Deconverting from <%s> to <%s>", octstr_get_cstr(content->type), deconverters[i].result_type); new_body = deconverters[i].deconvert(content); if (new_body != NULL) { long s = octstr_len(content->body); octstr_destroy(content->body); octstr_destroy(content->type); content->body = new_body; content->type = octstr_create(deconverters[i].result_type); debug("wap.convert",0,"WSP: Content-type is " "now <%s>, size %ld bytes (before: %ld bytes), content body is:", deconverters[i].result_type, octstr_len(new_body), s); octstr_dump(new_body, 0); return 1; } debug("wap.deconvert",0,"WSP: Content convertion failed!"); failed = 1; } } return (failed ? -1 : 0); } /* Add a header identifying our gateway version */ static void add_kannel_version(List *headers) { http_header_add(headers, "X-WAP-Gateway", GW_NAME "/" GW_VERSION); } /* Add Accept-Charset: headers for stuff the WML compiler can * convert to UTF-8. */ /* XXX This is not really correct, since we will not be able * to handle those charsets for all content types, just WML/XHTML. */ static void add_charset_headers(List *headers) { if (!http_charset_accepted(headers, "utf-8")) http_header_add(headers, "Accept-Charset", "utf-8"); } /* Add Accept: headers for stuff we can convert for the phone */ static void add_accept_headers(List *headers) { int i; for (i = 0; i < NUM_CONVERTERS; i++) { if (http_type_accepted(headers, "*/*") || ( http_type_accepted(headers, converters[i].result_type) && !http_type_accepted(headers, converters[i].type))) { http_header_add(headers, "Accept", converters[i].type); } } } /* Add X-WAP-Network-Client-IP: header to proxy client IP to HTTP server */ static void add_network_info(List *headers, WAPAddrTuple *addr_tuple) { if (octstr_len(addr_tuple->remote->address) > 0) { http_header_add(headers, "X-WAP-Network-Client-IP", octstr_get_cstr(addr_tuple->remote->address)); } } /* Add X-WAP-Session-ID: header to request */ static void add_session_id(List *headers, long session_id) { if (session_id != -1) { char buf[40]; sprintf(buf, "%ld", session_id); http_header_add(headers, "X-WAP-Session-ID", buf); } } /* Add X-WAP-Client-SDU-Size: to provide information on client capabilities */ static void add_client_sdu_size(List *headers, long sdu_size) { if (sdu_size > 0) { Octstr *buf; buf = octstr_format("%ld", sdu_size); http_header_add(headers, "X-WAP-Client-SDU-Size", octstr_get_cstr(buf)); octstr_destroy(buf); } } /* Add proxy Via: header to request with our Kannel version */ static void add_via(List *headers) { Octstr *os; Octstr *version; version = http_header_value(headers, octstr_imm("Encoding-Version")); os = octstr_format("WAP/%s %S (" GW_NAME "/%s)", (version ? octstr_get_cstr(version) : "1.1"), get_official_name(), GW_VERSION); http_header_add(headers, "Via", octstr_get_cstr(os)); octstr_destroy(os); octstr_destroy(version); } /* * Add an X-WAP.TOD header to the response headers. It is defined in * the "WAP Caching Model" specification. * We generate it in textual form and let WSP header packing convert it * to binary form. */ static void add_x_wap_tod(List *headers) { Octstr *gateway_time; gateway_time = date_format_http(time(NULL)); if (gateway_time == NULL) { warning(0, "Could not add X-WAP.TOD response header."); return; } http_header_add(headers, "X-WAP.TOD", octstr_get_cstr(gateway_time)); octstr_destroy(gateway_time); } /* Add MSISDN provisioning information to HTTP header */ static void add_msisdn(List *headers, WAPAddrTuple *addr_tuple, Octstr *send_msisdn_header) { Octstr *msisdn = NULL; Octstr *value = NULL; if (send_msisdn_header == NULL || octstr_len(send_msisdn_header) == 0) return; /* * Security considerations. If there are headers with the header name we * use to pass on the MSISDN number, then remove them. */ if ((value = http_header_value(headers, send_msisdn_header)) != NULL) { warning(0, "MSISDN header <%s> already present on request, " "header value=<%s>", octstr_get_cstr(send_msisdn_header), octstr_get_cstr(value)); http_header_remove_all(headers, octstr_get_cstr(send_msisdn_header)); } /* * XXX Add generic msisdn provisioning cleanly in here! * See revision 1.89 for Bruno's try. */ /* We do not accept NULL values to be added to the HTTP header */ if ((msisdn = radius_acct_get_msisdn(addr_tuple->remote->address)) != NULL) { http_header_add(headers, octstr_get_cstr(send_msisdn_header), octstr_get_cstr(msisdn)); } octstr_destroy(value); octstr_destroy(msisdn); } /* * Map WSP UAProf headers 'Profile', 'Profile-Diff' to W-HTTP headers * 'X-WAP-Profile', 'X-WAP-Profile-Diff' according to WAP-248-UAProf-20011020-a, * section 9.2.3.3. */ static void map_uaprof_headers(List *headers) { Octstr *os; Octstr *version; version = http_header_value(headers, octstr_imm("Encoding-Version")); os = octstr_format("WAP/%s %S (" GW_NAME "/%s)", (version ? octstr_get_cstr(version) : "1.1"), get_official_name(), GW_VERSION); http_header_add(headers, "Via", octstr_get_cstr(os)); octstr_destroy(os); octstr_destroy(version); } /* XXX DAVI: Disabled in cvs revision 1.81 for Opengroup tests static void add_referer_url(List *headers, Octstr *url) { if (octstr_len(url) > 0) { http_header_add(headers, "Referer", octstr_get_cstr(url)); } } */ static void set_referer_url(Octstr *url, WSPMachine *sm) { gw_assert(url != NULL); gw_assert(sm != NULL); octstr_destroy(sm->referer_url); sm->referer_url = octstr_duplicate(url); } static Octstr *get_referer_url(const WSPMachine *sm) { return sm ? sm->referer_url : NULL; } /* * Return the reply from an HTTP request to the phone via a WSP session. */ static void return_session_reply(long server_transaction_id, long status, List *headers, Octstr *body, long session_id) { WAPEvent *e; e = wap_event_create(S_MethodResult_Req); e->u.S_MethodResult_Req.server_transaction_id = server_transaction_id; e->u.S_MethodResult_Req.status = status; e->u.S_MethodResult_Req.response_headers = headers; e->u.S_MethodResult_Req.response_body = body; e->u.S_MethodResult_Req.session_id = session_id; wsp_session_dispatch_event(e); } /* * Return the reply from an HTTP request to the phone via connectionless * WSP. */ static void return_unit_reply(WAPAddrTuple *tuple, long transaction_id, long status, List *headers, Octstr *body) { WAPEvent *e; e = wap_event_create(S_Unit_MethodResult_Req); e->u.S_Unit_MethodResult_Req.addr_tuple = wap_addr_tuple_duplicate(tuple); e->u.S_Unit_MethodResult_Req.transaction_id = transaction_id; e->u.S_Unit_MethodResult_Req.status = status; e->u.S_Unit_MethodResult_Req.response_headers = headers; e->u.S_Unit_MethodResult_Req.response_body = body; wsp_unit_dispatch_event(e); } static void normalize_charset(struct content * content, List* device_headers) { Octstr* charset; if ((charset = find_charset_encoding(content->body)) == NULL) { if (octstr_len(content->charset) > 0) { charset = octstr_duplicate(content->charset); } else { charset = octstr_imm("UTF-8"); } } debug("wap-appl",0,"Normalizing charset from %s", octstr_get_cstr(charset)); if (octstr_case_compare(charset, octstr_imm("UTF-8")) != 0 && !http_charset_accepted(device_headers, octstr_get_cstr(charset))) { if (!http_charset_accepted(device_headers, "UTF-8")) { warning(0, "WSP: Device doesn't support charset <%s> neither UTF-8", octstr_get_cstr(charset)); } else { debug("wsp",0,"Converting wml/xhtml from charset <%s> to UTF-8", octstr_get_cstr(charset)); if (charset_convert(content->body, octstr_get_cstr(charset), "UTF-8") >= 0) { octstr_destroy(content->charset); content->charset = octstr_create("UTF-8"); } } } octstr_destroy(charset); } /* * Return an HTTP reply back to the phone. */ static void return_reply(int status, Octstr *content_body, List *headers, long sdu_size, WAPEvent *orig_event, long session_id, Octstr *method, Octstr *url, int x_wap_tod, List *request_headers, Octstr *msisdn) { struct content content; int converted; WSPMachine *sm; List *device_headers, *t_headers; WAPAddrTuple *addr_tuple; Octstr *ua, *server; content.url = url; content.body = content_body; content.version = content.type = content.charset = NULL; server = ua = NULL; /* Get session machine for this session. If this was a connection-less * request be obviously will not find any session machine entry. */ sm = find_session_machine_by_id(session_id); device_headers = gwlist_create(); /* ensure we pass only the original headers to the convertion routine */ t_headers = (orig_event->type == S_MethodInvoke_Ind) ? orig_event->u.S_MethodInvoke_Ind.session_headers : NULL; if (t_headers != NULL) http_header_combine(device_headers, t_headers); t_headers = (orig_event->type == S_MethodInvoke_Ind) ? orig_event->u.S_MethodInvoke_Ind.request_headers : orig_event->u.S_Unit_MethodInvoke_Ind.request_headers; if (t_headers != NULL) http_header_combine(device_headers, t_headers); /* * We are acting as a proxy. Hence ensure we log a correct HTTP response * code to our access-log file to allow identification of failed proxying * requests in the main accesss-log. */ /* get client IP and User-Agent identifier */ addr_tuple = (orig_event->type == S_MethodInvoke_Ind) ? orig_event->u.S_MethodInvoke_Ind.addr_tuple : orig_event->u.S_Unit_MethodInvoke_Ind.addr_tuple; ua = http_header_value(request_headers, octstr_imm("User-Agent")); if (headers != NULL) { /* get response content type and Server identifier */ http_header_get_content_type(headers, &content.type, &content.charset); server = http_header_value(headers, octstr_imm("Server")); } /* log the access */ /* XXX make this configurable in the future */ alog("%s %s %s <%s> (%s, charset='%s') %ld %d <%s> <%s>", octstr_get_cstr(addr_tuple->remote->address), msisdn ? octstr_get_cstr(msisdn) : "-", octstr_get_cstr(method), octstr_get_cstr(url), content.type ? octstr_get_cstr(content.type) : "", content.charset ? octstr_get_cstr(content.charset) : "", octstr_len(content.body), status < 0 ? HTTP_BAD_GATEWAY : status, ua ? octstr_get_cstr(ua) : "", server ? octstr_get_cstr(server) : ""); octstr_destroy(ua); octstr_destroy(server); if (status < 0) { error(0, "WSP: HTTP lookup failed, oops."); /* smart WSP error messaging?! */ if (wsp_smart_errors) { Octstr *referer_url; status = HTTP_OK; content.type = octstr_create("text/vnd.wap.wml"); content.charset = octstr_create(""); /* * check if a referer for this URL exists and * get back to the previous page in this case */ if ((referer_url = get_referer_url(find_session_machine_by_id(session_id)))) { content.body = error_requesting_back(url, referer_url); debug("wap.wsp",0,"WSP: returning smart error WML deck for referer URL"); } /* * if there is no referer to retun to, check if we have a * device-home defined and return to that, otherwise simply * drop an error wml deck. */ else if (device_home != NULL) { content.body = error_requesting_back(url, device_home); debug("wap.wsp",0,"WSP: returning smart error WML deck for device-home URL"); } else { content.body = error_requesting(url); debug("wap.wsp",0,"WSP: returning smart error WML deck"); } /* * if we did not connect at all there is no content in * the headers list, so create for the upcoming transformation */ if (headers == NULL) headers = http_create_empty_headers(); converted = convert_content(&content, device_headers, 0); if (converted == 1) http_header_mark_transformation(headers, content.body, content.type); } else { /* no WSP smart error messaging */ status = HTTP_BAD_GATEWAY; content.type = octstr_create("text/plain"); content.charset = octstr_create(""); content.body = octstr_create(""); } } else { /* received response by HTTP server */ #ifdef ENABLE_COOKIES if (session_id != -1) if (get_cookies(headers, find_session_machine_by_id(session_id)) == -1) error(0, "WSP: Failed to extract cookies"); #endif /* * XXX why do we transcode charsets on the content body here?! * Why is this not in the scope of the HTTP server, rather * then doing this inside Kannel?! st. */ /* * Adapts content body's charset to device. * If device doesn't support body's charset but supports UTF-8, this * block tries to convert body to UTF-8. * (This is required for Sharp GX20 for example) */ if (octstr_search(content.type, octstr_imm("text/vnd.wap.wml"), 0) >= 0 || octstr_search(content.type, octstr_imm("application/xhtml+xml"), 0) >= 0 || octstr_search(content.type, octstr_imm("application/vnd.wap.xhtml+xml"), 0) >= 0) { normalize_charset(&content, device_headers); } /* set WBXML Encoding-Version for wml->wmlc conversion */ if (sm != NULL) { content.version = http_header_value(sm->http_headers, octstr_imm("Encoding-Version")); } else { content.version = NULL; } /* convert content-type by our own converter table */ converted = convert_content(&content, device_headers, octstr_compare(method, octstr_imm("HEAD")) == 0); if (converted < 0) { warning(0, "WSP: All converters for `%s' at `%s' failed.", octstr_get_cstr(content.type), octstr_get_cstr(url)); /* * Don't change status; just send the client what we did get. * Or if smart error messages are configured, send a wmlc deck * with accurate information. */ if (wsp_smart_errors) { octstr_destroy(content.body); octstr_destroy(content.charset); content.body = error_converting(url, content.type); content.charset = octstr_create("UTF-8"); debug("wap.wsp",0,"WSP: returning smart error WML deck for failed converters"); converted = convert_content(&content, device_headers, 0); if (converted == 1) http_header_mark_transformation(headers, content.body, content.type); } } else if (converted == 1) { http_header_mark_transformation(headers, content.body, content.type); /* * set referer URL to WSPMachine, but only if this was a converted * content-type, like .wml */ if (session_id != -1) { debug("wap.wsp.http",0,"WSP: Setting Referer URL to <%s>", octstr_get_cstr(url)); if ((sm = find_session_machine_by_id(session_id)) != NULL) { set_referer_url(url, sm); } else { error(0,"WSP: Failed to find session machine for ID %ld", session_id); } } } /* if converted == 0 then we pass the content wihtout modification */ } if (headers == NULL) headers = http_create_empty_headers(); http_remove_hop_headers(headers); http_header_remove_all(headers, "X-WAP.TOD"); if (x_wap_tod) add_x_wap_tod(headers); if (content.body == NULL) content.body = octstr_create(""); /* * Deal with otherwise wap-aware servers that return text/html error * messages if they report an error. * (Normally we leave the content type alone even if the client doesn't * claim to accept it, because the server might know better than the * gateway.) */ if (http_status_class(status) != HTTP_STATUS_SUCCESSFUL && !http_type_accepted(request_headers, octstr_get_cstr(content.type))) { warning(0, "WSP: Content type <%s> not supported by client," " deleting body.", octstr_get_cstr(content.type)); octstr_destroy(content.body); content.body = octstr_create(""); octstr_destroy(content.type); content.type = octstr_create("text/plain"); http_header_mark_transformation(headers, content.body, content.type); } /* remove body if request method was HEAD, we act strictly here */ else if (octstr_compare(method, octstr_imm("HEAD")) == 0) { octstr_destroy(content.body); content.body = octstr_create(""); /* change to text/plain if received content-type is not accepted */ if (!http_type_accepted(request_headers, "*/*") && !http_type_accepted(request_headers, octstr_get_cstr(content.type))) { octstr_destroy(content.type); content.type = octstr_create("text/plain"); } debug("wsp",0,"WSP: HEAD request, removing body, content-type is now <%s>", octstr_get_cstr(content.type)); http_header_mark_transformation(headers, content.body, content.type); } #ifdef ENABLE_NOT_ACCEPTED /* Returns HTTP response 406 if content-type is not supported by device */ else if (request_headers && content.type && !http_type_accepted(request_headers, octstr_get_cstr(content.type)) && !http_type_accepted(request_headers, "*/*")) { warning(0, "WSP: content-type <%s> not supported", octstr_get_cstr(content.type)); status = HTTP_NOT_ACCEPTABLE; octstr_destroy(content.type); content.type = octstr_create("text/plain"); octstr_destroy(content.charset); octstr_destroy(content.body); content.charset = octstr_create(""); content.body = octstr_create(""); http_header_mark_transformation(headers, content.body, content.type); } #endif /* * If the response is too large to be sent to the client, * suppress it and inform the client. */ if (octstr_len(content.body) > sdu_size && sdu_size > 0) { /* * Only change the status if it indicated success. * If it indicated an error, then that information is * more useful to the client than our "Bad Gateway" would be. * The too-large body is probably an error page in html. */ /* XXX add WSP smart messaging here too */ if (http_status_class(status) == HTTP_STATUS_SUCCESSFUL) status = HTTP_BAD_GATEWAY; warning(0, "WSP: Entity at %s too large (size %ld B, limit %lu B)", octstr_get_cstr(url), octstr_len(content.body), sdu_size); octstr_destroy(content.body); content.body = octstr_create(""); http_header_mark_transformation(headers, content.body, content.type); } if (orig_event->type == S_MethodInvoke_Ind) { return_session_reply(orig_event->u.S_MethodInvoke_Ind.server_transaction_id, status, headers, content.body, session_id); } else { return_unit_reply(orig_event->u.S_Unit_MethodInvoke_Ind.addr_tuple, orig_event->u.S_Unit_MethodInvoke_Ind.transaction_id, status, headers, content.body); } octstr_destroy(content.version); /* body was re-used above */ octstr_destroy(content.type); /* body was re-used above */ octstr_destroy(content.charset); octstr_destroy(url); /* same as content.url */ http_destroy_headers(device_headers); counter_decrease(fetches); } /* * This thread receives replies from HTTP layer and sends them back to * the phone. */ static void return_replies_thread(void *arg) { Octstr *body; struct request_data *p; int status; Octstr *final_url; List *headers; while (run_status == running) { p = http_receive_result(caller, &status, &final_url, &headers, &body); if (p == NULL) break; return_reply(status, body, headers, p->client_SDU_size, p->event, p->session_id, p->method, p->url, p->x_wap_tod, p->request_headers, p->msisdn); wap_event_destroy(p->event); http_destroy_headers(p->request_headers); octstr_destroy(p->msisdn); gw_free(p); octstr_destroy(final_url); } } /* * This WML deck is returned when the user asks for the magic * URL "kannel:alive". */ #define HEALTH_DECK \ "" \ "" \ "

Ok

" static void start_fetch(WAPEvent *event) { int ret; long client_SDU_size; /* 0 means no limit */ Octstr *url; List *session_headers; List *request_headers; List *actual_headers; List *resp_headers; WAPAddrTuple *addr_tuple; long session_id; Octstr *content_body; Octstr *method; /* type of request, normally a get or a post */ Octstr *request_body; int x_wap_tod; /* X-WAP.TOD header was present in request */ Octstr *magic_url; struct request_data *p; Octstr *send_msisdn_query, *send_msisdn_header, *send_msisdn_format; int accept_cookies; Octstr *msisdn; counter_increase(fetches); if (event->type == S_MethodInvoke_Ind) { struct S_MethodInvoke_Ind *p; /* WSP, connection orientated */ p = &event->u.S_MethodInvoke_Ind; session_headers = p->session_headers; request_headers = p->request_headers; url = octstr_duplicate(p->request_uri); addr_tuple = p->addr_tuple; session_id = p->session_id; client_SDU_size = p->client_SDU_size; request_body = octstr_duplicate(p->request_body); method = p->method; } else { struct S_Unit_MethodInvoke_Ind *p; /* WDP, non orientated */ p = &event->u.S_Unit_MethodInvoke_Ind; session_headers = NULL; request_headers = p->request_headers; url = octstr_duplicate(p->request_uri); addr_tuple = p->addr_tuple; session_id = -1; client_SDU_size = 0; /* No limit */ request_body = octstr_duplicate(p->request_body); method = p->method; } msisdn = radius_acct_get_msisdn(addr_tuple->remote->address); info(0, "Fetching URL <%s> for MSISDN <%s>, IP <%s:%ld>", octstr_get_cstr(url), msisdn ? octstr_get_cstr(msisdn) : "", addr_tuple->remote->address ? octstr_get_cstr(addr_tuple->remote->address) : "", addr_tuple->remote->port); /* * XXX this URL mapping needs to be rebuild! st. */ /* try to rewrite URL */ wap_map_url(&url, &send_msisdn_query, &send_msisdn_header, &send_msisdn_format, &accept_cookies); /* if no mapping found, then use our RADIUS acct proxy header */ if (send_msisdn_header == NULL) send_msisdn_header = octstr_create("X-WAP-Network-Client-MSISDN"); actual_headers = gwlist_create(); if (session_headers != NULL) http_header_combine(actual_headers, session_headers); if (request_headers != NULL) http_header_combine(actual_headers, request_headers); x_wap_tod = http_header_remove_all(actual_headers, "X-WAP.TOD"); add_accept_headers(actual_headers); add_charset_headers(actual_headers); add_network_info(actual_headers, addr_tuple); add_client_sdu_size(actual_headers, client_SDU_size); add_via(actual_headers); #ifdef ENABLE_COOKIES /* DAVI: to finish - accept_cookies -1, * use global accept-cookies, 0 = no, 1 = yes ? */ if (accept_cookies != 0 && (session_id != -1) && /* DAVI (set_cookies(url, actual_headers, find_session_machine_by_id(session_id)) == -1)) */ (set_cookies(actual_headers, find_session_machine_by_id(session_id)) == -1)) error(0, "WSP: Failed to add cookies"); #endif /* set referer URL to HTTP header from WSPMachine */ /* * XXX This makes Open Group's test suite wml/events/tasks/go/5 failing, * which requires that device is *not* sending referer, but Kannel drops * it in. We have to remove this for now. */ /* if (session_id != -1) { if ((referer_url = get_referer_url(find_session_machine_by_id(session_id))) != NULL) { add_referer_url(actual_headers, referer_url); } } */ add_kannel_version(actual_headers); add_session_id(actual_headers, session_id); add_msisdn(actual_headers, addr_tuple, send_msisdn_header); octstr_destroy(send_msisdn_query); octstr_destroy(send_msisdn_header); octstr_destroy(send_msisdn_format); http_remove_hop_headers(actual_headers); http_header_pack(actual_headers); magic_url = octstr_imm("kannel:alive"); /* check if this request is a call for our magic URL */ if (octstr_str_compare(method, "GET") == 0 && octstr_compare(url, magic_url) == 0) { ret = HTTP_OK; resp_headers = gwlist_create(); http_header_add(resp_headers, "Content-Type", "text/vnd.wap.wml"); content_body = octstr_create(HEALTH_DECK); octstr_destroy(request_body); return_reply(ret, content_body, resp_headers, client_SDU_size, event, session_id, method, url, x_wap_tod, actual_headers, msisdn); wap_event_destroy(event); http_destroy_headers(actual_headers); octstr_destroy(msisdn); } /* otherwise it should be a GET, POST or HEAD request type */ else if (octstr_str_compare(method, "GET") == 0 || octstr_str_compare(method, "POST") == 0 || octstr_str_compare(method, "HEAD") == 0) { /* we don't allow a body within a GET or HEAD request */ if (request_body != NULL && (octstr_str_compare(method, "GET") == 0 || octstr_str_compare(method, "HEAD") == 0)) { octstr_destroy(request_body); request_body = NULL; } /* * Call deconvert_content() here for transformations of binary * encoded POST requests from the client into plain text decoded * POST requests for the HTTP server. * Mainly this is used for multipart/form-data transmissions, * including MMS on-the-fly message decoding. * When we are doing mms, the phone POSTs contents and acknowled- * gements. In this case, we dont do not deconvert anything. */ if (octstr_str_compare(method, "POST") == 0 && request_body && octstr_len(request_body)) { struct content content; int converted; http_header_get_content_type(actual_headers, &content.type, &content.charset); content.body = request_body; converted = deconvert_content(&content); if (converted == 1) http_header_mark_transformation(actual_headers, content.body, content.type); request_body = content.body; octstr_destroy(content.type); octstr_destroy(content.charset); } /* struct that is used for the HTTP response identifier */ p = gw_malloc(sizeof(*p)); p->client_SDU_size = client_SDU_size; p->event = event; p->session_id = session_id; p->method = method; p->url = url; p->x_wap_tod = x_wap_tod; p->request_headers = actual_headers; p->msisdn = msisdn; /* issue the request to the HTTP server */ http_start_request(caller, http_name2method(method), url, actual_headers, request_body, 0, p, NULL); octstr_destroy(request_body); } /* we don't support the WSP/HTTP method the client asked us */ else { error(0, "WSP: Method %s not supported.", octstr_get_cstr(method)); content_body = octstr_create(""); resp_headers = http_create_empty_headers(); ret = HTTP_NOT_IMPLEMENTED; octstr_destroy(request_body); return_reply(ret, content_body, resp_headers, client_SDU_size, event, session_id, method, url, x_wap_tod, actual_headers, msisdn); wap_event_destroy(event); http_destroy_headers(actual_headers); octstr_destroy(msisdn); } } /* Shut up WMLScript compiler status/trace messages. */ static void dev_null(const char *data, size_t len, void *context) { /* nothing */ } static Octstr *convert_wml_to_wmlc(struct content *content) { Octstr *wmlc; int ret; /* content->charset is passed from the HTTP header parsing */ ret = wml_compile(content->body, content->charset, &wmlc, content->version); /* wmlc is created implicitely in wml_compile() */ if (ret == 0) return wmlc; octstr_destroy(wmlc); warning(0, "WSP: WML compilation failed."); return NULL; } static Octstr *convert_wmlscript_to_wmlscriptc(struct content *content) { WsCompilerParams params; WsCompilerPtr compiler; WsResult result; unsigned char *result_data; size_t result_size; Octstr *wmlscriptc; memset(¶ms, 0, sizeof(params)); params.use_latin1_strings = 0; params.print_symbolic_assembler = 0; params.print_assembler = 0; params.meta_name_cb = NULL; params.meta_name_cb_context = NULL; params.meta_http_equiv_cb = NULL; params.meta_http_equiv_cb_context = NULL; params.stdout_cb = dev_null; params.stderr_cb = dev_null; compiler = ws_create(¶ms); if (compiler == NULL) { panic(0, "WSP: could not create WMLScript compiler"); } result = ws_compile_data(compiler, octstr_get_cstr(content->url), octstr_get_cstr(content->body), octstr_len(content->body), &result_data, &result_size); if (result != WS_OK) { warning(0, "WSP: WMLScript compilation failed: %s", ws_result_to_string(result)); wmlscriptc = NULL; } else { wmlscriptc = octstr_create_from_data(result_data, result_size); } return wmlscriptc; } /* * XXX There's a big bug in http_get_content_type that * assumes that header parameter is charset without looking at * parameter key. Good!. I'll use its value to catch boundary * value for now * ie. "Content-Type: (foo/bar);something=(value)" it gets value * without caring about what is "something" */ /* DAVI: To-Do static Octstr *convert_multipart_mixed(struct content *content) { Octstr *result = NULL; debug("wap.wsp.multipart.mixed", 0, "WSP.Multipart.Mixed, boundary=[%s]", octstr_get_cstr(content->charset)); result = octstr_duplicate(content->body); return result; } */ static Octstr *deconvert_multipart_formdata(struct content *content) { Octstr *mime; if ((mime_decompile(content->body, &mime)) == 0) return mime; return NULL; } /* DAVI: To-Do static Octstr *deconvert_mms_message(struct content *content) { Octstr *mime; if ((mms_decompile(content->body, &mime)) == 0) return mime; return NULL; } */ /* The interface for capability negotiation is a bit different from * the negotiation at WSP level, to make it easier to program. * The application layer gets a list of requested capabilities, * basically a straight decoding of the WSP level capabilities. * It replies with a list of all capabilities it wants to set or * refuse. (Refuse by setting cap->data to NULL). Any capabilities * it leaves out are considered "unknown; don't care". The WSP layer * will either process those itself, or refuse them. * * At the WSP level, not sending a reply to a capability means accepting * what the client proposed. If the application layer wants this to * happen, it should set cap->data to NULL and cap->accept to 1. * (The WSP layer does not try to guess what kind of reply would be * identical to what the client proposed, because the format of the * reply is often different from the format of the request, and this * is likely to be true for unknown capabilities too.) */ static List *negotiate_capabilities(List *req_caps) { /* Currently we don't know or care about any capabilities, * though it is likely that "Extended Methods" will be * the first. */ return gwlist_create(); } /* * Ota submodule implements indications, responses and confirmations part of * ota. */ /* * If Accept-Application is empty, add header indicating default application * wml ua (see ota 6.4.1). Otherwise decode application id (see http://www. * wapforum.org/wina/push-app-id.htm). FIXME: capability negotiation (no- * thing means default, if so negotiated). * Function does not allocate memory neither for headers nor application_ * headers. * Returns encoded application headers and input header list without them. */ static void check_application_headers(List **headers, List **application_headers) { List *inh; int i; Octstr *appid_name, *coded_octstr; char *appid_value, *coded_value; split_header_list(headers, &inh, "Accept-Application"); if (*headers == NULL || gwlist_len(inh) == 0) { http_header_add(*application_headers, "Accept-Application", "wml ua"); debug("wap.appl.push", 0, "APPL: No push application, assuming wml" " ua"); if (*headers != NULL) http_destroy_headers(inh); return; } i = 0; coded_value = NULL; appid_value = NULL; while (gwlist_len(inh) > 0) { http_header_get(inh, i, &appid_name, &coded_octstr); /* Greatest value reserved by WINA is 0xFF00 0000*/ coded_value = octstr_get_cstr(coded_octstr); if (coded_value != NULL) appid_value = wsp_application_id_to_cstr((long) coded_value); if (appid_value != NULL && coded_value != NULL) http_header_add(*application_headers, "Accept-Application", appid_value); else { error(0, "OTA: Unknown application is, skipping: "); octstr_dump(coded_octstr, 0, GW_ERROR); } i++; } debug("wap.appl.push", 0, "application headers were"); http_header_dump(*application_headers); http_destroy_headers(inh); octstr_destroy(appid_name); octstr_destroy(coded_octstr); } /* * Bearer-Indication field is defined in ota 6.4.1. * Skip the header, if it is malformed or if there is more than one bearer * indication. * Function does not allocate memory neither for headers nor bearer_headers. * Return encoded bearer indication header and input header list without it. */ static void decode_bearer_indication(List **headers, List **bearer_headers) { List *inb; Octstr *name, *coded_octstr; char *value; unsigned char coded_value; if (*headers == NULL) { debug("wap.appl", 0, "APPL: no client headers, continuing"); return; } split_header_list(headers, &inb, "Bearer-Indication"); if (gwlist_len(inb) == 0) { debug("wap.appl.push", 0, "APPL: No bearer indication headers," " continuing"); http_destroy_headers(inb); return; } if (gwlist_len(inb) > 1) { error(0, "APPL: To many bearer indication header(s), skipping" " them"); http_destroy_headers(inb); return; } http_header_get(inb, 0, &name, &coded_octstr); http_destroy_headers(inb); /* Greatest assigned number for a bearer type is 0xff, see wdp, appendix C */ coded_value = octstr_get_char(coded_octstr, 0); value = wsp_bearer_indication_to_cstr(coded_value); if (value != NULL && coded_value != 0) { http_header_add(*bearer_headers, "Bearer-Indication", value); debug("wap.appl.push", 0, "bearer indication header was"); http_header_dump(*bearer_headers); return; } else { error(0, "APPL: Illegal bearer indication value, skipping:"); octstr_dump(coded_octstr, 0, GW_ERROR); http_destroy_headers(*bearer_headers); return; } } /* * Separate headers into two lists, one having all headers named "name" and * the other rest of them. */ static void split_header_list(List **headers, List **new_headers, char *name) { if (*headers == NULL) return; *new_headers = http_header_find_all(*headers, name); http_header_remove_all(*headers, name); } /* * Find headers Accept-Application and Bearer-Indication amongst push headers, * decode them and add them to their proper field. */ static void indicate_push_connection(WAPEvent *e) { WAPEvent *ppg_event; List *push_headers, *application_headers, *bearer_headers; push_headers = http_header_duplicate(e->u.S_Connect_Ind.client_headers); application_headers = http_create_empty_headers(); bearer_headers = http_create_empty_headers(); ppg_event = wap_event_create(Pom_Connect_Ind); ppg_event->u.Pom_Connect_Ind.addr_tuple = wap_addr_tuple_duplicate(e->u.S_Connect_Ind.addr_tuple); ppg_event->u.Pom_Connect_Ind.requested_capabilities = wsp_cap_duplicate_list(e->u.S_Connect_Ind.requested_capabilities); check_application_headers(&push_headers, &application_headers); ppg_event->u.Pom_Connect_Ind.accept_application = application_headers; decode_bearer_indication(&push_headers, &bearer_headers); if (gwlist_len(bearer_headers) == 0) { http_destroy_headers(bearer_headers); ppg_event->u.Pom_Connect_Ind.bearer_indication = NULL; } else ppg_event->u.Pom_Connect_Ind.bearer_indication = bearer_headers; ppg_event->u.Pom_Connect_Ind.push_headers = push_headers; ppg_event->u.Pom_Connect_Ind.session_id = e->u.S_Connect_Ind.session_id; debug("wap.appl", 0, "APPL: making OTA connection indication to PPG"); wap_push_ppg_dispatch_event(ppg_event); } static void indicate_push_disconnect(WAPEvent *e) { WAPEvent *ppg_event; ppg_event = wap_event_create(Pom_Disconnect_Ind); ppg_event->u.Pom_Disconnect_Ind.reason_code = e->u.S_Disconnect_Ind.reason_code; ppg_event->u.Pom_Disconnect_Ind.error_headers = octstr_duplicate(e->u.S_Disconnect_Ind.error_headers); ppg_event->u.Pom_Disconnect_Ind.error_body = octstr_duplicate(e->u.S_Disconnect_Ind.error_body); ppg_event->u.Pom_Disconnect_Ind.session_handle = e->u.S_Disconnect_Ind.session_handle; wap_push_ppg_dispatch_event(ppg_event); } /* * We do not implement acknowledgement headers */ static void confirm_push(WAPEvent *e) { WAPEvent *ppg_event; ppg_event = wap_event_create(Po_ConfirmedPush_Cnf); ppg_event->u.Po_ConfirmedPush_Cnf.server_push_id = e->u.S_ConfirmedPush_Cnf.server_push_id; ppg_event->u.Po_ConfirmedPush_Cnf.session_handle = e->u.S_ConfirmedPush_Cnf.session_id; debug("wap.appl", 0, "OTA: confirming push for ppg"); wap_push_ppg_dispatch_event(ppg_event); } static void indicate_push_abort(WAPEvent *e) { WAPEvent *ppg_event; ppg_event = wap_event_create(Po_PushAbort_Ind); ppg_event->u.Po_PushAbort_Ind.push_id = e->u.S_PushAbort_Ind.push_id; ppg_event->u.Po_PushAbort_Ind.reason = e->u.S_PushAbort_Ind.reason; ppg_event->u.Po_PushAbort_Ind.session_handle = e->u.S_PushAbort_Ind.session_id; debug("wap.push.ota", 0, "OTA: making push abort indication for ppg"); wap_push_ppg_dispatch_event(ppg_event); } static void indicate_push_suspend(WAPEvent *e) { WAPEvent *ppg_event; ppg_event = wap_event_create(Pom_Suspend_Ind); ppg_event->u.Pom_Suspend_Ind.reason = e->u.S_Suspend_Ind.reason; ppg_event->u.Pom_Suspend_Ind.session_id = e->u.S_Suspend_Ind.session_id; wap_push_ppg_dispatch_event(ppg_event); } /* * Find Bearer-Indication amongst client headers, decode it and assign it to * a separate field in the event structure. */ static void indicate_push_resume(WAPEvent *e) { WAPEvent *ppg_event; List *push_headers, *bearer_headers; push_headers = http_header_duplicate(e->u.S_Resume_Ind.client_headers); bearer_headers = http_create_empty_headers(); ppg_event = wap_event_create(Pom_Resume_Ind); ppg_event->u.Pom_Resume_Ind.addr_tuple = wap_addr_tuple_duplicate( e->u.S_Resume_Ind.addr_tuple); decode_bearer_indication(&push_headers, &bearer_headers); if (gwlist_len(bearer_headers) == 0) { http_destroy_headers(bearer_headers); ppg_event->u.Pom_Resume_Ind.bearer_indication = NULL; } else ppg_event->u.Pom_Resume_Ind.bearer_indication = bearer_headers; ppg_event->u.Pom_Resume_Ind.client_headers = push_headers; ppg_event->u.Pom_Resume_Ind.session_id = e->u.S_Resume_Ind.session_id; wap_push_ppg_dispatch_event(ppg_event); } /* * Server headers are mentioned in table in ota 6.4.1, but none of the primit- * ives use them. They are optional in S_Connect_Res, so we do not use them. */ static void response_push_connection(WAPEvent *e) { WAPEvent *wsp_event; gw_assert(e->type = Pom_Connect_Res); wsp_event = wap_event_create(S_Connect_Res); wsp_event->u.S_Connect_Res.session_id = e->u.Pom_Connect_Res.session_id; wsp_event->u.S_Connect_Res.negotiated_capabilities = wsp_cap_duplicate_list(e->u.Pom_Connect_Res.negotiated_capabilities); debug("wap.appl", 0, "APPL: making push connect response"); wsp_session_dispatch_event(wsp_event); }