/* ====================================================================
* 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.
*/
/*
* wap_push_pap_compiler.c - implementation of wap_push_pap_compiler.h inter-
* face (compiling pap documents to Kannel WAPEvents)
*
* This module implements PAP document DTD and status codes, defined in
* WAP-164-PAP-19991108-a (called hereafter pap), chapter 9 and
* PPG client addressing (it is. parsing client address of a pap document),
* defined in
* WAP-151-PPGService-19990816-a (ppg), chapter 7.
*
* In addition, Wapforum specification WAP-200-WDP-20001212-a (wdp) is re-
* ferred.
*
* Compiler can be used by PI or PPG (it will handle all possible PAP DTD
* elements). It checks that attribute values are legal and that an element
* has only legal attributes, but does not otherwise validate PAP documents
* against PAP DTD. (XML validation is quite another matter, of course.)
* Client address is parsed out from the relevant PAP message attribute
* containing lots of additional data, see ppg, 7.1. We do not yet support
* user defined addresses.
*
* After compiling, some semantic analysing of the resulted event, and sett-
* ing some defaults (however, relying on them is quite a bad policy). In
* addition changing undefined values (any) to defined ones.
*
* By Aarno Syvänen for Wapit Ltd and for Wiral Ltd.
*/
#include
#include
#include
#include
#include
#include
#include
#include "shared.h"
#include "wap_push_pap_compiler.h"
#include "wap_push_ppg.h"
/****************************************************************************
*
* Global data structures
*
* Table for pap elements. These are defined in PAP, Chapter 9.
*/
static char *pap_elements[] = {
"pap",
"push-message",
"address",
"quality-of-service",
"push-response",
"progress-note",
"response-result",
"cancel-message",
"cancel-result",
"cancel-response",
"resultnotification-message",
"resultnotification-response",
"statusquery-message",
"statusquery-response",
"statusquery-result",
"ccq-message",
"ccq-response",
"badmessage-response"
};
#define NUM_ELEMENTS sizeof(pap_elements)/sizeof(pap_elements[0])
/*
* Table for PAP attributes. These are defined in pap, Chapter 9.
*/
struct pap_attributes_t {
char *name;
char *value;
};
typedef struct pap_attributes_t pap_attributes_t;
static pap_attributes_t pap_attributes[] = {
{ "product-name", NULL },
{ "push-id", NULL },
{ "deliver-before-timestamp", NULL },
{ "deliver-after-timestamp", NULL },
{ "source-reference", NULL },
{ "progress-notes-requested", "true" },
{ "progress-notes-requested", "false" },
{ "ppg-notify-requested-to", NULL },
{ "address-value", NULL },
{ "priority", "high" },
{ "priority", "medium" },
{ "priority", "low" },
{ "delivery-method", "confirmed" },
{ "delivery-method", "preferconfirmed" },
{ "delivery-method", "unconfirmed" },
{ "delivery-method", "notspecified" },
{ "network", NULL },
{ "network-required", "true" },
{ "network-required", "false" },
{ "bearer", NULL },
{ "bearer-required", "true" },
{ "bearer-required", "false" },
{ "sender-address", NULL },
{ "sender-name", NULL },
{ "reply-time", NULL },
{ "stage", NULL },
{ "note", NULL },
{ "time", NULL },
{ "code", NULL },
{ "desc", NULL },
{ "received-time", NULL },
{ "event-time", NULL },
{ "message-state", NULL },
{ "query-id", NULL },
{ "app-id", NULL },
{ "bad-message-fragment", NULL}
};
#define NUM_ATTRIBUTES sizeof(pap_attributes)/sizeof(pap_attributes[0])
/*
* Status codes are defined in pap, chapter 9.13.
*/
static int pap_codes[] = {
PAP_ACCEPTED_FOR_PROCESSING,
PAP_BAD_REQUEST,
PAP_FORBIDDEN,
PAP_ADDRESS_ERROR,
PAP_CAPABILITIES_MISMATCH,
PAP_DUPLICATE_PUSH_ID,
PAP_TRANSFORMATION_FAILURE,
PAP_REQUIRED_BEARER_NOT_AVAILABLE,
PAP_ABORT_USERPND
};
#define NUM_CODES sizeof(pap_codes)/sizeof(pap_codes[0])
/*
* Possible bearer types. These are defined in wdp, appendix C.
*/
static char *pap_bearer_types[] = {
"Any",
"USSD",
"SMS",
"GUTS/R-Data",
"CSD",
"Packet Data",
"GPRS",
"CDPD",
"FLEX",
"SDS",
"ReFLEX",
"MPAK",
"GHOST/R_DATA"
};
#define NUM_BEARER_TYPES sizeof(pap_bearer_types)/sizeof(pap_bearer_types[0])
/*
* Possible network types. These are defined in wdp, appendix C.
*/
static char *pap_network_types[] = {
"Any",
"GSM",
"ANSI-136",
"IS-95 CDMA",
"AMPS",
"PDC",
"IDEN",
"Paging network",
"PHS",
"TETRA",
"Mobitex",
};
#define NUM_NETWORK_TYPES sizeof(pap_network_types)/ \
sizeof(pap_network_types[0])
/****************************************************************************
*
* Prototypes of internal functions. Note that suffix 'Ptr' means '*'.
*/
static int parse_document(xmlDocPtr doc_p, WAPEvent **e);
static int parse_node(xmlNodePtr node, WAPEvent **e, long *type_of_address,
int *is_any);
static int parse_element(xmlNodePtr node, WAPEvent **e,
long *type_of_address, int *is_any);
static int parse_attribute(Octstr *element_name, xmlAttrPtr attribute,
WAPEvent **e, long *type_of_address, int *is_any);
static int parse_attr_value(Octstr *element_name, Octstr *attr_name,
Octstr *attr_value, WAPEvent **e,
long *type_of_address, int *is_any);
static int set_attribute_value(Octstr *element_name, Octstr *attr_value,
Octstr *attr_name, WAPEvent **e);
static int return_flag(Octstr *ros);
static void wap_event_accept_or_create(Octstr *element_name, WAPEvent **e);
static int parse_pap_value(Octstr *attr_name, Octstr *attr_value, WAPEvent **e);
static int parse_push_message_value(Octstr *attr_name, Octstr *attr_value,
WAPEvent **e);
static int parse_address_value(Octstr *attr_name, Octstr *attr_value,
WAPEvent **e, long *type_of_address);
static int parse_quality_of_service_value(Octstr *attr_name,
Octstr *attr_value, WAPEvent **e,
int *is_any);
static int parse_push_response_value(Octstr *attr_name, Octstr *attr_value,
WAPEvent **e);
static int parse_progress_note_value(Octstr *attr_name, Octstr *attr_value,
WAPEvent **e);
static int parse_bad_message_response_value(Octstr *attr_name,
Octstr *attr_value, WAPEvent **e);
static int parse_response_result_value(Octstr *attr_name,
Octstr *attr_value, WAPEvent **e);
static int parse_code(Octstr *attr_value);
static Octstr *parse_bearer(Octstr *attr_value);
static Octstr *parse_network(Octstr *attr_value);
static int parse_requirement(Octstr *attr_value);
static int parse_priority(Octstr *attr_value);
static int parse_delivery_method(Octstr *attr_value);
static int parse_state(Octstr *attr_value);
static long parse_wappush_client_address(Octstr **address, long pos,
long *type_of_address);
static long parse_ppg_specifier(Octstr **address, long pos);
static long parse_client_specifier(Octstr **address, long pos,
long *type_of_address);
static long parse_constant(const char *field_name, Octstr **address, long pos);
static long parse_dom_fragment(Octstr **address, long pos);
static long drop_character(Octstr **address, long pos);
static long parse_type(Octstr **address, Octstr **type_value, long pos);
static long parse_ext_qualifiers(Octstr **address, long pos,
Octstr *type_value);
static long parse_global_phone_number(Octstr **address, long pos);
static long parse_ipv4(Octstr **address, long pos);
static long parse_ipv6(Octstr **address, long pos);
static long parse_escaped_value(Octstr **address, long pos);
static Octstr *prepend_char(Octstr *address, unsigned char c);
static int qualifiers(Octstr *address, long pos, Octstr *type);
static long parse_qualifier_value(Octstr **address, long pos);
static long parse_qualifier_keyword(Octstr **address, long pos);
static long parse_ipv4_fragment(Octstr **address, long pos);
static long parse_ipv6_fragment(Octstr **address, long pos);
static int wina_bearer_identifier(Octstr *type_value);
static int create_peek_window(Octstr **address, long *pos);
static long rest_unescaped(Octstr **address, long pos);
static int issafe(Octstr **address, long pos);
static long accept_safe(Octstr **address, long pos);
static long accept_escaped(Octstr **address, long pos);
static long handle_two_terminators (Octstr **address, long pos,
unsigned char comma, unsigned char point, unsigned char c,
long fragment_parsed, long fragment_length);
static int uses_gsm_msisdn_address(long bearer_required, Octstr *bearer);
static int uses_ipv4_address(long bearer_required, Octstr *bearer);
static int uses_ipv6_address(long bearer_required, Octstr *bearer);
static int event_semantically_valid(WAPEvent *e, long type_of_address);
static char *address_type(long type_of_address);
static void set_defaults(WAPEvent **e, long type_of_address);
static void set_bearer_defaults(WAPEvent **e, long type_of_address);
static void set_network_defaults(WAPEvent **e, long type_of_address);
static int set_anys(WAPEvent **e, long type_of_address, int is_any);
static void set_any_value(int *is_any, Octstr *attr_name, Octstr *attr_value);
/*
* Macro for creating an octet string from a node content. This has two
* versions for different libxml node content implementation methods.
*/
#ifdef XML_USE_BUFFER_CONTENT
#define create_octstr_from_node(node) (octstr_create(node->content->content))
#else
#define create_octstr_from_node(node) (octstr_create(node->content))
#endif
/****************************************************************************
*
* Compile PAP control document to a corresponding Kannel event. Checks vali-
* dity of the document. The caller must initialize wap event to NULL. In add-
* ition, it must free memory allocated by this function.
*
* After compiling, some semantic analysing of the resulted event.
*
* Note that entities in the DTD are parameter entities and they can appear
* only in DTD (See site http://www.w3.org/TR/REC-xml, Chapter 4.1). So we do
* not need to worry about them in the document itself.
*
* Returns 0, when success
* -1, when a non-implemented pap feature is asked for
* -2, when error
* In addition, returns a newly created wap event corresponding the pap
* control message, if success, wap event NULL otherwise.
*/
int pap_compile(Octstr *pap_content, WAPEvent **e)
{
xmlDocPtr doc_p;
size_t oslen;
int ret;
if (octstr_search_char(pap_content, '\0', 0) != -1) {
warning(0, "PAP COMPILER: pap_compile: pap source contained a \\0"
" character");
return -2;
}
octstr_strip_blanks(pap_content);
oslen = octstr_len(pap_content);
doc_p = xmlParseMemory(octstr_get_cstr(pap_content), oslen);
if (doc_p == NULL) {
goto error;
}
if ((ret = parse_document(doc_p, e)) < 0) {
goto parserror;
}
xmlFreeDoc(doc_p);
return 0;
parserror:
xmlFreeDoc(doc_p);
wap_event_destroy(*e);
*e = NULL;
return ret;
error:
warning(0, "PAP COMPILER: pap_compile: parse error in pap source");
xmlFreeDoc(doc_p);
wap_event_destroy(*e);
*e = NULL;
return -2;
}
/****************************************************************************
*
* Implementation of internal functions
*
*/
enum {
NEITHER = 0,
BEARER_ANY = 1,
NETWORK_ANY = 2,
EITHER = 3,
ERROR_ANY = 4
};
/*
* Parse the document node of libxml syntax tree. FIXME: Add parsing of pap
* version.
* After parsing, some semantic analysing of the resulted event. Then set
* a default network and bearer deduced from address type, if the correspond-
* ing pap attribute is missing.
*
* Returns 0, when success
* -1, when a non-implemented pap feature is requested
* -2, when error
* In addition, return a newly created wap event corresponding the pap
* control message, if success, or partially parsed pap document, if not. Set
* a field containing address type.
*/
static int parse_document(xmlDocPtr doc_p, WAPEvent **e)
{
xmlNodePtr node;
int ret,
is_any; /* is bearer and/or network set any in qos
attribute */
long type_of_address;
gw_assert(doc_p);
node = xmlDocGetRootElement(doc_p);
is_any = NEITHER;
if ((ret = parse_node(node, e, &type_of_address, &is_any)) < 0)
return ret;
(*e)->u.Push_Message.address_type = type_of_address;
if ((ret= event_semantically_valid(*e, type_of_address)) == 0) {
warning(0, "wrong type of address for requested bearer");
return -2;
} else if (ret == -1) {
info(0, "reverting to default bearer and network");
set_defaults(e, type_of_address);
return 0;
}
if (!set_anys(e, type_of_address, is_any)) {
warning(0, "unable to handle any values in qos");
return -2;
} else {
debug("wap.push.pap.compiler", 0, "using defaults instead of anys");
}
wap_event_assert(*e);
return 0;
}
static int set_anys(WAPEvent **e, long type_of_address, int is_any)
{
switch (is_any) {
case NEITHER:
return 1;
case BEARER_ANY:
set_bearer_defaults(e, type_of_address);
return 1;
case NETWORK_ANY:
set_network_defaults(e, type_of_address);
return 1;
case EITHER:
set_defaults(e, type_of_address);
return 1;
default:
return 0;
}
}
/*
* We actually use address_type field of a wap event for cotrolling the bearer
* selection. Bearer and network filed are used for debugging purposes.
*/
static void set_defaults(WAPEvent **e, long type_of_address)
{
set_bearer_defaults(e, type_of_address);
set_network_defaults(e, type_of_address);
}
static void set_bearer_defaults(WAPEvent **e, long type_of_address)
{
gw_assert(type_of_address == ADDR_USER || type_of_address == ADDR_PLMN ||
type_of_address == ADDR_IPV4 || type_of_address == ADDR_IPV6 ||
type_of_address == ADDR_WINA);
if ((*e)->type != Push_Message)
return;
(*e)->u.Push_Message.bearer_required = PAP_TRUE;
octstr_destroy((*e)->u.Push_Message.bearer);
switch (type_of_address) {
case ADDR_PLMN:
(*e)->u.Push_Message.bearer = octstr_format("%s", "SMS");
break;
case ADDR_IPV4:
(*e)->u.Push_Message.bearer = octstr_format("%s", "CSD");
break;
case ADDR_IPV6:
break;
}
}
static void set_network_defaults(WAPEvent **e, long type_of_address)
{
gw_assert(type_of_address == ADDR_USER || type_of_address == ADDR_PLMN ||
type_of_address == ADDR_IPV4 || type_of_address == ADDR_IPV6 ||
type_of_address == ADDR_WINA);
if ((*e)->type != Push_Message)
return;
(*e)->u.Push_Message.network_required = PAP_TRUE;
octstr_destroy((*e)->u.Push_Message.network);
switch (type_of_address) {
case ADDR_PLMN:
(*e)->u.Push_Message.network = octstr_format("%s", "GSM");
break;
case ADDR_IPV4:
(*e)->u.Push_Message.network = octstr_format("%s", "GSM");
break;
case ADDR_IPV6:
break;
}
}
static char *address_type(long type_of_address)
{
switch(type_of_address) {
case ADDR_USER:
return "user defined address";
case ADDR_PLMN:
return "a phone number";
case ADDR_IPV4:
return "a IPv4 address";
case ADDR_IPV6:
return "a IPv6 address";
case ADDR_WINA:
return "a WINA accepted address";
default:
return "unknown address";
}
}
/*
* Do semantic analysis, when the event was Push_Message. Do not accept an IP
* address, when a non-IP bearer is requested, and a phone number, when an IP
* bearer is requested.
* Return 0, when event is unacceptable
* 1, when it is acceptable
* -1, when there are no bearer or network specified
*/
static int event_semantically_valid(WAPEvent *e, long type_of_address)
{
int ret;
debug("wap.push.pap.compiler", 0, "PAP COMPILER: doing semantic analysis"
" for address type %s", address_type(type_of_address));
if (e->type != Push_Message) {
return 1;
}
if (e->u.Push_Message.network_required !=
e->u.Push_Message.bearer_required) {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: network-required and"
" bearer-required must have same value");
return 0;
}
if (type_of_address == ADDR_PLMN) {
if ((ret = uses_gsm_msisdn_address(
e->u.Push_Message.bearer_required,
e->u.Push_Message.bearer)) == 0) {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: bearer does"
" not accept PLMN address");
return 0;
} else if (ret == -1) {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: network or"
"bearer missing, reverting to GSM+SMS");
return -1;
} else
return 1;
}
if (type_of_address == ADDR_IPV4) {
if ((ret = uses_ipv4_address(e->u.Push_Message.bearer_required,
e->u.Push_Message.bearer)) == 0) {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: bearer does"
" not accept IPv4 address");
return 0;
} else if (ret == -1) {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: network or"
" bearer missing, reverting to GSM+CSD");
return -1;
} else
return 1;
}
if (type_of_address == ADDR_IPV6) {
if ((ret = uses_ipv6_address(e->u.Push_Message.bearer_required,
e->u.Push_Message.bearer)) == 0) {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: network or"
" bearer does not accept IPv6 address");
return 0;
} else if (ret == -1) {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: network or"
" bearer missing, reverting Any+Any");
return -1;
} else
return 1;
}
return 0;
}
/*
* Bearers accepting IP addresses. These are defined in wdp, appendix c. Note
* that when ipv6 bearers begin to appear, they must be added to the following
* table. Currently none are specified.
*/
static char *ip6_bearers[] = {
"Any"
};
#define NUMBER_OF_IP6_BEARERS sizeof(ip6_bearers)/sizeof(ip6_bearers[0])
static char *ip4_bearers[] = {
"Any",
"CSD",
"Packet Data",
"GPRS",
"USSD"
};
#define NUMBER_OF_IP4_BEARERS sizeof(ip4_bearers)/sizeof(ip4_bearers[0])
/*
* Bearers accepting gsm msisdn addresses are defined in wdp, appendix c. We
* add any, because Kannel PPG will change this to SMS.
* Return -1, when there are no bearer defined
* 0, when a bearer not accepting msisdn address is found
* 1, when a bearer is accepting msisdn addresesses
*/
static int uses_gsm_msisdn_address(long bearer_required, Octstr *bearer)
{
if (!bearer_required)
return -1;
if (!bearer)
return 1;
return (octstr_case_compare(bearer, octstr_imm("SMS")) == 0 ||
octstr_case_compare(bearer, octstr_imm("GHOST/R_DATA")) == 0 ||
octstr_case_compare(bearer, octstr_imm("Any")) == 0);
}
/*
* Bearers accepting ipv4 addresses are defined in wdp, appendix c.
* Return -1, when there are no bearer defined
* 0, when a bearer not accepting ipv4 address is found
* 1, when a bearer is accepting ipv4 addresesses
*/
static int uses_ipv4_address(long bearer_required, Octstr *bearer)
{
long i;
if (!bearer_required) {
return -1;
}
if (!bearer)
return -1;
i = 0;
while (i < NUMBER_OF_IP4_BEARERS) {
if (octstr_case_compare(bearer, octstr_imm(ip4_bearers[i])) == 0) {
return 1;
}
++i;
}
return 0;
}
/*
* Bearers accepting ipv6 addresses (currently *not* accepting) are defined in
* wdp, appendix c.
* Return -1, when there are no bearer defined
* 0, when a bearer not accepting ipv6 address is found
* 1, when a bearer is accepting ipv6 addresesses
*/
static int uses_ipv6_address(long bearer_required, Octstr *bearer)
{
long i;
if (!bearer_required)
return -1;
if (!bearer)
return -1;
i = 0;
while (i < NUMBER_OF_IP6_BEARERS) {
if (octstr_case_compare(bearer, octstr_imm(ip6_bearers[i])) == 0) {
return 1;
}
++i;
}
return 0;
}
/*
* Parse node of the syntax tree. DTD, as defined in pap, chapter 9, contains
* only elements (entities are restricted to DTDs).
* The caller must initialize the value of is_any to 0.
*
* Output: a) a newly created wap event containing attributes from pap
* document node, if success; partially parsed node, if not.
* b) the type of of the client address
* c) is bearer and/or network any
* Returns 0, when success
* -1, when a non-implemented feature is requested
* -2, when error
*/
static int parse_node(xmlNodePtr node, WAPEvent **e, long *type_of_address,
int *is_any)
{
int ret;
switch (node->type) {
case XML_COMMENT_NODE: /* ignore text, comments and pi nodes */
case XML_PI_NODE:
case XML_TEXT_NODE:
break;
case XML_ELEMENT_NODE:
if ((ret = parse_element(node, e, type_of_address, is_any)) < 0) {
return ret;
}
break;
default:
warning(0, "PAP COMPILER: parse_node: Unknown XML node in PAP source");
return -2;
}
if (node->children != NULL)
if ((ret = parse_node(node->children, e, type_of_address,
is_any)) < 0) {
return ret;
}
if (node->next != NULL)
if ((ret = parse_node(node->next, e, type_of_address, is_any)) < 0) {
return ret;
}
return 0;
}
/*
* Parse elements of a PAP source.
*
* Output: a) a newly created wap event containing attributes from the
* element, if success; containing some unparsed attributes, if not.
* b) the type of the client address
* c) is bearer and/or network any
* Returns 0, when success
* -1, when a non-implemented feature is requested
* -2, when error
* In addition, return
*/
static int parse_element(xmlNodePtr node, WAPEvent **e, long *type_of_address,
int *is_any)
{
Octstr *name;
xmlAttrPtr attribute;
size_t i;
int ret;
name = octstr_create(node->name);
if (octstr_len(name) == 0) {
octstr_destroy(name);
debug("wap.push.pap.compiler", 0, "PAP COMPILER: element name length"
" zero");
return -2;
}
i = 0;
while (i < NUM_ELEMENTS) {
if (octstr_compare(name, octstr_imm(pap_elements[i])) == 0)
break;
++i;
}
if (i == NUM_ELEMENTS) {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: unknown element:");
octstr_dump(name, 0);
octstr_destroy(name);
return -2;
}
if (node->properties != NULL) {
attribute = node->properties;
while (attribute != NULL) {
if ((ret = parse_attribute(name, attribute, e,
type_of_address, is_any)) < 0) {
octstr_destroy(name);
return ret;
}
attribute = attribute->next;
}
}
octstr_destroy(name);
return 0; /* If we reach this point, our node does not
have any attributes left (or it had no
attributes to start with). This is *not*
an error. */
}
/*
* Parse attribute updates corresponding fields of the wap event. Check that
* both attribute name and value are papwise legal. If value is enumerated,
* legal values are stored in the attributes table. Otherwise, call a separate
* parsing function. If an attribute value is empty, use value "erroneous".
*
* Output: a) a newly created wap event containing parsed attribute from pap
* source, if successfull, an uncomplete wap event otherwise.
* b) the type of the client address
* c) is bearer and/or network set any
* Returns 0, when success
* -1, when a non-implemented feature is requested
* -2, when error
*/
static int parse_attribute(Octstr *element_name, xmlAttrPtr attribute,
WAPEvent **e, long *type_of_address, int *is_any)
{
Octstr *attr_name, *value, *nameos;
size_t i;
int ret;
nameos = octstr_imm("erroneous");
attr_name = octstr_create(attribute->name);
if (attribute->children != NULL)
value = create_octstr_from_node(attribute->children);
else
value = octstr_imm("erroneous");
i = 0;
while (i < NUM_ATTRIBUTES) {
if (octstr_compare(attr_name, nameos =
octstr_imm(pap_attributes[i].name)) == 0)
break;
++i;
}
if (i == NUM_ATTRIBUTES) {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: unknown attribute `%s' "
"within XML entity `%s'", octstr_get_cstr(attr_name),
octstr_get_cstr(element_name));
goto error;
}
/*
* Parse an attribute (it is, check cdata is has for a value) that is *not* an
* enumeration. Legal values are defined in pap, chapter 9.
*/
if (pap_attributes[i].value == NULL) {
ret = parse_attr_value(element_name, attr_name, value, e,
type_of_address, is_any);
if (ret == -2) {
goto error;
} else {
goto parsed;
}
}
while (octstr_compare(attr_name,
nameos = octstr_imm(pap_attributes[i].name)) == 0) {
if (octstr_compare(value, octstr_imm(pap_attributes[i].value)) == 0)
break;
++i;
}
if (octstr_compare(attr_name, nameos) != 0) {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: unknown attribute "
"value `%s' for attribute `%s' within XML entity `%s'",
octstr_get_cstr(value), octstr_get_cstr(attr_name),
octstr_get_cstr(element_name));
goto error;
}
/*
* Check that the value of the attribute is one enumerated for this attribute
* in pap, chapter 9.
*/
if (set_attribute_value(element_name, value, attr_name, e) == -1)
goto error;
octstr_destroy(attr_name);
octstr_destroy(value);
return 0;
error:
octstr_destroy(attr_name);
octstr_destroy(value);
return -2;
parsed:
octstr_destroy(attr_name);
octstr_destroy(value);
return ret;
}
/*
* Attribute value parsing functions for the PAP element.
* Defined in PAP, chapter 8.1.
*/
static int parse_pap_value(Octstr *attr_name, Octstr *attr_value, WAPEvent **e)
{
Octstr *ros;
if (*e != NULL)
wap_event_dump(*e);
ros = octstr_imm("erroneous");
if (octstr_compare(attr_name, octstr_imm("product-name")) == 0) {
/*
* XXX This is a kludge.
* We can't add the product-name value to the WAPEvent, because
* the wap_event_create() is created in the deeper layer, which
* means as soon as we see or .
* But we would have to decide which WAPEvent to create while
* being on the higher level.
* How's this to be solved?! -- Stipe
*/
return 0;
}
debug("wap.push.pap.compiler", 0, "PAP COMPILER: unknown pap"
" element attribute `%s'", octstr_get_cstr(attr_name));
return -2;
}
/*
* Value parsing functions return the newly created wap event containing
* attribute value from pap source, if successfull; NULL otherwise . Value
* types of attributes are defined in pap, chapter 9.
*/
static int parse_push_message_value(Octstr *attr_name, Octstr *attr_value,
WAPEvent **e)
{
Octstr *ros;
ros = octstr_imm("erroneous");
if (octstr_compare(attr_name, octstr_imm("push-id")) == 0) {
octstr_destroy((**e).u.Push_Message.pi_push_id);
(**e).u.Push_Message.pi_push_id = octstr_duplicate(attr_value);
return 0;
} else if (octstr_compare(attr_name,
octstr_imm("deliver-before-timestamp")) == 0) {
(**e).u.Push_Message.deliver_before_timestamp =
(ros = parse_date(attr_value)) ?
octstr_duplicate(attr_value) : octstr_imm("erroneous");
return return_flag(ros);
} else if (octstr_compare(attr_name,
octstr_imm("deliver-after-timestamp")) == 0) {
(**e).u.Push_Message.deliver_after_timestamp =
(ros = parse_date(attr_value)) ?
octstr_duplicate(attr_value) : octstr_imm("erroneous");
return return_flag(ros);
} else if (octstr_compare(attr_name,
octstr_imm("source-reference")) == 0) {
(**e).u.Push_Message.source_reference = octstr_duplicate(attr_value);
return 0;
} else if (octstr_compare(attr_name,
octstr_imm("ppg-notify-requested-to")) == 0) {
(**e).u.Push_Message.ppg_notify_requested_to =
octstr_duplicate(attr_value);
return 0;
}
debug("wap.push.pap.compiler", 0, "PAP COMPILER: unknown push message"
" element attribute");
return -2;
}
/*
* When there is no legal address to be stored in field (either parsing was
* unsuccessful or an unimplemented address format was requested by the push
* initiator) we use value "erroneous". This is necessary, because this a
* mandatory field.
*
* Output a) a newly created wap event
* b) the type of the client address
*/
static int parse_address_value(Octstr *attr_name, Octstr *attr_value,
WAPEvent **e, long *type_of_address)
{
int ret;
ret = -2;
if (octstr_compare(attr_name, octstr_imm("address-value")) == 0) {
octstr_destroy((**e).u.Push_Message.address_value);
(**e).u.Push_Message.address_value =
(ret = parse_address(&attr_value, type_of_address)) > -1 ?
octstr_duplicate(attr_value) : octstr_imm("erroneous");
return ret;
}
debug("wap.push.pap.compiler", 0, "PAP COMPILER: unknown address element"
" attribute");
return -2;
}
static int parse_quality_of_service_value(Octstr *attr_name,
Octstr *attr_value, WAPEvent **e,
int *is_any)
{
Octstr *ros;
ros = octstr_imm("erroneous");
if (octstr_compare(attr_name, octstr_imm("network")) == 0) {
(**e).u.Push_Message.network = (ros = parse_network(attr_value)) ?
octstr_duplicate(attr_value) : octstr_imm("erroneous");
set_any_value(is_any, attr_name, attr_value);
return return_flag(ros);
}
if (octstr_compare(attr_name, octstr_imm("bearer")) == 0) {
(**e).u.Push_Message.bearer = (ros = parse_bearer(attr_value)) ?
octstr_duplicate(attr_value) : octstr_imm("erroneous");
set_any_value(is_any, attr_name, attr_value);
return return_flag(ros);
}
debug("wap.push.pap.compiler", 0, "PAP COMPILER: unknown quality of"
" service attribute");
return -2;
}
static void set_any_value(int *is_any, Octstr *attr_name, Octstr *attr_value)
{
switch (*is_any) {
case NEITHER:
if (octstr_compare(attr_name, octstr_imm("bearer")) == 0 &&
octstr_case_compare(attr_value, octstr_imm("any")) == 0)
*is_any = BEARER_ANY;
else if (octstr_compare(attr_name, octstr_imm("network")) == 0 &&
octstr_case_compare(attr_value, octstr_imm("any")) == 0)
*is_any = NETWORK_ANY;
return;
case BEARER_ANY:
if (octstr_compare(attr_name, octstr_imm("network")) == 0 &&
octstr_case_compare(attr_value, octstr_imm("any")) == 0)
*is_any = EITHER;
return;
case NETWORK_ANY:
if (octstr_compare(attr_name, octstr_imm("bearer")) == 0 &&
octstr_case_compare(attr_value, octstr_imm("any")) == 0)
*is_any = EITHER;
return;
case EITHER:
debug("wap.push.pap.compiler", 0, "PAP COMPILER: problems with"
" setting any");
*is_any = ERROR_ANY;
return;
default:
debug("wap.push.pap.compiler", 0, "PAP COMPILER: wrong any value");
*is_any = ERROR_ANY;
return;
}
}
static int parse_push_response_value(Octstr *attr_name, Octstr *attr_value,
WAPEvent **e)
{
Octstr *ros;
int ret;
ret = -2;
ros = octstr_imm("erroneous");
if (octstr_compare(attr_name, octstr_imm("push-id")) == 0) {
octstr_destroy((**e).u.Push_Response.pi_push_id);
(**e).u.Push_Response.pi_push_id = octstr_duplicate(attr_value);
return 0;
} else if (octstr_compare(attr_name, octstr_imm("sender-address")) == 0) {
(**e).u.Push_Response.sender_address = octstr_duplicate(attr_value);
return 0;
} else if (octstr_compare(attr_name, octstr_imm("reply-time")) == 0) {
(**e).u.Push_Response.reply_time = (ros = parse_date(attr_value)) ?
octstr_duplicate(attr_value) : NULL;
return return_flag(ros);
} else if (octstr_compare(attr_name, octstr_imm("sender-name")) == 0) {
(**e).u.Push_Response.sender_name = octstr_duplicate(attr_value);
return 0;
}
debug("wap.push.pap.compiler", 0, "PAP COMPILER: unknown push response"
" element attribute");
return -2;
}
static int parse_progress_note_value(Octstr *attr_name, Octstr *attr_value,
WAPEvent **e)
{
Octstr *ros;
int ret;
ret = -2;
ros = octstr_imm("erroneous");
if (octstr_compare(attr_name, octstr_imm("stage")) == 0) {
(**e).u.Progress_Note.stage =
(ret = parse_state(attr_value)) ? ret : 0;
return ret;
} else if (octstr_compare(attr_name, octstr_imm("note")) == 0) {
(**e).u.Progress_Note.note = octstr_duplicate(attr_value);
return 0;
} else if (octstr_compare(attr_name, octstr_imm("time")) == 0) {
(**e).u.Progress_Note.time = (ros = parse_date(attr_value)) ?
octstr_duplicate(attr_value) : octstr_imm("erroneous");
return return_flag(ros);
}
debug("wap.push.pap.compiler", 0, "PAP COMPILER: unknown progress note"
" element attribute");
return -2;
}
static int parse_bad_message_response_value(Octstr *attr_name,
Octstr *attr_value, WAPEvent **e)
{
if (octstr_compare(attr_name, octstr_imm("code")) == 0) {
(**e).u.Bad_Message_Response.code = parse_code(attr_value);
return 0;
} else if (octstr_compare(attr_name, octstr_imm("desc")) == 0) {
(**e).u.Bad_Message_Response.desc = octstr_duplicate(attr_value);
return 0;
} else if (octstr_compare(attr_name,
octstr_imm("bad-message-fragment")) == 0) {
(**e).u.Bad_Message_Response.bad_message_fragment =
octstr_duplicate(attr_value);
return 0;
}
debug("wap.push.pap.compiler", 0, "PAP COMPILER: unknown bad message"
" response element attribute");
return -2;
}
static int parse_response_result_value(Octstr *attr_name,
Octstr *attr_value, WAPEvent **e)
{
if (octstr_compare(attr_name, octstr_imm("code")) == 0) {
(**e).u.Push_Response.code = parse_code(attr_value);
return 0;
} else if (octstr_compare(attr_name, octstr_imm("desc")) == 0) {
(**e).u.Push_Response.desc = octstr_duplicate(attr_value);
return 0;
}
debug("wap.push.pap.compiler", 0, "PAP COMPILER: unknown response result"
" attribute");
return -2;
}
/*
* Do not create multiple events. If *e points to NULL, we have not yet creat-
* ed a wap event. Create a wap event mandatory fields set to error values
* (these will be latter overwritten). This hack will disappear when we have
* PAP validation.
*/
static void wap_event_accept_or_create(Octstr *element_name, WAPEvent **e)
{
if (octstr_compare(element_name, octstr_imm("push-message")) == 0
&& *e == NULL) {
*e = wap_event_create(Push_Message);
(**e).u.Push_Message.pi_push_id = octstr_format("%s", "erroneous");
(**e).u.Push_Message.address_value = octstr_format("%s", "erroneous");
} else if (octstr_compare(element_name, octstr_imm("push-response")) == 0
&& *e == NULL) {
*e = wap_event_create(Push_Response);
(**e).u.Push_Response.pi_push_id = octstr_format("%s", "erroneous");
} else if (octstr_compare(element_name, octstr_imm("progress-note")) == 0
&& *e == NULL) {
*e = wap_event_create(Progress_Note);
} else if (octstr_compare(element_name,
octstr_imm("badmessage-response")) == 0 && *e == NULL) {
*e = wap_event_create(Bad_Message_Response);
}
}
static int return_flag(Octstr *ros)
{
if (ros) {
return 0;
} else {
return -2;
}
}
/*
* Validates non-enumeration attributes and stores their value to a newly
* created wap event e. (Even when attribute value parsing was not success-
* full.) We do not accept NULL or empty attributes (if this kind of an
* attribute is optional, we just drop it from the tokenised document).
*
* Output: a) a wap event, as created by subroutines
* b) the type of the client address
* c) is bearer or network set any
* Returns 0, when success,
* -1, when a non-implemented feature requested.
* -2, when an error
*/
static int parse_attr_value(Octstr *element_name, Octstr *attr_name,
Octstr *attr_value, WAPEvent **e,
long *type_of_address, int *is_any)
{
if (octstr_compare(attr_value, octstr_imm("erroneous")) == 0) {
debug("wap.push.pap.compiler", 0, "unknown value for an attribute");
return -2;
}
wap_event_accept_or_create(element_name, e);
if (octstr_compare(element_name, octstr_imm("pap")) == 0) {
return parse_pap_value(attr_name, attr_value, e);
} else if (octstr_compare(element_name, octstr_imm("push-message")) == 0) {
return parse_push_message_value(attr_name, attr_value, e);
} else if (octstr_compare(element_name, octstr_imm("address")) == 0) {
return parse_address_value(attr_name, attr_value, e, type_of_address);
} else if (octstr_compare(element_name,
octstr_imm("quality-of-service")) == 0) {
return parse_quality_of_service_value(attr_name, attr_value, e,
is_any);
} else if (octstr_compare(element_name,
octstr_imm("push-response")) == 0) {
return parse_push_response_value(attr_name, attr_value, e);
} else if (octstr_compare(element_name,
octstr_imm("progress-note")) == 0) {
return parse_progress_note_value(attr_name, attr_value, e);
} else if (octstr_compare(element_name,
octstr_imm("badmessage-response")) == 0) {
return parse_bad_message_response_value(attr_name, attr_value, e);
} else if (octstr_compare(element_name,
octstr_imm("response-result")) == 0) {
return parse_response_result_value(attr_name, attr_value, e);
}
return -2;
}
/*
* Stores values of enumeration fields of a pap control message to wap event e.
*/
static int set_attribute_value(Octstr *element_name, Octstr *attr_value,
Octstr *attr_name, WAPEvent **e)
{
int ret;
ret = -2;
if (octstr_compare(element_name, octstr_imm("push-message")) == 0) {
if (octstr_compare(attr_name,
octstr_imm("progress-notes-requested")) == 0)
(**e).u.Push_Message.progress_notes_requested =
(ret = parse_requirement(attr_value)) >= 0 ? ret : 0;
} else if (octstr_compare(element_name,
octstr_imm("quality-of-service")) == 0) {
if (octstr_compare(attr_name, octstr_imm("priority")) == 0)
(**e).u.Push_Message.priority =
(ret = parse_priority(attr_value)) >= 0 ? ret : 0;
else if (octstr_compare(attr_name, octstr_imm("delivery-method")) == 0)
(**e).u.Push_Message.delivery_method =
(ret = parse_delivery_method(attr_value)) >= 0 ? ret : 0;
else if (octstr_compare(attr_name,
octstr_imm("network-required")) == 0)
(**e).u.Push_Message.network_required =
(ret = parse_requirement(attr_value)) >= 0 ? ret : 0;
else if (octstr_compare(attr_name, octstr_imm("bearer-required")) == 0)
(**e).u.Push_Message.bearer_required =
(ret = parse_requirement(attr_value)) >= 0 ? ret : 0;
}
return ret;
}
/*
* We must recognize status class and treat unrecognized codes as a x000 code,
* as required by pap, 9.13, p 27.
*/
static int parse_code(Octstr *attr_value)
{
long attr_as_number,
len;
size_t i;
Octstr *ros;
for (i = 0; i < NUM_CODES; i++) {
ros = octstr_format("%d", pap_codes[i]);
if (octstr_compare(attr_value, ros) == 0) {
octstr_destroy(ros);
return pap_codes[i];
}
octstr_destroy(ros);
}
warning(0, "PAP COMPILER: parse_code: no such return code, reversing to"
" x000 code");
len = octstr_parse_long(&attr_as_number, attr_value, 0, 10);
if (attr_as_number >= PAP_OK && attr_as_number < PAP_BAD_REQUEST) {
attr_as_number = PAP_OK;
} else if (attr_as_number >= PAP_BAD_REQUEST &&
attr_as_number < PAP_INTERNAL_SERVER_ERROR) {
attr_as_number = PAP_BAD_REQUEST;
} else if (attr_as_number >= PAP_INTERNAL_SERVER_ERROR &&
attr_as_number < PAP_SERVICE_FAILURE) {
attr_as_number = PAP_INTERNAL_SERVER_ERROR;
} else if (attr_as_number >= PAP_SERVICE_FAILURE &&
attr_as_number < PAP_CLIENT_ABORTED) {
attr_as_number = PAP_SERVICE_FAILURE;
} else {
attr_as_number = PAP_CLIENT_ABORTED;
}
return attr_as_number;
}
static Octstr *parse_bearer(Octstr *attr_value)
{
size_t i;
Octstr *ros;
for (i = 0; i < NUM_BEARER_TYPES; i++) {
if (octstr_case_compare(attr_value,
ros = octstr_imm(pap_bearer_types[i])) == 0)
return ros;
}
warning(0, "no such bearer");
return NULL;
}
static Octstr *parse_network(Octstr *attr_value)
{
size_t i;
Octstr *ros;
for (i = 0; i < NUM_NETWORK_TYPES; i++) {
if (octstr_case_compare(attr_value,
ros = octstr_imm(pap_network_types[i])) == 0) {
return ros;
}
}
warning(0, "no such network");
return NULL;
}
/*
* Used for attributes accepting logical values.
*/
static int parse_requirement(Octstr *attr_value)
{
long attr_as_number;
attr_as_number = -2;
if (octstr_case_compare(attr_value, octstr_imm("false")) == 0)
attr_as_number = PAP_FALSE;
else if (octstr_case_compare(attr_value, octstr_imm("true")) == 0)
attr_as_number = PAP_TRUE;
else
warning(0, "in a requirement, value not a truth value");
return attr_as_number;
}
/*
* Priority is defined in pap, chapter 9.2.2.
*/
static int parse_priority(Octstr *attr_value)
{
long attr_as_number;
attr_as_number = -2;
if (octstr_case_compare(attr_value, octstr_imm("high")) == 0)
attr_as_number = PAP_HIGH;
else if (octstr_case_compare(attr_value, octstr_imm("medium")) == 0)
attr_as_number = PAP_MEDIUM;
else if (octstr_case_compare(attr_value, octstr_imm("low")) == 0)
attr_as_number = PAP_LOW;
else
warning(0, "illegal priority");
return attr_as_number;
}
/*
* Delivery-method is defined in pap, chapter 9.2.2.
*/
static int parse_delivery_method(Octstr *attr_value)
{
long attr_as_number;
attr_as_number = -2;
if (octstr_case_compare(attr_value, octstr_imm("confirmed")) == 0)
attr_as_number = PAP_CONFIRMED;
else if (octstr_case_compare(attr_value,
octstr_imm("preferconfirmed")) == 0)
attr_as_number = PAP_PREFERCONFIRMED;
else if (octstr_case_compare(attr_value, octstr_imm("unconfirmed")) == 0)
attr_as_number = PAP_UNCONFIRMED;
else if (octstr_case_compare(attr_value, octstr_imm("notspecified")) == 0)
attr_as_number = PAP_NOT_SPECIFIED;
else
warning(0, "illegal delivery method");
return attr_as_number;
}
/*
* PAP states are defined in ppg, chapter 6.
*/
static int parse_state(Octstr *attr_value)
{
long attr_as_number;
attr_as_number = -2;
if (octstr_case_compare(attr_value, octstr_imm("undeliverable")) == 0)
attr_as_number = PAP_UNDELIVERABLE;
else if (octstr_case_compare(attr_value, octstr_imm("pending")) == 0)
attr_as_number = PAP_PENDING;
else if (octstr_case_compare(attr_value, octstr_imm("expired")) == 0)
attr_as_number = PAP_EXPIRED;
else if (octstr_case_compare(attr_value, octstr_imm("delivered")) == 0)
attr_as_number = PAP_DELIVERED;
else if (octstr_case_compare(attr_value, octstr_imm("aborted")) == 0)
attr_as_number = PAP_ABORTED;
else if (octstr_case_compare(attr_value, octstr_imm("timeout")) == 0)
attr_as_number = PAP_TIMEOUT;
else if (octstr_case_compare(attr_value, octstr_imm("cancelled")) == 0)
attr_as_number = PAP_CANCELLED;
else
warning(0, "illegal ppg state");
return attr_as_number;
}
/*
* Check legality of pap client address attribute and transform it to the
* client address usable in Kannel wap address tuple data type. The grammar
* for client address is specified in ppg, chapter 7.1.
*
* Output: the address type of the client address
* Returns: 0, when success
* -1, a non-implemented pap feature requested by pi
* -2, address parsing error
*/
int parse_address(Octstr **address, long *type_of_address)
{
long pos;
Octstr *copy;
pos = octstr_len(*address) - 1;
/*
* Delete first separator, if there is one. This will help our parsing later.
*/
if (octstr_get_char(*address, 0) == '/')
octstr_delete(*address, 0, 1);
/*
* WAP-209, chapter 8 states that addresses with telephone numbers
* should not have a ppg specifier. WAP-151 grammar, however, makes it
* mandatory. Best way to solve this contradiction seems to be regarding
* ppg specifier optional - MMSC is very important type of pi.
*/
if (octstr_search_char(*address, '@', 0) >= 0) {
if ((pos = parse_ppg_specifier(address, pos)) < 0)
return -2;
}
if ((pos = parse_wappush_client_address(address, pos,
type_of_address)) == -2) {
warning(0, "illegal client address");
return -2;
} else if (pos == -1) {
warning(0, "unimplemented feature");
return -1;
}
info(0, "client address was <%s>, accepted",
octstr_get_cstr(copy = octstr_duplicate(*address)));
octstr_destroy(copy);
return pos;
}
/*
* Output: the type of the client address
*/
static long parse_wappush_client_address(Octstr **address, long pos,
long *type_of_address)
{
if ((pos = parse_client_specifier(address, pos, type_of_address)) < 0) {
return pos;
}
pos = parse_constant("WAPPUSH", address, pos);
return pos;
}
/*
* We are not interested of ppg specifier, but we must check its format,
* if we find it - it is optional.
*/
static long parse_ppg_specifier(Octstr **address, long pos)
{
if (pos >= 0) {
pos = parse_dom_fragment(address, pos);
}
while (octstr_get_char(*address, pos) != '@' && pos >= 0) {
if (octstr_get_char(*address, pos) == '.') {
octstr_delete(*address, pos, 1);
--pos;
} else {
return -2;
}
pos = parse_dom_fragment(address, pos);
}
pos = drop_character(address, pos);
if (octstr_get_char(*address, pos) == '/' && pos >= 0) {
octstr_delete(*address, pos, 1);
if (pos > 0)
--pos;
}
if (pos < 0) {
return -2;
}
return pos;
}
/*
* Output: the type of a client address.
* Return a negative value, when error, positive (the position of the parsing
* cursor) otherwise.
*/
static long parse_client_specifier(Octstr **address, long pos,
long *type_of_address)
{
Octstr *type_value;
type_value = octstr_create("");
if ((pos = parse_type(address, &type_value, pos)) < 0) {
goto parse_error;
}
pos = drop_character(address, pos);
if ((pos = parse_constant("/TYPE", address, pos)) < 0) {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: constant TYPE"
" missing from the client address");
goto parse_error;
}
if (octstr_case_compare(type_value, octstr_imm("USER")) == 0) {
*type_of_address = ADDR_USER;
goto not_implemented;
}
if ((pos = parse_ext_qualifiers(address, pos, type_value)) < 0) {
goto parse_error;
}
if (octstr_case_compare(type_value, octstr_imm("PLMN")) == 0) {
*type_of_address = ADDR_PLMN;
pos = parse_global_phone_number(address, pos);
}
else if (octstr_case_compare(type_value, octstr_imm("IPv4")) == 0) {
*type_of_address = ADDR_IPV4;
pos = parse_ipv4(address, pos);
}
else if (octstr_case_compare(type_value, octstr_imm("IPv6")) == 0) {
*type_of_address = ADDR_IPV6;
pos = parse_ipv6(address, pos);
}
else if (wina_bearer_identifier(type_value)) {
*type_of_address = ADDR_WINA;
pos = parse_escaped_value(address, pos);
}
else {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: wrong address type"
" in the client address");
goto parse_error;
}
octstr_destroy(type_value);
return pos;
not_implemented:
octstr_destroy(type_value);
return -1;
parse_error:
octstr_destroy(type_value);
return -2;
}
/*
* XXX We have a kludge here. WAP-249-PPGService-20010713-a defines in
* section 6.1 the constant strings "WAPPUSH" and "TYPE" in upper-case.
* But in the examples of section 6.2 they use lower-case too. Some PI
* vendors (ie. Jatayuu's MMSC) have implemented lower-case in their PAP
* documents. So we'll support this too for sake of operatibility -- st.
*/
static long parse_constant(const char *field_name, Octstr **address, long pos)
{
size_t i, size;
Octstr *nameos;
nameos = octstr_format("%s", field_name);
size = octstr_len(nameos);
i = 0;
/* convert both to lower case, see above note */
octstr_convert_range(nameos, 0, octstr_len(nameos), tolower);
octstr_convert_range(*address, 0, octstr_len(*address), tolower);
while (octstr_get_char(*address, pos - i) ==
octstr_get_char(nameos, size-1 - i) && i < size) {
++i;
}
while ((octstr_len(*address) > 0) && octstr_get_char(*address, pos) !=
octstr_get_char(nameos, 0) && pos >= 0) {
pos = drop_character(address, pos);
}
pos = drop_character(address, pos);
if (pos < 0 || i != size) {
debug("wap.push.pap.compiler", 0, "parse_constant: unparsable"
" constant %s", field_name);
octstr_destroy(nameos);
return -2;
}
octstr_destroy(nameos);
return pos;
}
static long parse_dom_fragment(Octstr **address, long pos)
{
unsigned char c;
if (pos >= 0) {
if (isalnum(octstr_get_char(*address, pos))) {
pos = drop_character(address, pos);
} else
return -2;
}
while ((c = octstr_get_char(*address, pos)) != '@' &&
octstr_get_char(*address, pos) != '.' && pos >= 0) {
if (isalnum(c) || c == '-') {
pos = drop_character(address, pos);
} else
return -2;
}
return pos;
}
static long drop_character(Octstr **address, long pos)
{
if (pos >= 0) {
octstr_delete(*address, pos, 1);
if (pos > 0)
--pos;
}
return pos;
}
static long parse_type(Octstr **address, Octstr **type_value, long pos)
{
unsigned char c;
while ((c = octstr_get_char(*address, pos)) != '=' && pos >= 0) {
*type_value = prepend_char(*type_value, c);
pos = drop_character(address, pos);
}
if (pos < 0)
return -2;
return pos;
}
static long parse_ext_qualifiers(Octstr **address, long pos,
Octstr *type)
{
int ret;
while ((ret = qualifiers(*address, pos, type)) == 1) {
if ((pos = parse_qualifier_value(address, pos)) < 0)
return pos;
if ((pos = parse_qualifier_keyword(address, pos)) < 0)
return pos;
}
if (ret == 1) {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: erroneous qualifiers"
" in the client address");
return -2;
}
return pos;
}
/*
* According to ppg, chapter 7.1, global phone number starts with +. Phone
* number is here an unique identifier, so if it does not conform the inter-
* national format, we return an error. (Is up to bearerbox to transform it
* to an usable phone number)
*/
static long parse_global_phone_number(Octstr **address, long pos)
{
unsigned char c;
while ((c = octstr_get_char(*address, pos)) != '+' && pos >= 0) {
if (!isdigit(c) && c != '-' && c != '.') {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: wrong separator"
" in a phone number (- and . allowed)");
return -2;
} else {
--pos;
}
}
if (pos == 0) {
debug("wap.push.pap.compiler", 0, "PAP COMPILER:a phone number must"
" start with +");
return -2;
}
if (pos > 0)
--pos;
pos = drop_character(address, pos);
return pos;
}
static long parse_ipv4(Octstr **address, long pos)
{
long i;
if ((pos = parse_ipv4_fragment(address, pos)) < 0) {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: wrong separator in a"
" ipv4 address");
return -2;
}
i = 1;
while (i <= 3 && octstr_get_char(*address, pos) != '=' && pos >= 0) {
pos = parse_ipv4_fragment(address, pos);
++i;
}
if (pos == 0) {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: missing separator at"
" beginning of a client address (=)");
return -2;
}
return pos;
}
static long parse_ipv6(Octstr **address, long pos)
{
long i;
if ((pos = parse_ipv6_fragment(address, pos)) < 0) {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: wrong separator in a"
" ipv6 address");
return -2;
}
i = 1;
while (i <= 7 && octstr_get_char(*address, pos) != '=' && pos >= 0) {
pos = parse_ipv6_fragment(address, pos);
++i;
}
if (pos == 0) {
debug("wap.push.pap.compiler", 0, "PAP COMPILER: missing separator at"
" beginning of a client address (=)");
return -2;
}
return pos;
}
/*
* WINA web page does not include address type identifiers. Following ones are
* from wdp, Appendix C.
*/
static char *bearer_address[] = {
"GSM_MSISDN",
"ANSI_136_MSISDN",
"IS_637_MSISDN",
"iDEN_MSISDN",
"FLEX_MSISDN",
"PHS_MSISDN",
"GSM_Service_Code",
"TETRA_ITSI",
"TETRA_MSISDN",
"ReFLEX_MSIDDN",
"MAN",
};
static size_t bearer_address_size = sizeof(bearer_address) /
sizeof(bearer_address[0]);
static int wina_bearer_identifier(Octstr *type_value)
{
size_t i;
i = 0;
while (i < bearer_address_size) {
if (octstr_case_compare(type_value,
octstr_imm(bearer_address[i])) == 0)
return 1;
++i;
}
debug("wap.push.pap.compiler", 0, "PAP COMPILER: a bearer not registered"
" by wina");
return 0;
}
/*
* Note that we parse backwards. First we create a window of three characters
* (representing a possible escaped character). If the first character of the
* window is not escape, we handle the last character and move the window one
* character backwards; if it is, we handle escaped sequence and create a new
* window. If we cannot create a window, rest of characters are unescaped.
*/
static long parse_escaped_value(Octstr **address, long pos)
{
int ret;
if (create_peek_window(address, &pos) == 0)
if ((pos = rest_unescaped(address, pos)) == -2)
return -2;
while (octstr_get_char(*address, pos) != '=' && pos >= 0) {
if ((ret = issafe(address, pos)) == 1) {
pos = accept_safe(address, pos);
} else if (ret == 0) {
if ((pos = accept_escaped(address, pos)) < 0)
return -2;
if (create_peek_window(address, &pos) == 0)
if ((pos = rest_unescaped(address, pos)) == -2)
return -2;
}
}
pos = drop_character(address, pos);
return pos;
}
static Octstr *prepend_char(Octstr *os, unsigned char c)
{
Octstr *tmp;
tmp = octstr_format("%c", c);
octstr_insert(os, tmp, 0);
octstr_destroy(tmp);
return os;
}
/*
* Ext qualifiers contain /, ipv4 address contains . , ipv6 address contains :.
* phone number contains + and escaped-value contain no specific tokens. Lastly
* mentioned are for future extensions, but we must parse them.
* Return 1, when qualifiers found
* 0, when not
* -1, when an error was found during the process
*/
static int qualifiers(Octstr *address, long pos, Octstr *type)
{
unsigned char term,
c;
long i;
i = pos;
c = 'E';
if (octstr_case_compare(type, octstr_imm("PLMN")) == 0)
term = '+';
else if (octstr_case_compare(type, octstr_imm("IPv4")) == 0)
term = '.';
else if (octstr_case_compare(type, octstr_imm("IPv6")) == 0)
term = ':';
else
term = 'N';
if (term != 'N') {
while ((c = octstr_get_char(address, i)) != term && i != 0) {
if (c == '/')
return 1;
--i;
}
if (i == 0)
return 0;
}
if (term == 'N') {
while (i != 0) {
if (c == '/')
return 1;
--i;
}
}
return 0;
}
static long parse_qualifier_value(Octstr **address, long pos)
{
unsigned char c;
while ((c = octstr_get_char(*address, pos)) != '=' && pos >= 0) {
if (c < 0x20 || (c > 0x2e && c < 0x30) || (c > 0x3c && c < 0x3e) ||
c > 0x7e)
return -2;
pos = drop_character(address, pos);
}
pos = drop_character(address, pos);
return pos;
}
static long parse_qualifier_keyword(Octstr **address, long pos)
{
unsigned char c;
while ((c = octstr_get_char(*address, pos)) != '/') {
if (isalnum(c) || c == '-') {
pos = drop_character(address, pos);
} else
return -2;
}
pos = drop_character(address, pos);
return pos;
}
static long parse_ipv4_fragment(Octstr **address, long pos)
{
long i;
unsigned char c;
i = 0;
c = '=';
if (isdigit(c = octstr_get_char(*address, pos)) && pos >= 0) {
--pos;
++i;
} else {
debug("wap.push.pap.compiler", 0, "non-digit found in ip address,"
" address unacceptable");
return -2;
}
while (i <= 3 && ((c = octstr_get_char(*address, pos)) != '.' && c != '=')
&& pos >= 0) {
if (isdigit(c)) {
--pos;
++i;
} else {
debug("wap.push.pap.compiler", 0, "parse_ipv4_fragment: non-digit"
" in ipv4 address, address unacceptable");
return -2;
}
}
pos = handle_two_terminators(address, pos, '.', '=', c, i, 3);
return pos;
}
static long parse_ipv6_fragment(Octstr **address, long pos)
{
long i;
unsigned char c;
i = 0;
if (isxdigit(octstr_get_char(*address, pos)) && pos >= 0) {
--pos;
++i;
} else {
return -2;
}
c = '=';
while (i <= 4 && ((c = octstr_get_char(*address, pos)) != ':' && c != '=')
&& pos >= 0) {
if (isxdigit(c)) {
--pos;
++i;
} else {
return -2;
}
}
pos = handle_two_terminators(address, pos, ':', '=', c, i, 4);
return pos;
}
/*
* Return -1, it was impossible to create the window because of there is no
* more enough characters left and 0 if OK.
*/
static int create_peek_window(Octstr **address, long *pos)
{
long i;
unsigned char c;
i = 0;
c = '=';
while (i < 2 && (c = octstr_get_char(*address, *pos)) != '=') {
if (*pos > 0)
--*pos;
++i;
}
if (c == '=')
return 0;
return 1;
}
static long rest_unescaped(Octstr **address, long pos)
{
long i,
ret;
for (i = 2; i > 0; i--) {
if ((ret = accept_safe(address, pos)) == -2)
return -2;
else if (ret == -1)
return pos;
}
return pos;
}
static int issafe(Octstr **address, long pos)
{
if (octstr_get_char(*address, pos) == '%')
return 0;
else
return 1;
}
static long accept_safe(Octstr **address, long pos)
{
unsigned char c;
c = octstr_get_char(*address, pos);
if ((isalnum(c) || c == '+' || c == '-' || c == '.' || c == '_') &&
pos >= 0)
--pos;
else if (c == '=')
return -1;
else
return -2;
return pos;
}
static long accept_escaped(Octstr **address, long pos)
{
Octstr *temp;
long i;
unsigned char c;
pos = drop_character(address, pos);
temp = octstr_create("");
for (i = 2; i > 0; i--) {
c = octstr_get_char(*address, pos + i);
temp = prepend_char(temp, c);
pos = drop_character(address, pos + i);
if (pos > 0)
--pos;
}
if (octstr_hex_to_binary(temp) < 0) {
octstr_destroy(temp);
return -2;
}
octstr_insert(*address, temp, pos + 2); /* To the end of the window */
octstr_destroy(temp);
return pos + 1; /* The position preceding the
inserted character */
}
/*
* Point ends the string to be parsed, comma separates its fragments.
*/
static long handle_two_terminators (Octstr **address, long pos,
unsigned char comma, unsigned char point, unsigned char c,
long fragment_parsed, long fragment_length)
{
if (fragment_parsed == fragment_length && c != comma && c != point)
return -2;
if (c == point)
octstr_delete(*address, pos, 1);
--pos;
return pos;
}