wap_push_ppg.c
资源名称:gateway-1.2.1 [点击查看]
上传用户:gzpyjq
上传日期:2013-01-31
资源大小:1852k
文件大小:92k
源码类别:
手机WAP编程
开发平台:
WINDOWS
- /*
- * wap_push_ppg.c: General logic of a push proxy gateway.
- *
- * This module implements following Wapforum specifications:
- * WAP-151-PPGService-19990816-a (called afterwards ppg),
- * WAP-164-PAP-19991108-a (pap),
- * WAP-164_100-PAP-20000218-a (pap implementation note).
- *
- * We refer following Wapforum specifications:
- * WAP-145-PushMessage-19990816-a (push message)
- * WAP-200-WDP-20001212-a (wdp)
- * WAP-203-WSP-20000504-a (wsp)
- * WAP-189-PushOTA-20000217-a (ota).
- *
- * In addition, rfcs 1521 and 2045 are referred.
- *
- * By Aarno Syv妌en for Wapit Ltd, Wiral Ltd and Global Networks Inc.
- */
- #include <time.h>
- #include <ctype.h>
- #include "wap_push_ppg.h"
- #include "wap/wap_events.h"
- #include "wap/wsp_caps.h"
- #include "wml_compiler.h"
- #include "wap-appl.h"
- #include "wap/wsp.h"
- #include "wap/wsp_strings.h"
- #include "wap_push_si_compiler.h"
- #include "wap_push_sl_compiler.h"
- #include "wap_push_pap_compiler.h"
- #include "wap_push_pap_mime.h"
- #include "wap_push_ppg_pushuser.h"
- enum {
- TIME_EXPIRED = 0,
- TIME_TOO_EARLY = 1,
- NO_CONSTRAINTS = 2
- };
- /*
- * Default values for configuration variables
- */
- enum {
- DEFAULT_HTTP_PORT = 8080,
- NO_HTTPS_PORT = -1,
- DEFAULT_NUMBER_OF_PUSHES = 100,
- PI_TRUSTED = 1,
- SSL_CONNECTION_OFF = 0,
- DEFAULT_NUMBER_OF_USERS = 1024,
- USER_CONFIGURATION_NOT_ADDED = 0
- };
- enum { USER_CONFIGURATION_ADDED = 1 };
- #define DEFAULT_PPG_URL "/wappush"
- /*****************************************************************************
- *
- * Internal data structures
- *
- * Give the status of the push ppg 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 external event queue for this module
- */
- static List *ppg_queue = NULL;
- /*
- * The internal event queue for this module (allowing listening of many ports)
- */
- static List *pap_queue = NULL;
- /*
- * List of ppg session machines (it is, of currently active sessions)
- */
- static List *ppg_machines = NULL;
- /*
- * List of currently active unit pushes (we need a threadsafe storage for them,
- * because pushes can be cancelled and queried):
- */
- static List *ppg_unit_pushes = NULL;
- /*
- * Counter to store our internal push id.
- */
- static Counter *push_id_counter = NULL;
- /*
- * We need a mapping between HTTPClient structures, used by http library, and
- * push ids, used by ppg.
- */
- static Dict *http_clients = NULL;
- /*
- * Mapping between urls used by pi and push ids used by ppg.
- */
- static Dict *urls = NULL;
- /*
- * Push content packed for compilers (wml, si, sl, co).
- */
- struct content {
- Octstr *body;
- Octstr *type;
- Octstr *charset;
- };
- static wap_dispatch_func_t *dispatch_to_ota;
- static wap_dispatch_func_t *dispatch_to_appl;
- /*
- * Configurable variables of ppg core group (for general configuration of a
- * ppg), with some default values.
- */
- static Octstr *ppg_url = NULL ;
- static long ppg_port = DEFAULT_HTTP_PORT;
- #ifdef HAVE_LIBSSL
- static long ppg_ssl_port = NO_HTTPS_PORT;
- #endif
- static long number_of_pushes = DEFAULT_NUMBER_OF_PUSHES;
- static int trusted_pi = PI_TRUSTED;
- static long number_of_users = DEFAULT_NUMBER_OF_USERS;
- static Octstr *ppg_deny_ip = NULL;
- static Octstr *ppg_allow_ip = NULL;
- static int user_configuration = USER_CONFIGURATION_NOT_ADDED;
- static Octstr *global_sender = NULL;
- #ifdef HAVE_LIBSSL
- static Octstr *ssl_server_cert_file = NULL;
- static Octstr *ssl_server_key_file = NULL;
- #endif
- struct PAPEvent {
- HTTPClient *client;
- Octstr *ip;
- Octstr *url;
- List *push_headers;
- Octstr *mime_content;
- List *cgivars;
- };
- typedef struct PAPEvent PAPEvent;
- /*****************************************************************************
- *
- * Prototypes of internal functions
- *
- * Event handling
- */
- static void ota_read_thread(void *arg);
- static void http_read_thread(void *arg);
- #ifdef HAVE_LIBSSL
- static void https_read_thread(void *arg);
- #endif
- static void handle_internal_event(WAPEvent *e);
- static void pap_request_thread(void *arg);
- static int handle_push_message(HTTPClient **c, WAPEvent *ppg_event, int status);
- static PAPEvent *pap_event_create(Octstr *ip, Octstr *url, List *push_headers,
- Octstr *mime_content, List *cgivars,
- HTTPClient *client);
- static void pap_event_destroy(PAPEvent *p);
- static void pap_event_destroy_item(void *p);
- static void pap_event_unpack(PAPEvent *p, Octstr **ip, Octstr **url,
- List **push_headers, Octstr **mime_content,
- List **cgivars, HTTPClient **client);
- /*
- * Constructors and destructors for machines.
- */
- static PPGSessionMachine *session_machine_create(WAPAddrTuple *tuple,
- WAPEvent *e);
- static void session_machine_destroy(void *p);
- static PPGPushMachine *push_machine_create(WAPEvent *e,
- WAPAddrTuple *tuple);
- static void push_machine_destroy(void *pm);
- static void push_machines_list_destroy(List *pl);
- /*
- * Communicating other modules (ota and appl)
- */
- static void create_session(WAPEvent *e, PPGPushMachine *pm);
- static void request_confirmed_push(long last, PPGPushMachine *pm,
- PPGSessionMachine *sm);
- static void request_unit_push(long last, PPGPushMachine *pm);
- static void request_push(long last, PPGPushMachine *sm);
- static int response_push_connection(WAPEvent *e, PPGSessionMachine *sm);
- static HTTPClient *response_push_message(PPGPushMachine *pm, long code,
- int status);
- /*
- * Functions to find machines using various identifiers, and related help
- * functions.
- */
- static PPGSessionMachine *session_find_using_pi_client_address(Octstr *addr);
- static PPGPushMachine *find_ppg_push_machine_using_pid(PPGSessionMachine *sm,
- long pid);
- static PPGPushMachine *find_ppg_push_machine_using_pi_push_id(
- PPGSessionMachine *sm, Octstr *pi_push_id);
- static PPGPushMachine *find_unit_ppg_push_machine_using_pi_push_id(
- Octstr *pi_push_id);
- static int push_has_pi_push_id(void *a, void *b);
- static int push_has_pid(void *a, void *b);
- static int session_has_pi_client_address(void *a, void *b);
- static int session_has_addr(void *a, void *b);
- static int session_has_sid(void *a, void *b);
- /*
- * Main logic of PPG.
- */
- static int check_capabilities(List *requested, List *assumed);
- static int transform_message(WAPEvent **e, WAPAddrTuple **tuple,
- List *push_headers, int connected, Octstr **type);
- static long check_x_wap_application_id_header(List **push_headers);
- static int pap_convert_content(struct content *content);
- static int pap_get_content(struct content *content);
- static int select_bearer_network(WAPEvent **e);
- static int delivery_time_constraints(WAPEvent *e, PPGPushMachine *pm);
- static void deliver_confirmed_push(long last, PPGPushMachine *pm,
- PPGSessionMachine *sm);
- static PPGPushMachine *deliver_unit_push(long last, PPGPushMachine *pm,
- PPGSessionMachine *sm, int session_exists);
- static int store_push_data(PPGPushMachine **pm, PPGSessionMachine *sm,
- WAPEvent *e, WAPAddrTuple *tuple, int cless);
- static PPGPushMachine *update_push_data_with_attribute(PPGSessionMachine **sm,
- PPGPushMachine *pm, long reason, long status);
- static void remove_push_data(PPGSessionMachine *sm, PPGPushMachine *pm,
- int cless);
- static void remove_session_data(PPGSessionMachine *sm, int status);
- static void remove_pushless_session(PPGSessionMachine *sm);
- static PPGSessionMachine *store_session_data(PPGSessionMachine *sm,
- WAPEvent *e, WAPAddrTuple *tuple, int *session_exists);
- static PPGSessionMachine *update_session_data_with_headers(
- PPGSessionMachine *sm, PPGPushMachine *pm);
- static void deliver_pending_pushes(PPGSessionMachine *sm, int last);
- static PPGPushMachine *abort_delivery(PPGSessionMachine *sm, int status);
- static PPGSessionMachine *update_session_data(PPGSessionMachine *sm, long sid,
- long port, List *caps);
- static int confirmation_requested(WAPEvent *e);
- static int cless_accepted(WAPEvent *e, PPGSessionMachine *sm);
- /*
- * Header functions
- */
- static int headers_acceptable(List *push_headers, Octstr **content_header);
- static int type_is(Octstr *content_header, char *required_type);
- static int get_mime_boundary(List *push_headers, Octstr *content_header,
- Octstr **boundary);
- static void change_header_value(List **push_headers, char *name, char *value);
- static void remove_mime_headers(List **push_headers);
- /*
- * Communicating with pi.
- */
- static void send_bad_message_response(HTTPClient **c, Octstr *body_fragment,
- int code, int status);
- static HTTPClient *send_push_response(WAPEvent *e, int status);
- static void send_to_pi(HTTPClient **c, Octstr *reply_body, int status);
- static void tell_fatal_error(HTTPClient **c, WAPEvent *e, Octstr *url,
- int status, int code);
- /*
- * PPG core authentication (not related to any particular user).
- */
- static int read_ppg_config(Cfg *cfg);
- static int ip_allowed_by_ppg(Octstr *ip);
- /*
- * Interface to various compilers
- */
- static Octstr *convert_wml_to_wmlc(struct content *content);
- static Octstr *convert_si_to_sic(struct content *content);
- static Octstr *convert_sl_to_slc(struct content *content);
- /*
- * Various utility functions
- */
- static Octstr *set_time(void);
- static int deliver_before_test_cleared(Octstr *before, struct tm now);
- static int deliver_after_test_cleared(Octstr *after, struct tm now);
- static void session_machine_assert(PPGSessionMachine *sm);
- static void push_machine_assert(PPGPushMachine *pm);
- static Octstr *tell_ppg_name(void);
- static Octstr *describe_code(long code);
- static long ota_abort_to_pap(long reason);
- static int content_transformable(List *push_headers);
- static WAPAddrTuple *set_addr_tuple(Octstr *address, long cliport,
- long servport, long address_type);
- static WAPAddrTuple *addr_tuple_change_cliport(WAPAddrTuple *tuple, long port);
- static void initialize_time_item_array(long time_data[], struct tm now);
- static int date_item_compare(Octstr *before, long time_data, long pos);
- static long parse_appid_header(Octstr **assigned_code);
- static Octstr *escape_fragment(Octstr *fragment);
- static int coriented_deliverable(long code);
- static int is_phone_number(long type_of_address);
- static void replace_octstr_char(Octstr *os1, Octstr *os2, long *pos);
- /*****************************************************************************
- *
- * EXTERNAL FUNCTIONS
- */
- enum {
- TYPE_HTTP = 0,
- TYPE_HTTPS = 1
- };
- void wap_push_ppg_init(wap_dispatch_func_t *ota_dispatch,
- wap_dispatch_func_t *appl_dispatch, Cfg *cfg)
- {
- user_configuration = read_ppg_config(cfg);
- if (user_configuration != USER_CONFIGURATION_NOT_ADDED) {
- ppg_queue = list_create();
- list_add_producer(ppg_queue);
- pap_queue = list_create();
- list_add_producer(pap_queue);
- push_id_counter = counter_create();
- ppg_machines = list_create();
- ppg_unit_pushes = list_create();
- dispatch_to_ota = ota_dispatch;
- dispatch_to_appl = appl_dispatch;
- http_open_port(ppg_port, TYPE_HTTP);
- #ifdef HAVE_LIBSSL
- if (ppg_ssl_port != NO_HTTPS_PORT)
- http_open_port(ppg_ssl_port, TYPE_HTTPS);
- #endif
- http_clients = dict_create(number_of_pushes, NULL);
- urls = dict_create(number_of_pushes, NULL);
- gw_assert(run_status == limbo);
- run_status = running;
- gwthread_create(ota_read_thread, NULL);
- gwthread_create(http_read_thread, NULL);
- #ifdef HAVE_LIBSSL
- if (ppg_ssl_port != NO_HTTPS_PORT)
- gwthread_create(https_read_thread, NULL);
- #endif
- gwthread_create(pap_request_thread, NULL);
- }
- }
- void wap_push_ppg_shutdown(void)
- {
- if (user_configuration != USER_CONFIGURATION_NOT_ADDED) {
- gw_assert(run_status == running);
- run_status = terminating;
- list_remove_producer(ppg_queue);
- list_remove_producer(pap_queue);
- octstr_destroy(ppg_url);
- http_close_all_ports();
- dict_destroy(http_clients);
- dict_destroy(urls);
- wap_push_ppg_pushuser_list_destroy();
- octstr_destroy(ppg_deny_ip);
- octstr_destroy(ppg_allow_ip);
- octstr_destroy(global_sender);
- gwthread_join_every(http_read_thread);
- #ifdef HAVE_LIBSSL
- if (ppg_ssl_port != NO_HTTPS_PORT)
- gwthread_join_every(https_read_thread);
- #endif
- gwthread_join_every(ota_read_thread);
- gwthread_join_every(pap_request_thread);
- list_destroy(ppg_queue, wap_event_destroy_item);
- list_destroy(pap_queue, pap_event_destroy_item);
- counter_destroy(push_id_counter);
- debug("wap.push.ppg", 0, "PPG: %ld push session machines left.",
- list_len(ppg_machines));
- list_destroy(ppg_machines, session_machine_destroy);
- debug("wap_push_ppg", 0, "PPG: %ld unit pushes left",
- list_len(ppg_unit_pushes));
- list_destroy(ppg_unit_pushes, push_machine_destroy);
- }
- }
- void wap_push_ppg_dispatch_event(WAPEvent *e)
- {
- gw_assert(run_status == running);
- list_produce(ppg_queue, e);
- }
- /*
- * We cannot know port the client is using when it establish the connection.
- * However, we must link session creation with a pending push request. Only
- * data available is the client address, so we check it here.
- * Return non-NULL (pointer to the session machine found), if we have one.
- */
- PPGSessionMachine *wap_push_ppg_have_push_session_for(WAPAddrTuple *tuple)
- {
- PPGSessionMachine *sm;
- gw_assert(tuple);
- sm = list_search(ppg_machines, tuple->remote->address, session_has_addr);
- return sm;
- }
- /*
- * Now initiators are identified by their session id. Return non-NULL (pointer
- * to the session machine found), if we have one. This function are used after
- * wsp has indicated session establishment, giving us a session id.
- */
- PPGSessionMachine *wap_push_ppg_have_push_session_for_sid(long sid)
- {
- PPGSessionMachine *sm;
- gw_assert(sid >= 0);
- sm = list_search(ppg_machines, &sid, session_has_sid);
- return sm;
- }
- /*****************************************************************************
- *
- * INTERNAL FUNCTIONS
- *
- * Read general ppg configuration and configuration specific for users (to the
- * list 'users').
- * Return 1 when an user configuration group is present, 0 otherwise.
- */
- static int read_ppg_config(Cfg *cfg)
- {
- CfgGroup *grp;
- List *list;
- if (cfg == NULL)
- return USER_CONFIGURATION_NOT_ADDED;
- grp = cfg_get_single_group(cfg, octstr_imm("ppg"));
- if ((ppg_url = cfg_get(grp, octstr_imm("ppg-url"))) == NULL)
- ppg_url = octstr_imm("/wappush");
- cfg_get_integer(&ppg_port, grp, octstr_imm("ppg-port"));
- cfg_get_integer(&number_of_pushes, grp, octstr_imm("concurrent-pushes"));
- cfg_get_bool(&trusted_pi, grp, octstr_imm("trusted-pi"));
- cfg_get_integer(&number_of_users, grp, octstr_imm("users"));
- ppg_deny_ip = cfg_get(grp, octstr_imm("ppg-deny-ip"));
- ppg_allow_ip = cfg_get(grp, octstr_imm("ppg-allow-ip"));
- if ((global_sender = cfg_get(grp, octstr_imm("global-sender"))) == NULL)
- global_sender = octstr_imm("1234");
- #ifdef HAVE_LIBSSL
- cfg_get_integer(&ppg_ssl_port, grp, octstr_imm("ppg-ssl-port"));
- ssl_server_cert_file = cfg_get(grp, octstr_imm("ssl-server-cert-file"));
- ssl_server_key_file = cfg_get(grp, octstr_imm("ssl-server-key-file"));
- if (ppg_ssl_port != NO_HTTPS_PORT) {
- if (ssl_server_cert_file == NULL || ssl_server_key_file == NULL)
- panic(0, "cannot continue without server cert and/or key files");
- use_global_server_certkey_file(ssl_server_cert_file, ssl_server_key_file);
- }
- octstr_destroy(ssl_server_cert_file);
- octstr_destroy(ssl_server_key_file);
- #endif
- if ((list = cfg_get_multi_group(cfg, octstr_imm("wap-push-user")))
- == NULL) {
- info(0, "No configuration for any user, continuing without");
- list_destroy(list, NULL);
- cfg_destroy(cfg);
- return USER_CONFIGURATION_NOT_ADDED;
- }
- if (!wap_push_ppg_pushuser_list_add(list, number_of_pushes,
- number_of_users)) {
- panic(0, "unable to create users configuration list, exiting");
- return USER_CONFIGURATION_NOT_ADDED;
- }
- cfg_destroy(cfg);
- return USER_CONFIGURATION_ADDED;
- }
- static int ip_allowed_by_ppg(Octstr *ip)
- {
- if (ip == NULL)
- return 0;
- if (trusted_pi)
- return 1;
- if (ppg_deny_ip == NULL && ppg_allow_ip == NULL) {
- warning(0, "Your ppg core configuration lacks allowed and denied"
- " ip lists");
- return 1;
- }
- if (ppg_deny_ip)
- if (octstr_compare(ppg_deny_ip, octstr_imm("*.*.*.*")) == 0) {
- panic(0, "Your ppg core configuration deny all ips, exiting");
- return 0;
- }
- if (ppg_allow_ip)
- if (octstr_compare(ppg_allow_ip, octstr_imm("*.*.*.*")) == 0) {
- warning(0, "Your ppg core configuration allow all ips");
- return 1;
- }
- if (ppg_deny_ip)
- if (wap_push_ppg_pushuser_search_ip_from_wildcarded_list(ppg_deny_ip, ip,
- octstr_imm(";"), octstr_imm("."))) {
- error(0, "ip found from denied list");
- return 0;
- }
- if (ppg_allow_ip)
- if (wap_push_ppg_pushuser_search_ip_from_wildcarded_list(ppg_allow_ip, ip,
- octstr_imm(";"), octstr_imm("."))) {
- debug("wap.push.ppg.pushuser", 0, "PPG: ip_allowed_by_ppg: ip found"
- " from allowed list");
- return 1;
- }
- warning(0, "did not found ip from any of core lists, deny it");
- return 0;
- }
- /*
- * Event handling functions
- */
- static void ota_read_thread (void *arg)
- {
- WAPEvent *e;
- while (run_status == running && (e = list_consume(ppg_queue)) != NULL) {
- handle_internal_event(e);
- }
- }
- /*
- * Pap event functions handle only copy pointers. They do not allocate memory.
- */
- static PAPEvent *pap_event_create(Octstr *ip, Octstr *url, List *push_headers,
- Octstr *mime_content, List *cgivars,
- HTTPClient *client)
- {
- PAPEvent *p;
- p = gw_malloc(sizeof(PAPEvent));
- p->ip = ip;
- p->url = url;
- p->push_headers = push_headers;
- p->mime_content = mime_content;
- p->cgivars = cgivars;
- p->client = client;
- return p;
- }
- static void pap_event_destroy(PAPEvent *p)
- {
- if (p == NULL)
- return;
- gw_free(p);
- }
- static void pap_event_destroy_item(void *p)
- {
- pap_event_destroy(p);
- }
- static void pap_event_unpack(PAPEvent *p, Octstr **ip, Octstr **url,
- List **push_headers, Octstr **mime_content,
- List **cgivars, HTTPClient **client)
- {
- *ip = p->ip;
- *url = p->url;
- *push_headers = p->push_headers;
- *mime_content = p->mime_content;
- *cgivars = p->cgivars;
- *client = p->client;
- }
- static void http_read_thread(void *arg)
- {
- PAPEvent *p;
- Octstr *ip;
- Octstr *url;
- List *push_headers;
- Octstr *mime_content;
- List *cgivars;
- HTTPClient *client;
- while (run_status == running) {
- client = http_accept_request(ppg_port, &ip, &url, &push_headers,
- &mime_content, &cgivars);
- if (client == NULL)
- break;
- p = pap_event_create(ip, url, push_headers, mime_content, cgivars,
- client);
- list_produce(pap_queue, p);
- }
- }
- #ifdef HAVE_LIBSSL
- static void https_read_thread(void *arg)
- {
- PAPEvent *p;
- Octstr *ip;
- Octstr *url;
- List *push_headers;
- Octstr *mime_content;
- List *cgivars;
- HTTPClient *client;
- while (run_status == running) {
- client = http_accept_request(ppg_ssl_port, &ip, &url, &push_headers,
- &mime_content, &cgivars);
- if (client == NULL)
- break;
- p = pap_event_create(ip, url, push_headers, mime_content, cgivars,
- client);
- list_produce(pap_queue, p);
- }
- }
- #endif
- /*
- * Authorization failure as such causes a challenge to the client (as required
- * by rfc 2617, chapter 1).
- * We store HTTPClient data structure corresponding a given push id, so that
- * we can send responses to the rigth address.
- * Pap chapter 14.4.1 states that we must return http status 202 after we have
- * accepted PAP message, even if it is unparsable. So only the non-existing
- * service error and some authorisation failures are handled at HTTP level.
- * When a phone number was unacceptable, we return a PAP level error, because
- * we cannot know this error before parsing the document.
- */
- static void pap_request_thread(void *arg)
- {
- WAPEvent *ppg_event;
- PAPEvent *p;
- size_t push_len;
- Octstr *pap_content,
- *push_data,
- *rdf_content,
- *mime_content,
- *plos, /* a temporary variable*/
- *boundary,
- *content_header, /* Content-Type MIME header */
- *url,
- *ip,
- *not_found,
- *username = NULL;
- int compiler_status,
- http_status;
- List *push_headers, /* MIME headers themselves */
- *content_headers, /* Headers from the content entity, see
- pap chapters 8.2, 13.1. Rfc 2045
- grammar calls these MIME-part-hea-
- ders */
- *cgivars;
- HTTPClient *client;
- http_status = 0;
- while (run_status == running && (p = list_consume(pap_queue)) != NULL) {
- http_status = HTTP_NOT_FOUND;
- pap_event_unpack(p, &ip, &url, &push_headers, &mime_content,
- &cgivars, &client);
- if (octstr_compare(url, ppg_url) != 0) {
- error(0, "Request <%s> from <%s>: service not found",
- octstr_get_cstr(url), octstr_get_cstr(ip));
- not_found = octstr_imm("Service not specifiedn");
- http_send_reply(client, http_status, push_headers, not_found);
- goto ferror;
- }
- http_status = HTTP_UNAUTHORIZED;
- if (!ip_allowed_by_ppg(ip)) {
- error(0, "Request <%s> from <%s>: ip forbidden, closing the"
- " client", octstr_get_cstr(url), octstr_get_cstr(ip));
- http_close_client(client);
- goto ferror;
- }
- if (!trusted_pi && user_configuration) {
- if (!wap_push_ppg_pushuser_authenticate(client, cgivars, ip,
- push_headers, &username)) {
- error(0, "Request <%s> from <%s>: authorisation failure",
- octstr_get_cstr(url), octstr_get_cstr(ip));
- goto ferror;
- }
- } else { /* J鰎g, this wont disappear again */
- username = octstr_imm("");
- }
- http_status = HTTP_ACCEPTED;
- info(0, "PPG: Accept request <%s> from <%s>", octstr_get_cstr(url),
- octstr_get_cstr(ip));
- if (octstr_len(mime_content) == 0) {
- warning(0, "PPG: No MIME content received, the request"
- " unacceptable");
- send_bad_message_response(&client, octstr_imm("No MIME content"),
- PAP_BAD_REQUEST, http_status);
- if (client == NULL)
- break;
- goto ferror;
- }
- if (!push_headers) {
- warning(0, "PPG: No push headers received , the request"
- " unacceptable");
- send_bad_message_response(&client, octstr_imm("No push headers"),
- PAP_BAD_REQUEST, http_status);
- if (client == NULL)
- break;
- goto ferror;
- }
- octstr_destroy(ip);
- http_remove_hop_headers(push_headers);
- remove_mime_headers(&push_headers);
- if (!headers_acceptable(push_headers, &content_header)) {
- warning(0, "PPG: Unparsable push headers, the request"
- " unacceptable");
- send_bad_message_response(&client, content_header, PAP_BAD_REQUEST,
- http_status);
- if (client == NULL)
- break;
- goto herror;
- }
- if (get_mime_boundary(push_headers, content_header, &boundary) == -1) {
- warning(0, "PPG: No MIME boundary, the request unacceptable");
- send_bad_message_response(&client, content_header, PAP_BAD_REQUEST,
- http_status);
- if (client == NULL)
- break;
- goto berror;
- }
- gw_assert(mime_content);
- if (!mime_parse(boundary, mime_content, &pap_content, &push_data,
- &content_headers, &rdf_content)) {
- send_bad_message_response(&client, mime_content, PAP_BAD_REQUEST,
- http_status);
- if (client == NULL)
- break;
- warning(0, "PPG: unable to parse mime content, the request"
- " unacceptable");
- goto clean;
- } else {
- debug("wap.push.ppg", 0, "PPG: http_read_thread: pap multipart"
- " accepted");
- }
- push_len = octstr_len(push_data);
- http_header_remove_all(push_headers, "Content-Type");
- http_append_headers(push_headers, content_headers);
- change_header_value(&push_headers, "Content-Length",
- octstr_get_cstr(plos = octstr_format("%d", push_len)));
- octstr_destroy(plos);
- octstr_destroy(content_header);
- http_destroy_headers(content_headers);
- ppg_event = NULL;
- if ((compiler_status = pap_compile(pap_content, &ppg_event)) == -2) {
- send_bad_message_response(&client, pap_content, PAP_BAD_REQUEST,
- http_status);
- if (client == NULL)
- break;
- warning(0, "PPG: pap control entity erroneous, the request"
- " unacceptable");
- goto no_compile;
- } else if (compiler_status == -1) {
- send_bad_message_response(&client, pap_content, PAP_BAD_REQUEST,
- http_status);
- if (client == NULL)
- break;
- warning(0, "PPG: non implemented pap feature requested, the"
- " request unacceptable");
- goto no_compile;
- } else {
- if (!dict_put_once(http_clients,
- ppg_event->u.Push_Message.pi_push_id, client)) {
- warning(0, "PPG: duplicate push id, the request unacceptable");
- tell_fatal_error(&client, ppg_event, url, http_status,
- PAP_DUPLICATE_PUSH_ID);
- if (client == NULL)
- break;
- goto not_acceptable;
- }
- dict_put(urls, ppg_event->u.Push_Message.pi_push_id, url);
- if (is_phone_number(ppg_event->u.Push_Message.address_type)) {
- if (!trusted_pi && user_configuration &&
- !wap_push_ppg_pushuser_client_phone_number_acceptable(
- username, ppg_event->u.Push_Message.address_value)) {
- tell_fatal_error(&client, ppg_event, url, http_status,
- PAP_FORBIDDEN);
- if (client == NULL)
- break;
- goto not_acceptable;
- }
- }
- debug("wap.push.ppg", 0, "PPG: http_read_thread: pap control"
- " entity compiled ok");
- ppg_event->u.Push_Message.push_headers =
- http_header_duplicate(push_headers);
- ppg_event->u.Push_Message.push_data = octstr_duplicate(push_data);
- if (!handle_push_message(&client, ppg_event, http_status)) {
- if (client == NULL)
- break;
- goto no_transform;
- }
- }
- pap_event_destroy(p);
- http_destroy_headers(push_headers);
- http_destroy_cgiargs(cgivars);
- octstr_destroy(username);
- octstr_destroy(mime_content);
- octstr_destroy(pap_content);
- octstr_destroy(push_data);
- octstr_destroy(rdf_content);
- octstr_destroy(boundary);
- continue;
- no_transform:
- pap_event_destroy(p);
- http_destroy_headers(push_headers);
- http_destroy_cgiargs(cgivars);
- octstr_destroy(username);
- octstr_destroy(mime_content);
- octstr_destroy(pap_content);
- octstr_destroy(push_data);
- octstr_destroy(rdf_content);
- octstr_destroy(boundary);
- continue;
- no_compile:
- pap_event_destroy(p);
- http_destroy_headers(push_headers);
- http_destroy_cgiargs(cgivars);
- octstr_destroy(username);
- octstr_destroy(mime_content);
- octstr_destroy(push_data);
- octstr_destroy(rdf_content);
- octstr_destroy(boundary);
- octstr_destroy(url);
- continue;
- not_acceptable:
- pap_event_destroy(p);
- http_destroy_headers(push_headers);
- http_destroy_cgiargs(cgivars);
- octstr_destroy(username);
- octstr_destroy(mime_content);
- octstr_destroy(pap_content);
- octstr_destroy(push_data);
- octstr_destroy(rdf_content);
- octstr_destroy(boundary);
- octstr_destroy(url);
- continue;
- clean:
- pap_event_destroy(p);
- http_destroy_headers(push_headers);
- http_destroy_headers(content_headers);
- octstr_destroy(pap_content);
- octstr_destroy(push_data);
- octstr_destroy(rdf_content);
- octstr_destroy(content_header);
- octstr_destroy(boundary);
- octstr_destroy(url);
- continue;
- ferror:
- pap_event_destroy(p);
- http_destroy_headers(push_headers);
- http_destroy_cgiargs(cgivars);
- octstr_destroy(username);
- octstr_destroy(url);
- octstr_destroy(ip);
- octstr_destroy(mime_content);
- continue;
- herror:
- pap_event_destroy(p);
- http_destroy_headers(push_headers);
- http_destroy_cgiargs(cgivars);
- octstr_destroy(username);
- octstr_destroy(url);
- continue;
- berror:
- pap_event_destroy(p);
- http_destroy_headers(push_headers);
- http_destroy_cgiargs(cgivars);
- octstr_destroy(username);
- octstr_destroy(mime_content);
- octstr_destroy(content_header);
- octstr_destroy(boundary);
- octstr_destroy(url);
- continue;
- }
- }
- /*
- * Operations needed when push proxy gateway receives a new push message are
- * defined in ppg Chapter 6. We create machines when error, too, because we
- * must then have a reportable message error state.
- * Output: current HTTP Client state.
- * Return 1 if the push content was OK, 0 if it was not transformable.
- */
- static int handle_push_message(HTTPClient **c, WAPEvent *e, int status)
- {
- int cless,
- session_exists,
- bearer_supported,
- dummy,
- constraints,
- message_transformable,
- coriented_possible;
- long coded_appid_value;
- PPGPushMachine *pm;
- PPGSessionMachine *sm;
- WAPAddrTuple *tuple;
- Octstr *push_data,
- *cliaddr,
- *type;
- List *push_headers;
- push_data = e->u.Push_Message.push_data;
- push_headers = e->u.Push_Message.push_headers;
- cliaddr = e->u.Push_Message.address_value;
- session_exists = 0;
- sm = session_find_using_pi_client_address(cliaddr);
- coded_appid_value = check_x_wap_application_id_header(&push_headers);
- cless = cless_accepted(e, sm);
- message_transformable = transform_message(&e, &tuple, push_headers, cless,
- &type);
- if (!sm && !cless) {
- sm = store_session_data(sm, e, tuple, &session_exists);
- }
- if (!store_push_data(&pm, sm, e, tuple, cless)) {
- warning(0, "PPG: handle_push_message: duplicate push id");
- *c = response_push_message(pm, PAP_DUPLICATE_PUSH_ID, status);
- goto no_start;
- }
- if (!message_transformable) {
- pm = update_push_data_with_attribute(&sm, pm,
- PAP_TRANSFORMATION_FAILURE, PAP_UNDELIVERABLE1);
- if (tuple != NULL)
- *c = response_push_message(pm, PAP_TRANSFORMATION_FAILURE, status);
- else
- *c = response_push_message(pm, PAP_ADDRESS_ERROR, status);
- goto no_transformation;
- }
- dummy = 0;
- pm = update_push_data_with_attribute(&sm, pm, dummy, PAP_PENDING);
- bearer_supported = select_bearer_network(&e);
- if (!bearer_supported) {
- pm = update_push_data_with_attribute(&sm, pm, dummy,
- PAP_UNDELIVERABLE2);
- *c = response_push_message(pm, PAP_REQUIRED_BEARER_NOT_AVAILABLE, status);
- goto no_start;
- }
- if ((constraints = delivery_time_constraints(e, pm)) == TIME_EXPIRED) {
- pm = update_push_data_with_attribute(&sm, pm, PAP_FORBIDDEN,
- PAP_EXPIRED);
- *c = response_push_message(pm, PAP_FORBIDDEN, status);
- goto no_start;
- }
- /*
- * If time is to early for delivering the push message, we do not remove push
- * data. We response PI here, so that "accepted for processing" means "no
- * error messages to come".
- */
- *c = response_push_message(pm, PAP_ACCEPTED_FOR_PROCESSING, status);
- info(0, "PPG: handle_push_message: push message accepted for processing");
- if (constraints == TIME_TOO_EARLY)
- goto store_push;
- if (constraints == NO_CONSTRAINTS) {
- http_header_mark_transformation(pm->push_headers, pm->push_data, type);
- if (sm)
- sm = update_session_data_with_headers(sm, pm);
- if (!confirmation_requested(e)) {
- pm = deliver_unit_push(NOT_LAST, pm, sm, session_exists);
- goto unit_push_delivered;
- }
- if (session_exists) {
- deliver_confirmed_push(NOT_LAST, pm, sm);
- } else {
- coriented_possible = coriented_deliverable(coded_appid_value);
- http_header_remove_all(e->u.Push_Message.push_headers,
- "Content-Type");
- if (coriented_possible) {
- create_session(e, pm);
- } else {
- warning(0, "PPG: handle_push_message: wrong app id for confirmed"
- " push session creation");
- *c = response_push_message(pm, PAP_BAD_REQUEST, status);
- }
- }
- }
- wap_addr_tuple_destroy(tuple);
- octstr_destroy(type);
- wap_event_destroy(e);
- return 1;
- unit_push_delivered:
- wap_addr_tuple_destroy(tuple);
- remove_push_data(sm, pm, cless);
- octstr_destroy(type);
- wap_event_destroy(e);
- return 1;
- store_push:
- wap_addr_tuple_destroy(tuple);
- octstr_destroy(type);
- wap_event_destroy(e);
- return 1;
- no_transformation:
- wap_addr_tuple_destroy(tuple);
- remove_push_data(sm, pm, cless);
- if (sm)
- remove_pushless_session(sm);
- wap_event_destroy(e);
- return 0;
- no_start:
- wap_addr_tuple_destroy(tuple);
- octstr_destroy(type);
- remove_push_data(sm, pm, cless);
- if (sm)
- remove_pushless_session(sm);
- wap_event_destroy(e);
- return 1;
- }
- /*
- * These events come from OTA layer
- */
- static void handle_internal_event(WAPEvent *e)
- {
- long sid,
- pid,
- reason,
- port;
- int http_status;
- PPGPushMachine *pm;
- PPGSessionMachine *sm;
- WAPAddrTuple *tuple;
- List *caps;
- http_status = HTTP_OK;
- switch (e->type) {
- /*
- * Pap, Chapter 11.1.3 states that if client is incapable, we should abort the
- * push and inform PI. We do this here.
- * In addition, we store session id used as an alias for address tuple and do
- * all pushes pending for this initiator (or abort them).
- */
- case Pom_Connect_Ind:
- debug("wap.push.ppg", 0, "PPG: handle_internal_event: connect"
- " indication from OTA");
- sid = e->u.Pom_Connect_Ind.session_id;
- tuple = e->u.Pom_Connect_Ind.addr_tuple;
- port = tuple->remote->port;
- caps = e->u.Pom_Connect_Ind.requested_capabilities;
- sm = wap_push_ppg_have_push_session_for(tuple);
- sm = update_session_data(sm, sid, port, caps);
- if (!response_push_connection(e, sm)) {
- pm = abort_delivery(sm, http_status);
- wap_event_destroy(e);
- return;
- }
- /*
- * hard-coded until we have bearer control implemented
- */
- deliver_pending_pushes(sm, NOT_LAST);
- wap_event_destroy(e);
- break;
- case Pom_Disconnect_Ind:
- debug("wap.push.ppg", 0, "PPG: handle_internal_event: disconnect"
- " indication from OTA");
- sm = wap_push_ppg_have_push_session_for_sid(
- e->u.Pom_Disconnect_Ind.session_handle);
- remove_session_data(sm, http_status);
- wap_event_destroy(e);
- break;
- /*
- * Only the client can close a session. So we leave session open, even when
- * there are no active pushes. Note that we do not store PAP attribute very
- * long time. Point is that result notification message, if asked, will rep-
- * ort this fact to PI, after which there is no need to store it any more.
- */
- case Po_ConfirmedPush_Cnf:
- debug("wap.push.ppg", 0, "PPG: handle_internal_event: push"
- " confirmation from OTA");
- sid = e->u.Po_ConfirmedPush_Cnf.session_handle;
- pid = e->u.Po_ConfirmedPush_Cnf.server_push_id;
- sm = wap_push_ppg_have_push_session_for_sid(sid);
- pm = find_ppg_push_machine_using_pid(sm, pid);
- pm = update_push_data_with_attribute(&sm, pm, PAP_CONFIRMED,
- PAP_DELIVERED2);
- wap_event_destroy(e);
- remove_push_data(sm, pm, 0);
- break;
- /*
- * Again, PAP attribute will be reported to PI by using result notification.
- */
- case Po_PushAbort_Ind:
- debug("wap.push.ppg", 0, "PPG: handle_internal_event: abort"
- " indication from OTA");
- sid = e->u.Po_PushAbort_Ind.session_handle;
- pid = e->u.Po_PushAbort_Ind.push_id;
- sm = wap_push_ppg_have_push_session_for_sid(sid);
- pm = find_ppg_push_machine_using_pid(sm, pid);
- session_machine_assert(sm);
- push_machine_assert(pm);
- reason = e->u.Po_PushAbort_Ind.reason;
- pm = update_push_data_with_attribute(&sm, pm, reason, PAP_ABORTED);
- remove_session_data(sm, http_status);
- wap_event_destroy(e);
- break;
- /*
- * FIXME TRU: Add timeout (a mandatory feature!)
- */
- default:
- debug("wap.ppg", 0, "PPG: handle_internal_event: an unhandled event");
- wap_event_dump(e);
- wap_event_destroy(e);
- break;
- }
- }
- /*
- * Functions related to various ppg machine types.
- *
- * We do not set session id here: it is told to us by wsp.
- */
- static PPGSessionMachine *session_machine_create(WAPAddrTuple *tuple,
- WAPEvent *e)
- {
- PPGSessionMachine *m;
- gw_assert(e->type = Push_Message);
- m = gw_malloc(sizeof(PPGSessionMachine));
- #define INTEGER(name) m->name = 0;
- #define OCTSTR(name) m->name = NULL;
- #define ADDRTUPLE(name) m->name = NULL;
- #define PUSHMACHINES(name) m->name = list_create();
- #define CAPABILITIES(name) m->name = NULL;
- #define MACHINE(fields) fields
- #include "wap_ppg_session_machine.def"
- m->pi_client_address = octstr_duplicate(e->u.Push_Message.address_value);
- m->addr_tuple = wap_addr_tuple_duplicate(tuple);
- m->assumed_capabilities =
- wsp_cap_duplicate_list(e->u.Push_Message.pi_capabilities);
- m->preferconfirmed_value = PAP_CONFIRMED;
- list_append(ppg_machines, m);
- debug("wap.push.ppg", 0, "PPG: Created PPGSessionMachine %ld",
- m->session_id);
- return m;
- }
- static void session_machine_destroy(void *p)
- {
- PPGSessionMachine *sm;
- if (p == NULL)
- return;
- sm = p;
- debug("wap.push.ppg", 0, "PPG: destroying PPGSEssionMachine %ld",
- sm->session_id);
- #define OCTSTR(name) octstr_destroy(sm->name);
- #define ADDRTUPLE(name) wap_addr_tuple_destroy(sm->name);
- #define INTEGER(name) sm->name = 0;
- #define PUSHMACHINES(name) push_machines_list_destroy(sm->name);
- #define CAPABILITIES(name) wsp_cap_destroy_list(sm->name);
- #define MACHINE(fields) fields
- #include "wap_ppg_session_machine.def"
- gw_free(sm);
- }
- /*
- * FIXME: PPG's trust policy (flags authenticated and trusted).
- * We return pointer to the created push machine and push id it uses.
- */
- static PPGPushMachine *push_machine_create(WAPEvent *e, WAPAddrTuple *tuple)
- {
- PPGPushMachine *m;
- m = gw_malloc(sizeof(PPGPushMachine));
- #define INTEGER(name) m->name = 0;
- #define OCTSTR(name) m->name = NULL;
- #define OPTIONAL_OCTSTR(name) m->name = NULL;
- #define ADDRTUPLE(name) m->name = NULL;
- #define CAPABILITIES m->name = NULL;
- #define HTTPHEADER(name) m->name = NULL;
- #define MACHINE(fields) fields
- #include "wap_ppg_push_machine.def"
- m->addr_tuple = wap_addr_tuple_duplicate(tuple);
- m->pi_push_id = octstr_duplicate(e->u.Push_Message.pi_push_id);
- m->push_id = counter_increase(push_id_counter);
- m->delivery_method = e->u.Push_Message.delivery_method;
- m->deliver_after_timestamp =
- octstr_duplicate(e->u.Push_Message.deliver_after_timestamp);
- m->priority = e->u.Push_Message.priority;
- m->push_headers = http_header_duplicate(e->u.Push_Message.push_headers);
- m->push_data = octstr_duplicate(e->u.Push_Message.push_data);
- m->address_type = e->u.Push_Message.address_type;
- m->progress_notes_requested = e->u.Push_Message.progress_notes_requested;
- if (e->u.Push_Message.progress_notes_requested)
- m->ppg_notify_requested_to =
- octstr_duplicate(e->u.Push_Message.ppg_notify_requested_to);
- debug("wap.push.ppg", 0, "PPG: push machine %ld created", m->push_id);
- return m;
- }
- /*
- * Contrary to the normal Kannel style, we do not remove from a list here.
- * That is because we now have two different push lists.
- */
- static void push_machine_destroy(void *p)
- {
- PPGPushMachine *pm;
- if (p == NULL)
- return;
- pm = p;
- debug("wap.push.ppg", 0, "PPG: destroying push machine %ld",
- pm->push_id);
- #define OCTSTR(name) octstr_destroy(pm->name);
- #define OPTIONAL_OCTSTR(name) octstr_destroy(pm->name);
- #define INTEGER(name)
- #define ADDRTUPLE(name) wap_addr_tuple_destroy(pm->name);
- #define CAPABILITIES(name) wap_cap_destroy_list(pm->name);
- #define HTTPHEADER(name) http_destroy_headers(pm->name);
- #define MACHINE(fields) fields
- #include "wap_ppg_push_machine.def"
- gw_free(p);
- }
- static void push_machines_list_destroy(List *machines)
- {
- if (machines == NULL)
- return;
- list_destroy(machines, push_machine_destroy);
- }
- static int session_has_addr(void *a, void *b)
- {
- Octstr *cliaddr;
- PPGSessionMachine *sm;
- cliaddr = b;
- sm = a;
- return octstr_compare(sm->addr_tuple->remote->address, cliaddr) == 0;
- }
- static int session_has_sid(void *a, void *b)
- {
- PPGSessionMachine *sm;
- long *sid;
- sid = b;
- sm = a;
- return *sid == sm->session_id;
- }
- /*
- * Here session machine address tuples have connection-oriented ports, because
- * these are used when establishing the connection an doing pushes. But session
- * creation request must be to the the connectionless push port of the client.
- * So we change ports here.
- */
- static void create_session(WAPEvent *e, PPGPushMachine *pm)
- {
- WAPEvent *ota_event;
- List *push_headers;
- gw_assert(e->type == Push_Message);
- push_machine_assert(pm);
- push_headers = http_header_duplicate(e->u.Push_Message.push_headers);
- ota_event = wap_event_create(Pom_SessionRequest_Req);
- ota_event->u.Pom_SessionRequest_Req.addr_tuple =
- addr_tuple_change_cliport(pm->addr_tuple,
- CONNECTIONLESS_PUSH_CLIPORT);
- ota_event->u.Pom_SessionRequest_Req.push_headers = push_headers;
- ota_event->u.Pom_SessionRequest_Req.push_id = pm->push_id;
- ota_event->u.Pom_SessionRequest_Req.address_type = pm->address_type;
- dispatch_to_ota(ota_event);
- }
- /*
- * We store data to push machine, because it is possible that we do not have
- * a session when push request happens.
- */
- static void request_confirmed_push(long last, PPGPushMachine *pm,
- PPGSessionMachine *sm)
- {
- WAPEvent *ota_event;
- List *push_headers;
- gw_assert(last == 0 || last == 1);
- push_machine_assert(pm);
- session_machine_assert(sm);
- push_headers = http_header_duplicate(pm->push_headers);
- ota_event = wap_event_create(Po_ConfirmedPush_Req);
- ota_event->u.Po_ConfirmedPush_Req.server_push_id = pm->push_id;
- ota_event->u.Po_ConfirmedPush_Req.push_headers = push_headers;
- ota_event->u.Po_ConfirmedPush_Req.authenticated = pm->authenticated;
- ota_event->u.Po_ConfirmedPush_Req.trusted = pm->trusted;
- ota_event->u.Po_ConfirmedPush_Req.last = last;
- if (pm->push_data != NULL)
- ota_event->u.Po_ConfirmedPush_Req.push_body =
- octstr_duplicate(pm->push_data);
- else
- ota_event->u.Po_ConfirmedPush_Req.push_body = NULL;
- ota_event->u.Po_ConfirmedPush_Req.session_handle = sm->session_id;
- debug("wap.push.ota", 0, "PPG: confirmed push request to OTA");
- dispatch_to_ota(ota_event);
- }
- /*
- * There is to types of unit push requests: requesting ip services and sms
- * services. Address type tells the difference.
- */
- static void request_unit_push(long last, PPGPushMachine *pm)
- {
- WAPEvent *ota_event;
- List *push_headers;
- gw_assert(last == 0 || last == 1);
- push_machine_assert(pm);
- push_headers = http_header_duplicate(pm->push_headers);
- ota_event = wap_event_create(Po_Unit_Push_Req);
- ota_event->u.Po_Unit_Push_Req.addr_tuple =
- wap_addr_tuple_duplicate(pm->addr_tuple);
- ota_event->u.Po_Unit_Push_Req.push_id = pm->push_id;
- ota_event->u.Po_Unit_Push_Req.push_headers = push_headers;
- ota_event->u.Po_Unit_Push_Req.authenticated = pm->authenticated;
- ota_event->u.Po_Unit_Push_Req.trusted = pm->trusted;
- ota_event->u.Po_Unit_Push_Req.last = last;
- ota_event->u.Po_Unit_Push_Req.address_type = pm->address_type;
- ota_event->u.Po_Unit_Push_Req.push_body =
- octstr_duplicate(pm->push_data);
- dispatch_to_ota(ota_event);
- debug("wap.push.ppg", 0, "PPG: OTA request for unit push");
- }
- static void request_push(long last, PPGPushMachine *pm)
- {
- WAPEvent *ota_event;
- List *push_headers;
- gw_assert(last == 0 || last == 1);
- push_machine_assert(pm);
- push_headers = http_header_duplicate(pm->push_headers);
- ota_event = wap_event_create(Po_Push_Req);
- ota_event->u.Po_Push_Req.push_headers = push_headers;
- ota_event->u.Po_Push_Req.authenticated = pm->authenticated;
- ota_event->u.Po_Push_Req.trusted = pm->trusted;
- ota_event->u.Po_Push_Req.last = last;
- if (pm->push_data != NULL)
- ota_event->u.Po_Push_Req.push_body =
- octstr_duplicate(pm->push_data);
- else
- ota_event->u.Po_Push_Req.push_body = NULL;
- ota_event->u.Po_Push_Req.session_handle = pm->session_id;
- debug("wap.push.ppg", 0, "PPG: OTA request for push");
- dispatch_to_ota(ota_event);
- }
- /*
- * According to pap, Chapter 11, capabilities can be
- *
- * a) queried by PI
- * b) told to PI when a client is subscribing
- * c) assumed
- *
- * In case c) we got capabilities from third part of the push message (other
- * cases PI knows what it is doing), and we check is the client capable to
- * handle the message.
- * Requested capabilities are client capabilities, assumed capabilities are
- * PI capabilities. If there is no assumed capabilities, PI knows client capab-
- * ilities by method a) or method b).
- * Returns 1, if the client is capable, 0 when it is not.
- */
- static int response_push_connection(WAPEvent *e, PPGSessionMachine *sm)
- {
- WAPEvent *appl_event;
- gw_assert(e->type == Pom_Connect_Ind);
- if (sm->assumed_capabilities != NULL && check_capabilities(
- e->u.Pom_Connect_Ind.requested_capabilities,
- sm->assumed_capabilities) == 0)
- return 0;
- appl_event = wap_event_create(Pom_Connect_Res);
- appl_event->u.Pom_Connect_Res.negotiated_capabilities =
- wsp_cap_duplicate_list(e->u.Pom_Connect_Ind.requested_capabilities);
- appl_event->u.Pom_Connect_Res.session_id = e->u.Pom_Connect_Ind.session_id;
- dispatch_to_appl(appl_event);
- return 1;
- }
- /*
- * Push response, from pap, Chapter 9.3.
- * Inputs error code, in PAP format.
- * Return the current value of HTTPClient.
- */
- static HTTPClient *response_push_message(PPGPushMachine *pm, long code, int status)
- {
- WAPEvent *e;
- HTTPClient *c;
- push_machine_assert(pm);
- e = wap_event_create(Push_Response);
- e->u.Push_Response.pi_push_id = octstr_duplicate(pm->pi_push_id);
- e->u.Push_Response.sender_name = tell_ppg_name();
- e->u.Push_Response.reply_time = set_time();
- e->u.Push_Response.code = code;
- e->u.Push_Response.desc = describe_code(code);
- c = send_push_response(e, status);
- return c;
- }
- static int check_capabilities(List *requested, List *assumed)
- {
- int is_capable;
- is_capable = 1;
- return is_capable;
- }
- /*
- * Time of creation of the response (pap, chapter 9.3). We convert UNIX time
- * to ISO8601, it is, YYYY-MM-DDThh:mm:ssZ, T and Z being literal strings (we
- * use gw_gmtime to turn UNIX time to broken time).
- */
- static Octstr *set_time(void)
- {
- Octstr *current_time;
- struct tm now;
- now = gw_gmtime(time(NULL));
- current_time = octstr_format("%04d-%02d-%02dT%02d:%02d:%02dZ",
- now.tm_year + 1900, now.tm_mon + 1,
- now.tm_mday, now.tm_hour, now.tm_min,
- now.tm_sec);
- return current_time;
- }
- static void session_machine_assert(PPGSessionMachine *sm)
- {
- gw_assert(sm);
- gw_assert(sm->session_id >= 0);
- gw_assert(sm->addr_tuple);
- gw_assert(sm->pi_client_address);
- }
- static void push_machine_assert(PPGPushMachine *pm)
- {
- gw_assert(pm);
- gw_assert(pm->pi_push_id);
- gw_assert(pm->push_id >= 0);
- gw_assert(pm->session_id >= 0);
- gw_assert(pm->addr_tuple);
- gw_assert(pm->trusted == 1 || pm->trusted == 0);
- gw_assert(pm->authenticated == 1 || pm->authenticated == 0);
- }
- /*
- * Message transformations performed by PPG are defined in ppg, 6.1.2.1. Ppg,
- * chapter 6.1.1, states that we MUST reject a push having an erroneous PAP
- * push message element. So we must validate it even when we do not compile
- * it.
- * We do not do any (formally optional, but phones may disagree) header
- * conversions to the binary format here, these are responsibility of our OTA
- * module (gw/wap_push_ota.c).
- * FIXME: Remove all headers which default values are known to the client.
- *
- * Return
- * a) message, either transformed or not (if there is no-transform cache
- * directive or wml code is erroneous)
- * b) The transformed gw address. Use here global-sender, when the bearer
- * is SMS (some SMS centers would require this).
- * c) the transformed message content type
- *
- * Returned flag tells was the transformation (if any) successfull or not. Error
- * flag is returned when there is no push headers, there is no Content-Type header
- * or push content does not compile. We should have checked existence of push
- * headers earlier, but let us be carefull.
- */
- static int transform_message(WAPEvent **e, WAPAddrTuple **tuple,
- List *push_headers, int cless_accepted, Octstr **type)
- {
- int message_deliverable;
- struct content content;
- Octstr *cliaddr;
- long cliport,
- servport,
- address_type;
- gw_assert((**e).type == Push_Message);
- if ((**e).u.Push_Message.push_headers == NULL)
- goto herror;
- cliaddr = (**e).u.Push_Message.address_value;
- push_headers = (**e).u.Push_Message.push_headers;
- if (!cless_accepted) {
- cliport = CONNECTED_CLIPORT;
- servport = CONNECTED_SERVPORT;
- } else {
- cliport = CONNECTIONLESS_PUSH_CLIPORT;
- servport = CONNECTIONLESS_SERVPORT;
- }
- address_type = (**e).u.Push_Message.address_type;
- *tuple = set_addr_tuple(cliaddr, cliport, servport, address_type);
- if (!content_transformable(push_headers))
- goto no_transform;
- content.body = (**e).u.Push_Message.push_data;
- if (content.body == NULL)
- goto no_transform;
- content.type = http_header_find_first(push_headers,
- "Content-Transfer-Encoding");
- if (content.type) {
- octstr_strip_blanks(content.type);
- debug("wap.push.ppg", 0, "PPG: Content-Transfer-Encoding is "%s"",
- octstr_get_cstr (content.type));
- message_deliverable = pap_get_content(&content);
- if (message_deliverable) {
- change_header_value(&push_headers, "Content-Transfer-Encoding",
- "binary");
- } else {
- goto error;
- }
- }
- http_header_get_content_type(push_headers, &content.type,
- &content.charset);
- message_deliverable = pap_convert_content(&content);
- if (content.type == NULL)
- goto error;
- if (message_deliverable) {
- *type = content.type;
- } else {
- goto error;
- }
- (**e).u.Push_Message.push_data = content.body;
- octstr_destroy(content.charset);
- debug("wap.push.ppg", 0, "PPG: transform_message: push message content"
- " and headers valid");
- return 1;
- herror:
- warning(0, "PPG: transform_message: no push headers, cannot accept");
- return 0;
- error:
- warning(0, "PPG: transform_message: push content erroneous, cannot"
- " accept");
- octstr_destroy(content.type);
- octstr_destroy(content.charset);
- return 0;
- no_transform:
- warning(0, "PPG: transform_message: push content non transformable");
- octstr_destroy(content.type);
- octstr_destroy(content.charset);
- return 1;
- }
- /*
- * Transform X-WAP-Application headers as per ppg 6.1.2.1. Note that missing
- * header means that wml.ua is assumed.
- * Return coded value (starting with 0), when the id was not wml.ua
- * -1, when id was wml.ua (or no application id was present)
- * -2, when error
- */
- static long check_x_wap_application_id_header(List **push_headers)
- {
- Octstr *appid_content;
- long coded_value;
- Octstr *cos;
- if (*push_headers == NULL)
- return -2;
- appid_content = http_header_find_first(*push_headers,
- "X-WAP-Application-Id");
- if (appid_content == NULL) {
- octstr_destroy(appid_content);
- return -1;
- }
- if ((coded_value = parse_appid_header(&appid_content)) < 0) {
- octstr_destroy(appid_content);
- return -2;
- }
- if (coded_value == 2) {
- octstr_destroy(appid_content);
- http_header_remove_all(*push_headers, "X-WAP-Application-Id");
- return -1;
- }
- cos = octstr_format("%ld", coded_value);
- http_header_remove_all(*push_headers, "X-WAP-Application-Id");
- http_header_add(*push_headers, "X-WAP-Application-Id", octstr_get_cstr(cos));
- octstr_destroy(appid_content);
- octstr_destroy(cos);
- return coded_value;
- }
- /*
- * Check do we have a no-transform cache directive amongst the headers.
- */
- static int content_transformable(List *push_headers)
- {
- List *cache_directives;
- long i;
- Octstr *header_name,
- *header_value;
- gw_assert(push_headers);
- cache_directives = http_header_find_all(push_headers, "Cache-Control");
- if (list_len(cache_directives) == 0) {
- http_destroy_headers(cache_directives);
- return 1;
- }
- i = 0;
- while (i < list_len(cache_directives)) {
- http_header_get(cache_directives, i, &header_name, &header_value);
- if (octstr_compare(header_value, octstr_imm("no-transform")) == 0) {
- http_destroy_headers(cache_directives);
- octstr_destroy(header_name);
- octstr_destroy(header_value);
- return 0;
- }
- ++i;
- }
- http_destroy_headers(cache_directives);
- octstr_destroy(header_name);
- octstr_destroy(header_value);
- return 1;
- }
- /*
- * Convert push content to compact binary format (this can be wmlc, sic, slc
- * or coc). Current status wml, sl and si compiled, others passed.
- */
- static Octstr *convert_wml_to_wmlc(struct content *content)
- {
- Octstr *wmlc;
- if (wml_compile(content->body, content->charset, &wmlc) == 0)
- return wmlc;
- warning(0, "PPG: wml compilation failed");
- return NULL;
- }
- static Octstr *convert_si_to_sic(struct content *content)
- {
- Octstr *sic;
- if (si_compile(content->body, content->charset, &sic) == 0)
- return sic;
- warning(0, "PPG: si compilation failed");
- return NULL;
- }
- static Octstr *convert_sl_to_slc(struct content *content)
- {
- Octstr *slc;
- if (sl_compile(content->body, content->charset, &slc) == 0)
- return slc;
- warning(0, "PPG: sl compilation failed");
- return NULL;
- }
- static Octstr *extract_base64(struct content *content)
- {
- Octstr *orig = octstr_duplicate(content->body);
- octstr_base64_to_binary(orig);
- return orig;
- }
- 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.si",
- "application/vnd.wap.sic",
- convert_si_to_sic },
- { "text/vnd.wap.sl",
- "application/vnd.wap.slc",
- convert_sl_to_slc}
- };
- #define NUM_CONVERTERS ((long) (sizeof(converters) / sizeof(converters[0])))
- static struct {
- char *transfer_encoding;
- Octstr *(*extract) (struct content *);
- } extractors[] = {
- { "base64",
- extract_base64 }
- };
- #define NUM_EXTRACTORS ((long) (sizeof(extractors) / sizeof(extractors[0])))
- /*
- * Compile wap defined contents, accept others without modifications. Push
- * message 6.3 states that push content can be any MIME accepted content type.
- */
- static int pap_convert_content(struct content *content)
- {
- long i;
- Octstr *new_body;
- for (i = 0; i < NUM_CONVERTERS; i++) {
- if (octstr_compare(content->type,
- octstr_imm(converters[i].type)) == 0) {
- new_body = converters[i].convert(content);
- if (new_body == NULL)
- return 0;
- octstr_destroy(content->body);
- content->body = new_body;
- octstr_destroy(content->type);
- content->type = octstr_create(converters[i].result_type);
- return 1;
- }
- }
- return 1;
- }
- /*
- * MIME specifies a number of content transfer encodings.
- * This function tries to get the original version of the
- * content.
- */
- static int pap_get_content(struct content *content)
- {
- long i;
- Octstr *new_body;
- for (i = 0; i < NUM_EXTRACTORS; i++) {
- if (octstr_case_compare(content->type,
- octstr_imm(extractors[i].transfer_encoding)) == 0) {
- new_body = extractors[i].extract(content);
- if (new_body == NULL)
- return 0;
- octstr_destroy(content->body);
- content->body = new_body;
- octstr_destroy(content->type);
- content->type = NULL;
- return 1;
- }
- }
- return 1;
- }
- /*
- * Bearer and network types are defined in wdp, Appendix C. Any means any net-
- * work supporting IPv4 or IPv6.
- */
- static char *bearers[] = {
- "Any",
- "SMS",
- "CSD",
- "GPRS",
- "Packet Data",
- "CDPD"
- };
- #define NUMBER_OF_BEARERS sizeof(bearers)/sizeof(bearers[0])
- static char *networks[] = {
- "Any",
- "GSM",
- "IS-95 CDMA",
- "ANSI-136",
- "AMPS",
- "PDC",
- "IDEN",
- "PHS",
- "TETRA"
- };
- #define NUMBER_OF_NETWORKS sizeof(networks)/sizeof(networks[0])
- /*
- * We support networks using IP as a bearer and GSM using SMS as bearer, so we
- * must reject others. Default bearer is IP, it is (currently) not-SMS. After
- * the check we change meaning of the bearer_required-attribute: it will tell
- * do we use WAP over SMS.
- */
- int select_bearer_network(WAPEvent **e)
- {
- Octstr *bearer,
- *network;
- int bearer_required,
- network_required;
- size_t i,
- j;
- gw_assert((**e).type == Push_Message);
- bearer_required = (**e).u.Push_Message.bearer_required;
- network_required = (**e).u.Push_Message.network_required;
- bearer = octstr_imm("Any");
- network = octstr_imm("Any");
- if (!bearer_required || !network_required)
- return 1;
- if (bearer_required)
- bearer = (**e).u.Push_Message.bearer;
- if (network_required)
- network = (**e).u.Push_Message.network;
- for (i = 0; i < NUMBER_OF_NETWORKS ; ++i) {
- if (octstr_case_compare(bearer, octstr_imm(bearers[i])) == 0)
- break;
- }
- for (j = 0; j < NUMBER_OF_BEARERS ; ++j) {
- if (octstr_case_compare(bearer, octstr_imm(bearers[j])) == 0)
- break;
- }
- if (i == NUMBER_OF_NETWORKS || j == NUMBER_OF_BEARERS)
- return 0;
- return 1;
- }
- static int session_has_pi_client_address(void *a, void *b)
- {
- Octstr *caddr;
- PPGSessionMachine *sm;
- caddr = b;
- sm = a;
- return octstr_compare(caddr, sm->pi_client_address) == 0;
- }
- /*
- * PI client address is composed of a client specifier and a PPG specifier (see
- * ppg, chapter 7). So it is equivalent with gw address quadruplet.
- */
- PPGSessionMachine *session_find_using_pi_client_address(Octstr *caddr)
- {
- PPGSessionMachine *sm;
- sm = list_search(ppg_machines, caddr, session_has_pi_client_address);
- return sm;
- }
- /*
- * Give PPG a human readable name.
- */
- static Octstr *tell_ppg_name(void)
- {
- return octstr_format("%S; WAP/1.3 (" GW_NAME "/%s)", get_official_name(),
- VERSION);
- }
- /*
- * Delivery time constraints are a) deliver before and b) deliver after. It is
- * possible that service required is after some time and before other. So we
- * test first condition a). Note that 'now' satisfy both constraints.
- * Returns: 0 delivery time expired
- * 1 too early to send the message
- * 2 no constraints
- */
- static int delivery_time_constraints(WAPEvent *e, PPGPushMachine *pm)
- {
- Octstr *before,
- *after;
- struct tm now;
- gw_assert(e->type = Push_Message);
- before = e->u.Push_Message.deliver_before_timestamp;
- after = pm->deliver_after_timestamp;
- now = gw_gmtime(time(NULL));
- if (!deliver_before_test_cleared(before, now)) {
- info(0, "PPG: delivery deadline expired, dropping the push message");
- return 0;
- }
- if (!deliver_after_test_cleared(after, now)) {
- debug("wap.push.ppg", 0, "PPG: too early to push the message,"
- " waiting");
- return 1;
- }
- return 2;
- }
- /*
- * Give verbose description of the result code. Conversion table for descrip-
- * tion.
- */
- struct description_t {
- long reason;
- char *description;
- };
- typedef struct description_t description_t;
- static description_t pap_desc[] = {
- { PAP_OK, "The request succeeded"},
- { PAP_ACCEPTED_FOR_PROCESSING, "The request has been accepted for"
- " processing"},
- { PAP_BAD_REQUEST, "Not understood due to malformed syntax"},
- { PAP_FORBIDDEN, "Request was refused"},
- { PAP_ADDRESS_ERROR, "The client specified not recognised"},
- { PAP_CAPABILITIES_MISMATCH, "Capabilities assumed by PI were not"
- " acceptable for the client specified"},
- { PAP_DUPLICATE_PUSH_ID, "Push id supplied was not unique"},
- { PAP_INTERNAL_SERVER_ERROR, "Server could not fulfill the request due"
- " to an internal error"},
- { PAP_TRANSFORMATION_FAILURE, "PPG was unable to perform a transformation"
- " of the message"},
- { PAP_REQUIRED_BEARER_NOT_AVAILABLE, "Required bearer not available"},
- { PAP_SERVICE_FAILURE, "The service failed. The client may re-attempt"
- " the operation"},
- { PAP_CLIENT_ABORTED, "The client aborted the operation. No reason given"},
- { WSP_ABORT_USERREQ, "Wsp requested abort"},
- { WSP_ABORT_USERRFS, "Wsp refused push message. Do not try again"},
- { WSP_ABORT_USERPND, "Push message cannot be delivered to intended"
- " destination by the wsp"},
- { WSP_ABORT_USERDCR, "Push message discarded due to resource shortage in"
- " wsp"},
- { WSP_ABORT_USERDCU, "Content type of the push message cannot be"
- " processed by the wsp"}
- };
- static size_t desc_tab_size = sizeof(pap_desc) / sizeof(pap_desc[0]);
- static Octstr *describe_code(long code)
- {
- Octstr *desc;
- size_t i;
- for (i = 0; i < desc_tab_size; i++) {
- if (pap_desc[i].reason == code) {
- desc = octstr_create(pap_desc[i].description);
- return desc;
- }
- }
- return octstr_imm("unknown PAP code");
- }
- /*
- * Remove push data from the list of connectionless pushes, if cless is true,
- * otherwise from the list of pushes belonging to session machine sm.
- */
- static void remove_push_data(PPGSessionMachine *sm, PPGPushMachine *pm,
- int cless)
- {
- push_machine_assert(pm);
- if (cless) {
- list_delete_equal(ppg_unit_pushes, pm);
- } else {
- session_machine_assert(sm);
- list_delete_equal(sm->push_machines, pm);
- }
- push_machine_destroy(pm);
- }
- /*
- * If cless is true, store push to the list connectionless pushes, otherwise
- * in the push list of the session machine sm.
- * We must create a push machine even when an error occurred, because this is
- * used for storing the relevant pap error state and other data for this push.
- * There should not be any duplicate push ids here (this is tested by http_
- * read_thread), but let us be carefull.
- * Return a pointer the push machine newly created and a flag telling was the
- * push id duplicate.
- */
- static int store_push_data(PPGPushMachine **pm, PPGSessionMachine *sm,
- WAPEvent *e, WAPAddrTuple *tuple, int cless)
- {
- Octstr *pi_push_id;
- int duplicate_push_id;
- gw_assert(e->type == Push_Message);
- pi_push_id = e->u.Push_Message.pi_push_id;
- duplicate_push_id = 0;
- if (((!cless) &&
- (find_ppg_push_machine_using_pi_push_id(sm, pi_push_id) != NULL)) ||
- ((cless) &&
- (find_unit_ppg_push_machine_using_pi_push_id(pi_push_id) != NULL)))
- duplicate_push_id = 1;
- *pm = push_machine_create(e, tuple);
- if (duplicate_push_id)
- return !duplicate_push_id;
- if (!cless) {
- list_append(sm->push_machines, *pm);
- debug("wap.push.ppg", 0, "PPG: store_push_data: push machine %ld"
- " appended to push list of sm machine %ld", (*pm)->push_id,
- sm->session_id);
- } else {
- list_append(ppg_unit_pushes, *pm);
- debug("wap.push.ppg", 0, "PPG: store_push_data: push machine %ld"
- " appended to unit push list", (*pm)->push_id);
- }
- return !duplicate_push_id;
- }
- /*
- * Deliver confirmed push. Note that if push is confirmed, PAP attribute is up-
- * dated only after an additional event (confirmation, abort or time-out).
- */
- static void deliver_confirmed_push(long last, PPGPushMachine *pm,
- PPGSessionMachine *sm)
- {
- request_confirmed_push(last, pm, sm);
- }
- /*
- * Ppg, chapter 6.1.2.2 , subchapter delivery, says that if push is unconform-
- * ed, we can use either Po-Unit-Push.req or Po-Push.req primitive. We use Po-
- * Push.req, if have an already established session (other words, sm != NULL).
- * In addition, update PAP attribute. Return pointer to the updated push mach-
- * ine.
- */
- static PPGPushMachine *deliver_unit_push(long last, PPGPushMachine *pm,
- PPGSessionMachine *sm, int session_exists)
- {
- push_machine_assert(pm);
- if (!session_exists)
- request_unit_push(last, pm);
- else
- request_push(last, pm);
- pm = update_push_data_with_attribute(&sm, pm, PAP_UNCONFIRMED,
- PAP_DELIVERED1);
- info(0, "PPG: unconfirmed push delivered to OTA");
- return pm;
- }
- /*
- * Deliver all pushes queued by session machine sm (it is, make a relevant OTA
- * request). Update PAP attribute, if push is unconfirmed.
- */
- static void deliver_pending_pushes(PPGSessionMachine *sm, int last)
- {
- PPGPushMachine *pm;
- long i;
- session_machine_assert(sm);
- gw_assert(list_len(sm->push_machines) > 0);
- i = 0;
- while (i < list_len(sm->push_machines)) {
- pm = list_get(sm->push_machines, i);
- push_machine_assert(pm);
- if (pm->delivery_method == PAP_UNCONFIRMED) {
- request_push(last, pm);
- pm = update_push_data_with_attribute(&sm, pm, PAP_UNCONFIRMED,
- PAP_DELIVERED1);
- remove_push_data(sm, pm, sm == NULL);
- } else {
- request_confirmed_push(last, pm, sm);
- ++i;
- }
- }
- }
- /*
- * Abort all pushes queued by session machine sm. In addition, update PAP
- * attribute and notify PI.
- */
- static PPGPushMachine *abort_delivery(PPGSessionMachine *sm, int status)
- {
- PPGPushMachine *pm;
- long reason,
- code;
- session_machine_assert(sm);
- pm = NULL;
- reason = PAP_ABORT_USERPND;
- code = PAP_CAPABILITIES_MISMATCH;
- while (list_len(sm->push_machines) > 0) {
- pm = list_get(sm->push_machines, 0);
- push_machine_assert(pm);
- pm = update_push_data_with_attribute(&sm, pm, reason, PAP_ABORTED);
- response_push_message(pm, code, status);
- remove_push_data(sm, pm, sm == NULL);
- }
- return pm;
- }
- /*
- * Remove a session, even if it have active pushes. These are aborted, and we
- * must inform PI about this. Client abort codes are defined in pap, 9.14.5,
- * which refers to wsp, Appendix A, table 35.
- */
- static void remove_session_data(PPGSessionMachine *sm, int status)
- {
- long code;
- PPGPushMachine *pm;
- session_machine_assert(sm);
- code = PAP_ABORT_USERPND;
- while (list_len(sm->push_machines) > 0) {
- pm = list_get(sm->push_machines, 0);
- response_push_message(pm, code, status);
- remove_push_data(sm, pm, sm == NULL);
- }
- list_delete_equal(ppg_machines, sm);
- session_machine_destroy(sm);
- }
- /*
- * Remove session, if it has no active pushes.
- */
- static void remove_pushless_session(PPGSessionMachine *sm)
- {
- session_machine_assert(sm);
- if (list_len(sm->push_machines) == 0) {
- list_delete_equal(ppg_machines, sm);
- session_machine_destroy(sm);
- }
- }
- /*
- * If session machine not exist, create a session machine and store session
- * data. If session exists, ignore.
- * Return pointer to the session machine, and a flag did we have a session
- * before executing this function. (Session data is needed to implement the
- * PAP attribute. It does not mean that a session exists.)
- */
- static PPGSessionMachine *store_session_data(PPGSessionMachine *sm,
- WAPEvent *e, WAPAddrTuple *tuple, int *session_exists)
- {
- gw_assert(e->type == Push_Message);
- if (sm == NULL) {
- sm = session_machine_create(tuple, e);
- *session_exists = 0;
- } else
- *session_exists = 1;
- return sm;
- }
- static PPGSessionMachine *update_session_data_with_headers(
- PPGSessionMachine *sm, PPGPushMachine *pm)
- {
- list_delete_matching(sm->push_machines, &pm->push_id, push_has_pid);
- list_append(sm->push_machines, pm);
- return sm;
- }
- /*
- * Ppg 6.1.2.2, subchapter delivery, states that if the delivery method is not
- * confirmed or unconfirmed, PPG may select an implementation specific type of
- * the primitive. We use an unconfirmed push, if QoS is notspecified, and
- * confirmed one, when it is preferconfirmed (we do support confirmed push).
- */
- static int confirmation_requested(WAPEvent *e)
- {
- gw_assert(e->type = Push_Message);
- return e->u.Push_Message.delivery_method == PAP_CONFIRMED ||
- e->u.Push_Message.delivery_method == PAP_PREFERCONFIRMED;
- }
- static int push_has_pid(void *a, void *b)
- {
- long *pid;
- PPGPushMachine *pm;
- pid = b;
- pm = a;
- return *pid == pm->push_id;
- }
- static PPGPushMachine *find_ppg_push_machine_using_pid(PPGSessionMachine *sm,
- long pid)
- {
- PPGPushMachine *pm;
- gw_assert(pid >= 0);
- session_machine_assert(sm);
- pm = list_search(sm->push_machines, &pid, push_has_pid);
- return pm;
- }
- static int push_has_pi_push_id(void *a, void *b)
- {
- Octstr *pi_push_id;
- PPGPushMachine *pm;
- pi_push_id = b;
- pm = a;
- return octstr_compare(pm->pi_push_id, pi_push_id) == 0;
- }
- static PPGPushMachine *find_ppg_push_machine_using_pi_push_id(
- PPGSessionMachine *sm, Octstr *pi_push_id)
- {
- PPGPushMachine *pm;
- gw_assert(pi_push_id);
- session_machine_assert(sm);
- pm = list_search(sm->push_machines, pi_push_id, push_has_pi_push_id);
- return pm;
- }
- static PPGPushMachine *find_unit_ppg_push_machine_using_pi_push_id(
- Octstr *pi_push_id)
- {
- PPGPushMachine *pm;
- gw_assert(pi_push_id);
- pm = list_search(ppg_unit_pushes, pi_push_id, push_has_pi_push_id);
- return pm;
- }
- /*
- * Store a new value of the push attribute into a push machine. It is to be
- * found from the list of unit pushes, if connectionless push was asked
- * (sm == NULL), otherwise from the the push list of the session machine sm.
- * Returns updated push machine and session machine (this one has an updated
- * push machines list).
- */
- static PPGPushMachine *update_push_data_with_attribute(PPGSessionMachine **sm,
- PPGPushMachine *qm, long reason, long status)
- {
- push_machine_assert(qm);
- switch (status) {
- case PAP_UNDELIVERABLE1:
- qm->message_state = PAP_UNDELIVERABLE;
- qm->code = PAP_BAD_REQUEST;
- break;
- case PAP_UNDELIVERABLE2:
- qm->code = reason;
- qm->message_state = PAP_UNDELIVERABLE;
- qm->desc = describe_code(reason);
- break;
- case PAP_ABORTED:
- qm->message_state = status;
- qm->code = ota_abort_to_pap(reason);
- qm->event_time = set_time();
- qm->desc = describe_code(reason);
- break;
- case PAP_DELIVERED1:
- qm->message_state = PAP_DELIVERED;
- qm->delivery_method = PAP_UNCONFIRMED;
- qm->event_time = set_time();
- break;
- case PAP_DELIVERED2:
- qm->message_state = PAP_DELIVERED;
- qm->delivery_method = PAP_CONFIRMED;
- qm->event_time = set_time();
- break;
- case PAP_EXPIRED:
- qm->message_state = PAP_EXPIRED;
- qm->event_time = set_time();
- qm->desc = describe_code(reason);
- break;
- case PAP_PENDING:
- qm->message_state = PAP_PENDING;
- break;
- default:
- error(0, "WAP_PUSH_PPG: update_push_data_with_attribute: Non"
- " existing push machine status: %ld", status);
- break;
- }
- if (*sm != NULL){
- list_delete_matching((**sm).push_machines, &qm->push_id, push_has_pid);
- list_append((**sm).push_machines, qm);
- list_delete_equal(ppg_machines, *sm);
- list_append(ppg_machines, *sm);
- } else {
- list_delete_matching(ppg_unit_pushes, &qm->push_id, push_has_pid);
- list_append(ppg_unit_pushes, qm);
- }
- return qm;
- }
- /*
- * Store session id, client port and caps list received from application layer.
- */
- static PPGSessionMachine *update_session_data(PPGSessionMachine *m,
- long sid, long port, List *caps)
- {
- session_machine_assert(m);
- gw_assert(sid >= 0);
- m->session_id = sid;
- m->addr_tuple->remote->port = port;
- m->client_capabilities = wsp_cap_duplicate_list(caps);
- list_delete_equal(ppg_machines, m);
- list_append(ppg_machines, m);
- return m;
- }
- /*
- * Convert OTA abort codes (ota 6.3.3) to corresponding PAP status codes. These
- * are defined in pap 9.14.5.
- */
- static long ota_abort_to_pap(long reason)
- {
- long offset;
- offset = reason - 0xEA;
- reason = 5026 + offset;
- return reason;
- }
- /*
- * Accept connectionless push when PI wants connectionless push and there is
- * no sessions open.
- */
- static int cless_accepted(WAPEvent *e, PPGSessionMachine *sm)
- {
- gw_assert(e->type == Push_Message);
- return (e->u.Push_Message.delivery_method == PAP_UNCONFIRMED ||
- e->u.Push_Message.delivery_method == PAP_NOT_SPECIFIED) &&
- (sm == NULL);
- }
- /*
- * Application ids start with 0 and -1 means that default (wml.ua) was used.
- */
- static int coriented_deliverable(long appid_code)
- {
- return appid_code > -1;
- }
- /*
- * Compare PAP message timestamp, in PAP message format, and stored in octstr,
- * to gm (UTC) broken time. Return true, if before is after now, or if the
- * service in question was not requested by PI. PAP time format is defined in
- * pap, chapter 9.2.
- */
- static void initialize_time_item_array(long time_data[], struct tm now)
- {
- time_data[0] = now.tm_year + 1900;
- time_data[1] = now.tm_mon + 1;
- time_data[2] = now.tm_mday;
- time_data[3] = now.tm_hour;
- time_data[4] = now.tm_min;
- time_data[5] = now.tm_sec;
- }
- static int date_item_compare(Octstr *condition, long time_data, long pos)
- {
- long data;
- if (octstr_parse_long(&data, condition, pos, 10) < 0) {
- return 0;
- }
- if (data < time_data) {
- return -1;
- }
- if (data > time_data) {
- return 1;
- }
- return 0;
- }
- /*
- * We do not accept timestamps equalling now. Return true, if the service was
- * not requested.
- */
- static int deliver_before_test_cleared(Octstr *before, struct tm now)
- {
- long time_data[6];
- long j;
- if (before == NULL)
- return 1;
- initialize_time_item_array(time_data, now);
- if (date_item_compare(before, time_data[0], 0) == 1)
- return 1;
- if (date_item_compare(before, time_data[0], 0) == -1)
- return 0;
- for (j = 5; j < octstr_len(before); j += 3) {
- if (date_item_compare(before, time_data[(j-5)/3 + 1], j) == 1)
- return 1;
- if (date_item_compare(before, time_data[(j-5)/3 + 1], j) == -1)
- return 0;
- }
- return 0;
- }
- /*
- * Ditto. Return true, if after is before now (or the service was not request-
- * ed). Do not accept timestamps equalling now.
- */
- static int deliver_after_test_cleared(Octstr *after, struct tm now)
- {
- long time_data[6];
- long j;
- if (after == NULL)
- return 1;
- initialize_time_item_array(time_data, now);
- if (date_item_compare(after, time_data[0], 0) == -1)
- return 1;
- if (date_item_compare(after, time_data[0], 0) == 1)
- return 0;
- for (j = 5; j < octstr_len(after); j += 3) {
- if (date_item_compare(after, time_data[(j-5)/3 + 1], j) == -1)
- return 1;
- if (date_item_compare(after, time_data[(j-5)/3 + 1], j) == 1)
- return 0;
- }
- return 0;
- }
- /*
- * We exchange here server and client addresses and ports, because our WDP,
- * written for pull, exchange them, too. Similarly server address INADDR_ANY is
- * used for compability reasons, when the bearer is ip. When it is SMS, the
- * server address is global-sender.
- */
- static WAPAddrTuple *set_addr_tuple(Octstr *address, long cliport,
- long servport, long address_type)
- {
- Octstr *cliaddr;
- WAPAddrTuple *tuple;
- gw_assert(address);
- if (address_type == ADDR_PLMN)
- cliaddr = global_sender;
- else
- cliaddr = octstr_imm("0.0.0.0");
- tuple = wap_addr_tuple_create(address, cliport, cliaddr, servport);
- return tuple;
- }
- /*
- * We are not interested about parsing URI fully - we check only does it cont-
- * ain application id reserved by WINA or the part containing assigned code.
- * Otherwise (regardless of it being an URI or assigned code) we simply pass
- * it forward.
- * These are defined by WINA at http://www.wapforum.org/wina/push-app-id.htm.
- */
- static char *wina_uri[] =
- { "*",
- "push.sia",
- "wml.ua",
- "wta.ua",
- "mms.ua",
- "push.syncml",
- "loc.ua"
- };
- #define NUMBER_OF_WINA_URIS sizeof(wina_uri)/sizeof(wina_uri[0])
- /*
- * X-Wap-Application-Id header is defined in Push Message, chapter 6.2.2.1.
- * First check do we a header with an app-encoding field and a coded value.
- * If not, try to find push application id from table of wina approved values.
- * Return coded value value of application id in question, or an error code:
- * -1, error
- * 0, * (meaning any application acceptable)
- * 1, push.sia
- * 2, wml.ua
- * 3, wta.ua
- * 4, mms.ua
- * 5, push.syncml
- * 6, loc.ua
- */
- static long parse_appid_header(Octstr **appid_content)
- {
- long pos,
- coded_value;
- size_t i;
- if ((pos = octstr_search(*appid_content, octstr_imm(";"), 0)) >= 0) {
- octstr_delete(*appid_content, pos,
- octstr_len(octstr_imm(";app-encoding=")));
- octstr_delete(*appid_content, 0, pos); /* the URI part */
- return -1;
- }
- i = 0;
- while (i < NUMBER_OF_WINA_URIS) {
- if ((pos = octstr_case_search(*appid_content,
- octstr_imm(wina_uri[i]), 0)) >= 0)
- break;
- ++i;
- }
- if (i == NUMBER_OF_WINA_URIS) {
- octstr_destroy(*appid_content);
- *appid_content = octstr_format("%ld", 2); /* assigned number */
- return -1; /* for wml ua */
- }
- octstr_delete(*appid_content, 0, pos); /* again the URI */
- if ((coded_value = wsp_string_to_application_id(*appid_content)) >= 0) {
- octstr_destroy(*appid_content);
- *appid_content = octstr_format("%ld", coded_value);
- return coded_value;
- }
- return -1;
- }
- static WAPAddrTuple *addr_tuple_change_cliport(WAPAddrTuple *tuple, long port)
- {
- WAPAddrTuple *dubble;
- if (tuple == NULL)
- return NULL;
- dubble = wap_addr_tuple_create(tuple->remote->address,
- port,
- tuple->local->address,
- tuple->local->port);
- return dubble;
- }
- /*
- * Pi uses multipart/related content type when communicating with ppg. (see
- * pap, Chapter 8) and subtype application/xml.
- * Check if push headers are acceptable according this rule. In addition,
- * return the field value of Content-Type header, if any and error string if
- * none (this string is used by send_bad_message_response).
- */
- static int headers_acceptable(List *push_headers, Octstr **content_header)
- {
- gw_assert(push_headers);
- *content_header = http_header_find_first(push_headers, "Content-Type");
- if (*content_header == NULL) {
- *content_header = octstr_format("%s", "no content type header found");
- goto error;
- }
- if (!type_is(*content_header, "multipart/related")) {
- goto error;
- }
- if (!type_is(*content_header, "application/xml")) {
- goto error;
- }
- return 1;
- error:
- warning(0, "PPG: headers_acceptable: got unacceptable push headers");
- return 0;
- }
- /*
- * Content-Type header field is defined in rfc 1521, chapter 4. We are looking
- * after type multipart/related or "multipart/related" and parameter
- * type=application/xml or type="application/xml", as required by pap, chapter
- * 8.
- */
- static int type_is(Octstr *content_header, char *name)
- {
- Octstr *quoted_type,
- *osname;
- osname = octstr_imm(name);
- if (octstr_case_search(content_header, osname, 0) >= 0)
- return 1;
- quoted_type = octstr_create("");
- octstr_format_append(quoted_type, "%c", '"');
- octstr_append(quoted_type, osname);
- octstr_format_append(quoted_type, "%c", '"');
- if (octstr_case_search(content_header, quoted_type, 0) >= 0) {
- octstr_destroy(quoted_type);
- return 1;
- }
- octstr_destroy(quoted_type);
- return 0;
- }
- /*
- * Again looking after a parameter, this time of type boundary=XXX or boundary=
- * "XXX".
- */
- static int get_mime_boundary(List *push_headers, Octstr *content_header,
- Octstr **boundary)
- {
- long pos;
- Octstr *bos;
- int c, quoted = 0;
- pos = 0;
- if ((pos = octstr_case_search(content_header,
- bos = octstr_imm("boundary="), 0)) < 0) {
- warning(0, "PPG: get_mime_boundary: no boundary specified");
- return -1;
- }
- pos += octstr_len(bos);
- if (octstr_get_char(content_header, pos) == '"') {
- ++pos;
- quoted = 1;
- }
- *boundary = octstr_create("");
- while ((c = octstr_get_char(content_header, pos)) != -1) {
- if (c == ';' || (quoted && c == '"') || (!quoted && c == ' '))
- break;
- octstr_format_append(*boundary, "%c", c);
- ++pos;
- }
- return 0;
- }
- static void change_header_value(List **push_headers, char *name, char *value)
- {
- http_header_remove_all(*push_headers, name);
- http_header_add(*push_headers, name, value);
- }
- /*
- * Some application level protocols may use MIME headers. This may cause problems
- * to them.
- */
- static void remove_mime_headers(List **push_headers)
- {
- http_header_remove_all(*push_headers, "MIME-Version");
- }
- /*
- * Badmessage-response element is redefined in pap, implementation note,
- * chapter 5. Do not add to the document a fragment being NULL or empty.
- * Return current status of HTTPClient.
- */
- static void send_bad_message_response(HTTPClient **c, Octstr *fragment,
- int code, int status)
- {
- Octstr *reply_body;
- reply_body = octstr_format("%s",
- "<?xml version="1.0"?>"
- "<!DOCTYPE pap PUBLIC "-//WAPFORUM//DTD PAP 1.0//EN""
- " "http://www.wapforum.org/DTD/pap_1.0.dtd">"
- "<pap>"
- "<badmessage-response code="");
- octstr_format_append(reply_body, "%d", code);
- octstr_format_append(reply_body, "%s", """
- " desc="");
- octstr_format_append(reply_body, "%s", "Not understood due to malformed"
- " syntax");
- octstr_format_append(reply_body, "%s", """);
- if (fragment != NULL && octstr_len(fragment) != 0) {
- octstr_format_append(reply_body, "%s", " bad-message-fragment="");
- octstr_format_append(reply_body, "%S", escape_fragment(fragment));
- octstr_format_append(reply_body, "%s", """);
- }
- octstr_format_append(reply_body, "%s", ">"
- "</badmessage-response>"
- "</pap>");
- debug("wap.push.ppg", 0, "PPG: send_bad_message_response: telling pi");
- send_to_pi(c, reply_body, status);
- octstr_destroy(fragment);
- }
- /*
- * Push response is defined in pap, chapter 9.3. Mapping between push ids and
- * http clients is done by using http_clients. We remove (push id, http client)
- * pair from the dictionary after the mapping has been done.
- * Return current status of HTTPClient.
- */
- static HTTPClient *send_push_response(WAPEvent *e, int status)
- {
- Octstr *reply_body,
- *url;
- HTTPClient *c;
- gw_assert(e->type == Push_Response);
- url = dict_get(urls, e->u.Push_Response.pi_push_id);
- dict_remove(urls, e->u.Push_Response.pi_push_id);
- reply_body = octstr_format("%s",
- "<?xml version="1.0"?>"
- "<!DOCTYPE pap PUBLIC "-//WAPFORUM//DTD PAP 1.0//EN""
- " "http://www.wapforum.org/DTD/pap_1.0.dtd">"
- "<pap>"
- "<push-response push-id="");
- octstr_format_append(reply_body, "%S", e->u.Push_Response.pi_push_id);
- octstr_format_append(reply_body, "%s", """);
- if (e->u.Push_Response.sender_name != NULL) {
- octstr_format_append(reply_body, "%s",
- " sender-name="");
- octstr_format_append(reply_body, "%S",
- e->u.Push_Response.sender_name);
- octstr_format_append(reply_body, "%s", """);
- }
- if (e->u.Push_Response.reply_time != NULL) {
- octstr_format_append(reply_body, "%s",
- " reply-time="");
- octstr_format_append(reply_body, "%S",
- e->u.Push_Response.reply_time);
- octstr_format_append(reply_body, "%s", """);
- }
- if (url != NULL) {
- octstr_format_append(reply_body, "%s",
- " sender-address="");
- octstr_format_append(reply_body, "%S", url);
- octstr_format_append(reply_body, "%s", """);
- }
- octstr_format_append(reply_body, "%s", ">"
- "</push-response>"
- "<response-result code ="");
- octstr_format_append(reply_body, "%d", e->u.Push_Response.code);
- octstr_format_append(reply_body, "%s", """);
- if (e->u.Push_Response.desc != NULL) {
- octstr_format_append(reply_body, "%s", " desc="");
- octstr_format_append(reply_body, "%S", e->u.Push_Response.desc);
- octstr_format_append(reply_body, """);
- }
- octstr_format_append(reply_body, "%s", ">"
- "</response-result>"
- "</pap>");
- octstr_destroy(url);
- c = dict_get(http_clients, e->u.Push_Response.pi_push_id);
- dict_remove(http_clients, e->u.Push_Response.pi_push_id);
- debug("wap.push.ppg", 0, "PPG: send_push_response: telling pi");
- send_to_pi(&c, reply_body, status);
- wap_event_destroy(e);
- return c;
- }
- /*
- * Ppg notifies pi about duplicate push id by sending a push response document
- * to it. Note that we never put the push id to the dict in this case.
- * Return current c value.
- */
- static void tell_fatal_error(HTTPClient **c, WAPEvent *e, Octstr *url,
- int status, int code)
- {
- Octstr *reply_body,
- *dos, /* temporaries */
- *tos,
- *sos;
- gw_assert(e->type == Push_Message);
- reply_body = octstr_format("%s",
- "<?xml version="1.0"?>"
- "<!DOCTYPE pap PUBLIC "-//WAPFORUM//DTD PAP 1.0//EN""
- " "http://www.wapforum.org/DTD/pap_1.0.dtd">"
- "<pap>"
- "<push-response push-id="");
- octstr_format_append(reply_body, "%S", e->u.Push_Message.pi_push_id);
- octstr_format_append(reply_body, "%s", """);
- octstr_format_append(reply_body, "%s",
- " sender-name="");
- octstr_format_append(reply_body, "%S", tos = tell_ppg_name());
- octstr_format_append(reply_body, "%s", """);
- octstr_format_append(reply_body, "%s",
- " reply-time="");
- octstr_format_append(reply_body, "%S", sos = set_time());
- octstr_format_append(reply_body, "%s", """);
- octstr_format_append(reply_body, "%s",
- " sender-address="");
- octstr_format_append(reply_body, "%S", url);
- octstr_format_append(reply_body, "%s", """);
- octstr_format_append(reply_body, "%s", ">"
- "</push-response>"
- "<response-result code ="");
- octstr_format_append(reply_body, "%d", code);
- octstr_format_append(reply_body, "%s", """);
- octstr_format_append(reply_body, "%s", " desc="");
- octstr_format_append(reply_body, "%S", dos = describe_code(code));
- octstr_format_append(reply_body, """);
- octstr_format_append(reply_body, "%s", ">"
- "</response-result>"
- "</pap>");
- debug("wap.push.ppg", 0, "PPG: tell_fatal_error: %s", octstr_get_cstr(dos));
- send_to_pi(c, reply_body, status);
- octstr_destroy(dos);
- octstr_destroy(tos);
- octstr_destroy(sos);
- wap_event_destroy(e);
- }
- /*
- * Does the HTTP reply to pi. Test has HTTPClient disappeared.
- * Return current c value.
- */
- static void send_to_pi(HTTPClient **c, Octstr *reply_body, int status) {
- List *reply_headers;
- reply_headers = http_create_empty_headers();
- http_header_add(reply_headers, "Content-Type", "application/xml");
- if (*c != NULL)
- http_send_reply(*c, status, reply_headers, reply_body);
- octstr_destroy(reply_body);
- http_destroy_headers(reply_headers);
- }
- /*
- * Escape characters not allowed in the value of an attribute. Pap does not
- * define escape sequences for message fragments; try common xml ones.
- */
- static Octstr *escape_fragment(Octstr *fragment)
- {
- long i;
- int c;
- i = 0;
- while (i < octstr_len(fragment)) {
- if ((c = octstr_get_char(fragment, i)) == '"') {
- replace_octstr_char(fragment, octstr_imm("""), &i);
- } else if (c == '<') {
- replace_octstr_char(fragment, octstr_imm("<"), &i);
- } else if (c == '>') {
- replace_octstr_char(fragment, octstr_imm(">"), &i);
- } else if (c == '&') {
- replace_octstr_char(fragment, octstr_imm("&"), &i);
- }
- ++i;
- }
- return fragment;
- }
- static int is_phone_number(long address_type)
- {
- return address_type == ADDR_PLMN;
- }
- static void replace_octstr_char(Octstr *os1, Octstr *os2, long *pos)
- {
- octstr_delete(os1, *pos, 1);
- octstr_insert(os1, os2, *pos);
- *pos += octstr_len(os2) - 1;
- }