diff options
Diffstat (limited to 'pjsip')
61 files changed, 29389 insertions, 29184 deletions
diff --git a/pjsip/include/pjsip-simple/event_notify.h b/pjsip/include/pjsip-simple/event_notify.h index 15561505..5f002582 100644 --- a/pjsip/include/pjsip-simple/event_notify.h +++ b/pjsip/include/pjsip-simple/event_notify.h @@ -1,330 +1,330 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIMPLE_EVENT_NOTIFY_H__
-#define __PJSIP_SIMPLE_EVENT_NOTIFY_H__
-
-/**
- * @file event_notify.h
- * @brief SIP Specific Event Notification Extension (RFC 3265)
- */
-
-#include <pjsip/sip_types.h>
-#include <pjsip/sip_auth.h>
-#include <pjsip_simple/event_notify_msg.h>
-#include <pj/timer.h>
-
-/**
- * @defgroup PJSIP_EVENT_NOT SIP Event Notification (RFC 3265) Module
- * @ingroup PJSIP_SIMPLE
- * @{
- *
- * This module provides the implementation of SIP Extension for SIP Specific
- * Event Notification (RFC 3265). It extends PJSIP by supporting SUBSCRIBE and
- * NOTIFY methods.
- *
- * This module itself is extensible; new event packages can be registered to
- * this module to handle specific extensions (such as presence).
- */
-
-PJ_BEGIN_DECL
-
-typedef struct pjsip_event_sub_cb pjsip_event_sub_cb;
-typedef struct pjsip_event_sub pjsip_event_sub;
-
-/**
- * This enumeration describes subscription state as described in the RFC 3265.
- * The standard specifies that extensions may define additional states. In the
- * case where the state is not known, the subscription state will be set to
- * PJSIP_EVENT_SUB_STATE_UNKNOWN, and the token will be kept in state_str
- * member of the susbcription structure.
- */
-typedef enum pjsip_event_sub_state
-{
- /** State is NULL. */
- PJSIP_EVENT_SUB_STATE_NULL,
-
- /** Subscription is active. */
- PJSIP_EVENT_SUB_STATE_ACTIVE,
-
- /** Subscription is pending. */
- PJSIP_EVENT_SUB_STATE_PENDING,
-
- /** Subscription is terminated. */
- PJSIP_EVENT_SUB_STATE_TERMINATED,
-
- /** Subscription state can not be determined. Application can query
- * the state information in state_str member.
- */
- PJSIP_EVENT_SUB_STATE_UNKNOWN,
-
-} pjsip_event_sub_state;
-
-/**
- * This structure describes notification to be called when incoming SUBSCRIBE
- * request is received. The module will call the callback registered by package
- * that matches the event description in the incoming SUBSCRIBE.
- */
-typedef struct pjsip_event_sub_pkg_cb
-{
- /**
- * This callback is called to first enquery the package whether it wants
- * to accept incoming SUBSCRIBE request. If it does, then on_subscribe
- * will be called.
- *
- * @param rdata The incoming request.
- * @param status The status code to be returned back to subscriber.
- */
- void (*on_query_subscribe)(pjsip_rx_data *rdata, int *status);
-
- /**
- * This callback is called when the module receives incoming SUBSCRIBE
- * request.
- *
- * @param sub The subscription instance.
- * @param rdata The received buffer.
- * @param cb Callback to be registered to the subscription instance.
- * @param expires The expiration to be set.
- */
- void (*on_subscribe)(pjsip_event_sub *sub, pjsip_rx_data *rdata,
- pjsip_event_sub_cb **cb, int *expires);
-
-} pjsip_event_sub_pkg_cb;
-
-/**
- * This structure describes callback that is registered by application or
- * package to receive notifications about a subscription.
- */
-struct pjsip_event_sub_cb
-{
- /**
- * This callback is used by both subscriber and notifier. It is called
- * when the subscription has been terminated.
- *
- * @param sub The subscription instance.
- * @param reason The termination reason.
- */
- void (*on_sub_terminated)(pjsip_event_sub *sub, const pj_str_t *reason);
-
- /**
- * This callback is called when we received SUBSCRIBE request to refresh
- * the subscription.
- *
- * @param sub The subscription instance.
- * @param rdata The received SUBSCRIBE request.
- */
- void (*on_received_refresh)(pjsip_event_sub *sub, pjsip_rx_data *rdata);
-
- /**
- * This callback is called when the module receives final response on
- * previously sent SUBSCRIBE request.
- *
- * @param sub The subscription instance.
- * @param event The event.
- */
- void (*on_received_sub_response)(pjsip_event_sub *sub, pjsip_event *event);
-
- /**
- * This callback is called when the module receives incoming NOTIFY
- * request.
- *
- * @param sub The subscription instance.
- * @param rdata The received data.
- */
- void (*on_received_notify)(pjsip_event_sub *sub, pjsip_rx_data *rdata);
-
- /**
- * This callback is called when the module receives final response to
- * previously sent NOTIFY request.
- *
- * @param sub The subscription instance.
- * @param event The event.
- */
- void (*on_received_notify_response)(pjsip_event_sub *sub, pjsip_event *event);
-
-};
-
-/**
- * This structure describes an event subscription record. The structure is used
- * to represent both subscriber and notifier.
- */
-struct pjsip_event_sub
-{
- pj_pool_t *pool; /**< Pool. */
- pjsip_endpoint *endpt; /**< Endpoint. */
- pjsip_event_sub_cb cb; /**< Callback. */
- pj_mutex_t *mutex; /**< Mutex. */
- pjsip_role_e role; /**< Role (UAC=subscriber, UAS=notifier) */
- pjsip_event_sub_state state; /**< Subscription state. */
- pj_str_t state_str; /**< String describing the state. */
- pjsip_from_hdr *from; /**< Cached local info (From) */
- pjsip_to_hdr *to; /**< Cached remote info (To) */
- pjsip_contact_hdr *contact; /**< Cached local contact. */
- pjsip_cid_hdr *call_id; /**< Cached Call-ID */
- int cseq; /**< Outgoing CSeq */
- pjsip_event_hdr *event; /**< Event description. */
- pjsip_expires_hdr *uac_expires; /**< Cached Expires header (UAC only). */
- pjsip_accept_hdr *local_accept; /**< Local Accept header. */
- pjsip_route_hdr route_set; /**< Route-set. */
-
- pj_str_t key; /**< Key in the hash table. */
- void *user_data; /**< Application data. */
- int default_interval; /**< Refresh interval. */
- pj_timer_entry timer; /**< Internal timer. */
- pj_time_val expiry_time; /**< Time when subscription expires. */
- int pending_tsx; /**< Number of pending transactions. */
- pj_bool_t delete_flag; /**< Pending deletion flag. */
-
- pjsip_auth_session auth_sess; /**< Authorization sessions. */
- unsigned cred_cnt; /**< Number of credentials. */
- pjsip_cred_info *cred_info; /**< Array of credentials. */
-};
-
-
-
-
-/**
- * Initialize the module and get the instance of the module to be registered to
- * endpoint.
- *
- * @return The module instance.
- */
-PJ_DECL(pjsip_module*) pjsip_event_sub_get_module(void);
-
-
-/**
- * Register event package.
- *
- * @param event The event identification for the package.
- * @param accept_cnt Number of strings in Accept array.
- * @param accept Array of Accept value.
- * @param cb Callback to receive incoming SUBSCRIBE for the package.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event_name,
- int accept_cnt,
- const pj_str_t accept[],
- const pjsip_event_sub_pkg_cb *cb );
-
-
-/**
- * Create initial subscription instance (client).
- *
- * @param endpt The endpoint.
- * @param from URL to put in From header.
- * @param to The target resource.
- * @param event Event package.
- * @param expires Expiration time.
- * @param accept Accept specification.
- * @param user_data Application data to attach to this subscription.
- *
- * @return New client subscription instance.
- */
-PJ_DECL(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt,
- const pj_str_t *from,
- const pj_str_t *to,
- const pj_str_t *event,
- int expires,
- int accept_cnt,
- const pj_str_t accept[],
- void *user_data,
- const pjsip_event_sub_cb *cb);
-
-/**
- * Set credentials to be used for outgoing request messages.
- *
- * @param sub Subscription instance.
- * @param count Number of credentials.
- * @param cred Array of credential info.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub,
- int count,
- const pjsip_cred_info cred[]);
-
-/**
- * Set route set for outgoing requests.
- *
- * @param sub Subscription instance.
- * @param route_set List of route headers.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub,
- const pjsip_route_hdr *route_set );
-
-
-/**
- * Send SUBSCRIBE request.
- *
- * @param sub Subscription instance.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub );
-
-/**
- * Terminate subscription (client). This will send unsubscription request to
- * notifier.
- *
- * @param sub Client subscription instance.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub );
-
-
-/**
- * For notifier, send NOTIFY request to subscriber, and set the state of
- * the subscription.
- *
- * @param sub The server subscription (notifier) instance.
- * @param state New state to set.
- * @param reason Specify reason if new state is terminated, otherwise
- * put NULL.
- * @param type Description of content type.
- * @param body Text body to send with the NOTIFY, or NULL if the
- * NOTIFY request should not contain any message body.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_notify( pjsip_event_sub *sub,
- pjsip_event_sub_state state,
- const pj_str_t *reason,
- pjsip_msg_body *body);
-
-/**
- * Destroy subscription instance.
- *
- * @param sub The client or server subscription instance.
- *
- * @return Zero on success, one if the subscription will be
- * deleted automatically later, or -1 on error.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub);
-
-
-PJ_END_DECL
-
-/**
- * @}
- */
-
-#endif /* __PJSIP_SIMPLE_EVENT_NOTIFY_H__ */
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIMPLE_EVENT_NOTIFY_H__ +#define __PJSIP_SIMPLE_EVENT_NOTIFY_H__ + +/** + * @file event_notify.h + * @brief SIP Specific Event Notification Extension (RFC 3265) + */ + +#include <pjsip/sip_types.h> +#include <pjsip/sip_auth.h> +#include <pjsip_simple/event_notify_msg.h> +#include <pj/timer.h> + +/** + * @defgroup PJSIP_EVENT_NOT SIP Event Notification (RFC 3265) Module + * @ingroup PJSIP_SIMPLE + * @{ + * + * This module provides the implementation of SIP Extension for SIP Specific + * Event Notification (RFC 3265). It extends PJSIP by supporting SUBSCRIBE and + * NOTIFY methods. + * + * This module itself is extensible; new event packages can be registered to + * this module to handle specific extensions (such as presence). + */ + +PJ_BEGIN_DECL + +typedef struct pjsip_event_sub_cb pjsip_event_sub_cb; +typedef struct pjsip_event_sub pjsip_event_sub; + +/** + * This enumeration describes subscription state as described in the RFC 3265. + * The standard specifies that extensions may define additional states. In the + * case where the state is not known, the subscription state will be set to + * PJSIP_EVENT_SUB_STATE_UNKNOWN, and the token will be kept in state_str + * member of the susbcription structure. + */ +typedef enum pjsip_event_sub_state +{ + /** State is NULL. */ + PJSIP_EVENT_SUB_STATE_NULL, + + /** Subscription is active. */ + PJSIP_EVENT_SUB_STATE_ACTIVE, + + /** Subscription is pending. */ + PJSIP_EVENT_SUB_STATE_PENDING, + + /** Subscription is terminated. */ + PJSIP_EVENT_SUB_STATE_TERMINATED, + + /** Subscription state can not be determined. Application can query + * the state information in state_str member. + */ + PJSIP_EVENT_SUB_STATE_UNKNOWN, + +} pjsip_event_sub_state; + +/** + * This structure describes notification to be called when incoming SUBSCRIBE + * request is received. The module will call the callback registered by package + * that matches the event description in the incoming SUBSCRIBE. + */ +typedef struct pjsip_event_sub_pkg_cb +{ + /** + * This callback is called to first enquery the package whether it wants + * to accept incoming SUBSCRIBE request. If it does, then on_subscribe + * will be called. + * + * @param rdata The incoming request. + * @param status The status code to be returned back to subscriber. + */ + void (*on_query_subscribe)(pjsip_rx_data *rdata, int *status); + + /** + * This callback is called when the module receives incoming SUBSCRIBE + * request. + * + * @param sub The subscription instance. + * @param rdata The received buffer. + * @param cb Callback to be registered to the subscription instance. + * @param expires The expiration to be set. + */ + void (*on_subscribe)(pjsip_event_sub *sub, pjsip_rx_data *rdata, + pjsip_event_sub_cb **cb, int *expires); + +} pjsip_event_sub_pkg_cb; + +/** + * This structure describes callback that is registered by application or + * package to receive notifications about a subscription. + */ +struct pjsip_event_sub_cb +{ + /** + * This callback is used by both subscriber and notifier. It is called + * when the subscription has been terminated. + * + * @param sub The subscription instance. + * @param reason The termination reason. + */ + void (*on_sub_terminated)(pjsip_event_sub *sub, const pj_str_t *reason); + + /** + * This callback is called when we received SUBSCRIBE request to refresh + * the subscription. + * + * @param sub The subscription instance. + * @param rdata The received SUBSCRIBE request. + */ + void (*on_received_refresh)(pjsip_event_sub *sub, pjsip_rx_data *rdata); + + /** + * This callback is called when the module receives final response on + * previously sent SUBSCRIBE request. + * + * @param sub The subscription instance. + * @param event The event. + */ + void (*on_received_sub_response)(pjsip_event_sub *sub, pjsip_event *event); + + /** + * This callback is called when the module receives incoming NOTIFY + * request. + * + * @param sub The subscription instance. + * @param rdata The received data. + */ + void (*on_received_notify)(pjsip_event_sub *sub, pjsip_rx_data *rdata); + + /** + * This callback is called when the module receives final response to + * previously sent NOTIFY request. + * + * @param sub The subscription instance. + * @param event The event. + */ + void (*on_received_notify_response)(pjsip_event_sub *sub, pjsip_event *event); + +}; + +/** + * This structure describes an event subscription record. The structure is used + * to represent both subscriber and notifier. + */ +struct pjsip_event_sub +{ + pj_pool_t *pool; /**< Pool. */ + pjsip_endpoint *endpt; /**< Endpoint. */ + pjsip_event_sub_cb cb; /**< Callback. */ + pj_mutex_t *mutex; /**< Mutex. */ + pjsip_role_e role; /**< Role (UAC=subscriber, UAS=notifier) */ + pjsip_event_sub_state state; /**< Subscription state. */ + pj_str_t state_str; /**< String describing the state. */ + pjsip_from_hdr *from; /**< Cached local info (From) */ + pjsip_to_hdr *to; /**< Cached remote info (To) */ + pjsip_contact_hdr *contact; /**< Cached local contact. */ + pjsip_cid_hdr *call_id; /**< Cached Call-ID */ + int cseq; /**< Outgoing CSeq */ + pjsip_event_hdr *event; /**< Event description. */ + pjsip_expires_hdr *uac_expires; /**< Cached Expires header (UAC only). */ + pjsip_accept_hdr *local_accept; /**< Local Accept header. */ + pjsip_route_hdr route_set; /**< Route-set. */ + + pj_str_t key; /**< Key in the hash table. */ + void *user_data; /**< Application data. */ + int default_interval; /**< Refresh interval. */ + pj_timer_entry timer; /**< Internal timer. */ + pj_time_val expiry_time; /**< Time when subscription expires. */ + int pending_tsx; /**< Number of pending transactions. */ + pj_bool_t delete_flag; /**< Pending deletion flag. */ + + pjsip_auth_session auth_sess; /**< Authorization sessions. */ + unsigned cred_cnt; /**< Number of credentials. */ + pjsip_cred_info *cred_info; /**< Array of credentials. */ +}; + + + + +/** + * Initialize the module and get the instance of the module to be registered to + * endpoint. + * + * @return The module instance. + */ +PJ_DECL(pjsip_module*) pjsip_event_sub_get_module(void); + + +/** + * Register event package. + * + * @param event The event identification for the package. + * @param accept_cnt Number of strings in Accept array. + * @param accept Array of Accept value. + * @param cb Callback to receive incoming SUBSCRIBE for the package. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event_name, + int accept_cnt, + const pj_str_t accept[], + const pjsip_event_sub_pkg_cb *cb ); + + +/** + * Create initial subscription instance (client). + * + * @param endpt The endpoint. + * @param from URL to put in From header. + * @param to The target resource. + * @param event Event package. + * @param expires Expiration time. + * @param accept Accept specification. + * @param user_data Application data to attach to this subscription. + * + * @return New client subscription instance. + */ +PJ_DECL(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt, + const pj_str_t *from, + const pj_str_t *to, + const pj_str_t *event, + int expires, + int accept_cnt, + const pj_str_t accept[], + void *user_data, + const pjsip_event_sub_cb *cb); + +/** + * Set credentials to be used for outgoing request messages. + * + * @param sub Subscription instance. + * @param count Number of credentials. + * @param cred Array of credential info. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub, + int count, + const pjsip_cred_info cred[]); + +/** + * Set route set for outgoing requests. + * + * @param sub Subscription instance. + * @param route_set List of route headers. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub, + const pjsip_route_hdr *route_set ); + + +/** + * Send SUBSCRIBE request. + * + * @param sub Subscription instance. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub ); + +/** + * Terminate subscription (client). This will send unsubscription request to + * notifier. + * + * @param sub Client subscription instance. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub ); + + +/** + * For notifier, send NOTIFY request to subscriber, and set the state of + * the subscription. + * + * @param sub The server subscription (notifier) instance. + * @param state New state to set. + * @param reason Specify reason if new state is terminated, otherwise + * put NULL. + * @param type Description of content type. + * @param body Text body to send with the NOTIFY, or NULL if the + * NOTIFY request should not contain any message body. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_event_sub_notify( pjsip_event_sub *sub, + pjsip_event_sub_state state, + const pj_str_t *reason, + pjsip_msg_body *body); + +/** + * Destroy subscription instance. + * + * @param sub The client or server subscription instance. + * + * @return Zero on success, one if the subscription will be + * deleted automatically later, or -1 on error. + */ +PJ_DECL(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub); + + +PJ_END_DECL + +/** + * @} + */ + +#endif /* __PJSIP_SIMPLE_EVENT_NOTIFY_H__ */ diff --git a/pjsip/include/pjsip-simple/event_notify_msg.h b/pjsip/include/pjsip-simple/event_notify_msg.h index 95f5a48a..76b7d35c 100644 --- a/pjsip/include/pjsip-simple/event_notify_msg.h +++ b/pjsip/include/pjsip-simple/event_notify_msg.h @@ -1,117 +1,117 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__
-#define __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__
-
-/**
- * @file event_notify_msg.h
- * @brief SIP Event Notification Headers (RFC 3265)
- */
-#include <pjsip/sip_msg.h>
-
-/**
- * @ingroup PJSIP_EVENT_NOT
- * @{
- */
-
-PJ_BEGIN_DECL
-
-
-/** Max events in Allow-Events header. */
-#define PJSIP_MAX_ALLOW_EVENTS 16
-
-/**
- * This structure describes Event header.
- */
-typedef struct pjsip_event_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_event_hdr)
- pj_str_t event_type; /**< Event name. */
- pj_str_t id_param; /**< Optional event ID parameter. */
- pj_str_t other_param; /**< Other parameter, concatenated together. */
-} pjsip_event_hdr;
-
-/**
- * Create an Event header.
- *
- * @param pool The pool.
- *
- * @return New Event header instance.
- */
-PJ_DECL(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool);
-
-
-/**
- * This structure describes Allow-Events header.
- */
-typedef struct pjsip_allow_events_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_allow_events_hdr)
- int event_cnt; /**< Number of event names. */
- pj_str_t events[PJSIP_MAX_ALLOW_EVENTS]; /**< Event names. */
-} pjsip_allow_events_hdr;
-
-
-/**
- * Create a new Allow-Events header.
- *
- * @param pool. The pool.
- *
- * @return Allow-Events header.
- */
-PJ_DECL(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool);
-
-
-/**
- * This structure describes Subscription-State header.
- */
-typedef struct pjsip_sub_state_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_sub_state_hdr)
- pj_str_t sub_state; /**< Subscription state. */
- pj_str_t reason_param; /**< Optional termination reason. */
- int expires_param; /**< Expires param, or -1. */
- int retry_after; /**< Retry after param, or -1. */
- pj_str_t other_param; /**< Other parameter, concatenated together. */
-} pjsip_sub_state_hdr;
-
-/**
- * Create new Subscription-State header.
- *
- * @param pool The pool.
- *
- * @return Subscription-State header.
- */
-PJ_DECL(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool);
-
-/**
- * Initialize parser for event notify module.
- */
-PJ_DEF(void) pjsip_event_notify_init_parser(void);
-
-
-PJ_END_DECL
-
-
-/**
- * @}
- */
-
-#endif /* __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__ +#define __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__ + +/** + * @file event_notify_msg.h + * @brief SIP Event Notification Headers (RFC 3265) + */ +#include <pjsip/sip_msg.h> + +/** + * @ingroup PJSIP_EVENT_NOT + * @{ + */ + +PJ_BEGIN_DECL + + +/** Max events in Allow-Events header. */ +#define PJSIP_MAX_ALLOW_EVENTS 16 + +/** + * This structure describes Event header. + */ +typedef struct pjsip_event_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_event_hdr) + pj_str_t event_type; /**< Event name. */ + pj_str_t id_param; /**< Optional event ID parameter. */ + pj_str_t other_param; /**< Other parameter, concatenated together. */ +} pjsip_event_hdr; + +/** + * Create an Event header. + * + * @param pool The pool. + * + * @return New Event header instance. + */ +PJ_DECL(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool); + + +/** + * This structure describes Allow-Events header. + */ +typedef struct pjsip_allow_events_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_allow_events_hdr) + int event_cnt; /**< Number of event names. */ + pj_str_t events[PJSIP_MAX_ALLOW_EVENTS]; /**< Event names. */ +} pjsip_allow_events_hdr; + + +/** + * Create a new Allow-Events header. + * + * @param pool. The pool. + * + * @return Allow-Events header. + */ +PJ_DECL(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool); + + +/** + * This structure describes Subscription-State header. + */ +typedef struct pjsip_sub_state_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_sub_state_hdr) + pj_str_t sub_state; /**< Subscription state. */ + pj_str_t reason_param; /**< Optional termination reason. */ + int expires_param; /**< Expires param, or -1. */ + int retry_after; /**< Retry after param, or -1. */ + pj_str_t other_param; /**< Other parameter, concatenated together. */ +} pjsip_sub_state_hdr; + +/** + * Create new Subscription-State header. + * + * @param pool The pool. + * + * @return Subscription-State header. + */ +PJ_DECL(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool); + +/** + * Initialize parser for event notify module. + */ +PJ_DEF(void) pjsip_event_notify_init_parser(void); + + +PJ_END_DECL + + +/** + * @} + */ + +#endif /* __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__ */ + diff --git a/pjsip/include/pjsip-simple/messaging.h b/pjsip/include/pjsip-simple/messaging.h index 25bf5dd5..742d739c 100644 --- a/pjsip/include/pjsip-simple/messaging.h +++ b/pjsip/include/pjsip-simple/messaging.h @@ -1,268 +1,268 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIMPLE_MESSAGING_H__
-#define __PJSIP_SIMPLE_MESSAGING_H__
-
-/**
- * @file messaging.h
- * @brief Instant Messaging Extension (RFC 3428)
- */
-
-#include <pjsip/sip_msg.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_MESSAGING SIP Instant Messaging (RFC 3428) Module
- * @ingroup PJSIP_SIMPLE
- * @{
- *
- * This module provides the implementation of SIP Extension for Instant
- * Messaging (RFC 3428). It extends PJSIP by supporting MESSAGE method.
- *
- * The RFC 3428 doesn't provide any means of dialog for the purpose of sending/
- * receiving instant messaging. IM with SIP is basicly sessionless, which means
- * that there is absolutely no correlation between IM messages sent or received
- * by a host. Any correlation between IM messages is only perceivable by
- * user, phsychologically.
- *
- * However, the RFC doesn't prohibit sending IM within a dialog (presumably
- * using the same Call-ID and CSeq namespace), although it prohibits creating
- * a dialog specificly for creating IM session.
- *
- * The implementation here is modeled to support both ways of sending IM msgs,
- * i.e. sending IM message individually and sending IM message within a dialog.
- * Although IM message can be associated with a dialog, this implementation of
- * IM module is completely independent of the User Agent library in PJSIP. Yes,
- * that's what is called modularity, and it demonstrates the clearness
- * of PJSIP design (the last sentence is of course marketing crap :)).
- *
- * To send an IM message as part of dialog, application would first create the
- * message using #pjsip_messaging_create_msg, using dialog's Call-ID, CSeq,
- * From, and To header, then send the message using #pjsip_dlg_send_msg instead
- * of #pjsip_messaging_send_msg.
- *
- * To send IM messages individually, application has two options. The first is
- * to create the request with #pjsip_messaging_create_msg then send it with
- * #pjsip_messaging_send_msg. But this way, application has to pre-construct
- * From and To header first, which is not too convenient.
- *
- * The second option (to send IM messages not associated with a dialog) is to
- * first create an 'IM session'. An 'IM session' here is not a SIP dialog, as
- * it doesn't have Contact header etc. An 'IM session' here is just a local
- * state to cache most of IM headers, for convenience and optimization. Appl
- * creates an IM session with #pjsip_messaging_create_session, and destroy
- * the session with #pjsip_messaging_destroy_session. To create an outgoing
- * IM message, application would call #pjsip_messaging_session_create_msg,
- * and to send the message it would use #pjsip_messaging_send_msg.
- *
- * Message authorization is handled by application, as usual, by inserting a
- * proper WWW-Authenticate or Proxy-Authenticate header before sending the
- * message.
- *
- * And the last bit, to handle incoing IM messages.
- *
- * To handle incoming IM messages, application would register a global callback
- * to be called when incoming messages arrive, by registering with function
- * #pjsip_messaging_set_incoming_callback. This will be the global callback
- * for all incoming IM messages. Although the message was sent as part of
- * a dialog, it would still come here. And as long as the request has proper
- * identification (Call-ID, From/To tag), the dialog will be aware of the
- * request and update it's state (remote CSeq) accordingly.
- */
-
-
-
-/**
- * Typedef for callback to be called when outgoing message has been sent
- * and a final response has been received.
- */
-typedef void (*pjsip_messaging_cb)(void *token, int status_code);
-
-/**
- * Typedef for callback to receive incoming message.
- *
- * @param rdata Incoming message data.
- *
- * @return The status code to be returned back to original sender.
- * Application must return a final status code upon returning
- * from this function, or otherwise the stack will respond
- * with error.
- */
-typedef int (*pjsip_on_new_msg_cb)(pjsip_rx_data *rdata);
-
-/**
- * Opaque data type for instant messaging session.
- */
-typedef struct pjsip_messaging_session pjsip_messaging_session;
-
-/**
- * Get the messaging module.
- *
- * @return SIP module.
- */
-PJ_DECL(pjsip_module*) pjsip_messaging_get_module();
-
-/**
- * Set the global callback to be called when incoming message is received.
- *
- * @param cb The callback to be called when incoming message is received.
- *
- * @return The previous callback.
- */
-PJ_DECL(pjsip_on_new_msg_cb)
-pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb);
-
-
-/**
- * Create an instant message transmit data buffer using the specified arguments.
- * The returned transmit data buffers will have it's reference counter set
- * to 1, and when application send the buffer, the send function will decrement
- * the reference counter. When the reference counter reach zero, the buffer
- * will be deleted. As long as the function does not increment the buffer's
- * reference counter between creating and sending the request, the buffer
- * will always be deleted and no memory leak will occur.
- *
- * @param endpt Endpoint instance.
- * @param target Target URL.
- * @param from The "From" header, which content will be copied to request.
- * If the "From" header doesn't have a tag parameter, the
- * function will generate one.
- * @param to The "To" header, which content will be copied to request.
- * @param call_id Optionally specify Call-ID, or put NULL to make this
- * function generate a unique Call-ID automatically.
- * @param cseq Optionally specify CSeq, or put -1 to make this function
- * generate a random CSeq.
- * @param text Optionally specify "text/plain" message body, or put NULL
- * if application wants to put body other than "text/plain"
- * manually.
- *
- * @return SIP transmit data buffer, which reference count has been
- * set to 1.
- */
-PJ_DECL(pjsip_tx_data*)
-pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt,
- const pjsip_uri *target,
- const pjsip_from_hdr *from,
- const pjsip_to_hdr *to,
- const pjsip_cid_hdr *call_id,
- int cseq,
- const pj_str_t *text);
-
-/**
- * Create instant message, by specifying URL string for both From and To header.
- *
- * @param endpt Endpoint instance.
- * @param target Target URL.
- * @param from URL of the sender.
- * @param to URL of the recipient.
- * @param call_id Optionally specify Call-ID, or put NULL to make this
- * function generate a unique Call-ID automatically.
- * @param cseq Optionally specify CSeq, or put -1 to make this function
- * generate a random CSeq.
- * @param text Optionally specify "text/plain" message body, or put NULL
- * if application wants to put body other than "text/plain"
- * manually.
- *
- * @return SIP transmit data buffer, which reference count has been
- * set to 1.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_messaging_create_msg( pjsip_endpoint *endpt,
- const pj_str_t *target,
- const pj_str_t *from,
- const pj_str_t *to,
- const pj_str_t *call_id,
- int cseq,
- const pj_str_t *text);
-
-/**
- * Send the instant message transmit buffer and attach a callback to be called
- * when the request has received a final response. This function will decrement
- * the transmit buffer's reference counter, and when the reference counter
- * reach zero, the buffer will be deleted. As long as the function does not
- * increment the buffer's reference counter between creating the request and
- * calling this function, the buffer will always be deleted regardless whether
- * the sending was failed or succeeded.
- *
- * @param endpt Endpoint instance.
- * @param tdata Transmit data buffer.
- * @param token Token to be associated with the SIP transaction which sends
- * this request.
- * @param cb The callback to be called when the SIP request has received
- * a final response from destination.
- *
- * @return Zero if the transaction was started successfully. Note that
- * this doesn't mean the request has been received successfully
- * by remote recipient.
- */
-PJ_DECL(pj_status_t) pjsip_messaging_send_msg( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- void *token,
- pjsip_messaging_cb cb );
-
-/**
- * Create an instant messaging session, which can conveniently be used to send
- * multiple instant messages to the same recipient.
- *
- * @param endpt Endpoint instance.
- * @param from URI of sender. The function will add a unique tag parameter
- * to this URI in the From header.
- * @param to URI of recipient.
- *
- * @return Messaging session.
- */
-PJ_DECL(pjsip_messaging_session*)
-pjsip_messaging_create_session( pjsip_endpoint *endpt,
- const pj_str_t *from,
- const pj_str_t *to );
-
-/**
- * Create an instant message using instant messaging session, and optionally
- * attach a text message.
- *
- * @param ses The instant messaging session.
- * @param text Optional "text/plain" message to be attached as the
- * message body. If this parameter is NULL, then no message
- * body will be created, and application can attach any
- * type of message body before the request is sent.
- *
- * @return SIP transmit data buffer, which reference counter has been
- * set to 1.
- */
-PJ_DECL(pjsip_tx_data*)
-pjsip_messaging_session_create_msg( pjsip_messaging_session *ses,
- const pj_str_t *text );
-
-/**
- * Destroy an instant messaging session.
- *
- * @param ses The instant messaging session.
- *
- * @return Zero on successfull.
- */
-PJ_DECL(pj_status_t)
-pjsip_messaging_destroy_session( pjsip_messaging_session *ses );
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIMPLE_MESSAGING_H__ +#define __PJSIP_SIMPLE_MESSAGING_H__ + +/** + * @file messaging.h + * @brief Instant Messaging Extension (RFC 3428) + */ + +#include <pjsip/sip_msg.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_MESSAGING SIP Instant Messaging (RFC 3428) Module + * @ingroup PJSIP_SIMPLE + * @{ + * + * This module provides the implementation of SIP Extension for Instant + * Messaging (RFC 3428). It extends PJSIP by supporting MESSAGE method. + * + * The RFC 3428 doesn't provide any means of dialog for the purpose of sending/ + * receiving instant messaging. IM with SIP is basicly sessionless, which means + * that there is absolutely no correlation between IM messages sent or received + * by a host. Any correlation between IM messages is only perceivable by + * user, phsychologically. + * + * However, the RFC doesn't prohibit sending IM within a dialog (presumably + * using the same Call-ID and CSeq namespace), although it prohibits creating + * a dialog specificly for creating IM session. + * + * The implementation here is modeled to support both ways of sending IM msgs, + * i.e. sending IM message individually and sending IM message within a dialog. + * Although IM message can be associated with a dialog, this implementation of + * IM module is completely independent of the User Agent library in PJSIP. Yes, + * that's what is called modularity, and it demonstrates the clearness + * of PJSIP design (the last sentence is of course marketing crap :)). + * + * To send an IM message as part of dialog, application would first create the + * message using #pjsip_messaging_create_msg, using dialog's Call-ID, CSeq, + * From, and To header, then send the message using #pjsip_dlg_send_msg instead + * of #pjsip_messaging_send_msg. + * + * To send IM messages individually, application has two options. The first is + * to create the request with #pjsip_messaging_create_msg then send it with + * #pjsip_messaging_send_msg. But this way, application has to pre-construct + * From and To header first, which is not too convenient. + * + * The second option (to send IM messages not associated with a dialog) is to + * first create an 'IM session'. An 'IM session' here is not a SIP dialog, as + * it doesn't have Contact header etc. An 'IM session' here is just a local + * state to cache most of IM headers, for convenience and optimization. Appl + * creates an IM session with #pjsip_messaging_create_session, and destroy + * the session with #pjsip_messaging_destroy_session. To create an outgoing + * IM message, application would call #pjsip_messaging_session_create_msg, + * and to send the message it would use #pjsip_messaging_send_msg. + * + * Message authorization is handled by application, as usual, by inserting a + * proper WWW-Authenticate or Proxy-Authenticate header before sending the + * message. + * + * And the last bit, to handle incoing IM messages. + * + * To handle incoming IM messages, application would register a global callback + * to be called when incoming messages arrive, by registering with function + * #pjsip_messaging_set_incoming_callback. This will be the global callback + * for all incoming IM messages. Although the message was sent as part of + * a dialog, it would still come here. And as long as the request has proper + * identification (Call-ID, From/To tag), the dialog will be aware of the + * request and update it's state (remote CSeq) accordingly. + */ + + + +/** + * Typedef for callback to be called when outgoing message has been sent + * and a final response has been received. + */ +typedef void (*pjsip_messaging_cb)(void *token, int status_code); + +/** + * Typedef for callback to receive incoming message. + * + * @param rdata Incoming message data. + * + * @return The status code to be returned back to original sender. + * Application must return a final status code upon returning + * from this function, or otherwise the stack will respond + * with error. + */ +typedef int (*pjsip_on_new_msg_cb)(pjsip_rx_data *rdata); + +/** + * Opaque data type for instant messaging session. + */ +typedef struct pjsip_messaging_session pjsip_messaging_session; + +/** + * Get the messaging module. + * + * @return SIP module. + */ +PJ_DECL(pjsip_module*) pjsip_messaging_get_module(); + +/** + * Set the global callback to be called when incoming message is received. + * + * @param cb The callback to be called when incoming message is received. + * + * @return The previous callback. + */ +PJ_DECL(pjsip_on_new_msg_cb) +pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb); + + +/** + * Create an instant message transmit data buffer using the specified arguments. + * The returned transmit data buffers will have it's reference counter set + * to 1, and when application send the buffer, the send function will decrement + * the reference counter. When the reference counter reach zero, the buffer + * will be deleted. As long as the function does not increment the buffer's + * reference counter between creating and sending the request, the buffer + * will always be deleted and no memory leak will occur. + * + * @param endpt Endpoint instance. + * @param target Target URL. + * @param from The "From" header, which content will be copied to request. + * If the "From" header doesn't have a tag parameter, the + * function will generate one. + * @param to The "To" header, which content will be copied to request. + * @param call_id Optionally specify Call-ID, or put NULL to make this + * function generate a unique Call-ID automatically. + * @param cseq Optionally specify CSeq, or put -1 to make this function + * generate a random CSeq. + * @param text Optionally specify "text/plain" message body, or put NULL + * if application wants to put body other than "text/plain" + * manually. + * + * @return SIP transmit data buffer, which reference count has been + * set to 1. + */ +PJ_DECL(pjsip_tx_data*) +pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt, + const pjsip_uri *target, + const pjsip_from_hdr *from, + const pjsip_to_hdr *to, + const pjsip_cid_hdr *call_id, + int cseq, + const pj_str_t *text); + +/** + * Create instant message, by specifying URL string for both From and To header. + * + * @param endpt Endpoint instance. + * @param target Target URL. + * @param from URL of the sender. + * @param to URL of the recipient. + * @param call_id Optionally specify Call-ID, or put NULL to make this + * function generate a unique Call-ID automatically. + * @param cseq Optionally specify CSeq, or put -1 to make this function + * generate a random CSeq. + * @param text Optionally specify "text/plain" message body, or put NULL + * if application wants to put body other than "text/plain" + * manually. + * + * @return SIP transmit data buffer, which reference count has been + * set to 1. + */ +PJ_DECL(pjsip_tx_data*) pjsip_messaging_create_msg( pjsip_endpoint *endpt, + const pj_str_t *target, + const pj_str_t *from, + const pj_str_t *to, + const pj_str_t *call_id, + int cseq, + const pj_str_t *text); + +/** + * Send the instant message transmit buffer and attach a callback to be called + * when the request has received a final response. This function will decrement + * the transmit buffer's reference counter, and when the reference counter + * reach zero, the buffer will be deleted. As long as the function does not + * increment the buffer's reference counter between creating the request and + * calling this function, the buffer will always be deleted regardless whether + * the sending was failed or succeeded. + * + * @param endpt Endpoint instance. + * @param tdata Transmit data buffer. + * @param token Token to be associated with the SIP transaction which sends + * this request. + * @param cb The callback to be called when the SIP request has received + * a final response from destination. + * + * @return Zero if the transaction was started successfully. Note that + * this doesn't mean the request has been received successfully + * by remote recipient. + */ +PJ_DECL(pj_status_t) pjsip_messaging_send_msg( pjsip_endpoint *endpt, + pjsip_tx_data *tdata, + void *token, + pjsip_messaging_cb cb ); + +/** + * Create an instant messaging session, which can conveniently be used to send + * multiple instant messages to the same recipient. + * + * @param endpt Endpoint instance. + * @param from URI of sender. The function will add a unique tag parameter + * to this URI in the From header. + * @param to URI of recipient. + * + * @return Messaging session. + */ +PJ_DECL(pjsip_messaging_session*) +pjsip_messaging_create_session( pjsip_endpoint *endpt, + const pj_str_t *from, + const pj_str_t *to ); + +/** + * Create an instant message using instant messaging session, and optionally + * attach a text message. + * + * @param ses The instant messaging session. + * @param text Optional "text/plain" message to be attached as the + * message body. If this parameter is NULL, then no message + * body will be created, and application can attach any + * type of message body before the request is sent. + * + * @return SIP transmit data buffer, which reference counter has been + * set to 1. + */ +PJ_DECL(pjsip_tx_data*) +pjsip_messaging_session_create_msg( pjsip_messaging_session *ses, + const pj_str_t *text ); + +/** + * Destroy an instant messaging session. + * + * @param ses The instant messaging session. + * + * @return Zero on successfull. + */ +PJ_DECL(pj_status_t) +pjsip_messaging_destroy_session( pjsip_messaging_session *ses ); + +/** + * @} + */ + +PJ_END_DECL + +#endif diff --git a/pjsip/include/pjsip-simple/pidf.h b/pjsip/include/pjsip-simple/pidf.h index 70e12907..b9dd4509 100644 --- a/pjsip/include/pjsip-simple/pidf.h +++ b/pjsip/include/pjsip-simple/pidf.h @@ -1,176 +1,176 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIMPLE_PIDF_H__
-#define __PJSIP_SIMPLE_PIDF_H__
-
-/**
- * @file pidf.h
- * @brief PIDF/Presence Information Data Format (RFC 3863)
- */
-#include <pj/types.h>
-#include <pj/xml.h>
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJSIP_SIMPLE_PIDF PIDF/Presence Information Data Format (RFC 3863)
- * @ingroup PJSIP_SIMPLE
- * @{
- *
- * This file provides tools for manipulating Presence Information Data
- * Format (PIDF) as described in RFC 3863.
- */
-typedef struct pj_xml_node pjpidf_pres;
-typedef struct pj_xml_node pjpidf_tuple;
-typedef struct pj_xml_node pjpidf_status;
-typedef struct pj_xml_node pjpidf_note;
-
-typedef struct pjpidf_status_op
-{
- void (*construct)(pj_pool_t*, pjpidf_status*);
- pj_bool_t (*is_basic_open)(const pjpidf_status*);
- void (*set_basic_open)(pjpidf_status*, pj_bool_t);
-} pjpidf_status_op;
-
-typedef struct pjpidf_tuple_op
-{
- void (*construct)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
-
- const pj_str_t* (*get_id)(const pjpidf_tuple* );
- void (*set_id)(pj_pool_t*, pjpidf_tuple *, const pj_str_t*);
-
- pjpidf_status* (*get_status)(pjpidf_tuple* );
-
- const pj_str_t* (*get_contact)(const pjpidf_tuple*);
- void (*set_contact)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
- void (*set_contact_prio)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
- const pj_str_t* (*get_contact_prio)(const pjpidf_tuple*);
-
- pjpidf_note* (*add_note)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
- pjpidf_note* (*get_first_note)(pjpidf_tuple*);
- pjpidf_note* (*get_next_note)(pjpidf_tuple*, pjpidf_note*);
-
- const pj_str_t* (*get_timestamp)(const pjpidf_tuple*);
- void (*set_timestamp)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
- void (*set_timestamp_np)(pj_pool_t*,pjpidf_tuple*, pj_str_t*);
-
-} pjpidf_tuple_op;
-
-typedef struct pjpidf_pres_op
-{
- void (*construct)(pj_pool_t*, pjpidf_pres*, const pj_str_t*);
-
- pjpidf_tuple* (*add_tuple)(pj_pool_t*, pjpidf_pres*, const pj_str_t*);
- pjpidf_tuple* (*get_first_tuple)(pjpidf_pres*);
- pjpidf_tuple* (*get_next_tuple)(pjpidf_pres*, pjpidf_tuple*);
- pjpidf_tuple* (*find_tuple)(pjpidf_pres*, const pj_str_t*);
- void (*remove_tuple)(pjpidf_pres*, pjpidf_tuple*);
-
- pjpidf_note* (*add_note)(pj_pool_t*, pjpidf_pres*, const pj_str_t*);
- pjpidf_note* (*get_first_note)(pjpidf_pres*);
- pjpidf_note* (*get_next_note)(pjpidf_pres*, pjpidf_note*);
-
-} pjpidf_pres_op;
-
-
-extern struct pjpidf_op_desc
-{
- pjpidf_pres_op pres;
- pjpidf_tuple_op tuple;
- pjpidf_status_op status;
-} pjpidf_op;
-
-
-/******************************************************************************
- * Top level API for managing presence document.
- *****************************************************************************/
-PJ_DECL(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity);
-PJ_DECL(pjpidf_pres*) pjpidf_parse(pj_pool_t *pool, char *text, int len);
-PJ_DECL(int) pjpidf_print(const pjpidf_pres* pres, char *buf, int len);
-
-
-/******************************************************************************
- * API for managing Presence node.
- *****************************************************************************/
-PJ_DECL(void) pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres,
- const pj_str_t *entity);
-PJ_DECL(pjpidf_tuple*) pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres,
- const pj_str_t *id);
-PJ_DECL(pjpidf_tuple*) pjpidf_pres_get_first_tuple(pjpidf_pres *pres);
-PJ_DECL(pjpidf_tuple*) pjpidf_pres_get_next_tuple(pjpidf_pres *pres,
- pjpidf_tuple *t);
-PJ_DECL(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres,
- const pj_str_t *id);
-PJ_DECL(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres,
- pjpidf_tuple*);
-
-PJ_DECL(pjpidf_note*) pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres,
- const pj_str_t *text);
-PJ_DECL(pjpidf_note*) pjpidf_pres_get_first_note(pjpidf_pres *pres);
-PJ_DECL(pjpidf_note*) pjpidf_pres_get_next_note(pjpidf_pres*, pjpidf_note*);
-
-
-/******************************************************************************
- * API for managing Tuple node.
- *****************************************************************************/
-PJ_DECL(void) pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *id);
-PJ_DECL(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t );
-PJ_DECL(void) pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *id);
-
-PJ_DECL(pjpidf_status*) pjpidf_tuple_get_status(pjpidf_tuple *t);
-
-PJ_DECL(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t);
-PJ_DECL(void) pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *contact);
-PJ_DECL(void) pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *prio);
-PJ_DECL(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t);
-
-PJ_DECL(pjpidf_note*) pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *text);
-PJ_DECL(pjpidf_note*) pjpidf_tuple_get_first_note(pjpidf_tuple *t);
-PJ_DECL(pjpidf_note*) pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n);
-
-PJ_DECL(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t);
-PJ_DECL(void) pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *ts);
-PJ_DECL(void) pjpidf_tuple_set_timestamp_np( pj_pool_t*, pjpidf_tuple *t,
- pj_str_t *ts);
-
-
-/******************************************************************************
- * API for managing Status node.
- *****************************************************************************/
-PJ_DECL(void) pjpidf_status_construct(pj_pool_t*, pjpidf_status*);
-PJ_DECL(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status*);
-PJ_DECL(void) pjpidf_status_set_basic_open(pjpidf_status*, pj_bool_t);
-
-
-/**
- * @}
- */
-
-
-PJ_END_DECL
-
-
-#endif /* __PJSIP_SIMPLE_PIDF_H__ */
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIMPLE_PIDF_H__ +#define __PJSIP_SIMPLE_PIDF_H__ + +/** + * @file pidf.h + * @brief PIDF/Presence Information Data Format (RFC 3863) + */ +#include <pj/types.h> +#include <pj/xml.h> + +PJ_BEGIN_DECL + + +/** + * @defgroup PJSIP_SIMPLE_PIDF PIDF/Presence Information Data Format (RFC 3863) + * @ingroup PJSIP_SIMPLE + * @{ + * + * This file provides tools for manipulating Presence Information Data + * Format (PIDF) as described in RFC 3863. + */ +typedef struct pj_xml_node pjpidf_pres; +typedef struct pj_xml_node pjpidf_tuple; +typedef struct pj_xml_node pjpidf_status; +typedef struct pj_xml_node pjpidf_note; + +typedef struct pjpidf_status_op +{ + void (*construct)(pj_pool_t*, pjpidf_status*); + pj_bool_t (*is_basic_open)(const pjpidf_status*); + void (*set_basic_open)(pjpidf_status*, pj_bool_t); +} pjpidf_status_op; + +typedef struct pjpidf_tuple_op +{ + void (*construct)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); + + const pj_str_t* (*get_id)(const pjpidf_tuple* ); + void (*set_id)(pj_pool_t*, pjpidf_tuple *, const pj_str_t*); + + pjpidf_status* (*get_status)(pjpidf_tuple* ); + + const pj_str_t* (*get_contact)(const pjpidf_tuple*); + void (*set_contact)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); + void (*set_contact_prio)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); + const pj_str_t* (*get_contact_prio)(const pjpidf_tuple*); + + pjpidf_note* (*add_note)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); + pjpidf_note* (*get_first_note)(pjpidf_tuple*); + pjpidf_note* (*get_next_note)(pjpidf_tuple*, pjpidf_note*); + + const pj_str_t* (*get_timestamp)(const pjpidf_tuple*); + void (*set_timestamp)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); + void (*set_timestamp_np)(pj_pool_t*,pjpidf_tuple*, pj_str_t*); + +} pjpidf_tuple_op; + +typedef struct pjpidf_pres_op +{ + void (*construct)(pj_pool_t*, pjpidf_pres*, const pj_str_t*); + + pjpidf_tuple* (*add_tuple)(pj_pool_t*, pjpidf_pres*, const pj_str_t*); + pjpidf_tuple* (*get_first_tuple)(pjpidf_pres*); + pjpidf_tuple* (*get_next_tuple)(pjpidf_pres*, pjpidf_tuple*); + pjpidf_tuple* (*find_tuple)(pjpidf_pres*, const pj_str_t*); + void (*remove_tuple)(pjpidf_pres*, pjpidf_tuple*); + + pjpidf_note* (*add_note)(pj_pool_t*, pjpidf_pres*, const pj_str_t*); + pjpidf_note* (*get_first_note)(pjpidf_pres*); + pjpidf_note* (*get_next_note)(pjpidf_pres*, pjpidf_note*); + +} pjpidf_pres_op; + + +extern struct pjpidf_op_desc +{ + pjpidf_pres_op pres; + pjpidf_tuple_op tuple; + pjpidf_status_op status; +} pjpidf_op; + + +/****************************************************************************** + * Top level API for managing presence document. + *****************************************************************************/ +PJ_DECL(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity); +PJ_DECL(pjpidf_pres*) pjpidf_parse(pj_pool_t *pool, char *text, int len); +PJ_DECL(int) pjpidf_print(const pjpidf_pres* pres, char *buf, int len); + + +/****************************************************************************** + * API for managing Presence node. + *****************************************************************************/ +PJ_DECL(void) pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres, + const pj_str_t *entity); +PJ_DECL(pjpidf_tuple*) pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres, + const pj_str_t *id); +PJ_DECL(pjpidf_tuple*) pjpidf_pres_get_first_tuple(pjpidf_pres *pres); +PJ_DECL(pjpidf_tuple*) pjpidf_pres_get_next_tuple(pjpidf_pres *pres, + pjpidf_tuple *t); +PJ_DECL(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres, + const pj_str_t *id); +PJ_DECL(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres, + pjpidf_tuple*); + +PJ_DECL(pjpidf_note*) pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres, + const pj_str_t *text); +PJ_DECL(pjpidf_note*) pjpidf_pres_get_first_note(pjpidf_pres *pres); +PJ_DECL(pjpidf_note*) pjpidf_pres_get_next_note(pjpidf_pres*, pjpidf_note*); + + +/****************************************************************************** + * API for managing Tuple node. + *****************************************************************************/ +PJ_DECL(void) pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *id); +PJ_DECL(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t ); +PJ_DECL(void) pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *id); + +PJ_DECL(pjpidf_status*) pjpidf_tuple_get_status(pjpidf_tuple *t); + +PJ_DECL(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t); +PJ_DECL(void) pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *contact); +PJ_DECL(void) pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *prio); +PJ_DECL(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t); + +PJ_DECL(pjpidf_note*) pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *text); +PJ_DECL(pjpidf_note*) pjpidf_tuple_get_first_note(pjpidf_tuple *t); +PJ_DECL(pjpidf_note*) pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n); + +PJ_DECL(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t); +PJ_DECL(void) pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *ts); +PJ_DECL(void) pjpidf_tuple_set_timestamp_np( pj_pool_t*, pjpidf_tuple *t, + pj_str_t *ts); + + +/****************************************************************************** + * API for managing Status node. + *****************************************************************************/ +PJ_DECL(void) pjpidf_status_construct(pj_pool_t*, pjpidf_status*); +PJ_DECL(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status*); +PJ_DECL(void) pjpidf_status_set_basic_open(pjpidf_status*, pj_bool_t); + + +/** + * @} + */ + + +PJ_END_DECL + + +#endif /* __PJSIP_SIMPLE_PIDF_H__ */ diff --git a/pjsip/include/pjsip-simple/presence.h b/pjsip/include/pjsip-simple/presence.h index e4f34274..180ac4d1 100644 --- a/pjsip/include/pjsip-simple/presence.h +++ b/pjsip/include/pjsip-simple/presence.h @@ -1,229 +1,229 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIMPLE_PRESENCE_H__
-#define __PJSIP_SIMPLE_PRESENCE_H__
-
-/**
- * @file presence.h
- * @brief SIP Extension for Presence (RFC 3856)
- */
-#include <pjsip_simple/event_notify.h>
-#include <pjsip_simple/pidf.h>
-#include <pjsip_simple/xpidf.h>
-
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJSIP_SIMPLE_PRES SIP Extension for Presence (RFC 3856)
- * @ingroup PJSIP_SIMPLE
- * @{
- *
- * This module contains the implementation of SIP Presence Extension as
- * described in RFC 3856. It uses the SIP Event Notification framework
- * (event_notify.h) and extends the framework by implementing "presence"
- * event package.
- */
-
-/**
- * Presence message body type.
- */
-typedef enum pjsip_pres_type
-{
- PJSIP_PRES_TYPE_PIDF,
- PJSIP_PRES_TYPE_XPIDF,
-} pjsip_pres_type;
-
-/**
- * This structure describe a presentity, for both subscriber and notifier.
- */
-typedef struct pjsip_presentity
-{
- pjsip_event_sub *sub; /**< Event subscribtion record. */
- pjsip_pres_type pres_type; /**< Presentity type. */
- pjsip_msg_body *uas_body; /**< Message body (UAS only). */
- union {
- pjpidf_pres *pidf;
- pjxpidf_pres *xpidf;
- } uas_data; /**< UAS data. */
- pj_str_t timestamp; /**< Time of last update. */
- void *user_data; /**< Application data. */
-} pjsip_presentity;
-
-
-/**
- * This structure describe callback that is registered to receive notification
- * from the presence module.
- */
-typedef struct pjsip_presence_cb
-{
- /**
- * This callback is first called when the module receives incoming
- * SUBSCRIBE request to determine whether application wants to accept
- * the request. If it does, then on_presence_request will be called.
- *
- * @param rdata The received message.
- * @return Application should return 2xx to accept the request,
- * or failure status (>=300) to reject the request.
- */
- void (*accept_presence)(pjsip_rx_data *rdata, int *status);
-
- /**
- * This callback is called when the module receive the first presence
- * subscription request.
- *
- * @param pres The presence descriptor.
- * @param rdata The incoming request.
- * @param timeout Timeout to be set for incoming request. Otherwise
- * app can just leave this and accept the default.
- */
- void (*on_received_request)(pjsip_presentity *pres, pjsip_rx_data *rdata,
- int *timeout);
-
- /**
- * This callback is called when the module received subscription refresh
- * request.
- *
- * @param pres The presence descriptor.
- * @param rdata The incoming request.
- */
- void (*on_received_refresh)(pjsip_presentity *pres, pjsip_rx_data *rdata);
-
- /**
- * This callback is called when the module receives incoming NOTIFY
- * request.
- *
- * @param pres The presence descriptor.
- * @param open The latest status of the presentity.
- */
- void (*on_received_update)(pjsip_presentity *pres, pj_bool_t open);
-
- /**
- * This callback is called when the subscription has terminated.
- *
- * @param sub The subscription instance.
- * @param reason The termination reason.
- */
- void (*on_terminated)(pjsip_presentity *pres, const pj_str_t *reason);
-
-} pjsip_presence_cb;
-
-
-/**
- * Initialize the presence module and register callback.
- *
- * @param cb Callback structure.
- */
-PJ_DECL(void) pjsip_presence_init(const pjsip_presence_cb *cb);
-
-
-/**
- * Create to presence subscription of a presentity URL.
- *
- * @param endpt Endpoint instance.
- * @param local_url Local URL.
- * @param remote_url Remote URL which the presence is being subscribed.
- * @param expires The expiration.
- * @param user_data User data to attach to presence subscription.
- *
- * @return The presence structure if successfull, or NULL if
- * failed.
- */
-PJ_DECL(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt,
- const pj_str_t *local_url,
- const pj_str_t *remote_url,
- int expires,
- void *user_data );
-
-/**
- * Set credentials to be used by this presentity for outgoing requests.
- *
- * @param pres Presentity instance.
- * @param count Number of credentials in the array.
- * @param cred Array of credentials.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres,
- int count,
- const pjsip_cred_info cred[]);
-
-/**
- * Set route set for outgoing requests.
- *
- * @param pres Presentity instance.
- * @param route_set List of route headers.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres,
- const pjsip_route_hdr *hdr );
-
-/**
- * Send SUBSCRIBE request for the specified presentity.
- *
- * @param pres The presentity instance.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres );
-
-/**
- * Ceased the presence subscription.
- *
- * @param pres The presence structure.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres );
-
-/**
- * Notify subscriber about change in local status.
- *
- * @param pres The presence structure.
- * @param state Set the state of the subscription.
- * @param open Set the presence status (open or closed).
- *
- * @return Zero if a NOTIFY request can be sent.
- */
-PJ_DECL(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres,
- pjsip_event_sub_state state,
- pj_bool_t open );
-
-/**
- * Destroy presence structure and the underlying subscription.
- *
- * @param pres The presence structure.
- *
- * @return Zero if the subscription was destroyed, or one if
- * the subscription can not be destroyed immediately
- * and will be destroyed later, or -1 if failed.
- */
-PJ_DECL(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres );
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJSIP_SIMPLE_PRESENCE_H__ */
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIMPLE_PRESENCE_H__ +#define __PJSIP_SIMPLE_PRESENCE_H__ + +/** + * @file presence.h + * @brief SIP Extension for Presence (RFC 3856) + */ +#include <pjsip_simple/event_notify.h> +#include <pjsip_simple/pidf.h> +#include <pjsip_simple/xpidf.h> + + +PJ_BEGIN_DECL + + +/** + * @defgroup PJSIP_SIMPLE_PRES SIP Extension for Presence (RFC 3856) + * @ingroup PJSIP_SIMPLE + * @{ + * + * This module contains the implementation of SIP Presence Extension as + * described in RFC 3856. It uses the SIP Event Notification framework + * (event_notify.h) and extends the framework by implementing "presence" + * event package. + */ + +/** + * Presence message body type. + */ +typedef enum pjsip_pres_type +{ + PJSIP_PRES_TYPE_PIDF, + PJSIP_PRES_TYPE_XPIDF, +} pjsip_pres_type; + +/** + * This structure describe a presentity, for both subscriber and notifier. + */ +typedef struct pjsip_presentity +{ + pjsip_event_sub *sub; /**< Event subscribtion record. */ + pjsip_pres_type pres_type; /**< Presentity type. */ + pjsip_msg_body *uas_body; /**< Message body (UAS only). */ + union { + pjpidf_pres *pidf; + pjxpidf_pres *xpidf; + } uas_data; /**< UAS data. */ + pj_str_t timestamp; /**< Time of last update. */ + void *user_data; /**< Application data. */ +} pjsip_presentity; + + +/** + * This structure describe callback that is registered to receive notification + * from the presence module. + */ +typedef struct pjsip_presence_cb +{ + /** + * This callback is first called when the module receives incoming + * SUBSCRIBE request to determine whether application wants to accept + * the request. If it does, then on_presence_request will be called. + * + * @param rdata The received message. + * @return Application should return 2xx to accept the request, + * or failure status (>=300) to reject the request. + */ + void (*accept_presence)(pjsip_rx_data *rdata, int *status); + + /** + * This callback is called when the module receive the first presence + * subscription request. + * + * @param pres The presence descriptor. + * @param rdata The incoming request. + * @param timeout Timeout to be set for incoming request. Otherwise + * app can just leave this and accept the default. + */ + void (*on_received_request)(pjsip_presentity *pres, pjsip_rx_data *rdata, + int *timeout); + + /** + * This callback is called when the module received subscription refresh + * request. + * + * @param pres The presence descriptor. + * @param rdata The incoming request. + */ + void (*on_received_refresh)(pjsip_presentity *pres, pjsip_rx_data *rdata); + + /** + * This callback is called when the module receives incoming NOTIFY + * request. + * + * @param pres The presence descriptor. + * @param open The latest status of the presentity. + */ + void (*on_received_update)(pjsip_presentity *pres, pj_bool_t open); + + /** + * This callback is called when the subscription has terminated. + * + * @param sub The subscription instance. + * @param reason The termination reason. + */ + void (*on_terminated)(pjsip_presentity *pres, const pj_str_t *reason); + +} pjsip_presence_cb; + + +/** + * Initialize the presence module and register callback. + * + * @param cb Callback structure. + */ +PJ_DECL(void) pjsip_presence_init(const pjsip_presence_cb *cb); + + +/** + * Create to presence subscription of a presentity URL. + * + * @param endpt Endpoint instance. + * @param local_url Local URL. + * @param remote_url Remote URL which the presence is being subscribed. + * @param expires The expiration. + * @param user_data User data to attach to presence subscription. + * + * @return The presence structure if successfull, or NULL if + * failed. + */ +PJ_DECL(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt, + const pj_str_t *local_url, + const pj_str_t *remote_url, + int expires, + void *user_data ); + +/** + * Set credentials to be used by this presentity for outgoing requests. + * + * @param pres Presentity instance. + * @param count Number of credentials in the array. + * @param cred Array of credentials. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres, + int count, + const pjsip_cred_info cred[]); + +/** + * Set route set for outgoing requests. + * + * @param pres Presentity instance. + * @param route_set List of route headers. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres, + const pjsip_route_hdr *hdr ); + +/** + * Send SUBSCRIBE request for the specified presentity. + * + * @param pres The presentity instance. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres ); + +/** + * Ceased the presence subscription. + * + * @param pres The presence structure. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres ); + +/** + * Notify subscriber about change in local status. + * + * @param pres The presence structure. + * @param state Set the state of the subscription. + * @param open Set the presence status (open or closed). + * + * @return Zero if a NOTIFY request can be sent. + */ +PJ_DECL(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres, + pjsip_event_sub_state state, + pj_bool_t open ); + +/** + * Destroy presence structure and the underlying subscription. + * + * @param pres The presence structure. + * + * @return Zero if the subscription was destroyed, or one if + * the subscription can not be destroyed immediately + * and will be destroyed later, or -1 if failed. + */ +PJ_DECL(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres ); + + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJSIP_SIMPLE_PRESENCE_H__ */ diff --git a/pjsip/include/pjsip-simple/xpidf.h b/pjsip/include/pjsip-simple/xpidf.h index a94a4f92..59d3c398 100644 --- a/pjsip/include/pjsip-simple/xpidf.h +++ b/pjsip/include/pjsip-simple/xpidf.h @@ -1,133 +1,133 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIMPLE_XPIDF_H__
-#define __PJSIP_SIMPLE_XPIDF_H__
-
-/**
- * @file xpidf.h
- * @brief XPIDF/Presence Information Data Format
- */
-#include <pj/types.h>
-#include <pj/xml.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_SIMPLE_XPIDF XPIDF/Presence Information Data Format
- * @ingroup PJSIP_SIMPLE
- * @{
- *
- * This is an old presence data format as described in:
- * draft-rosenberg-impp-pidf-00.txt.
- *
- * We won't support this format extensively here, as it seems there's not
- * too many implementations support this anymore, as it shouldn't.
- */
-
-/** Type definitions for XPIDF root document. */
-typedef pj_xml_node pjxpidf_pres;
-
-
-/**
- * Create a new XPIDF document.
- *
- * @param pool Pool.
- * @param uri URI to set in the XPIDF document.
- *
- * @return XPIDF document.
- */
-PJ_DECL(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri);
-
-
-/**
- * Parse XPIDF document.
- *
- * @param pool Pool.
- * @param text Input text.
- * @param len Length of input text.
- *
- * @return XPIDF document.
- */
-PJ_DECL(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len);
-
-
-/**
- * Print XPIDF document.
- *
- * @param pres The XPIDF document to print.
- * @param text Buffer to place the output.
- * @param len Length of the buffer.
- *
- * @return The length printed.
- */
-PJ_DECL(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len);
-
-
-/**
- * Get URI in the XPIDF document
- *
- * @param pres XPIDF document
- *
- * @return The URI, or an empty string.
- */
-PJ_DECL(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres);
-
-
-/**
- * Set the URI of the XPIDF document.
- *
- * @param pool Pool.
- * @param pres The XPIDF document.
- * @param uri URI to set in the XPIDF document.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres,
- const pj_str_t *uri);
-
-
-/**
- * Get presence status in the XPIDF document.
- *
- * @param pres XPIDF document.
- *
- * @return True to indicate the contact is online.
- */
-PJ_DECL(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres);
-
-
-/**
- * Set presence status in the XPIDF document.
- *
- * @param pres XPIDF document.
- * @param status Status to set, True for online, False for offline.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t status);
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJSIP_SIMPLE_XPIDF_H__ */
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIMPLE_XPIDF_H__ +#define __PJSIP_SIMPLE_XPIDF_H__ + +/** + * @file xpidf.h + * @brief XPIDF/Presence Information Data Format + */ +#include <pj/types.h> +#include <pj/xml.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_SIMPLE_XPIDF XPIDF/Presence Information Data Format + * @ingroup PJSIP_SIMPLE + * @{ + * + * This is an old presence data format as described in: + * draft-rosenberg-impp-pidf-00.txt. + * + * We won't support this format extensively here, as it seems there's not + * too many implementations support this anymore, as it shouldn't. + */ + +/** Type definitions for XPIDF root document. */ +typedef pj_xml_node pjxpidf_pres; + + +/** + * Create a new XPIDF document. + * + * @param pool Pool. + * @param uri URI to set in the XPIDF document. + * + * @return XPIDF document. + */ +PJ_DECL(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri); + + +/** + * Parse XPIDF document. + * + * @param pool Pool. + * @param text Input text. + * @param len Length of input text. + * + * @return XPIDF document. + */ +PJ_DECL(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len); + + +/** + * Print XPIDF document. + * + * @param pres The XPIDF document to print. + * @param text Buffer to place the output. + * @param len Length of the buffer. + * + * @return The length printed. + */ +PJ_DECL(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len); + + +/** + * Get URI in the XPIDF document + * + * @param pres XPIDF document + * + * @return The URI, or an empty string. + */ +PJ_DECL(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres); + + +/** + * Set the URI of the XPIDF document. + * + * @param pool Pool. + * @param pres The XPIDF document. + * @param uri URI to set in the XPIDF document. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres, + const pj_str_t *uri); + + +/** + * Get presence status in the XPIDF document. + * + * @param pres XPIDF document. + * + * @return True to indicate the contact is online. + */ +PJ_DECL(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres); + + +/** + * Set presence status in the XPIDF document. + * + * @param pres XPIDF document. + * @param status Status to set, True for online, False for offline. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t status); + + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJSIP_SIMPLE_XPIDF_H__ */ diff --git a/pjsip/include/pjsip-ua/sip_dialog.h b/pjsip/include/pjsip-ua/sip_dialog.h index b02ddb35..d46e3873 100644 --- a/pjsip/include/pjsip-ua/sip_dialog.h +++ b/pjsip/include/pjsip-ua/sip_dialog.h @@ -1,635 +1,635 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_DIALOG_H__
-#define __PJSIP_DIALOG_H__
-
-/**
- * @file dialog.h
- * @brief SIP Dialog abstraction
- */
-
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_auth.h>
-#include <pj/sock.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSUA_DIALOG SIP Dialog
- * @ingroup PJSUA
- * @{
- * \brief
- * This file contains SIP dialog, a higher level abstraction of SIP session.
- *
- * \par Overview
- * A SIP dialog is an abstraction of communication session between two user
- * agents that persist for some time. The dialog facilitates sequencing of
- * messages between the user agents and proper routing of requests between both
- * of them. The dialog represents a context in which to interpret SIP messages.
- * However method independent User Agent processing for requests and responses
- * outside of a dialog exists, hence a dialog is not necessary for message
- * processing.
- *
- * A dialog is identified at each User Agent with a dialog Id, which consists
- * of a Call-Id value, a local tag and a remote tag.
- *
- * A dialog contains certain pieces of data needed for further message
- * transmissions within the dialog. This data consists of:
- * - Dialog Id - used to identify the dialog.
- * - Local sequence number - used to order requests from the UA to its peer.
- * - Remote sequence number - used to order requests from its peer to the UA.
- * - Local URI - the address of the local party.
- * - Remote URI - the address of the remote party.
- * - Remote target - the address from the Contact header field of the request
- * or response or refresh request or response.
- * - "secure" boolean - determines if the dialog is secure.
- * - Route set - an ordered list of URIs. The route set is the list of servers
- * that need to be traversed to send a request to the peer.
- * - Authentication info - array of authentication credentials to be used
- * by the dialog to authenticate to proxies and servers.
- *
- * \par Manipulating Dialog
- * Application should use functions declared in this file to do something with
- * the dialog. Among other things, application can:
- * - create outgoing dialog (#pjsip_dlg_init()).
- * - sends outgoing invitation (#pjsip_dlg_invite()).
- * - sends response (provisional and final) to incoming invitation
- * (#pjsip_dlg_answer())
- * - disconnect dialog (#pjsip_dlg_disconnect()).
- * - send other request (#pjsip_dlg_create_request() and #pjsip_dlg_send_msg())
- *
- * \par Getting Dialog's Notification
- * Dialog emits notification about various things that's happening to it (e.g.
- * a message is received, dialog state has changed, etc.). Normally it is in
- * the interest of the application to capture these notifications, by
- * supplying the function to be called when the event occurs in #pjsip_dlg_callback
- * structure, and register this structure to user agent by calling
- * #pjsip_ua_set_dialog_callback().
- *
- * \par Incoming Invitation
- * Upon receiving a new incoming invitation, user agent will automatically create
- * a new dialog, and inform application via \b pjsip_dlg_callback.
- */
-
-/** Forward declaration for user agent structure. */
-typedef struct pjsip_user_agent pjsip_user_agent;
-
-/** Forward declaration for dialog structure. */
-typedef struct pjsip_dlg pjsip_dlg;
-
-/**
- * \brief Type of events that are reported by the dialog to the application callback
- * function.
- */
-typedef enum pjsip_dlg_event_e
-{
- /** Dialog state has changed. */
- PJSIP_DIALOG_EVENT_STATE_CHANGED,
-
- /** Any mid-call messages (reinvitation, message, etc.). */
- PJSIP_DIALOG_EVENT_MID_CALL_REQUEST,
-
- /** Other events (low level events). */
- PJSIP_DIALOG_EVENT_OTHER,
-
-} pjsip_dlg_event_e;
-
-
-/**
- * \brief Structure registered by applications to receive dialog notifications
- * from the User Agent.
- *
- * Applications registers this structure to get notifications from the User Agent
- * about dialog state changes and other events. Application can set any of
- * the callback function to NULL if it doesn't want to handle the notification,
- * however, setting some callbacks to NULL probably will cause some undesired
- * result (such as setting \b on_incoming to NULL will cause the creation of
- * a lot of dialogs with no owner).
- */
-struct pjsip_dlg_callback
-{
- /**
- * This is a low level, uninterpreted callback that is called by framework
- * for all kinds of events, such as transaction events, dialog events, etc.
- * @param dlg The dialog.
- * @param dlg_event The type of dialog event.
- * @param event The event descriptor.
- */
- void (*on_all_events)(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event,
- pjsip_event *event );
-
- /**
- * This is a low level callback that is called by the framework when the
- * underlying transaction is about to send outgoing message. This callback
- * is provided to allow application to modify the message before it is
- * transmitted.
- * @param dlg The dialog.
- * @param tsx The transaction that transmits the message.
- * @param tdata The transmission data, which contains the message.
- * @param retransmission The number of times this message has been sent.
- * Zero indicates the message is about to be sent the first time,
- * one indicates this is the first retransmission, etc.
- */
- void (*on_before_tx)(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata, pj_bool_t retransmission);
-
- /**
- * This is a low level callback that is called by the framework when the dialog
- * has sent a message. Note that a receive of retransmission will not trigger
- * this callback since retransmission is handled internally by transaction.
- * @param dlg The dialog.
- * @param tsx The transaction that transmits the message.
- * @param tdata The transmission data, which contains the message.
- */
- void (*on_tx_msg)(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata);
-
- /**
- * This is a low level callback that is called by the framework when the
- * dialog has received a message. Note that a receipt of retransmission
- * will not trigger this callback since retransmission is handled internally
- * by transaction.
- * @param dlg The dialog.
- * @param tsx The transaction that receives the message.
- * @param rdata The receive data, which contains the message.
- */
- void (*on_rx_msg)(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_rx_data *rdata);
-
- /**
- * This callback is called by the framework when the user agent
- * instance receives an incoming INVITE message.
- * @param dlg The new dialog that's just created to handle the incoming call.
- * @param tsx The INVITE transaction that's just created.
- * @param rdata The receive data, which contains the INVITE message.
- */
- void (*on_incoming)(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_rx_data *rdata);
-
- /**
- * This callback is called by the framework when the dialog is sending
- * the first outgoing INVITE message.
- * @param dlg The dialog.
- * @param tsx The INVITE transaction.
- * @param tdata The transmit data, which contains the INVITE message.
- */
- void (*on_calling)(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata);
-
- /**
- * This callback is called by the framework when the initial INVITE
- * transaction has sent/received provisional response.
- * @param dlg The dialog.
- * @param tsx The transaction.
- * @param event The event, which src_type will always be either
- * PJSIP_EVENT_RX_MSG or PJSIP_EVENT_TX_MSG. The provisional
- * response message itself will be in either \b rdata or \b tdata.
- * @see pjsip_event.
- */
- void (*on_provisional)(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_event *event);
-
- /**
- * This callback is called for both UAS and UAC dialog when 200 response
- * to INVITE is sent or received.
- * @param dlg The dialog.
- * @param event The event, which src_type can only be either
- * PJSIP_EVENT_TX_MSG or PJSIP_EVENT_RX_MSG.
- * @see pjsip_event
- */
- void (*on_connecting)(pjsip_dlg *dlg, pjsip_event *event);
-
- /**
- * This callback is called for both UAS and UAC when an ACK request is
- * sent or received by the dialog.
- * @param dlg The dialog.
- * @param event The event, which src_type can only be either
- * PJSIP_EVENT_TX_MSG or PJSIP_EVENT_RX_MSG.
- * @see pjsip_event
- */
- void (*on_established)(pjsip_dlg *dlg, pjsip_event *event);
-
- /**
- * This callback is called when the dialog is disconnected, i.e. upon
- * sending/receiving non-200 response to INVITE, sending/receiving
- * CANCEL to initial INVITE, and sending/receiving BYE.
- *
- * @param dlg The dialog.
- * @param event The event.
- * @see pjsip_event
- */
- void (*on_disconnected)(pjsip_dlg *dlg, pjsip_event *event);
-
- /**
- * This callback is called when the dialog is about to be destroyed.
- * @param dlg The dialog.
- */
- void (*on_terminated)(pjsip_dlg *dlg);
-
- /**
- * This callback will be called when the dialog receives mid call events
- * such as re-invitation or incoming pager.
- *
- * @param dlg The dialog.
- * @param event The event.
- */
- void (*on_mid_call_events)(pjsip_dlg *dlg, pjsip_event *event);
-
-}; /* struct pjsip_dlg_callback */
-
-
-
-/**
- * Dialog state.
- */
-typedef enum pjsip_dlg_state_e
-{
- /**
- * State NULL is after the dialog is instantiated but before any
- * initialization is done.
- */
- PJSIP_DIALOG_STATE_NULL,
-
- /**
- * State INCOMING is after the (callee) dialog has been initialized with
- * the incoming request, but before any responses is sent by the dialog.
- */
- PJSIP_DIALOG_STATE_INCOMING,
-
- /**
- * State CALLING is after the (caller) dialog has sent outgoing invitation
- * but before any responses are received.
- */
- PJSIP_DIALOG_STATE_CALLING,
-
- /**
- * State PROCEEDING is after the dialog sent/received provisional
- * responses, but before final response is sent/received.
- */
- PJSIP_DIALOG_STATE_PROCEEDING,
-
- /**
- * State CONNECTING is after the dialog has sent/received final response
- * to the invitation, but before acknowledgement is sent.
- */
- PJSIP_DIALOG_STATE_CONNECTING,
-
- /**
- * State ESTABLISHED occurs after the invitation has been accepted and
- * acknowledged.
- */
- PJSIP_DIALOG_STATE_ESTABLISHED,
-
- /**
- * State DISCONNECTED occurs after either party successfully disconnect
- * the session.
- */
- PJSIP_DIALOG_STATE_DISCONNECTED,
-
- /**
- * State TERMINATE occurs when the dialog is ready to be destroyed.
- */
- PJSIP_DIALOG_STATE_TERMINATED,
-
-} pjsip_dlg_state_e;
-
-
-/**
- * Get the dialog string state.
- *
- * @param state Dialog state.
- * @return The string describing the state.
- */
-const char *pjsip_dlg_state_str(pjsip_dlg_state_e state);
-
-/**
- * This structure is used to describe dialog's participants, which in this
- * case is local party (i.e. us) and remote party.
- */
-typedef struct pjsip_dlg_party
-{
- pjsip_uri *target; /**< Target URL. */
- pjsip_fromto_hdr *info; /**< URL in From/To header. */
- pj_str_t tag; /**< Tag. */
- pjsip_contact_hdr *contact; /**< URL in Contact. */
- pj_sockaddr_in addr; /**< The current transport address. */
- int cseq; /**< Sequence number counter. */
-} pjsip_dlg_party;
-
-
-/**
- * This structure describes the dialog structure.
- */
-struct pjsip_dlg
-{
- PJ_DECL_LIST_MEMBER(struct pjsip_dlg)
-
- char obj_name[PJ_MAX_OBJ_NAME]; /**< Log identification. */
-
- pjsip_user_agent *ua; /**< User agent instance. */
- pj_pool_t *pool; /**< Dialog's pool. */
- pjsip_dlg_state_e state; /**< Dialog's call state. */
- pjsip_role_e role; /**< Dialog's role. */
- pj_mutex_t *mutex; /**< Dialog's mutex. */
-
- pjsip_dlg_party local; /**< Local party info. */
- pjsip_dlg_party remote; /**< Remote party info. */
-
- pjsip_cid_hdr *call_id; /**< Call-ID */
- pj_bool_t secure; /**< Use secure transport? */
-
- pjsip_route_hdr route_set; /**< Dialog's route set. */
- pjsip_transaction *invite_tsx; /**< Current INVITE transaction. */
- int pending_tsx_count; /**< Total pending tsx count. */
-
- int cred_count; /**< Number of credentials. */
- pjsip_cred_info *cred_info; /**< Array of credentials. */
-
- pjsip_auth_session auth_sess; /**< List of auth session. */
-
- pjsip_msg_body *body;
-
- void *user_data; /**< Application's data. */
-
- int (*handle_tsx_event)(struct pjsip_dlg *, /**< Internal state handler.*/
- pjsip_transaction *,
- pjsip_event *);
-};
-
-
-/**
- * Initialize dialog with local and remote info. This function is normally
- * called after application creates the dialog with #pjsip_ua_create_dialog
- * for UAC dialogs.
- *
- * This function will initialize local and remote info from the URL, generate
- * a globally unique Call-ID, initialize CSeq, and initialize other dialog's
- * internal attributes.
- *
- * @param dlg The dialog to initialize.
- * @param local_info URI/name address to be used as local info
- * (From and Contact headers).
- * @param remote_info URI/name address to be used as remote info (To header).
- * @param target URI for initial remote's target, or NULL to set the
- * initial target the same as remote_info.
- *
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg,
- const pj_str_t *local_info,
- const pj_str_t *remote_info,
- const pj_str_t *target);
-
-
-/**
- * Set authentication credentials to be used by this dialog.
- *
- * If authentication credentials are set for the dialog, the dialog will try to
- * perform authentication automatically using the credentials supplied, and
- * also cache the last Authorization or Proxy-Authorization headers for next
- * requests.
- *
- * If none of the credentials are suitable or accepted by remote, then
- * the dialog will just pass the authorization failure response back to
- * application.
- *
- * @param dlg The dialog.
- * @param count Number of credentials in the array.
- * @param cred Array of credentials.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_dlg_set_credentials( pjsip_dlg *dlg,
- int count,
- const pjsip_cred_info cred[]);
-
-/**
- * Override local contact details.
- *
- * Call this function to change the contact details to be advertised in Contact
- * header. Application normally need to call this function for incoming calls
- * before answering the call with 200/OK, because for an incoming dialogs, the
- * initial local contact info are generated from the To header, which is
- * normally not the appropriate one.
- *
- * @param dlg The dialog.
- * @param contact The contact to use.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg,
- const pj_str_t *contact );
-
-
-/**
- * Set initial route set to be used by the dialog. This initial route set
- * governs where and how the initial INVITE request will be routed. This
- * initial route set will be overwritten with the route set found in the
- * 2xx response of INVITE.
- *
- * Application only needs to call this function if it wants to have custom
- * route for individual dialogs. If only a single route for all dialogs is
- * needed, then application can set the global route by calling function
- * #pjsip_endpt_set_proxies().
- *
- * @param dlg The dialog.
- * @param route_set The route set list.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg,
- const pjsip_route_hdr *route_set );
-
-
-/**
- * Variation of #pjsip_dlg_set_route_set where the headers will be used
- * as it is (i.e. without cloned).
- *
- * @param dlg The dialog.
- * @param route_set The route set list.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg,
- pjsip_route_hdr *route_set);
-
-/**
- * Create initial outgoing INVITE message.
- *
- * This function is just a simple wrapper to #pjsip_dlg_create_request(),
- * so it follows the same rule there. In addition, this function also adds
- * \b Allow header to the outgoing request.
- *
- * After the message is successfully created, application must call
- * #pjsip_dlg_send_msg() to actually send the message and update the dialog's
- * state. Note that upon return the reference counter of the transmit data
- * will be set to one.
- *
- * @param dlg The dialog.
- *
- * @return The dialog transmit data, or NULL.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg );
-
-
-/**
- * Answer incoming dialog invitation, with either provisional responses
- * or a final response. Application can only call this function when there's
- * a pending invitation to be answered.
- *
- * After the message is successfully created, application must call
- * #pjsip_dlg_send_msg() to actually send the message and update the dialog's
- * state. Note that upon return the reference counter of the transmit data
- * will be set to one.
- *
- * @param dlg The dialog.
- * @param code The response code, which can be:
- * - 100-199 Provisional response (application can issue multiple
- * provisional responses).
- * - 200-299 To answer the invitation (normally status code 200
- * is sent).
- * - 300-699 To reject the invitation.
- * @return Transmit data if successfull.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code );
-
-
-/**
- * High level function to create message to disconnect dialog. Depending
- * on dialog's state, this function will either create CANCEL, final response,
- * or BYE message. A status code must be supplied, which will be set if dialog
- * will be transmitting a final response to INVITE.
- *
- * After the message is successfully created, application must call
- * #pjsip_dlg_send_msg to actually send the message and update the dialog's
- * state. Note that upon return the reference counter of the transmit data
- * will be set to one.
- *
- * @param dlg The dialog.
- * @param status_code The status code for disconnection.
- * @return Transmit data if successfull.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg, int status_code);
-
-/**
- * Create CANCEL message to cancel pending outgoing dialog invitation.
- * Normally application should call #pjsip_dlg_disconnect() instead, because
- * that function will create the correct message regardless of the state of
- * the dialog.
- *
- * Application can call this function at anytime after it issues outgoing
- * invitation and before receiving final response. However, there's no
- * guarantee that the invitation will be successfully cancelled, since the
- * CANCEL request and the final response can pass over in the wire. So the
- * application must prepare to have the dialog connected even after the
- * dialog is cancelled.
- *
- * The final state of the dialog will be reported in the dialog callback.
- * If the CANCEL request succeeded, then the dialog will be disconnected with
- * status code \a PJSIP_SC_REQUEST_TERMINATED.
- *
- * After the message is successfully created, application must call
- * #pjsip_dlg_send_msg() to actually send the message and update the dialog's
- * state.
- *
- * Upon return of this function, the reference counter of the transmit data
- * will be set to one.
- *
- * @param dlg The dialog.
- * @return The dialog transmit data containing the CANCEL message,
- * or NULL.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg );
-
-
-/**
- * Create BYE message. Application shouldn't normally need to use this function,
- * but rather it's preferable to use #pjsip_dlg_disconnect() instead because
- * that function will work to disconnect the session no matter what the state
- * is.
- *
- * After the message is successfully created, application must call
- * #pjsip_dlg_send_msg() to actually send the message and update the dialog's
- * state. Note that upon return the reference counter of the transmit data
- * will be set to one.
- *
- * @param dlg The dialog.
- * @return The BYE message or NULL.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg );
-
-/**
- * This function is called by application to create new outgoing request
- * message for this dialog. After the request is created, application can
- * modify the message (such adding headers), and eventually send the request
- * by calling #pjsip_dlg_send_msg().
- *
- * This function will initialize the request message with dialog's properties
- * as follows:
- * - the request line is initialized with the method and the target is
- * initialized from current remote target.
- * - \b From, \b To, \b Contact, and \b Call-Id headers will be added.
- * - An initial \b CSeq header will be provided (although the value will be
- * verified again when the message is actually sent with #pjsip_dlg_send_msg().
- * - \b Route headers will be added from dialog's route set.
- * - Authentication headers (\b Authorization or \b Proxy-Authorization) will
- * be added from dialog's authorization cache.
- *
- * Note that upon return the reference counter of the transmit data
- * will be set to one. When the message is sent, #pjsip_dlg_send_msg() will
- * decrement the reference counter, and when the reference counter reach zero,
- * the message will be deleted.
- *
- * @param dlg The dialog.
- * @param method The request method.
- * @param cseq Specify CSeq, or -1 to let the dialog specify CSeq.
- *
- * @return Transmit data for the new request.
- *
- * @see pjsip_dlg_send_msg()
- */
-PJ_DECL(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg,
- const pjsip_method *method,
- int cseq);
-
-
-/**
- * This function can be called by application to send outgoing message (request
- * or response) to remote party. Note that after calling this function, the
- * transmit data will be deleted regardless of the return status. To prevent
- * deletion, application must increase the reference count, but then it will
- * be responsible to delete this transmit data itself (by decreasing the
- * reference count).
- *
- * @param dlg The dialog.
- * @param tdata The transmit data, which contains the request message.
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg,
- pjsip_tx_data *tdata );
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_DIALOG_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_DIALOG_H__ +#define __PJSIP_DIALOG_H__ + +/** + * @file dialog.h + * @brief SIP Dialog abstraction + */ + +#include <pjsip/sip_msg.h> +#include <pjsip/sip_auth.h> +#include <pj/sock.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSUA_DIALOG SIP Dialog + * @ingroup PJSUA + * @{ + * \brief + * This file contains SIP dialog, a higher level abstraction of SIP session. + * + * \par Overview + * A SIP dialog is an abstraction of communication session between two user + * agents that persist for some time. The dialog facilitates sequencing of + * messages between the user agents and proper routing of requests between both + * of them. The dialog represents a context in which to interpret SIP messages. + * However method independent User Agent processing for requests and responses + * outside of a dialog exists, hence a dialog is not necessary for message + * processing. + * + * A dialog is identified at each User Agent with a dialog Id, which consists + * of a Call-Id value, a local tag and a remote tag. + * + * A dialog contains certain pieces of data needed for further message + * transmissions within the dialog. This data consists of: + * - Dialog Id - used to identify the dialog. + * - Local sequence number - used to order requests from the UA to its peer. + * - Remote sequence number - used to order requests from its peer to the UA. + * - Local URI - the address of the local party. + * - Remote URI - the address of the remote party. + * - Remote target - the address from the Contact header field of the request + * or response or refresh request or response. + * - "secure" boolean - determines if the dialog is secure. + * - Route set - an ordered list of URIs. The route set is the list of servers + * that need to be traversed to send a request to the peer. + * - Authentication info - array of authentication credentials to be used + * by the dialog to authenticate to proxies and servers. + * + * \par Manipulating Dialog + * Application should use functions declared in this file to do something with + * the dialog. Among other things, application can: + * - create outgoing dialog (#pjsip_dlg_init()). + * - sends outgoing invitation (#pjsip_dlg_invite()). + * - sends response (provisional and final) to incoming invitation + * (#pjsip_dlg_answer()) + * - disconnect dialog (#pjsip_dlg_disconnect()). + * - send other request (#pjsip_dlg_create_request() and #pjsip_dlg_send_msg()) + * + * \par Getting Dialog's Notification + * Dialog emits notification about various things that's happening to it (e.g. + * a message is received, dialog state has changed, etc.). Normally it is in + * the interest of the application to capture these notifications, by + * supplying the function to be called when the event occurs in #pjsip_dlg_callback + * structure, and register this structure to user agent by calling + * #pjsip_ua_set_dialog_callback(). + * + * \par Incoming Invitation + * Upon receiving a new incoming invitation, user agent will automatically create + * a new dialog, and inform application via \b pjsip_dlg_callback. + */ + +/** Forward declaration for user agent structure. */ +typedef struct pjsip_user_agent pjsip_user_agent; + +/** Forward declaration for dialog structure. */ +typedef struct pjsip_dlg pjsip_dlg; + +/** + * \brief Type of events that are reported by the dialog to the application callback + * function. + */ +typedef enum pjsip_dlg_event_e +{ + /** Dialog state has changed. */ + PJSIP_DIALOG_EVENT_STATE_CHANGED, + + /** Any mid-call messages (reinvitation, message, etc.). */ + PJSIP_DIALOG_EVENT_MID_CALL_REQUEST, + + /** Other events (low level events). */ + PJSIP_DIALOG_EVENT_OTHER, + +} pjsip_dlg_event_e; + + +/** + * \brief Structure registered by applications to receive dialog notifications + * from the User Agent. + * + * Applications registers this structure to get notifications from the User Agent + * about dialog state changes and other events. Application can set any of + * the callback function to NULL if it doesn't want to handle the notification, + * however, setting some callbacks to NULL probably will cause some undesired + * result (such as setting \b on_incoming to NULL will cause the creation of + * a lot of dialogs with no owner). + */ +struct pjsip_dlg_callback +{ + /** + * This is a low level, uninterpreted callback that is called by framework + * for all kinds of events, such as transaction events, dialog events, etc. + * @param dlg The dialog. + * @param dlg_event The type of dialog event. + * @param event The event descriptor. + */ + void (*on_all_events)(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event, + pjsip_event *event ); + + /** + * This is a low level callback that is called by the framework when the + * underlying transaction is about to send outgoing message. This callback + * is provided to allow application to modify the message before it is + * transmitted. + * @param dlg The dialog. + * @param tsx The transaction that transmits the message. + * @param tdata The transmission data, which contains the message. + * @param retransmission The number of times this message has been sent. + * Zero indicates the message is about to be sent the first time, + * one indicates this is the first retransmission, etc. + */ + void (*on_before_tx)(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata, pj_bool_t retransmission); + + /** + * This is a low level callback that is called by the framework when the dialog + * has sent a message. Note that a receive of retransmission will not trigger + * this callback since retransmission is handled internally by transaction. + * @param dlg The dialog. + * @param tsx The transaction that transmits the message. + * @param tdata The transmission data, which contains the message. + */ + void (*on_tx_msg)(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata); + + /** + * This is a low level callback that is called by the framework when the + * dialog has received a message. Note that a receipt of retransmission + * will not trigger this callback since retransmission is handled internally + * by transaction. + * @param dlg The dialog. + * @param tsx The transaction that receives the message. + * @param rdata The receive data, which contains the message. + */ + void (*on_rx_msg)(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_rx_data *rdata); + + /** + * This callback is called by the framework when the user agent + * instance receives an incoming INVITE message. + * @param dlg The new dialog that's just created to handle the incoming call. + * @param tsx The INVITE transaction that's just created. + * @param rdata The receive data, which contains the INVITE message. + */ + void (*on_incoming)(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_rx_data *rdata); + + /** + * This callback is called by the framework when the dialog is sending + * the first outgoing INVITE message. + * @param dlg The dialog. + * @param tsx The INVITE transaction. + * @param tdata The transmit data, which contains the INVITE message. + */ + void (*on_calling)(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata); + + /** + * This callback is called by the framework when the initial INVITE + * transaction has sent/received provisional response. + * @param dlg The dialog. + * @param tsx The transaction. + * @param event The event, which src_type will always be either + * PJSIP_EVENT_RX_MSG or PJSIP_EVENT_TX_MSG. The provisional + * response message itself will be in either \b rdata or \b tdata. + * @see pjsip_event. + */ + void (*on_provisional)(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_event *event); + + /** + * This callback is called for both UAS and UAC dialog when 200 response + * to INVITE is sent or received. + * @param dlg The dialog. + * @param event The event, which src_type can only be either + * PJSIP_EVENT_TX_MSG or PJSIP_EVENT_RX_MSG. + * @see pjsip_event + */ + void (*on_connecting)(pjsip_dlg *dlg, pjsip_event *event); + + /** + * This callback is called for both UAS and UAC when an ACK request is + * sent or received by the dialog. + * @param dlg The dialog. + * @param event The event, which src_type can only be either + * PJSIP_EVENT_TX_MSG or PJSIP_EVENT_RX_MSG. + * @see pjsip_event + */ + void (*on_established)(pjsip_dlg *dlg, pjsip_event *event); + + /** + * This callback is called when the dialog is disconnected, i.e. upon + * sending/receiving non-200 response to INVITE, sending/receiving + * CANCEL to initial INVITE, and sending/receiving BYE. + * + * @param dlg The dialog. + * @param event The event. + * @see pjsip_event + */ + void (*on_disconnected)(pjsip_dlg *dlg, pjsip_event *event); + + /** + * This callback is called when the dialog is about to be destroyed. + * @param dlg The dialog. + */ + void (*on_terminated)(pjsip_dlg *dlg); + + /** + * This callback will be called when the dialog receives mid call events + * such as re-invitation or incoming pager. + * + * @param dlg The dialog. + * @param event The event. + */ + void (*on_mid_call_events)(pjsip_dlg *dlg, pjsip_event *event); + +}; /* struct pjsip_dlg_callback */ + + + +/** + * Dialog state. + */ +typedef enum pjsip_dlg_state_e +{ + /** + * State NULL is after the dialog is instantiated but before any + * initialization is done. + */ + PJSIP_DIALOG_STATE_NULL, + + /** + * State INCOMING is after the (callee) dialog has been initialized with + * the incoming request, but before any responses is sent by the dialog. + */ + PJSIP_DIALOG_STATE_INCOMING, + + /** + * State CALLING is after the (caller) dialog has sent outgoing invitation + * but before any responses are received. + */ + PJSIP_DIALOG_STATE_CALLING, + + /** + * State PROCEEDING is after the dialog sent/received provisional + * responses, but before final response is sent/received. + */ + PJSIP_DIALOG_STATE_PROCEEDING, + + /** + * State CONNECTING is after the dialog has sent/received final response + * to the invitation, but before acknowledgement is sent. + */ + PJSIP_DIALOG_STATE_CONNECTING, + + /** + * State ESTABLISHED occurs after the invitation has been accepted and + * acknowledged. + */ + PJSIP_DIALOG_STATE_ESTABLISHED, + + /** + * State DISCONNECTED occurs after either party successfully disconnect + * the session. + */ + PJSIP_DIALOG_STATE_DISCONNECTED, + + /** + * State TERMINATE occurs when the dialog is ready to be destroyed. + */ + PJSIP_DIALOG_STATE_TERMINATED, + +} pjsip_dlg_state_e; + + +/** + * Get the dialog string state. + * + * @param state Dialog state. + * @return The string describing the state. + */ +const char *pjsip_dlg_state_str(pjsip_dlg_state_e state); + +/** + * This structure is used to describe dialog's participants, which in this + * case is local party (i.e. us) and remote party. + */ +typedef struct pjsip_dlg_party +{ + pjsip_uri *target; /**< Target URL. */ + pjsip_fromto_hdr *info; /**< URL in From/To header. */ + pj_str_t tag; /**< Tag. */ + pjsip_contact_hdr *contact; /**< URL in Contact. */ + pj_sockaddr_in addr; /**< The current transport address. */ + int cseq; /**< Sequence number counter. */ +} pjsip_dlg_party; + + +/** + * This structure describes the dialog structure. + */ +struct pjsip_dlg +{ + PJ_DECL_LIST_MEMBER(struct pjsip_dlg) + + char obj_name[PJ_MAX_OBJ_NAME]; /**< Log identification. */ + + pjsip_user_agent *ua; /**< User agent instance. */ + pj_pool_t *pool; /**< Dialog's pool. */ + pjsip_dlg_state_e state; /**< Dialog's call state. */ + pjsip_role_e role; /**< Dialog's role. */ + pj_mutex_t *mutex; /**< Dialog's mutex. */ + + pjsip_dlg_party local; /**< Local party info. */ + pjsip_dlg_party remote; /**< Remote party info. */ + + pjsip_cid_hdr *call_id; /**< Call-ID */ + pj_bool_t secure; /**< Use secure transport? */ + + pjsip_route_hdr route_set; /**< Dialog's route set. */ + pjsip_transaction *invite_tsx; /**< Current INVITE transaction. */ + int pending_tsx_count; /**< Total pending tsx count. */ + + int cred_count; /**< Number of credentials. */ + pjsip_cred_info *cred_info; /**< Array of credentials. */ + + pjsip_auth_session auth_sess; /**< List of auth session. */ + + pjsip_msg_body *body; + + void *user_data; /**< Application's data. */ + + int (*handle_tsx_event)(struct pjsip_dlg *, /**< Internal state handler.*/ + pjsip_transaction *, + pjsip_event *); +}; + + +/** + * Initialize dialog with local and remote info. This function is normally + * called after application creates the dialog with #pjsip_ua_create_dialog + * for UAC dialogs. + * + * This function will initialize local and remote info from the URL, generate + * a globally unique Call-ID, initialize CSeq, and initialize other dialog's + * internal attributes. + * + * @param dlg The dialog to initialize. + * @param local_info URI/name address to be used as local info + * (From and Contact headers). + * @param remote_info URI/name address to be used as remote info (To header). + * @param target URI for initial remote's target, or NULL to set the + * initial target the same as remote_info. + * + * @return zero on success. + */ +PJ_DECL(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg, + const pj_str_t *local_info, + const pj_str_t *remote_info, + const pj_str_t *target); + + +/** + * Set authentication credentials to be used by this dialog. + * + * If authentication credentials are set for the dialog, the dialog will try to + * perform authentication automatically using the credentials supplied, and + * also cache the last Authorization or Proxy-Authorization headers for next + * requests. + * + * If none of the credentials are suitable or accepted by remote, then + * the dialog will just pass the authorization failure response back to + * application. + * + * @param dlg The dialog. + * @param count Number of credentials in the array. + * @param cred Array of credentials. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_dlg_set_credentials( pjsip_dlg *dlg, + int count, + const pjsip_cred_info cred[]); + +/** + * Override local contact details. + * + * Call this function to change the contact details to be advertised in Contact + * header. Application normally need to call this function for incoming calls + * before answering the call with 200/OK, because for an incoming dialogs, the + * initial local contact info are generated from the To header, which is + * normally not the appropriate one. + * + * @param dlg The dialog. + * @param contact The contact to use. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg, + const pj_str_t *contact ); + + +/** + * Set initial route set to be used by the dialog. This initial route set + * governs where and how the initial INVITE request will be routed. This + * initial route set will be overwritten with the route set found in the + * 2xx response of INVITE. + * + * Application only needs to call this function if it wants to have custom + * route for individual dialogs. If only a single route for all dialogs is + * needed, then application can set the global route by calling function + * #pjsip_endpt_set_proxies(). + * + * @param dlg The dialog. + * @param route_set The route set list. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg, + const pjsip_route_hdr *route_set ); + + +/** + * Variation of #pjsip_dlg_set_route_set where the headers will be used + * as it is (i.e. without cloned). + * + * @param dlg The dialog. + * @param route_set The route set list. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg, + pjsip_route_hdr *route_set); + +/** + * Create initial outgoing INVITE message. + * + * This function is just a simple wrapper to #pjsip_dlg_create_request(), + * so it follows the same rule there. In addition, this function also adds + * \b Allow header to the outgoing request. + * + * After the message is successfully created, application must call + * #pjsip_dlg_send_msg() to actually send the message and update the dialog's + * state. Note that upon return the reference counter of the transmit data + * will be set to one. + * + * @param dlg The dialog. + * + * @return The dialog transmit data, or NULL. + */ +PJ_DECL(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg ); + + +/** + * Answer incoming dialog invitation, with either provisional responses + * or a final response. Application can only call this function when there's + * a pending invitation to be answered. + * + * After the message is successfully created, application must call + * #pjsip_dlg_send_msg() to actually send the message and update the dialog's + * state. Note that upon return the reference counter of the transmit data + * will be set to one. + * + * @param dlg The dialog. + * @param code The response code, which can be: + * - 100-199 Provisional response (application can issue multiple + * provisional responses). + * - 200-299 To answer the invitation (normally status code 200 + * is sent). + * - 300-699 To reject the invitation. + * @return Transmit data if successfull. + */ +PJ_DECL(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code ); + + +/** + * High level function to create message to disconnect dialog. Depending + * on dialog's state, this function will either create CANCEL, final response, + * or BYE message. A status code must be supplied, which will be set if dialog + * will be transmitting a final response to INVITE. + * + * After the message is successfully created, application must call + * #pjsip_dlg_send_msg to actually send the message and update the dialog's + * state. Note that upon return the reference counter of the transmit data + * will be set to one. + * + * @param dlg The dialog. + * @param status_code The status code for disconnection. + * @return Transmit data if successfull. + */ +PJ_DECL(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg, int status_code); + +/** + * Create CANCEL message to cancel pending outgoing dialog invitation. + * Normally application should call #pjsip_dlg_disconnect() instead, because + * that function will create the correct message regardless of the state of + * the dialog. + * + * Application can call this function at anytime after it issues outgoing + * invitation and before receiving final response. However, there's no + * guarantee that the invitation will be successfully cancelled, since the + * CANCEL request and the final response can pass over in the wire. So the + * application must prepare to have the dialog connected even after the + * dialog is cancelled. + * + * The final state of the dialog will be reported in the dialog callback. + * If the CANCEL request succeeded, then the dialog will be disconnected with + * status code \a PJSIP_SC_REQUEST_TERMINATED. + * + * After the message is successfully created, application must call + * #pjsip_dlg_send_msg() to actually send the message and update the dialog's + * state. + * + * Upon return of this function, the reference counter of the transmit data + * will be set to one. + * + * @param dlg The dialog. + * @return The dialog transmit data containing the CANCEL message, + * or NULL. + */ +PJ_DECL(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg ); + + +/** + * Create BYE message. Application shouldn't normally need to use this function, + * but rather it's preferable to use #pjsip_dlg_disconnect() instead because + * that function will work to disconnect the session no matter what the state + * is. + * + * After the message is successfully created, application must call + * #pjsip_dlg_send_msg() to actually send the message and update the dialog's + * state. Note that upon return the reference counter of the transmit data + * will be set to one. + * + * @param dlg The dialog. + * @return The BYE message or NULL. + */ +PJ_DECL(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg ); + +/** + * This function is called by application to create new outgoing request + * message for this dialog. After the request is created, application can + * modify the message (such adding headers), and eventually send the request + * by calling #pjsip_dlg_send_msg(). + * + * This function will initialize the request message with dialog's properties + * as follows: + * - the request line is initialized with the method and the target is + * initialized from current remote target. + * - \b From, \b To, \b Contact, and \b Call-Id headers will be added. + * - An initial \b CSeq header will be provided (although the value will be + * verified again when the message is actually sent with #pjsip_dlg_send_msg(). + * - \b Route headers will be added from dialog's route set. + * - Authentication headers (\b Authorization or \b Proxy-Authorization) will + * be added from dialog's authorization cache. + * + * Note that upon return the reference counter of the transmit data + * will be set to one. When the message is sent, #pjsip_dlg_send_msg() will + * decrement the reference counter, and when the reference counter reach zero, + * the message will be deleted. + * + * @param dlg The dialog. + * @param method The request method. + * @param cseq Specify CSeq, or -1 to let the dialog specify CSeq. + * + * @return Transmit data for the new request. + * + * @see pjsip_dlg_send_msg() + */ +PJ_DECL(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg, + const pjsip_method *method, + int cseq); + + +/** + * This function can be called by application to send outgoing message (request + * or response) to remote party. Note that after calling this function, the + * transmit data will be deleted regardless of the return status. To prevent + * deletion, application must increase the reference count, but then it will + * be responsible to delete this transmit data itself (by decreasing the + * reference count). + * + * @param dlg The dialog. + * @param tdata The transmit data, which contains the request message. + * @return zero on success. + */ +PJ_DECL(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg, + pjsip_tx_data *tdata ); + + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_DIALOG_H__ */ + diff --git a/pjsip/include/pjsip-ua/sip_regc.h b/pjsip/include/pjsip-ua/sip_regc.h index b9752dc1..5a4e2ebe 100644 --- a/pjsip/include/pjsip-ua/sip_regc.h +++ b/pjsip/include/pjsip-ua/sip_regc.h @@ -1,210 +1,210 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_REG_H__
-#define __PJSIP_SIP_REG_H__
-
-/**
- * @file sip_reg.h
- * @brief SIP Registration Client
- */
-
-#include <pjsip/sip_types.h>
-#include <pjsip/sip_auth.h>
-#include <pjsip_mod_ua/sip_ua.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSUA_REGC SIP Registration Client
- * @ingroup PJSUA
- * @{
- * \brief
- * API for performing registration for user agent.
- */
-
-/** Typedef for client registration data. */
-typedef struct pjsip_regc pjsip_regc;
-
-/** Maximum contacts in registration. */
-#define PJSIP_REGC_MAX_CONTACT 10
-
-/** Expiration not specified. */
-#define PJSIP_REGC_EXPIRATION_NOT_SPECIFIED ((pj_uint32_t)0xFFFFFFFFUL)
-
-/** Buffer to hold all contacts. */
-#define PJSIP_REGC_CONTACT_BUF_SIZE 512
-
-/** Structure to hold parameters when calling application's callback.
- * The application's callback is called when the client registration process
- * has finished.
- */
-struct pjsip_regc_cbparam
-{
- pjsip_regc *regc;
- void *token;
- int code;
- pj_str_t reason;
- pjsip_rx_data *rdata;
- int contact_cnt;
- int expiration;
- pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
-};
-
-
-/** Type declaration for callback to receive registration result. */
-typedef void pjsip_regc_cb(struct pjsip_regc_cbparam *param);
-
-
-/**
- * Get the module instance for client registration module.
- *
- * @return client registration module.
- */
-PJ_DECL(pjsip_module*) pjsip_regc_get_module(void);
-
-
-/**
- * Create client registration structure.
- *
- * @param endpt Endpoint, used to allocate pool from.
- * @param token A data to be associated with the client registration struct.
- * @param cb Pointer to callback function to receive registration status.
- *
- * @return client registration structure.
- */
-PJ_DECL(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
- pjsip_regc_cb *cb);
-
-
-/**
- * Destroy client registration structure. If a registration transaction is
- * in progress, then the structure will be deleted only after a final response
- * has been received, and in this case, the callback won't be called.
- *
- * @param regc The client registration structure.
- */
-PJ_DECL(void) pjsip_regc_destroy(pjsip_regc *regc);
-
-/**
- * Get the memory pool associated with a registration client handle.
- *
- * @param regc The client registration structure.
- * @return pool handle.
- */
-PJ_DECL(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc);
-
-/**
- * Initialize client registration structure with various information needed to
- * perform the registration.
- *
- * @param regc The client registration structure.
- * @param from_url The person performing the registration, must be a SIP URL type.
- * @param to_url The address of record for which the registration is targetd, must
- * be a SIP/SIPS URL.
- * @param ccnt Number of contacts in the array.
- * @param contact Array of contacts.
- * @param expires Default expiration interval (in seconds) to be applied for
- * contact URL that doesn't have expiration settings. If the
- * value PJSIP_REGC_EXPIRATION_NOT_SPECIFIED is given, then
- * no default expiration will be applied.
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_regc_init(pjsip_regc *regc,
- const pj_str_t *srv_url,
- const pj_str_t *from_url,
- const pj_str_t *to_url,
- int ccnt,
- const pj_str_t contact[],
- pj_uint32_t expires);
-
-
-/**
- * Set authentication credentials to use by this registration.
- *
- * @param dlg The registration structure.
- * @param count Number of credentials in the array.
- * @param cred Array of credentials.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
- int count,
- const pjsip_cred_info cred[] );
-
-/**
- * Create REGISTER request for the specified client registration structure.
- *
- * After successfull registration, application can inspect the contacts in
- * the client registration structure to list what contacts are associaciated
- * with the address of record being targeted in the registration.
- *
- * @param regc The client registration structure.
- * @param autoreg If non zero, the library will automatically refresh the
- * next registration until application unregister.
- *
- * @return SIP REGISTER request.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg);
-
-
-/**
- * Create REGISTER request to unregister all contacts from server records.
- *
- * @param regc The client registration structure.
- *
- * @return SIP REGISTER request.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc);
-
-/**
- * Update Contact details in the client registration structure.
- *
- * @param regc The client registration structure.
- * @param ccnt Number of contacts.
- * @param contact Array of contacts.
- * @return zero if sucessfull.
- */
-PJ_DECL(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
- int ccnt,
- const pj_str_t contact[] );
-
-/**
- * Update the expires value.
- *
- * @param regc The client registration structure.
- * @param expires The new expires value.
- * @return zero on successfull.
- */
-PJ_DECL(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
- pj_uint32_t expires );
-
-/**
- * Sends outgoing REGISTER request.
- * The process will complete asynchronously, and application
- * will be notified via the callback when the process completes.
- *
- * @param regc The client registration structure.
- * @param tdata Transmit data.
- */
-PJ_DECL(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata);
-
-
-PJ_END_DECL
-
-#endif /* __PJSIP_REG_H__ */
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIP_REG_H__ +#define __PJSIP_SIP_REG_H__ + +/** + * @file sip_reg.h + * @brief SIP Registration Client + */ + +#include <pjsip/sip_types.h> +#include <pjsip/sip_auth.h> +#include <pjsip_mod_ua/sip_ua.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSUA_REGC SIP Registration Client + * @ingroup PJSUA + * @{ + * \brief + * API for performing registration for user agent. + */ + +/** Typedef for client registration data. */ +typedef struct pjsip_regc pjsip_regc; + +/** Maximum contacts in registration. */ +#define PJSIP_REGC_MAX_CONTACT 10 + +/** Expiration not specified. */ +#define PJSIP_REGC_EXPIRATION_NOT_SPECIFIED ((pj_uint32_t)0xFFFFFFFFUL) + +/** Buffer to hold all contacts. */ +#define PJSIP_REGC_CONTACT_BUF_SIZE 512 + +/** Structure to hold parameters when calling application's callback. + * The application's callback is called when the client registration process + * has finished. + */ +struct pjsip_regc_cbparam +{ + pjsip_regc *regc; + void *token; + int code; + pj_str_t reason; + pjsip_rx_data *rdata; + int contact_cnt; + int expiration; + pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT]; +}; + + +/** Type declaration for callback to receive registration result. */ +typedef void pjsip_regc_cb(struct pjsip_regc_cbparam *param); + + +/** + * Get the module instance for client registration module. + * + * @return client registration module. + */ +PJ_DECL(pjsip_module*) pjsip_regc_get_module(void); + + +/** + * Create client registration structure. + * + * @param endpt Endpoint, used to allocate pool from. + * @param token A data to be associated with the client registration struct. + * @param cb Pointer to callback function to receive registration status. + * + * @return client registration structure. + */ +PJ_DECL(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token, + pjsip_regc_cb *cb); + + +/** + * Destroy client registration structure. If a registration transaction is + * in progress, then the structure will be deleted only after a final response + * has been received, and in this case, the callback won't be called. + * + * @param regc The client registration structure. + */ +PJ_DECL(void) pjsip_regc_destroy(pjsip_regc *regc); + +/** + * Get the memory pool associated with a registration client handle. + * + * @param regc The client registration structure. + * @return pool handle. + */ +PJ_DECL(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc); + +/** + * Initialize client registration structure with various information needed to + * perform the registration. + * + * @param regc The client registration structure. + * @param from_url The person performing the registration, must be a SIP URL type. + * @param to_url The address of record for which the registration is targetd, must + * be a SIP/SIPS URL. + * @param ccnt Number of contacts in the array. + * @param contact Array of contacts. + * @param expires Default expiration interval (in seconds) to be applied for + * contact URL that doesn't have expiration settings. If the + * value PJSIP_REGC_EXPIRATION_NOT_SPECIFIED is given, then + * no default expiration will be applied. + * @return zero on success. + */ +PJ_DECL(pj_status_t) pjsip_regc_init(pjsip_regc *regc, + const pj_str_t *srv_url, + const pj_str_t *from_url, + const pj_str_t *to_url, + int ccnt, + const pj_str_t contact[], + pj_uint32_t expires); + + +/** + * Set authentication credentials to use by this registration. + * + * @param dlg The registration structure. + * @param count Number of credentials in the array. + * @param cred Array of credentials. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc, + int count, + const pjsip_cred_info cred[] ); + +/** + * Create REGISTER request for the specified client registration structure. + * + * After successfull registration, application can inspect the contacts in + * the client registration structure to list what contacts are associaciated + * with the address of record being targeted in the registration. + * + * @param regc The client registration structure. + * @param autoreg If non zero, the library will automatically refresh the + * next registration until application unregister. + * + * @return SIP REGISTER request. + */ +PJ_DECL(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg); + + +/** + * Create REGISTER request to unregister all contacts from server records. + * + * @param regc The client registration structure. + * + * @return SIP REGISTER request. + */ +PJ_DECL(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc); + +/** + * Update Contact details in the client registration structure. + * + * @param regc The client registration structure. + * @param ccnt Number of contacts. + * @param contact Array of contacts. + * @return zero if sucessfull. + */ +PJ_DECL(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc, + int ccnt, + const pj_str_t contact[] ); + +/** + * Update the expires value. + * + * @param regc The client registration structure. + * @param expires The new expires value. + * @return zero on successfull. + */ +PJ_DECL(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc, + pj_uint32_t expires ); + +/** + * Sends outgoing REGISTER request. + * The process will complete asynchronously, and application + * will be notified via the callback when the process completes. + * + * @param regc The client registration structure. + * @param tdata Transmit data. + */ +PJ_DECL(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata); + + +PJ_END_DECL + +#endif /* __PJSIP_REG_H__ */ diff --git a/pjsip/include/pjsip-ua/sip_ua.h b/pjsip/include/pjsip-ua/sip_ua.h index d2cc605a..92bf0db1 100644 --- a/pjsip/include/pjsip-ua/sip_ua.h +++ b/pjsip/include/pjsip-ua/sip_ua.h @@ -1,97 +1,97 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_UA_H__
-#define __PJSIP_SIP_UA_H__
-
-/**
- * @file ua.h
- * @brief SIP User Agent Library
- */
-
-#include <pjsip_mod_ua/sip_dialog.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSUA SIP User Agent Stack
- */
-
-/**
- * @defgroup PJSUA_UA SIP User Agent
- * @ingroup PJSUA
- * @{
- * \brief
- * User Agent manages the interactions between application and SIP dialogs.
- */
-
-typedef struct pjsip_dlg_callback pjsip_dlg_callback;
-
-/**
- * \brief This structure describes a User Agent instance.
- */
-struct pjsip_user_agent
-{
- pjsip_endpoint *endpt;
- pj_pool_t *pool;
- pj_mutex_t *mutex;
- pj_uint32_t mod_id;
- pj_hash_table_t *dlg_table;
- pjsip_dlg_callback *dlg_cb;
- pj_list dlg_list;
-};
-
-/**
- * Create a new dialog.
- */
-PJ_DECL(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua,
- pjsip_role_e role );
-
-
-/**
- * Destroy dialog.
- */
-PJ_DECL(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg );
-
-
-/**
- * Register callback to receive dialog notifications.
- */
-PJ_DECL(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua,
- pjsip_dlg_callback *cb );
-
-
-/**
- * Get the module interface for the UA module.
- */
-PJ_DECL(pjsip_module*) pjsip_ua_get_module(void);
-
-
-/**
- * Dump user agent state to log file.
- */
-PJ_DECL(void) pjsip_ua_dump( pjsip_user_agent *ua );
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_UA_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIP_UA_H__ +#define __PJSIP_SIP_UA_H__ + +/** + * @file ua.h + * @brief SIP User Agent Library + */ + +#include <pjsip_mod_ua/sip_dialog.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSUA SIP User Agent Stack + */ + +/** + * @defgroup PJSUA_UA SIP User Agent + * @ingroup PJSUA + * @{ + * \brief + * User Agent manages the interactions between application and SIP dialogs. + */ + +typedef struct pjsip_dlg_callback pjsip_dlg_callback; + +/** + * \brief This structure describes a User Agent instance. + */ +struct pjsip_user_agent +{ + pjsip_endpoint *endpt; + pj_pool_t *pool; + pj_mutex_t *mutex; + pj_uint32_t mod_id; + pj_hash_table_t *dlg_table; + pjsip_dlg_callback *dlg_cb; + pj_list dlg_list; +}; + +/** + * Create a new dialog. + */ +PJ_DECL(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua, + pjsip_role_e role ); + + +/** + * Destroy dialog. + */ +PJ_DECL(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg ); + + +/** + * Register callback to receive dialog notifications. + */ +PJ_DECL(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua, + pjsip_dlg_callback *cb ); + + +/** + * Get the module interface for the UA module. + */ +PJ_DECL(pjsip_module*) pjsip_ua_get_module(void); + + +/** + * Dump user agent state to log file. + */ +PJ_DECL(void) pjsip_ua_dump( pjsip_user_agent *ua ); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_UA_H__ */ + diff --git a/pjsip/include/pjsip/print_util.h b/pjsip/include/pjsip/print_util.h index 56f05851..b1362a72 100644 --- a/pjsip/include/pjsip/print_util.h +++ b/pjsip/include/pjsip/print_util.h @@ -1,144 +1,144 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_PRINT_H__
-#define __PJSIP_PRINT_H__
-
-/* Minimum space left in the buffer */
-#define MIN_SPACE 10
-
-#define copy_advance_check(buf,str) \
- do { \
- if ((str).slen+MIN_SPACE >= (endbuf-buf)) return -1; \
- pj_memcpy(buf, (str).ptr, (str).slen); \
- buf += (str).slen; \
- } while (0)
-
-/*
-static char *imp_copy_advance_pair(char *buf, char *endbuf, const char *str1, int len1, const pj_str_t *str2)
-{
- if (str2->slen) {
- int printed = len1+str2->slen;
- if (printed+MIN_SPACE >= (endbuf-buf)) return NULL;
- pj_memcpy(buf,str1,len1);
- pj_memcpy(buf+len1, str2->ptr, str2->slen);
- return buf + printed;
- } else
- return buf;
-}
-*/
-
-#define copy_advance_pair_check(buf,str1,len1,str2) \
- do { \
- if (str2.slen) { \
- printed = len1+str2.slen; \
- if (printed+MIN_SPACE >= (endbuf-buf)) return -1; \
- pj_memcpy(buf,str1,len1); \
- pj_memcpy(buf+len1, str2.ptr, str2.slen); \
- buf += printed; \
- } \
- } while (0)
-/*
-#define copy_advance_pair(buf,str1,len1,str2) \
- do { \
- buf = imp_copy_advance_pair(buf, endbuf, str1, len1, &str2); \
- if (buf == NULL) return -1; \
- } while (0)
-*/
-
-#define copy_advance_pair_quote_check(buf,str1,len1,str2,quotebegin,quoteend) \
- do { \
- if (str2.slen) { \
- printed = len1+str2.slen+2; \
- if (printed+MIN_SPACE >= (endbuf-buf)) return -1; \
- pj_memcpy(buf,str1,len1); \
- *(buf+len1)=quotebegin; \
- pj_memcpy(buf+len1+1, str2.ptr, str2.slen); \
- *(buf+printed-1) = quoteend; \
- buf += printed; \
- } \
- } while (0)
-
-#define copy_advance_pair_escape(buf,str1,len1,str2,unres) \
- do { \
- if (str2.slen) { \
- pj_ssize_t esc_len; \
- if (len1+str2.slen+MIN_SPACE >= (endbuf-buf)) return -1; \
- pj_memcpy(buf,str1,len1); \
- buf += len1; \
- esc_len=pj_strncpy2_escape(buf, &str2, (endbuf-buf), &unres); \
- if (esc_len < 0) return -1; \
- buf += esc_len; \
- if (endbuf-buf < MIN_SPACE) return -1; \
- } \
- } while (0)
-
-
-#define copy_advance_no_check(buf,str) \
- do { \
- pj_memcpy(buf, (str).ptr, (str).slen); \
- buf += (str).slen; \
- } while (0)
-
-#define copy_advance_escape(buf,str,unres) \
- do { \
- pj_ssize_t len = \
- pj_strncpy2_escape(buf, &(str), (endbuf-buf), &(unres)); \
- if (len < 0) return -1; \
- buf += len; \
- if (endbuf-buf < MIN_SPACE) return -1; \
- } while (0)
-
-#define copy_advance_pair_no_check(buf,str1,len1,str2) \
- if (str2.slen) { \
- pj_memcpy(buf,str1,len1); \
- pj_memcpy(buf+len1, str2.ptr, str2.slen); \
- buf += len1+str2.slen; \
- }
-
-#define copy_advance copy_advance_check
-#define copy_advance_pair copy_advance_pair_check
-#define copy_advance_pair_quote copy_advance_pair_quote_check
-
-#define copy_advance_pair_quote_cond(buf,str1,len1,str2,quotebegin,quoteend) \
- do { \
- if (str2.slen && *str2.ptr!=quotebegin) \
- copy_advance_pair_quote(buf,str1,len1,str2,quotebegin,quoteend); \
- else \
- copy_advance_pair(buf,str1,len1,str2); \
- } while (0)
-
-/*
- * Internal type declarations.
- */
-typedef void* (*pjsip_hdr_clone_fptr)(pj_pool_t *, const void*);
-typedef int (*pjsip_hdr_print_fptr)(void *hdr, char *buf, pj_size_t len);
-
-extern const pj_str_t pjsip_hdr_names[];
-
-PJ_INLINE(void) init_hdr(void *hptr, pjsip_hdr_e htype, void *vptr)
-{
- pjsip_hdr *hdr = hptr;
- hdr->type = htype;
- hdr->name = hdr->sname = pjsip_hdr_names[htype];
- hdr->vptr = vptr;
- pj_list_init(hdr);
-}
-
-#endif /* __PJSIP_PRINT_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_PRINT_H__ +#define __PJSIP_PRINT_H__ + +/* Minimum space left in the buffer */ +#define MIN_SPACE 10 + +#define copy_advance_check(buf,str) \ + do { \ + if ((str).slen+MIN_SPACE >= (endbuf-buf)) return -1; \ + pj_memcpy(buf, (str).ptr, (str).slen); \ + buf += (str).slen; \ + } while (0) + +/* +static char *imp_copy_advance_pair(char *buf, char *endbuf, const char *str1, int len1, const pj_str_t *str2) +{ + if (str2->slen) { + int printed = len1+str2->slen; + if (printed+MIN_SPACE >= (endbuf-buf)) return NULL; + pj_memcpy(buf,str1,len1); + pj_memcpy(buf+len1, str2->ptr, str2->slen); + return buf + printed; + } else + return buf; +} +*/ + +#define copy_advance_pair_check(buf,str1,len1,str2) \ + do { \ + if (str2.slen) { \ + printed = len1+str2.slen; \ + if (printed+MIN_SPACE >= (endbuf-buf)) return -1; \ + pj_memcpy(buf,str1,len1); \ + pj_memcpy(buf+len1, str2.ptr, str2.slen); \ + buf += printed; \ + } \ + } while (0) +/* +#define copy_advance_pair(buf,str1,len1,str2) \ + do { \ + buf = imp_copy_advance_pair(buf, endbuf, str1, len1, &str2); \ + if (buf == NULL) return -1; \ + } while (0) +*/ + +#define copy_advance_pair_quote_check(buf,str1,len1,str2,quotebegin,quoteend) \ + do { \ + if (str2.slen) { \ + printed = len1+str2.slen+2; \ + if (printed+MIN_SPACE >= (endbuf-buf)) return -1; \ + pj_memcpy(buf,str1,len1); \ + *(buf+len1)=quotebegin; \ + pj_memcpy(buf+len1+1, str2.ptr, str2.slen); \ + *(buf+printed-1) = quoteend; \ + buf += printed; \ + } \ + } while (0) + +#define copy_advance_pair_escape(buf,str1,len1,str2,unres) \ + do { \ + if (str2.slen) { \ + pj_ssize_t esc_len; \ + if (len1+str2.slen+MIN_SPACE >= (endbuf-buf)) return -1; \ + pj_memcpy(buf,str1,len1); \ + buf += len1; \ + esc_len=pj_strncpy2_escape(buf, &str2, (endbuf-buf), &unres); \ + if (esc_len < 0) return -1; \ + buf += esc_len; \ + if (endbuf-buf < MIN_SPACE) return -1; \ + } \ + } while (0) + + +#define copy_advance_no_check(buf,str) \ + do { \ + pj_memcpy(buf, (str).ptr, (str).slen); \ + buf += (str).slen; \ + } while (0) + +#define copy_advance_escape(buf,str,unres) \ + do { \ + pj_ssize_t len = \ + pj_strncpy2_escape(buf, &(str), (endbuf-buf), &(unres)); \ + if (len < 0) return -1; \ + buf += len; \ + if (endbuf-buf < MIN_SPACE) return -1; \ + } while (0) + +#define copy_advance_pair_no_check(buf,str1,len1,str2) \ + if (str2.slen) { \ + pj_memcpy(buf,str1,len1); \ + pj_memcpy(buf+len1, str2.ptr, str2.slen); \ + buf += len1+str2.slen; \ + } + +#define copy_advance copy_advance_check +#define copy_advance_pair copy_advance_pair_check +#define copy_advance_pair_quote copy_advance_pair_quote_check + +#define copy_advance_pair_quote_cond(buf,str1,len1,str2,quotebegin,quoteend) \ + do { \ + if (str2.slen && *str2.ptr!=quotebegin) \ + copy_advance_pair_quote(buf,str1,len1,str2,quotebegin,quoteend); \ + else \ + copy_advance_pair(buf,str1,len1,str2); \ + } while (0) + +/* + * Internal type declarations. + */ +typedef void* (*pjsip_hdr_clone_fptr)(pj_pool_t *, const void*); +typedef int (*pjsip_hdr_print_fptr)(void *hdr, char *buf, pj_size_t len); + +extern const pj_str_t pjsip_hdr_names[]; + +PJ_INLINE(void) init_hdr(void *hptr, pjsip_hdr_e htype, void *vptr) +{ + pjsip_hdr *hdr = hptr; + hdr->type = htype; + hdr->name = hdr->sname = pjsip_hdr_names[htype]; + hdr->vptr = vptr; + pj_list_init(hdr); +} + +#endif /* __PJSIP_PRINT_H__ */ + diff --git a/pjsip/include/pjsip/sip_auth.h b/pjsip/include/pjsip/sip_auth.h index 56a5fe26..7d8b9c62 100644 --- a/pjsip/include/pjsip/sip_auth.h +++ b/pjsip/include/pjsip/sip_auth.h @@ -1,247 +1,247 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_AUTH_SIP_AUTH_H__
-#define __PJSIP_AUTH_SIP_AUTH_H__
-
-/**
- * @file pjsip_auth.h
- * @brief SIP Authorization Module.
- */
-
-#include <pjsip/sip_config.h>
-#include <pjsip/sip_auth_msg.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_AUTH_API Authorization API's
- * @ingroup PJSIP_AUTH
- * @{
- */
-
- /** Type of data in the credential information. */
-typedef enum pjsip_cred_data_type
-{
- PJSIP_CRED_DATA_PLAIN_PASSWD, /**< Plain text password. */
- PJSIP_CRED_DATA_DIGEST, /**< Hashed digest. */
-} pjsip_cred_data_type;
-
-/** Authentication's quality of protection (qop) type. */
-typedef enum pjsip_auth_qop_type
-{
- PJSIP_AUTH_QOP_NONE, /**< No quality of protection. */
- PJSIP_AUTH_QOP_AUTH, /**< Authentication. */
- PJSIP_AUTH_QOP_AUTH_INT, /**< Authentication with integrity protection. */
- PJSIP_AUTH_QOP_UNKNOWN, /**< Unknown protection. */
-} pjsip_auth_qop_type;
-
-
-/**
- * This structure describes credential information.
- * A credential information is a static, persistent information that identifies
- * username and password required to authorize to a specific realm.
- */
-struct pjsip_cred_info
-{
- pj_str_t realm; /**< Realm. */
- pj_str_t scheme; /**< Scheme. */
- pj_str_t username; /**< User name. */
- int data_type; /**< Type of data. */
- pj_str_t data; /**< The data, which can be a plaintext
- password or a hashed digest. */
-};
-
-/**
- * This structure describes cached value of previously sent Authorization
- * or Proxy-Authorization header. The authentication framework keeps a list
- * of this structure and will resend the same header to the same server
- * as long as the method, uri, and nonce stays the same.
- */
-typedef struct pjsip_cached_auth_hdr
-{
- PJ_DECL_LIST_MEMBER(struct pjsip_cached_auth_hdr);
-
- pjsip_method method;
- pjsip_authorization_hdr *hdr;
-
-} pjsip_cached_auth_hdr;
-
-
-/**
- * This structure describes authentication information for the specified
- * realm. Each instance of this structure describes authentication "session"
- * between this endpoint and remote server. This "session" information is
- * usefull to keep information that persists for more than one challenge,
- * such as nonce-count and cnonce value.
- *
- * Other than that, this structure also keeps the last authorization headers
- * that have been sent in the cache list.
- */
-typedef struct pjsip_auth_session
-{
- PJ_DECL_LIST_MEMBER(struct pjsip_auth_session);
-
- pj_str_t realm;
- pj_bool_t is_proxy;
- pjsip_auth_qop_type qop_value;
-#if PJSIP_AUTH_QOP_SUPPORT
- pj_uint32_t nc;
- pj_str_t cnonce;
-#endif
-#if PJSIP_AUTH_AUTO_SEND_NEXT
- pjsip_www_authenticate_hdr *last_chal;
-#endif
-#if PJSIP_AUTH_HEADER_CACHING
- pjsip_cached_auth_hdr cached_hdr;
-#endif
-
-} pjsip_auth_session;
-
-
-/**
- * Create authorization header for the specified credential.
- * Application calls this function to create Authorization or Proxy-Authorization
- * header after receiving WWW-Authenticate or Proxy-Authenticate challenge
- * (normally in 401/407 response).
- * If authorization session argument is specified, this function will update
- * the session with the updated information if required (e.g. to update
- * nonce-count when qop is "auth" or "auth-int"). This function will also
- * save the authorization header in the session's cached header list.
- *
- * @param req_pool Pool to allocate new header for the request.
- * @param hdr The WWW-Authenticate or Proxy-Authenticate found in
- * the response.
- * @param uri The URI for which authorization is targeted to.
- * @param cred_info The credential to be used for authentication.
- * @param method The method.
- * @param sess_pool Session pool to update session or to allocate message
- * in the cache. May be NULL if auth_sess is NULL.
- * @param auth_sess If not NULL, this specifies the specific authentication
- * session to be used or updated.
- *
- * @return The Authorization header, which can be typecasted to
- * Proxy-Authorization.
- */
-PJ_DECL(pjsip_authorization_hdr*) pjsip_auth_respond(
- pj_pool_t *req_pool,
- const pjsip_www_authenticate_hdr *hdr,
- const pjsip_uri *uri,
- const pjsip_cred_info *cred_info,
- const pjsip_method *method,
- pj_pool_t *sess_pool,
- pjsip_auth_session *auth_sess);
-
-/**
- * Verify digest in the authorization request.
- *
- * @param hdr The incoming Authorization/Proxy-Authorization header.
- * @param method The method.
- * @param password The plaintext password to verify.
- *
- * @return Non-zero if authorization succeed.
- */
-PJ_DECL(pj_bool_t) pjsip_auth_verify( const pjsip_authorization_hdr *hdr,
- const pj_str_t *method,
- const pjsip_cred_info *cred_info );
-
-
-/**
- * This function can be used to find credential information which matches
- * the specified realm.
- *
- * @param count Number of credentials in the parameter.
- * @param cred The array of credentials.
- * @param realm Realm to search.
- * @param scheme Authentication scheme.
- *
- * @return The credential which matches the specified realm.
- */
-PJ_DECL(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count,
- const pjsip_cred_info cred[],
- const pj_str_t *realm,
- const pj_str_t *scheme );
-
-
-/**
- * Initialize new request message with authorization headers.
- * This function will put Authorization/Proxy-Authorization headers to the
- * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING)
- * and the session has previously sent Authorization/Proxy-Authorization header
- * with the same method, then the same Authorization/Proxy-Authorization header
- * will be resent from the cache only if qop is not present. If the stack is
- * configured to automatically generate next Authorization/Proxy-Authorization
- * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy-
- * Authorization headers are calculated and generated when they are not present
- * in the case or if authorization session has qop.
- *
- * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag
- * are not set, this function will do nothing. The stack then will only send
- * Authorization/Proxy-Authorization to respond 401/407 response.
- *
- * @param sess_pool Session level pool, where memory will be allocated from
- * for data that persists across requests (e.g. caching).
- * @param tdata The request message to be initialized.
- * @param sess_list List of authorization sessions that have been recorded.
- * @param cred_count Number of credentials.
- * @param cred_info Array of credentials.
- *
- * @return Zero if successfull.
- */
-PJ_DECL(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool,
- pjsip_tx_data *tdata,
- pjsip_auth_session *sess_list,
- int cred_count,
- const pjsip_cred_info cred_info[]);
-
-/**
- * Call this function when a transaction failed with 401 or 407 response.
- * This function will reinitialize the original request message with the
- * authentication challenge found in the response message, and add the
- * new authorization header in the authorization cache.
- *
- * Note that upon return the reference counter of the transmit data
- * will be incremented.
- *
- * @param endpt Endpoint.
- * @param pool The pool to allocate memory for new cred_info.
- * @param cached_list Cached authorization headers.
- * @param cred_count Number of credentials.
- * @param cred_info Array of credentials to use.
- * @param tdata The original request message, which normally can be
- * retrieved from tsx->last_tx.
- * @param rdata The response message containing 401/407 status.
- *
- * @return New transmit data buffer, or NULL if the dialog
- * can not respond to the authorization challenge.
- */
-PJ_DECL(pjsip_tx_data*)
-pjsip_auth_reinit_req( pjsip_endpoint *endpt,
- pj_pool_t *ses_pool,
- pjsip_auth_session *sess_list,
- int cred_count, const pjsip_cred_info cred_info[],
- pjsip_tx_data *tdata, const pjsip_rx_data *rdata);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_AUTH_SIP_AUTH_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_AUTH_SIP_AUTH_H__ +#define __PJSIP_AUTH_SIP_AUTH_H__ + +/** + * @file pjsip_auth.h + * @brief SIP Authorization Module. + */ + +#include <pjsip/sip_config.h> +#include <pjsip/sip_auth_msg.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_AUTH_API Authorization API's + * @ingroup PJSIP_AUTH + * @{ + */ + + /** Type of data in the credential information. */ +typedef enum pjsip_cred_data_type +{ + PJSIP_CRED_DATA_PLAIN_PASSWD, /**< Plain text password. */ + PJSIP_CRED_DATA_DIGEST, /**< Hashed digest. */ +} pjsip_cred_data_type; + +/** Authentication's quality of protection (qop) type. */ +typedef enum pjsip_auth_qop_type +{ + PJSIP_AUTH_QOP_NONE, /**< No quality of protection. */ + PJSIP_AUTH_QOP_AUTH, /**< Authentication. */ + PJSIP_AUTH_QOP_AUTH_INT, /**< Authentication with integrity protection. */ + PJSIP_AUTH_QOP_UNKNOWN, /**< Unknown protection. */ +} pjsip_auth_qop_type; + + +/** + * This structure describes credential information. + * A credential information is a static, persistent information that identifies + * username and password required to authorize to a specific realm. + */ +struct pjsip_cred_info +{ + pj_str_t realm; /**< Realm. */ + pj_str_t scheme; /**< Scheme. */ + pj_str_t username; /**< User name. */ + int data_type; /**< Type of data. */ + pj_str_t data; /**< The data, which can be a plaintext + password or a hashed digest. */ +}; + +/** + * This structure describes cached value of previously sent Authorization + * or Proxy-Authorization header. The authentication framework keeps a list + * of this structure and will resend the same header to the same server + * as long as the method, uri, and nonce stays the same. + */ +typedef struct pjsip_cached_auth_hdr +{ + PJ_DECL_LIST_MEMBER(struct pjsip_cached_auth_hdr); + + pjsip_method method; + pjsip_authorization_hdr *hdr; + +} pjsip_cached_auth_hdr; + + +/** + * This structure describes authentication information for the specified + * realm. Each instance of this structure describes authentication "session" + * between this endpoint and remote server. This "session" information is + * usefull to keep information that persists for more than one challenge, + * such as nonce-count and cnonce value. + * + * Other than that, this structure also keeps the last authorization headers + * that have been sent in the cache list. + */ +typedef struct pjsip_auth_session +{ + PJ_DECL_LIST_MEMBER(struct pjsip_auth_session); + + pj_str_t realm; + pj_bool_t is_proxy; + pjsip_auth_qop_type qop_value; +#if PJSIP_AUTH_QOP_SUPPORT + pj_uint32_t nc; + pj_str_t cnonce; +#endif +#if PJSIP_AUTH_AUTO_SEND_NEXT + pjsip_www_authenticate_hdr *last_chal; +#endif +#if PJSIP_AUTH_HEADER_CACHING + pjsip_cached_auth_hdr cached_hdr; +#endif + +} pjsip_auth_session; + + +/** + * Create authorization header for the specified credential. + * Application calls this function to create Authorization or Proxy-Authorization + * header after receiving WWW-Authenticate or Proxy-Authenticate challenge + * (normally in 401/407 response). + * If authorization session argument is specified, this function will update + * the session with the updated information if required (e.g. to update + * nonce-count when qop is "auth" or "auth-int"). This function will also + * save the authorization header in the session's cached header list. + * + * @param req_pool Pool to allocate new header for the request. + * @param hdr The WWW-Authenticate or Proxy-Authenticate found in + * the response. + * @param uri The URI for which authorization is targeted to. + * @param cred_info The credential to be used for authentication. + * @param method The method. + * @param sess_pool Session pool to update session or to allocate message + * in the cache. May be NULL if auth_sess is NULL. + * @param auth_sess If not NULL, this specifies the specific authentication + * session to be used or updated. + * + * @return The Authorization header, which can be typecasted to + * Proxy-Authorization. + */ +PJ_DECL(pjsip_authorization_hdr*) pjsip_auth_respond( + pj_pool_t *req_pool, + const pjsip_www_authenticate_hdr *hdr, + const pjsip_uri *uri, + const pjsip_cred_info *cred_info, + const pjsip_method *method, + pj_pool_t *sess_pool, + pjsip_auth_session *auth_sess); + +/** + * Verify digest in the authorization request. + * + * @param hdr The incoming Authorization/Proxy-Authorization header. + * @param method The method. + * @param password The plaintext password to verify. + * + * @return Non-zero if authorization succeed. + */ +PJ_DECL(pj_bool_t) pjsip_auth_verify( const pjsip_authorization_hdr *hdr, + const pj_str_t *method, + const pjsip_cred_info *cred_info ); + + +/** + * This function can be used to find credential information which matches + * the specified realm. + * + * @param count Number of credentials in the parameter. + * @param cred The array of credentials. + * @param realm Realm to search. + * @param scheme Authentication scheme. + * + * @return The credential which matches the specified realm. + */ +PJ_DECL(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count, + const pjsip_cred_info cred[], + const pj_str_t *realm, + const pj_str_t *scheme ); + + +/** + * Initialize new request message with authorization headers. + * This function will put Authorization/Proxy-Authorization headers to the + * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING) + * and the session has previously sent Authorization/Proxy-Authorization header + * with the same method, then the same Authorization/Proxy-Authorization header + * will be resent from the cache only if qop is not present. If the stack is + * configured to automatically generate next Authorization/Proxy-Authorization + * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy- + * Authorization headers are calculated and generated when they are not present + * in the case or if authorization session has qop. + * + * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag + * are not set, this function will do nothing. The stack then will only send + * Authorization/Proxy-Authorization to respond 401/407 response. + * + * @param sess_pool Session level pool, where memory will be allocated from + * for data that persists across requests (e.g. caching). + * @param tdata The request message to be initialized. + * @param sess_list List of authorization sessions that have been recorded. + * @param cred_count Number of credentials. + * @param cred_info Array of credentials. + * + * @return Zero if successfull. + */ +PJ_DECL(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool, + pjsip_tx_data *tdata, + pjsip_auth_session *sess_list, + int cred_count, + const pjsip_cred_info cred_info[]); + +/** + * Call this function when a transaction failed with 401 or 407 response. + * This function will reinitialize the original request message with the + * authentication challenge found in the response message, and add the + * new authorization header in the authorization cache. + * + * Note that upon return the reference counter of the transmit data + * will be incremented. + * + * @param endpt Endpoint. + * @param pool The pool to allocate memory for new cred_info. + * @param cached_list Cached authorization headers. + * @param cred_count Number of credentials. + * @param cred_info Array of credentials to use. + * @param tdata The original request message, which normally can be + * retrieved from tsx->last_tx. + * @param rdata The response message containing 401/407 status. + * + * @return New transmit data buffer, or NULL if the dialog + * can not respond to the authorization challenge. + */ +PJ_DECL(pjsip_tx_data*) +pjsip_auth_reinit_req( pjsip_endpoint *endpt, + pj_pool_t *ses_pool, + pjsip_auth_session *sess_list, + int cred_count, const pjsip_cred_info cred_info[], + pjsip_tx_data *tdata, const pjsip_rx_data *rdata); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_AUTH_SIP_AUTH_H__ */ + diff --git a/pjsip/include/pjsip/sip_auth_msg.h b/pjsip/include/pjsip/sip_auth_msg.h index 5ef74d23..81d6cd09 100644 --- a/pjsip/include/pjsip/sip_auth_msg.h +++ b/pjsip/include/pjsip/sip_auth_msg.h @@ -1,209 +1,213 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_AUTH_SIP_AUTH_MSG_H__
-#define __PJSIP_AUTH_SIP_AUTH_MSG_H__
-
-#include <pjsip/sip_msg.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_MSG_AUTHORIZATION Header Field: Authorization and Proxy-Authorization
- * @brief Authorization and Proxy-Authorization header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * Common credential.
- */
-struct pjsip_common_credential
-{
- pj_str_t realm;
-};
-
-typedef struct pjsip_common_credential pjsip_common_credential;
-
-
-/**
- * This structure describe credential used in Authorization and
- * Proxy-Authorization header for digest authentication scheme.
- */
-struct pjsip_digest_credential
-{
- pj_str_t realm;
- pj_str_t username;
- pj_str_t nonce;
- pj_str_t uri;
- pj_str_t response;
- pj_str_t algorithm;
- pj_str_t cnonce;
- pj_str_t opaque;
- pj_str_t qop;
- pj_str_t nc;
- pj_str_t other_param;
-};
-
-typedef struct pjsip_digest_credential pjsip_digest_credential;
-
-/**
- * This structure describe credential used in Authorization and
- * Proxy-Authorization header for PGP authentication scheme.
- */
-struct pjsip_pgp_credential
-{
- pj_str_t realm;
- pj_str_t version;
- pj_str_t signature;
- pj_str_t signed_by;
- pj_str_t nonce;
-};
-
-typedef struct pjsip_pgp_credential pjsip_pgp_credential;
-
-/**
- * This structure describes SIP Authorization header (and also SIP
- * Proxy-Authorization header).
- */
-struct pjsip_authorization_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_authorization_hdr);
- pj_str_t scheme;
- union
- {
- pjsip_common_credential common;
- pjsip_digest_credential digest;
- pjsip_pgp_credential pgp;
- } credential;
-};
-
-typedef struct pjsip_authorization_hdr pjsip_authorization_hdr;
-
-/** SIP Proxy-Authorization header shares the same structure as SIP
- Authorization header.
- */
-typedef struct pjsip_authorization_hdr pjsip_proxy_authorization_hdr;
-
-/**
- * Create SIP Authorization header.
- * @param pool Pool where memory will be allocated from.
- * @return SIP Authorization header.
- */
-PJ_DECL(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool);
-
-/**
- * Create SIP Proxy-Authorization header.
- * @param pool Pool where memory will be allocated from.
- * @return SIP Proxy-Authorization header.
- */
-PJ_DECL(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-/**
- * @defgroup PJSIP_WWW_AUTH Header Field: Proxy-Authenticate and WWW-Authenticate
- * @brief Proxy-Authenticate and WWW-Authenticate.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-struct pjsip_common_challenge
-{
- pj_str_t realm;
-};
-
-typedef struct pjsip_common_challenge pjsip_common_challenge;
-
-/**
- * This structure describes authentication challenge used in Proxy-Authenticate
- * or WWW-Authenticate for digest authentication scheme.
- */
-struct pjsip_digest_challenge
-{
- pj_str_t realm;
- pj_str_t domain;
- pj_str_t nonce;
- pj_str_t opaque;
- int stale;
- pj_str_t algorithm;
- pj_str_t qop;
- pj_str_t other_param;
-};
-
-typedef struct pjsip_digest_challenge pjsip_digest_challenge;
-
-/**
- * This structure describes authentication challenge used in Proxy-Authenticate
- * or WWW-Authenticate for PGP authentication scheme.
- */
-struct pjsip_pgp_challenge
-{
- pj_str_t realm;
- pj_str_t version;
- pj_str_t micalgorithm;
- pj_str_t pubalgorithm;
- pj_str_t nonce;
-};
-
-typedef struct pjsip_pgp_challenge pjsip_pgp_challenge;
-
-/**
- * This structure describe SIP WWW-Authenticate header (Proxy-Authenticate
- * header also uses the same structure).
- */
-struct pjsip_www_authenticate_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_www_authenticate_hdr);
- pj_str_t scheme;
- union
- {
- pjsip_common_challenge common;
- pjsip_digest_challenge digest;
- pjsip_pgp_challenge pgp;
- } challenge;
-};
-
-typedef struct pjsip_www_authenticate_hdr pjsip_www_authenticate_hdr;
-typedef struct pjsip_www_authenticate_hdr pjsip_proxy_authenticate_hdr;
-
-
-/**
- * Create SIP WWW-Authenticate header.
- * @param pool Pool where memory will be allocated from.
- * @return SIP WWW-Authenticate header.
- */
-PJ_DECL(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool);
-
-/**
- * Create SIP Proxy-Authenticate header.
- * @param pool Pool where memory will be allocated from.
- * @return SIP Proxy-Authenticate header.
- */
-PJ_DECL(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_AUTH_SIP_AUTH_MSG_H__ */
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_AUTH_SIP_AUTH_MSG_H__ +#define __PJSIP_AUTH_SIP_AUTH_MSG_H__ + +#include <pjsip/sip_msg.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_MSG_AUTHORIZATION Header Field: Authorization and Proxy-Authorization + * @brief Authorization and Proxy-Authorization header field. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * Common credential. + */ +struct pjsip_common_credential +{ + pj_str_t realm; + pjsip_param other_param; +}; + +typedef struct pjsip_common_credential pjsip_common_credential; + + +/** + * This structure describe credential used in Authorization and + * Proxy-Authorization header for digest authentication scheme. + */ +struct pjsip_digest_credential +{ + pj_str_t realm; + pjsip_param other_param; + pj_str_t username; + pj_str_t nonce; + pj_str_t uri; + pj_str_t response; + pj_str_t algorithm; + pj_str_t cnonce; + pj_str_t opaque; + pj_str_t qop; + pj_str_t nc; +}; + +typedef struct pjsip_digest_credential pjsip_digest_credential; + +/** + * This structure describe credential used in Authorization and + * Proxy-Authorization header for PGP authentication scheme. + */ +struct pjsip_pgp_credential +{ + pj_str_t realm; + pjsip_param other_param; + pj_str_t version; + pj_str_t signature; + pj_str_t signed_by; + pj_str_t nonce; +}; + +typedef struct pjsip_pgp_credential pjsip_pgp_credential; + +/** + * This structure describes SIP Authorization header (and also SIP + * Proxy-Authorization header). + */ +struct pjsip_authorization_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_authorization_hdr); + pj_str_t scheme; + union + { + pjsip_common_credential common; + pjsip_digest_credential digest; + pjsip_pgp_credential pgp; + } credential; +}; + +typedef struct pjsip_authorization_hdr pjsip_authorization_hdr; + +/** SIP Proxy-Authorization header shares the same structure as SIP + Authorization header. + */ +typedef struct pjsip_authorization_hdr pjsip_proxy_authorization_hdr; + +/** + * Create SIP Authorization header. + * @param pool Pool where memory will be allocated from. + * @return SIP Authorization header. + */ +PJ_DECL(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool); + +/** + * Create SIP Proxy-Authorization header. + * @param pool Pool where memory will be allocated from. + * @return SIP Proxy-Authorization header. + */ +PJ_DECL(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + +/** + * @defgroup PJSIP_WWW_AUTH Header Field: Proxy-Authenticate and WWW-Authenticate + * @brief Proxy-Authenticate and WWW-Authenticate. + * @ingroup PJSIP_MSG + * @{ + */ + +struct pjsip_common_challenge +{ + pj_str_t realm; + pjsip_param other_param; +}; + +typedef struct pjsip_common_challenge pjsip_common_challenge; + +/** + * This structure describes authentication challenge used in Proxy-Authenticate + * or WWW-Authenticate for digest authentication scheme. + */ +struct pjsip_digest_challenge +{ + pj_str_t realm; + pjsip_param other_param; + pj_str_t domain; + pj_str_t nonce; + pj_str_t opaque; + int stale; + pj_str_t algorithm; + pj_str_t qop; +}; + +typedef struct pjsip_digest_challenge pjsip_digest_challenge; + +/** + * This structure describes authentication challenge used in Proxy-Authenticate + * or WWW-Authenticate for PGP authentication scheme. + */ +struct pjsip_pgp_challenge +{ + pj_str_t realm; + pjsip_param other_param; + pj_str_t version; + pj_str_t micalgorithm; + pj_str_t pubalgorithm; + pj_str_t nonce; +}; + +typedef struct pjsip_pgp_challenge pjsip_pgp_challenge; + +/** + * This structure describe SIP WWW-Authenticate header (Proxy-Authenticate + * header also uses the same structure). + */ +struct pjsip_www_authenticate_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_www_authenticate_hdr); + pj_str_t scheme; + union + { + pjsip_common_challenge common; + pjsip_digest_challenge digest; + pjsip_pgp_challenge pgp; + } challenge; +}; + +typedef struct pjsip_www_authenticate_hdr pjsip_www_authenticate_hdr; +typedef struct pjsip_www_authenticate_hdr pjsip_proxy_authenticate_hdr; + + +/** + * Create SIP WWW-Authenticate header. + * @param pool Pool where memory will be allocated from. + * @return SIP WWW-Authenticate header. + */ +PJ_DECL(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool); + +/** + * Create SIP Proxy-Authenticate header. + * @param pool Pool where memory will be allocated from. + * @return SIP Proxy-Authenticate header. + */ +PJ_DECL(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_AUTH_SIP_AUTH_MSG_H__ */ diff --git a/pjsip/include/pjsip/sip_auth_parser.h b/pjsip/include/pjsip/sip_auth_parser.h index ee96c73e..3a214bef 100644 --- a/pjsip/include/pjsip/sip_auth_parser.h +++ b/pjsip/include/pjsip/sip_auth_parser.h @@ -1,87 +1,87 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_AUTH_SIP_AUTH_PARSER_H__
-#define __PJSIP_AUTH_SIP_AUTH_PARSER_H__
-
-/**
- * @file pjsip_auth_parser.h
- * @brief SIP Authorization Parser Module.
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_AUTH_PARSER_MODULE Authorization Parser Module
- * @ingroup PJSIP_AUTH
- * @{
- */
-
-/**
- * Initialize and register authorization parser module.
- * This will register parser handler for various Authorization related headers
- * such as Authorization, WWW-Authenticate, Proxy-Authorizization, and
- * Proxy-Authenticate headers.
- *
- * @return PJ_SUCCESS or the appropriate status code.
- */
-PJ_DECL(pj_status_t) pjsip_auth_init_parser(void);
-
-/**
- * DeInitialize authorization parser module.
- */
-PJ_DECL(void) pjsip_auth_deinit_parser();
-
-
-extern const pj_str_t pjsip_USERNAME_STR,
- pjsip_REALM_STR,
- pjsip_NONCE_STR,
- pjsip_URI_STR,
- pjsip_RESPONSE_STR,
- pjsip_ALGORITHM_STR,
- pjsip_DOMAIN_STR,
- pjsip_STALE_STR,
- pjsip_QOP_STR,
- pjsip_CNONCE_STR,
- pjsip_OPAQUE_STR,
- pjsip_NC_STR,
- pjsip_TRUE_STR,
- pjsip_FALSE_STR,
- pjsip_DIGEST_STR,
- pjsip_PGP_STR,
- pjsip_MD5_STR,
- pjsip_AUTH_STR;
-/*
-extern const pj_str_t pjsip_QUOTED_TRUE_STR,
- pjsip_QUOTED_FALSE_STR,
- pjsip_QUOTED_DIGEST_STR,
- pjsip_QUOTED_PGP_STR,
- pjsip_QUOTED_MD5_STR,
- pjsip_QUOTED_AUTH_STR;
-*/
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_AUTH_SIP_AUTH_PARSER_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_AUTH_SIP_AUTH_PARSER_H__ +#define __PJSIP_AUTH_SIP_AUTH_PARSER_H__ + +/** + * @file pjsip_auth_parser.h + * @brief SIP Authorization Parser Module. + */ + +#include <pj/types.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_AUTH_PARSER_MODULE Authorization Parser Module + * @ingroup PJSIP_AUTH + * @{ + */ + +/** + * Initialize and register authorization parser module. + * This will register parser handler for various Authorization related headers + * such as Authorization, WWW-Authenticate, Proxy-Authorizization, and + * Proxy-Authenticate headers. + * + * @return PJ_SUCCESS or the appropriate status code. + */ +PJ_DECL(pj_status_t) pjsip_auth_init_parser(void); + +/** + * DeInitialize authorization parser module. + */ +PJ_DECL(void) pjsip_auth_deinit_parser(); + + +extern const pj_str_t pjsip_USERNAME_STR, + pjsip_REALM_STR, + pjsip_NONCE_STR, + pjsip_URI_STR, + pjsip_RESPONSE_STR, + pjsip_ALGORITHM_STR, + pjsip_DOMAIN_STR, + pjsip_STALE_STR, + pjsip_QOP_STR, + pjsip_CNONCE_STR, + pjsip_OPAQUE_STR, + pjsip_NC_STR, + pjsip_TRUE_STR, + pjsip_FALSE_STR, + pjsip_DIGEST_STR, + pjsip_PGP_STR, + pjsip_MD5_STR, + pjsip_AUTH_STR; +/* +extern const pj_str_t pjsip_QUOTED_TRUE_STR, + pjsip_QUOTED_FALSE_STR, + pjsip_QUOTED_DIGEST_STR, + pjsip_QUOTED_PGP_STR, + pjsip_QUOTED_MD5_STR, + pjsip_QUOTED_AUTH_STR; +*/ + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_AUTH_SIP_AUTH_PARSER_H__ */ + diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h index 5620c33b..98441918 100644 --- a/pjsip/include/pjsip/sip_config.h +++ b/pjsip/include/pjsip/sip_config.h @@ -1,163 +1,163 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_CONFIG_H__
-#define __PJSIP_SIP_CONFIG_H__
-
-#include <pj/config.h>
-
-/* Endpoint. */
-#define PJSIP_MAX_TIMER_COUNT (2*PJSIP_MAX_TSX_COUNT + 2*PJSIP_MAX_DIALOG_COUNT)
-#define PJSIP_POOL_LEN_ENDPT (2048+64*PJSIP_MAX_TSX_COUNT)
-#define PJSIP_POOL_INC_ENDPT (1024)
-
-/* Transport related constants. */
-#define PJSIP_MAX_TRANSPORTS (PJ_IOQUEUE_MAX_HANDLES)
-#define PJSIP_MAX_PKT_LEN 1500
-#define PJSIP_POOL_LEN_RDATA 2500
-#define PJSIP_POOL_INC_RDATA 512
-#define PJSIP_POOL_LEN_TRANSPORT 512
-#define PJSIP_POOL_INC_TRANSPORT 512
-#define PJSIP_POOL_LEN_TDATA 2500
-#define PJSIP_POOL_INC_TDATA 512
-#define PJSIP_POOL_LEN_UA (64 + 32*PJSIP_MAX_DIALOG_COUNT)
-#define PJSIP_POOL_INC_UA 0
-#define PJSIP_TRANSPORT_CLOSE_TIMEOUT 30
-#define PJSIP_MAX_TRANSPORT_USAGE 16
-
-#define PJSIP_MAX_FORWARDS_VALUE 70
-
-#define PJSIP_RFC3261_BRANCH_ID "z9hG4bK"
-#define PJSIP_RFC3261_BRANCH_LEN 7
-
-/* Message/URL related constants. */
-#define PJSIP_MAX_CALL_ID_LEN PJ_GUID_STRING_LENGTH
-#define PJSIP_MAX_TAG_LEN PJ_GUID_STRING_LENGTH
-#define PJSIP_MAX_BRANCH_LEN (PJSIP_RFC3261_BRANCH_LEN + PJ_GUID_STRING_LENGTH)
-#define PJSIP_MAX_URL_SIZE 256
-#define PJSIP_MAX_HNAME_LEN 64
-
-/* Transction related constants. */
-#define PJSIP_MAX_TSX_COUNT (16*1024)
-#define PJSIP_POOL_LEN_TSX 1536 //768
-#define PJSIP_POOL_INC_TSX 256
-#define PJSIP_MAX_TSX_KEY_LEN (PJSIP_MAX_URL_SIZE*2)
-
-/* Dialog related constants. */
-#define PJSIP_MAX_DIALOG_COUNT (16*1024)
-#define PJSIP_POOL_LEN_DIALOG 1200
-#define PJSIP_POOL_INC_DIALOG 512
-
-/* Transport manager hash table size (must be 2^n-1). */
-#define PJSIP_TPMGR_HTABLE_SIZE 31
-
-/* Transport idle timeout before it's destroyed. */
-#define PJSIP_TRANSPORT_IDLE_TIME 30
-
-/* Max entries to process in timer heap per poll. */
-#define PJSIP_MAX_TIMED_OUT_ENTRIES 10
-
-/* Module related constants. */
-#define PJSIP_MAX_MODULE 8
-
-/*****************************************************************************
- * Default timeout settings, in miliseconds.
- */
-
-//#define PJSIP_T1_TIMEOUT 15000
-//#define PJSIP_T2_TIMEOUT 60000
-
-/* T1 timeout value. */
-#if !defined(PJSIP_T1_TIMEOUT)
-# define PJSIP_T1_TIMEOUT 500
-#endif
-
-/* T2 timeout value. */
-#if !defined(PJSIP_T2_TIMEOUT)
-# define PJSIP_T2_TIMEOUT 4000
-#endif
-
-/* Completed timer for non-INVITE */
-#if !defined(PJSIP_T4_TIMEOUT)
-# define PJSIP_T4_TIMEOUT 5000
-#endif
-
-/* Completed timer for INVITE */
-#if !defined(PJSIP_TD_TIMEOUT)
-# define PJSIP_TD_TIMEOUT 32000
-#endif
-
-
-/*****************************************************************************
- * Authorization
- */
-
-/*
- * If this flag is set, the stack will keep the Authorization/Proxy-Authorization
- * headers that are sent in a cache. Future requests with the same realm and
- * the same method will use the headers in the cache (as long as no qop is
- * required by server).
- *
- * Turning on this flag will make authorization process goes faster, but
- * will grow the memory usage undefinitely until the dialog/registration
- * session is terminated.
- *
- * Default: 1
- */
-#if !defined(PJSIP_AUTH_HEADER_CACHING)
-# define PJSIP_AUTH_HEADER_CACHING 1
-#endif
-
-/*
- * If this flag is set, the stack will proactively send Authorization/Proxy-
- * Authorization header for next requests. If next request has the same method
- * with any of previous requests, then the last header which is saved in
- * the cache will be used (if PJSIP_AUTH_CACHING is set). Otherwise a fresh
- * header will be recalculated. If a particular server has requested qop, then
- * a fresh header will always be calculated.
- *
- * If this flag is NOT set, then the stack will only send Authorization/Proxy-
- * Authorization headers when it receives 401/407 response from server.
- *
- * Turning ON this flag will grow memory usage of a dialog/registration pool
- * indefinitely until it is terminated, because the stack needs to keep the
- * last WWW-Authenticate/Proxy-Authenticate challenge.
- *
- * Default: 1
- */
-#if !defined(PJSIP_AUTH_AUTO_SEND_NEXT)
-# define PJSIP_AUTH_AUTO_SEND_NEXT 1
-#endif
-
-/*
- * Support qop="auth" directive.
- * This option also requires client to cache the last challenge offered by
- * server.
- *
- * Default: 1
- */
-#if !defined(PJSIP_AUTH_QOP_SUPPORT)
-# define PJSIP_AUTH_QOP_SUPPORT 1
-#endif
-
-
-#include <pj/config.h>
-
-
-#endif /* __PJSIP_SIP_CONFIG_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIP_CONFIG_H__ +#define __PJSIP_SIP_CONFIG_H__ + +#include <pj/config.h> + +/* Endpoint. */ +#define PJSIP_MAX_TIMER_COUNT (2*PJSIP_MAX_TSX_COUNT + 2*PJSIP_MAX_DIALOG_COUNT) +#define PJSIP_POOL_LEN_ENDPT (2048+64*PJSIP_MAX_TSX_COUNT) +#define PJSIP_POOL_INC_ENDPT (1024) + +/* Transport related constants. */ +#define PJSIP_MAX_TRANSPORTS (PJ_IOQUEUE_MAX_HANDLES) +#define PJSIP_MAX_PKT_LEN 1500 +#define PJSIP_POOL_LEN_RDATA 2500 +#define PJSIP_POOL_INC_RDATA 512 +#define PJSIP_POOL_LEN_TRANSPORT 512 +#define PJSIP_POOL_INC_TRANSPORT 512 +#define PJSIP_POOL_LEN_TDATA 2500 +#define PJSIP_POOL_INC_TDATA 512 +#define PJSIP_POOL_LEN_UA (64 + 32*PJSIP_MAX_DIALOG_COUNT) +#define PJSIP_POOL_INC_UA 0 +#define PJSIP_TRANSPORT_CLOSE_TIMEOUT 30 +#define PJSIP_MAX_TRANSPORT_USAGE 16 + +#define PJSIP_MAX_FORWARDS_VALUE 70 + +#define PJSIP_RFC3261_BRANCH_ID "z9hG4bK" +#define PJSIP_RFC3261_BRANCH_LEN 7 + +/* Message/URL related constants. */ +#define PJSIP_MAX_CALL_ID_LEN PJ_GUID_STRING_LENGTH +#define PJSIP_MAX_TAG_LEN PJ_GUID_STRING_LENGTH +#define PJSIP_MAX_BRANCH_LEN (PJSIP_RFC3261_BRANCH_LEN + PJ_GUID_STRING_LENGTH) +#define PJSIP_MAX_URL_SIZE 256 +#define PJSIP_MAX_HNAME_LEN 64 + +/* Transction related constants. */ +#define PJSIP_MAX_TSX_COUNT (16*1024) +#define PJSIP_POOL_LEN_TSX 1536 //768 +#define PJSIP_POOL_INC_TSX 256 +#define PJSIP_MAX_TSX_KEY_LEN (PJSIP_MAX_URL_SIZE*2) + +/* Dialog related constants. */ +#define PJSIP_MAX_DIALOG_COUNT (16*1024) +#define PJSIP_POOL_LEN_DIALOG 1200 +#define PJSIP_POOL_INC_DIALOG 512 + +/* Transport manager hash table size (must be 2^n-1). */ +#define PJSIP_TPMGR_HTABLE_SIZE 31 + +/* Transport idle timeout before it's destroyed. */ +#define PJSIP_TRANSPORT_IDLE_TIME 30 + +/* Max entries to process in timer heap per poll. */ +#define PJSIP_MAX_TIMED_OUT_ENTRIES 10 + +/* Module related constants. */ +#define PJSIP_MAX_MODULE 8 + +/***************************************************************************** + * Default timeout settings, in miliseconds. + */ + +//#define PJSIP_T1_TIMEOUT 15000 +//#define PJSIP_T2_TIMEOUT 60000 + +/* T1 timeout value. */ +#if !defined(PJSIP_T1_TIMEOUT) +# define PJSIP_T1_TIMEOUT 500 +#endif + +/* T2 timeout value. */ +#if !defined(PJSIP_T2_TIMEOUT) +# define PJSIP_T2_TIMEOUT 4000 +#endif + +/* Completed timer for non-INVITE */ +#if !defined(PJSIP_T4_TIMEOUT) +# define PJSIP_T4_TIMEOUT 5000 +#endif + +/* Completed timer for INVITE */ +#if !defined(PJSIP_TD_TIMEOUT) +# define PJSIP_TD_TIMEOUT 32000 +#endif + + +/***************************************************************************** + * Authorization + */ + +/* + * If this flag is set, the stack will keep the Authorization/Proxy-Authorization + * headers that are sent in a cache. Future requests with the same realm and + * the same method will use the headers in the cache (as long as no qop is + * required by server). + * + * Turning on this flag will make authorization process goes faster, but + * will grow the memory usage undefinitely until the dialog/registration + * session is terminated. + * + * Default: 1 + */ +#if !defined(PJSIP_AUTH_HEADER_CACHING) +# define PJSIP_AUTH_HEADER_CACHING 1 +#endif + +/* + * If this flag is set, the stack will proactively send Authorization/Proxy- + * Authorization header for next requests. If next request has the same method + * with any of previous requests, then the last header which is saved in + * the cache will be used (if PJSIP_AUTH_CACHING is set). Otherwise a fresh + * header will be recalculated. If a particular server has requested qop, then + * a fresh header will always be calculated. + * + * If this flag is NOT set, then the stack will only send Authorization/Proxy- + * Authorization headers when it receives 401/407 response from server. + * + * Turning ON this flag will grow memory usage of a dialog/registration pool + * indefinitely until it is terminated, because the stack needs to keep the + * last WWW-Authenticate/Proxy-Authenticate challenge. + * + * Default: 1 + */ +#if !defined(PJSIP_AUTH_AUTO_SEND_NEXT) +# define PJSIP_AUTH_AUTO_SEND_NEXT 1 +#endif + +/* + * Support qop="auth" directive. + * This option also requires client to cache the last challenge offered by + * server. + * + * Default: 1 + */ +#if !defined(PJSIP_AUTH_QOP_SUPPORT) +# define PJSIP_AUTH_QOP_SUPPORT 1 +#endif + + +#include <pj/config.h> + + +#endif /* __PJSIP_SIP_CONFIG_H__ */ + diff --git a/pjsip/include/pjsip/sip_endpoint.h b/pjsip/include/pjsip/sip_endpoint.h index 37f4acee..3e0702f1 100644 --- a/pjsip/include/pjsip/sip_endpoint.h +++ b/pjsip/include/pjsip/sip_endpoint.h @@ -1,386 +1,386 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_ENDPOINT_H__
-#define __PJSIP_SIP_ENDPOINT_H__
-
-/**
- * @file sip_endpoint.h
- * @brief SIP Endpoint.
- */
-
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_resolve.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP SIP Stack Core
- * Implementation of core SIP protocol stack processing.
- */
-
-/**
- * @defgroup PJSIP_ENDPT SIP Endpoint
- * @ingroup PJSIP
- * @brief
- * Representation of SIP node instance.
- *
- * SIP Endpoint instance (pjsip_endpoint) can be viewed as the master/owner of
- * all SIP objects in an application. It performs the following roles:
- * - it manages the allocation/deallocation of memory pools for all objects.
- * - it manages listeners and transports, and how they are used by
- * transactions.
- * - it owns transaction hash table.
- * - it receives incoming messages from transport layer and automatically
- * dispatches them to the correct transaction (or create a new one).
- * - it has a single instance of timer management (timer heap).
- * - it manages modules, which is the primary means of extending the library.
- * - it provides single polling function for all objects and distributes
- * events.
- * - it provides SIP policy such as which outbound proxy to use for all
- * outgoing SIP request messages.
- * - it automatically handles incoming requests which can not be handled by
- * existing modules (such as when incoming request has unsupported method).
- * - and so on..
- *
- * Theoritically application can have multiple instances of SIP endpoint,
- * although it's not clear why application may want to do it.
- *
- * @{
- */
-
-/**
- * Create an instance of SIP endpoint from the specified pool factory.
- * The pool factory reference then will be kept by the endpoint, so that
- * future memory allocations by SIP components will be taken from the same
- * pool factory.
- *
- * @param pf Pool factory that will be used for the lifetime of
- * endpoint.
- * @param name Optional name to be specified for the endpoint.
- * If this parameter is NULL, then the name will use
- * local host name.
- * @param endpt Pointer to receive endpoint instance.
- *
- * @return PJ_SUCCESS on success.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf,
- const char *name,
- pjsip_endpoint **endpt);
-
-/**
- * Destroy endpoint instance. Application must make sure that all pending
- * transactions have been terminated properly, because this function does not
- * check for the presence of pending transactions.
- *
- * @param endpt The SIP endpoint to be destroyed.
- */
-PJ_DECL(void) pjsip_endpt_destroy(pjsip_endpoint *endpt);
-
-/**
- * Get endpoint name.
- *
- * @param endpt The SIP endpoint instance.
- *
- * @return Endpoint name, as was registered during endpoint
- * creation. The string is NULL terminated.
- */
-PJ_DECL(const pj_str_t*) pjsip_endpt_name(const pjsip_endpoint *endpt);
-
-/**
- * Poll for events. Application must call this function periodically to ensure
- * that all events from both transports and timer heap are handled in timely
- * manner. This function, like all other endpoint functions, is thread safe,
- * and application may have more than one thread concurrently calling this function.
- *
- * @param endpt The endpoint.
- * @param max_timeout Maximum time to wait for events, or NULL to wait forever
- * until event is received.
- */
-PJ_DECL(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt,
- const pj_time_val *max_timeout);
-
-/**
- * Dump endpoint status to the log. This will print the status to the log
- * with log level 3.
- *
- * @param endpt The endpoint.
- * @param detail If non zero, then it will dump a detailed output.
- * BEWARE that this option may crash the system because
- * it tries to access all memory pools.
- */
-PJ_DECL(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail );
-
-/**
- * Create pool from the endpoint. All SIP components should allocate their
- * memory pool by calling this function, to make sure that the pools are
- * allocated from the same pool factory. This function, like all other endpoint
- * functions, is thread safe.
- *
- * @param endpt The SIP endpoint.
- * @param pool_name Name to be assigned to the pool.
- * @param initial The initial size of the pool.
- * @param increment The resize size.
- * @return Memory pool, or NULL on failure.
- *
- * @see pj_pool_create
- */
-PJ_DECL(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt,
- const char *pool_name,
- pj_size_t initial,
- pj_size_t increment );
-
-/**
- * Return back pool to endpoint to be released back to the pool factory.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt The endpoint.
- * @param pool The pool to be destroyed.
- */
-PJ_DECL(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt,
- pj_pool_t *pool );
-
-/**
- * Schedule timer to endpoint's timer heap. Application must poll the endpoint
- * periodically (by calling #pjsip_endpt_handle_events) to ensure that the
- * timer events are handled in timely manner. When the timeout for the timer
- * has elapsed, the callback specified in the entry argument will be called.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt The endpoint.
- * @param entry The timer entry.
- * @param delay The relative delay of the timer.
- * @return PJ_OK (zero) if successfull.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt,
- pj_timer_entry *entry,
- const pj_time_val *delay );
-
-/**
- * Cancel the previously registered timer.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt The endpoint.
- * @param entry The timer entry previously registered.
- */
-PJ_DECL(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt,
- pj_timer_entry *entry );
-
-/**
- * Create a new transaction. After creating the transaction, application MUST
- * initialize the transaction as either UAC or UAS (by calling
- * #pjsip_tsx_init_uac or #pjsip_tsx_init_uas), then must register the
- * transaction to endpoint with #pjsip_endpt_register_tsx.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt The SIP endpoint.
- * @param p_tsx Pointer to receive the transaction.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create_tsx(pjsip_endpoint *endpt,
- pjsip_transaction **p_tsx);
-
-/**
- * Register the transaction to the endpoint's transaction table.
- * Before the transaction is registered, it must have been initialized as
- * either UAS or UAC by calling #pjsip_tsx_init_uac or #pjsip_tsx_init_uas.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt The SIP endpoint.
- * @param tsx The transaction.
- */
-PJ_DECL(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt,
- pjsip_transaction *tsx);
-
-/**
- * Forcefull destroy the transaction.
- * The only time where application needs to call this function is when the
- * transaction fails to initialize in #pjsip_tsx_init_uac or
- * #pjsip_tsx_init_uas. For other cases. the transaction will be destroyed
- * automaticly by endpoint.
- *
- * @param endpt The endpoint.
- * @param tsx The transaction to destroy.
- */
-PJ_DECL(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt,
- pjsip_transaction *tsx);
-
-/**
- * Create a new transmit data buffer.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt The endpoint.
- * @param p_tdata Pointer to receive transmit data buffer.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create_tdata( pjsip_endpoint *endpt,
- pjsip_tx_data **p_tdata);
-
-/**
- * Asynchronously resolve a SIP target host or domain according to rule
- * specified in RFC 3263 (Locating SIP Servers). When the resolving operation
- * has completed, the callback will be called.
- *
- * Note: at the moment we don't have implementation of RFC 3263 yet!
- *
- * @param resolver The resolver engine.
- * @param pool The pool to allocate resolver job.
- * @param target The target specification to be resolved.
- * @param token A user defined token to be passed back to callback function.
- * @param cb The callback function.
- */
-PJ_DECL(void) pjsip_endpt_resolve( pjsip_endpoint *endpt,
- pj_pool_t *pool,
- pjsip_host_port *target,
- void *token,
- pjsip_resolver_callback *cb);
-
-/**
- * Get transport manager instance.
- *
- * @param endpt The endpoint.
- *
- * @return Transport manager instance.
- */
-PJ_DECL(pjsip_tpmgr*) pjsip_endpt_get_tpmgr(pjsip_endpoint *endpt);
-
-/**
- * Get ioqueue instance.
- *
- * @param endpt The endpoint.
- *
- * @return The ioqueue.
- */
-PJ_DECL(pj_ioqueue_t*) pjsip_endpt_get_ioqueue(pjsip_endpoint *endpt);
-
-/**
- * Find a SIP transport suitable for sending SIP message to the specified
- * address. This function will complete asynchronously when the transport is
- * ready (for example, when TCP socket is connected), and when it completes,
- * the callback will be called with the status of the operation.
- *
- * @see pjsip_transport_get
- */
-PJ_DECL(pj_status_t) pjsip_endpt_alloc_transport( pjsip_endpoint *endpt,
- pjsip_transport_type_e type,
- const pj_sockaddr_in *remote,
- pjsip_transport **p_transport);
-
-/**
- * Get additional headers to be put in outgoing request message.
- * This function is normally called by transaction layer when sending outgoing
- * requests.
- *
- * @param endpt The endpoint.
- *
- * @return List of additional headers to be put in outgoing requests.
- */
-PJ_DECL(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt);
-
-/**
- * Get "Allow" header from endpoint. The endpoint builds the "Allow" header
- * from the list of methods supported by modules.
- *
- * @param endpt The endpoint.
- *
- * @return "Allow" header, or NULL if endpoint doesn't have "Allow" header.
- */
-PJ_DECL(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt );
-
-
-/**
- * Find transaction in endpoint's transaction table by the transaction's key.
- * This function normally is only used by modules. The key for a transaction
- * can be created by calling #pjsip_tsx_create_key.
- *
- * @param endpt The endpoint instance.
- * @param key Transaction key, as created with #pjsip_tsx_create_key.
- *
- * @return The transaction, or NULL if it's not found.
- */
-PJ_DECL(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt,
- const pj_str_t *key );
-
-/**
- * Set list of SIP proxies to be visited for all outbound request messages.
- * Application can call this function to specify how outgoing request messages
- * should be routed. For example, if outgoing requests should go through an
- * outbound proxy, then application can specify the URL of the proxy when
- * calling this function. More than one proxy can be specified, and the
- * order of which proxy is specified when calling this function specifies
- * the order of which proxy will be visited first by the request messages.
- *
- * @param endpt The endpoint instance.
- * @param url_cnt Number of proxies/URLs in the array.
- * @param url Array of proxy URL, which specifies the order of which
- * proxy will be visited first (e.g. url[0] will be visited
- * before url[1]).
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt,
- int url_cnt, const pj_str_t url[]);
-
-/**
- * Get the list of "Route" header that are configured for this endpoint.
- * The "Route" header specifies how outbound request messages will be sent,
- * and is built when application sets the outbound proxy.
- *
- * @param endpt The endpoint instance.
- *
- * @return List of "Route" header.
- */
-PJ_DECL(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt );
-
-/**
- * Log an error.
- */
-PJ_DECL(void) pjsip_endpt_log_error( pjsip_endpoint *endpt,
- const char *sender,
- pj_status_t error_code,
- const char *format,
- ... );
-
-#define PJSIP_ENDPT_LOG_ERROR(expr) \
- pjsip_endpt_log_error expr
-
-#define PJSIP_ENDPT_TRACE(tracing,expr) \
- do { \
- if ((tracing)) \
- PJ_LOG(4,expr); \
- } while (0)
-
-/**
- * @}
- */
-
-/*
- * Internal functions.
- */
-/*
- * Receive transaction events from transactions and put in the event queue
- * to be processed later.
- */
-void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt );
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_ENDPOINT_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIP_ENDPOINT_H__ +#define __PJSIP_SIP_ENDPOINT_H__ + +/** + * @file sip_endpoint.h + * @brief SIP Endpoint. + */ + +#include <pjsip/sip_transport.h> +#include <pjsip/sip_resolve.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP SIP Stack Core + * Implementation of core SIP protocol stack processing. + */ + +/** + * @defgroup PJSIP_ENDPT SIP Endpoint + * @ingroup PJSIP + * @brief + * Representation of SIP node instance. + * + * SIP Endpoint instance (pjsip_endpoint) can be viewed as the master/owner of + * all SIP objects in an application. It performs the following roles: + * - it manages the allocation/deallocation of memory pools for all objects. + * - it manages listeners and transports, and how they are used by + * transactions. + * - it owns transaction hash table. + * - it receives incoming messages from transport layer and automatically + * dispatches them to the correct transaction (or create a new one). + * - it has a single instance of timer management (timer heap). + * - it manages modules, which is the primary means of extending the library. + * - it provides single polling function for all objects and distributes + * events. + * - it provides SIP policy such as which outbound proxy to use for all + * outgoing SIP request messages. + * - it automatically handles incoming requests which can not be handled by + * existing modules (such as when incoming request has unsupported method). + * - and so on.. + * + * Theoritically application can have multiple instances of SIP endpoint, + * although it's not clear why application may want to do it. + * + * @{ + */ + +/** + * Create an instance of SIP endpoint from the specified pool factory. + * The pool factory reference then will be kept by the endpoint, so that + * future memory allocations by SIP components will be taken from the same + * pool factory. + * + * @param pf Pool factory that will be used for the lifetime of + * endpoint. + * @param name Optional name to be specified for the endpoint. + * If this parameter is NULL, then the name will use + * local host name. + * @param endpt Pointer to receive endpoint instance. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf, + const char *name, + pjsip_endpoint **endpt); + +/** + * Destroy endpoint instance. Application must make sure that all pending + * transactions have been terminated properly, because this function does not + * check for the presence of pending transactions. + * + * @param endpt The SIP endpoint to be destroyed. + */ +PJ_DECL(void) pjsip_endpt_destroy(pjsip_endpoint *endpt); + +/** + * Get endpoint name. + * + * @param endpt The SIP endpoint instance. + * + * @return Endpoint name, as was registered during endpoint + * creation. The string is NULL terminated. + */ +PJ_DECL(const pj_str_t*) pjsip_endpt_name(const pjsip_endpoint *endpt); + +/** + * Poll for events. Application must call this function periodically to ensure + * that all events from both transports and timer heap are handled in timely + * manner. This function, like all other endpoint functions, is thread safe, + * and application may have more than one thread concurrently calling this function. + * + * @param endpt The endpoint. + * @param max_timeout Maximum time to wait for events, or NULL to wait forever + * until event is received. + */ +PJ_DECL(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt, + const pj_time_val *max_timeout); + +/** + * Dump endpoint status to the log. This will print the status to the log + * with log level 3. + * + * @param endpt The endpoint. + * @param detail If non zero, then it will dump a detailed output. + * BEWARE that this option may crash the system because + * it tries to access all memory pools. + */ +PJ_DECL(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail ); + +/** + * Create pool from the endpoint. All SIP components should allocate their + * memory pool by calling this function, to make sure that the pools are + * allocated from the same pool factory. This function, like all other endpoint + * functions, is thread safe. + * + * @param endpt The SIP endpoint. + * @param pool_name Name to be assigned to the pool. + * @param initial The initial size of the pool. + * @param increment The resize size. + * @return Memory pool, or NULL on failure. + * + * @see pj_pool_create + */ +PJ_DECL(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt, + const char *pool_name, + pj_size_t initial, + pj_size_t increment ); + +/** + * Return back pool to endpoint to be released back to the pool factory. + * This function, like all other endpoint functions, is thread safe. + * + * @param endpt The endpoint. + * @param pool The pool to be destroyed. + */ +PJ_DECL(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt, + pj_pool_t *pool ); + +/** + * Schedule timer to endpoint's timer heap. Application must poll the endpoint + * periodically (by calling #pjsip_endpt_handle_events) to ensure that the + * timer events are handled in timely manner. When the timeout for the timer + * has elapsed, the callback specified in the entry argument will be called. + * This function, like all other endpoint functions, is thread safe. + * + * @param endpt The endpoint. + * @param entry The timer entry. + * @param delay The relative delay of the timer. + * @return PJ_OK (zero) if successfull. + */ +PJ_DECL(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt, + pj_timer_entry *entry, + const pj_time_val *delay ); + +/** + * Cancel the previously registered timer. + * This function, like all other endpoint functions, is thread safe. + * + * @param endpt The endpoint. + * @param entry The timer entry previously registered. + */ +PJ_DECL(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, + pj_timer_entry *entry ); + +/** + * Create a new transaction. After creating the transaction, application MUST + * initialize the transaction as either UAC or UAS (by calling + * #pjsip_tsx_init_uac or #pjsip_tsx_init_uas), then must register the + * transaction to endpoint with #pjsip_endpt_register_tsx. + * This function, like all other endpoint functions, is thread safe. + * + * @param endpt The SIP endpoint. + * @param p_tsx Pointer to receive the transaction. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsip_endpt_create_tsx(pjsip_endpoint *endpt, + pjsip_transaction **p_tsx); + +/** + * Register the transaction to the endpoint's transaction table. + * Before the transaction is registered, it must have been initialized as + * either UAS or UAC by calling #pjsip_tsx_init_uac or #pjsip_tsx_init_uas. + * This function, like all other endpoint functions, is thread safe. + * + * @param endpt The SIP endpoint. + * @param tsx The transaction. + */ +PJ_DECL(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt, + pjsip_transaction *tsx); + +/** + * Forcefull destroy the transaction. + * The only time where application needs to call this function is when the + * transaction fails to initialize in #pjsip_tsx_init_uac or + * #pjsip_tsx_init_uas. For other cases. the transaction will be destroyed + * automaticly by endpoint. + * + * @param endpt The endpoint. + * @param tsx The transaction to destroy. + */ +PJ_DECL(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt, + pjsip_transaction *tsx); + +/** + * Create a new transmit data buffer. + * This function, like all other endpoint functions, is thread safe. + * + * @param endpt The endpoint. + * @param p_tdata Pointer to receive transmit data buffer. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsip_endpt_create_tdata( pjsip_endpoint *endpt, + pjsip_tx_data **p_tdata); + +/** + * Asynchronously resolve a SIP target host or domain according to rule + * specified in RFC 3263 (Locating SIP Servers). When the resolving operation + * has completed, the callback will be called. + * + * Note: at the moment we don't have implementation of RFC 3263 yet! + * + * @param resolver The resolver engine. + * @param pool The pool to allocate resolver job. + * @param target The target specification to be resolved. + * @param token A user defined token to be passed back to callback function. + * @param cb The callback function. + */ +PJ_DECL(void) pjsip_endpt_resolve( pjsip_endpoint *endpt, + pj_pool_t *pool, + pjsip_host_port *target, + void *token, + pjsip_resolver_callback *cb); + +/** + * Get transport manager instance. + * + * @param endpt The endpoint. + * + * @return Transport manager instance. + */ +PJ_DECL(pjsip_tpmgr*) pjsip_endpt_get_tpmgr(pjsip_endpoint *endpt); + +/** + * Get ioqueue instance. + * + * @param endpt The endpoint. + * + * @return The ioqueue. + */ +PJ_DECL(pj_ioqueue_t*) pjsip_endpt_get_ioqueue(pjsip_endpoint *endpt); + +/** + * Find a SIP transport suitable for sending SIP message to the specified + * address. This function will complete asynchronously when the transport is + * ready (for example, when TCP socket is connected), and when it completes, + * the callback will be called with the status of the operation. + * + * @see pjsip_transport_get + */ +PJ_DECL(pj_status_t) pjsip_endpt_alloc_transport( pjsip_endpoint *endpt, + pjsip_transport_type_e type, + const pj_sockaddr_in *remote, + pjsip_transport **p_transport); + +/** + * Get additional headers to be put in outgoing request message. + * This function is normally called by transaction layer when sending outgoing + * requests. + * + * @param endpt The endpoint. + * + * @return List of additional headers to be put in outgoing requests. + */ +PJ_DECL(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt); + +/** + * Get "Allow" header from endpoint. The endpoint builds the "Allow" header + * from the list of methods supported by modules. + * + * @param endpt The endpoint. + * + * @return "Allow" header, or NULL if endpoint doesn't have "Allow" header. + */ +PJ_DECL(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt ); + + +/** + * Find transaction in endpoint's transaction table by the transaction's key. + * This function normally is only used by modules. The key for a transaction + * can be created by calling #pjsip_tsx_create_key. + * + * @param endpt The endpoint instance. + * @param key Transaction key, as created with #pjsip_tsx_create_key. + * + * @return The transaction, or NULL if it's not found. + */ +PJ_DECL(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt, + const pj_str_t *key ); + +/** + * Set list of SIP proxies to be visited for all outbound request messages. + * Application can call this function to specify how outgoing request messages + * should be routed. For example, if outgoing requests should go through an + * outbound proxy, then application can specify the URL of the proxy when + * calling this function. More than one proxy can be specified, and the + * order of which proxy is specified when calling this function specifies + * the order of which proxy will be visited first by the request messages. + * + * @param endpt The endpoint instance. + * @param url_cnt Number of proxies/URLs in the array. + * @param url Array of proxy URL, which specifies the order of which + * proxy will be visited first (e.g. url[0] will be visited + * before url[1]). + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt, + int url_cnt, const pj_str_t url[]); + +/** + * Get the list of "Route" header that are configured for this endpoint. + * The "Route" header specifies how outbound request messages will be sent, + * and is built when application sets the outbound proxy. + * + * @param endpt The endpoint instance. + * + * @return List of "Route" header. + */ +PJ_DECL(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt ); + +/** + * Log an error. + */ +PJ_DECL(void) pjsip_endpt_log_error( pjsip_endpoint *endpt, + const char *sender, + pj_status_t error_code, + const char *format, + ... ); + +#define PJSIP_ENDPT_LOG_ERROR(expr) \ + pjsip_endpt_log_error expr + +#define PJSIP_ENDPT_TRACE(tracing,expr) \ + do { \ + if ((tracing)) \ + PJ_LOG(4,expr); \ + } while (0) + +/** + * @} + */ + +/* + * Internal functions. + */ +/* + * Receive transaction events from transactions and put in the event queue + * to be processed later. + */ +void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt ); + +PJ_END_DECL + +#endif /* __PJSIP_SIP_ENDPOINT_H__ */ + diff --git a/pjsip/include/pjsip/sip_errno.h b/pjsip/include/pjsip/sip_errno.h index 2c8e6764..eefda673 100644 --- a/pjsip/include/pjsip/sip_errno.h +++ b/pjsip/include/pjsip/sip_errno.h @@ -1,228 +1,228 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_ERRNO_H__
-#define __PJSIP_SIP_ERRNO_H__
-
-#include <pj/errno.h>
-
-PJ_BEGIN_DECL
-
-/*
- * PJSIP error codes occupies 170000 - 219000, and mapped as follows:
- * - 170100 - 170799: mapped to SIP status code in response msg.
- * - 171000 - 171999: mapped to errors generated from PJSIP core.
- */
-
-/**
- * Get error message for the specified error code.
- *
- * @param status The error code.
- * @param buffer The buffer where to put the error message.
- * @param bufsize Size of the buffer.
- *
- * @return The error message as NULL terminated string,
- * wrapped with pj_str_t.
- */
-PJ_DECL(pj_str_t) pjsip_strerror( pj_status_t status, char *buffer,
- pj_size_t bufsize);
-
-/**
- * Start of error code relative to PJ_ERRNO_START_USER.
- */
-#define PJSIP_ERRNO_START (PJ_ERRNO_START_USER)
-
-/**
- * Create error value from SIP status code.
- * @param code SIP status code.
- * @return Error code in pj_status_t namespace.
- */
-#define PJSIP_ERRNO_FROM_SIP_STATUS(code) (PJSIP_ERRNO_START+code)
-
-/**
- * Get SIP status code from error value.
- * If conversion to SIP status code is not available, a SIP status code
- * 599 will be returned.
- *
- * @param status Error code in pj_status_t namespace.
- * @return SIP status code.
- */
-#define PJSIP_ERRNO_TO_SIP_STATUS(status) \
- ((status>=PJSIP_ERRNO_FROM_SIP_STATUS(100) && \
- status<PJSIP_ERRNO_FROM_SIP_STATUS(800)) ? \
- status-PJSIP_ERRNO_FROM_SIP_STATUS(0) : 599)
-
-
-/**
- * Start of PJSIP generated error code values.
- */
-#define PJSIP_ERRNO_START_PJSIP (PJSIP_ERRNO_START + 1000)
-
-/************************************************************
- * GENERIC SIP ERRORS
- ***********************************************************/
-/**
- * @hideinitializer
- * SIP object is busy.
- */
-#define PJSIP_EBUSY (PJSIP_ERRNO_START_PJSIP + 1) /* 171001 */
-/**
- * @hideinitializer
- * SIP object with the same type already exists.
- */
-#define PJSIP_ETYPEEXISTS (PJSIP_ERRNO_START_PJSIP + 2) /* 171002 */
-
-
-/************************************************************
- * MESSAGING ERRORS
- ***********************************************************/
-/**
- * @hideinitializer
- * Invalid message (syntax error)
- */
-#define PJSIP_EINVALIDMSG (PJSIP_ERRNO_START_PJSIP + 20) /* 171020 */
-/**
- * @hideinitializer
- * Unsupported URL scheme.
- */
-#define PJSIP_EINVALIDSCHEME (PJSIP_ERRNO_START_PJSIP + 21) /* 171021 */
-/**
- * @hideinitializer
- * Message too long. See also PJSIP_ERXOVERFLOW.
- */
-#define PJSIP_EMSGTOOLONG (PJSIP_ERRNO_START_PJSIP + 22) /* 171022 */
-/**
- * @hideinitializer
- * Message not completely received.
- */
-#define PJSIP_EPARTIALMSG (PJSIP_ERRNO_START_PJSIP + 23) /* 171023 */
-/**
- * @hideinitializer
- * Missing required header(s).
- */
-#define PJSIP_EMISSINGHDR (PJSIP_ERRNO_START_PJSIP + 24) /* 171024 */
-/**
- * @hideinitializer
- * Invalid Via header in response (sent-by, etc).
- */
-#define PJSIP_EINVALIDVIA (PJSIP_ERRNO_START_PJSIP + 25) /* 171025 */
-/**
- * @hideinitializer
- * Multiple Via headers in response.
- */
-#define PJSIP_EMULTIPLEVIA (PJSIP_ERRNO_START_PJSIP + 26) /* 171026 */
-
-/************************************************************
- * TRANSPORT ERRORS
- ***********************************************************/
-/**
- * @hideinitializer
- * Unsupported transport type.
- */
-#define PJSIP_EUNSUPTRANSPORT (PJSIP_ERRNO_START_PJSIP + 40) /* 171040 */
-/**
- * @hideinitializer
- * Buffer is being sent, operation still pending.
- */
-#define PJSIP_EPENDINGTX (PJSIP_ERRNO_START_PJSIP + 41) /* 171041 */
-/**
- * @hideinitializer
- * Rx buffer overflow. See also PJSIP_EMSGTOOLONG.
- */
-#define PJSIP_ERXOVERFLOW (PJSIP_ERRNO_START_PJSIP + 42)/* 171042 */
-
-
-/************************************************************
- * TRANSACTION ERRORS
- ***********************************************************/
-/**
- * @hideinitializer
- * Transaction has just been destroyed.
- */
-#define PJSIP_ETSXDESTROYED (PJSIP_ERRNO_START_PJSIP + 60) /* 171060 */
-
-
-/************************************************************
- * URI COMPARISON RESULTS
- ***********************************************************/
-/**
- * @hideinitializer
- * Scheme mismatch.
- */
-#define PJSIP_ECMPSCHEME (PJSIP_ERRNO_START_PJSIP + 80) /* 171080 */
-/**
- * @hideinitializer
- * User part mismatch.
- */
-#define PJSIP_ECMPUSER (PJSIP_ERRNO_START_PJSIP + 81) /* 171081 */
-/**
- * @hideinitializer
- * Password part mismatch.
- */
-#define PJSIP_ECMPPASSWD (PJSIP_ERRNO_START_PJSIP + 82) /* 171082 */
-/**
- * @hideinitializer
- * Host part mismatch.
- */
-#define PJSIP_ECMPHOST (PJSIP_ERRNO_START_PJSIP + 83) /* 171083 */
-/**
- * @hideinitializer
- * Port part mismatch.
- */
-#define PJSIP_ECMPPORT (PJSIP_ERRNO_START_PJSIP + 84) /* 171084 */
-/**
- * @hideinitializer
- * Transport parameter part mismatch.
- */
-#define PJSIP_ECMPTRANSPORTPRM (PJSIP_ERRNO_START_PJSIP + 85) /* 171085 */
-/**
- * @hideinitializer
- * TTL parameter part mismatch.
- */
-#define PJSIP_ECMPTTLPARAM (PJSIP_ERRNO_START_PJSIP + 86) /* 171086 */
-/**
- * @hideinitializer
- * User parameter part mismatch.
- */
-#define PJSIP_ECMPUSERPARAM (PJSIP_ERRNO_START_PJSIP + 87) /* 171087 */
-/**
- * @hideinitializer
- * Method parameter part mismatch.
- */
-#define PJSIP_ECMPMETHODPARAM (PJSIP_ERRNO_START_PJSIP + 88) /* 171088 */
-/**
- * @hideinitializer
- * Maddr parameter part mismatch.
- */
-#define PJSIP_ECMPMADDRPARAM (PJSIP_ERRNO_START_PJSIP + 89) /* 171089 */
-/**
- * @hideinitializer
- * Parameter part in other_param mismatch.
- */
-#define PJSIP_ECMPOTHERPARAM (PJSIP_ERRNO_START_PJSIP + 90) /* 171090 */
-/**
- * @hideinitializer
- * Parameter part in header_param mismatch.
- */
-#define PJSIP_ECMPHEADERPARAM (PJSIP_ERRNO_START_PJSIP + 91) /* 171091 */
-
-
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_ERRNO_H__ */
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIP_ERRNO_H__ +#define __PJSIP_SIP_ERRNO_H__ + +#include <pj/errno.h> + +PJ_BEGIN_DECL + +/* + * PJSIP error codes occupies 170000 - 219000, and mapped as follows: + * - 170100 - 170799: mapped to SIP status code in response msg. + * - 171000 - 171999: mapped to errors generated from PJSIP core. + */ + +/** + * Get error message for the specified error code. + * + * @param status The error code. + * @param buffer The buffer where to put the error message. + * @param bufsize Size of the buffer. + * + * @return The error message as NULL terminated string, + * wrapped with pj_str_t. + */ +PJ_DECL(pj_str_t) pjsip_strerror( pj_status_t status, char *buffer, + pj_size_t bufsize); + +/** + * Start of error code relative to PJ_ERRNO_START_USER. + */ +#define PJSIP_ERRNO_START (PJ_ERRNO_START_USER) + +/** + * Create error value from SIP status code. + * @param code SIP status code. + * @return Error code in pj_status_t namespace. + */ +#define PJSIP_ERRNO_FROM_SIP_STATUS(code) (PJSIP_ERRNO_START+code) + +/** + * Get SIP status code from error value. + * If conversion to SIP status code is not available, a SIP status code + * 599 will be returned. + * + * @param status Error code in pj_status_t namespace. + * @return SIP status code. + */ +#define PJSIP_ERRNO_TO_SIP_STATUS(status) \ + ((status>=PJSIP_ERRNO_FROM_SIP_STATUS(100) && \ + status<PJSIP_ERRNO_FROM_SIP_STATUS(800)) ? \ + status-PJSIP_ERRNO_FROM_SIP_STATUS(0) : 599) + + +/** + * Start of PJSIP generated error code values. + */ +#define PJSIP_ERRNO_START_PJSIP (PJSIP_ERRNO_START + 1000) + +/************************************************************ + * GENERIC SIP ERRORS + ***********************************************************/ +/** + * @hideinitializer + * SIP object is busy. + */ +#define PJSIP_EBUSY (PJSIP_ERRNO_START_PJSIP + 1) /* 171001 */ +/** + * @hideinitializer + * SIP object with the same type already exists. + */ +#define PJSIP_ETYPEEXISTS (PJSIP_ERRNO_START_PJSIP + 2) /* 171002 */ + + +/************************************************************ + * MESSAGING ERRORS + ***********************************************************/ +/** + * @hideinitializer + * Invalid message (syntax error) + */ +#define PJSIP_EINVALIDMSG (PJSIP_ERRNO_START_PJSIP + 20) /* 171020 */ +/** + * @hideinitializer + * Unsupported URL scheme. + */ +#define PJSIP_EINVALIDSCHEME (PJSIP_ERRNO_START_PJSIP + 21) /* 171021 */ +/** + * @hideinitializer + * Message too long. See also PJSIP_ERXOVERFLOW. + */ +#define PJSIP_EMSGTOOLONG (PJSIP_ERRNO_START_PJSIP + 22) /* 171022 */ +/** + * @hideinitializer + * Message not completely received. + */ +#define PJSIP_EPARTIALMSG (PJSIP_ERRNO_START_PJSIP + 23) /* 171023 */ +/** + * @hideinitializer + * Missing required header(s). + */ +#define PJSIP_EMISSINGHDR (PJSIP_ERRNO_START_PJSIP + 24) /* 171024 */ +/** + * @hideinitializer + * Invalid Via header in response (sent-by, etc). + */ +#define PJSIP_EINVALIDVIA (PJSIP_ERRNO_START_PJSIP + 25) /* 171025 */ +/** + * @hideinitializer + * Multiple Via headers in response. + */ +#define PJSIP_EMULTIPLEVIA (PJSIP_ERRNO_START_PJSIP + 26) /* 171026 */ + +/************************************************************ + * TRANSPORT ERRORS + ***********************************************************/ +/** + * @hideinitializer + * Unsupported transport type. + */ +#define PJSIP_EUNSUPTRANSPORT (PJSIP_ERRNO_START_PJSIP + 40) /* 171040 */ +/** + * @hideinitializer + * Buffer is being sent, operation still pending. + */ +#define PJSIP_EPENDINGTX (PJSIP_ERRNO_START_PJSIP + 41) /* 171041 */ +/** + * @hideinitializer + * Rx buffer overflow. See also PJSIP_EMSGTOOLONG. + */ +#define PJSIP_ERXOVERFLOW (PJSIP_ERRNO_START_PJSIP + 42)/* 171042 */ + + +/************************************************************ + * TRANSACTION ERRORS + ***********************************************************/ +/** + * @hideinitializer + * Transaction has just been destroyed. + */ +#define PJSIP_ETSXDESTROYED (PJSIP_ERRNO_START_PJSIP + 60) /* 171060 */ + + +/************************************************************ + * URI COMPARISON RESULTS + ***********************************************************/ +/** + * @hideinitializer + * Scheme mismatch. + */ +#define PJSIP_ECMPSCHEME (PJSIP_ERRNO_START_PJSIP + 80) /* 171080 */ +/** + * @hideinitializer + * User part mismatch. + */ +#define PJSIP_ECMPUSER (PJSIP_ERRNO_START_PJSIP + 81) /* 171081 */ +/** + * @hideinitializer + * Password part mismatch. + */ +#define PJSIP_ECMPPASSWD (PJSIP_ERRNO_START_PJSIP + 82) /* 171082 */ +/** + * @hideinitializer + * Host part mismatch. + */ +#define PJSIP_ECMPHOST (PJSIP_ERRNO_START_PJSIP + 83) /* 171083 */ +/** + * @hideinitializer + * Port part mismatch. + */ +#define PJSIP_ECMPPORT (PJSIP_ERRNO_START_PJSIP + 84) /* 171084 */ +/** + * @hideinitializer + * Transport parameter part mismatch. + */ +#define PJSIP_ECMPTRANSPORTPRM (PJSIP_ERRNO_START_PJSIP + 85) /* 171085 */ +/** + * @hideinitializer + * TTL parameter part mismatch. + */ +#define PJSIP_ECMPTTLPARAM (PJSIP_ERRNO_START_PJSIP + 86) /* 171086 */ +/** + * @hideinitializer + * User parameter part mismatch. + */ +#define PJSIP_ECMPUSERPARAM (PJSIP_ERRNO_START_PJSIP + 87) /* 171087 */ +/** + * @hideinitializer + * Method parameter part mismatch. + */ +#define PJSIP_ECMPMETHODPARAM (PJSIP_ERRNO_START_PJSIP + 88) /* 171088 */ +/** + * @hideinitializer + * Maddr parameter part mismatch. + */ +#define PJSIP_ECMPMADDRPARAM (PJSIP_ERRNO_START_PJSIP + 89) /* 171089 */ +/** + * @hideinitializer + * Parameter part in other_param mismatch. + */ +#define PJSIP_ECMPOTHERPARAM (PJSIP_ERRNO_START_PJSIP + 90) /* 171090 */ +/** + * @hideinitializer + * Parameter part in header_param mismatch. + */ +#define PJSIP_ECMPHEADERPARAM (PJSIP_ERRNO_START_PJSIP + 91) /* 171091 */ + + + +PJ_END_DECL + +#endif /* __PJSIP_SIP_ERRNO_H__ */ diff --git a/pjsip/include/pjsip/sip_event.h b/pjsip/include/pjsip/sip_event.h index e828ae96..22b00727 100644 --- a/pjsip/include/pjsip/sip_event.h +++ b/pjsip/include/pjsip/sip_event.h @@ -1,310 +1,310 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_EVENT_H__
-#define __PJSIP_SIP_EVENT_H__
-
-/**
- * @file sip_event.h
- * @brief SIP Event
- */
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_EVENT SIP Event
- * @ingroup PJSIP
- * @{
- */
-#include <pj/types.h>
-
-
-/**
- * Event IDs.
- */
-typedef enum pjsip_event_id_e
-{
- /** Unidentified event. */
- PJSIP_EVENT_UNKNOWN,
-
- /** Timer event, normally only used internally in transaction. */
- PJSIP_EVENT_TIMER,
-
- /** Message transmission event. */
- PJSIP_EVENT_TX_MSG,
-
- /** Message received event. */
- PJSIP_EVENT_RX_MSG,
-
- /** Transport error event. */
- PJSIP_EVENT_TRANSPORT_ERROR,
-
- /** Transaction state changed event. */
- PJSIP_EVENT_TSX_STATE,
-
- /** 2xx response received event. */
- PJSIP_EVENT_RX_200_MSG,
-
- /** ACK request received event. */
- PJSIP_EVENT_RX_ACK_MSG,
-
- /** Message discarded event. */
- PJSIP_EVENT_DISCARD_MSG,
-
- /** Indicates that the event was triggered by user action. */
- PJSIP_EVENT_USER,
-
- /** On before transmitting message. */
- PJSIP_EVENT_PRE_TX_MSG,
-
-} pjsip_event_id_e;
-
-
-/**
- * \struct
- * \brief Event descriptor to fully identify a SIP event.
- *
- * Events are the only way for a lower layer object to inform something
- * to higher layer objects. Normally this is achieved by means of callback,
- * i.e. the higher layer objects register a callback to handle the event on
- * the lower layer objects.
- *
- * This event descriptor is used for example by transactions, to inform
- * endpoint about events, and by transports, to inform endpoint about
- * unexpected transport error.
- */
-struct pjsip_event
-{
- /** This is necessary so that we can put events as a list. */
- PJ_DECL_LIST_MEMBER(struct pjsip_event);
-
- /** The event type, can be any value of \b pjsip_event_id_e.
- */
- pjsip_event_id_e type;
-
- /*
- * The event body.
- * By convention, the first member of each struct in the union must be
- * the pointer which is relevant to the event.
- */
- union
- {
- /** Timer event. */
- struct
- {
- pj_timer_entry *entry; /**< The timer entry. */
- } timer;
-
- /** Transaction state has changed event. */
- struct
- {
- union
- {
- pjsip_rx_data *rdata; /**< The incoming message. */
- pjsip_tx_data *tdata; /**< The outgoing message. */
- pj_timer_entry *timer; /**< The timer. */
- pj_status_t status;/**< Transport error status. */
- void *data; /**< Generic data. */
- } src;
- pjsip_transaction *tsx; /**< The transaction. */
- pjsip_event_id_e type; /**< Type of event source:
- * - PJSIP_EVENT_TX_MSG
- * - PJSIP_EVENT_RX_MSG,
- * - PJSIP_EVENT_TRANSPORT_ERROR
- * - PJSIP_EVENT_TIMER
- * - PJSIP_EVENT_USER
- */
- } tsx_state;
-
- /** Message transmission event. */
- struct
- {
- pjsip_tx_data *tdata; /**< The transmit data buffer. */
- pjsip_transaction *tsx; /**< The transaction. */
-
- } tx_msg;
-
- /** Pre-transmission event. */
- struct
- {
- pjsip_tx_data *tdata; /**< Msg to be transmitted. */
- pjsip_transaction *tsx; /**< The transaction. */
- int retcnt;/**< Retransmission count. */
- } pre_tx_msg;
-
- /** Transmission error event. */
- struct
- {
- pjsip_tx_data *tdata; /**< The transmit data. */
- pjsip_transaction *tsx; /**< The transaction. */
- } tx_error;
-
- /** Message arrival event. */
- struct
- {
- pjsip_rx_data *rdata; /**< The receive data buffer. */
- pjsip_transaction *tsx; /**< The transaction. */
- } rx_msg;
-
- /** Receipt of 200/INVITE response. */
- struct
- {
- pjsip_rx_data *rdata; /**< The 200 response msg. */
- } rx_200_msg;
-
- /** Receipt of ACK message. */
- struct
- {
- pjsip_rx_data *rdata; /**< The ack message. */
- } rx_ack_msg;
-
- /** Notification that endpoint has discarded a message. */
- struct
- {
- pjsip_rx_data *rdata; /**< The discarded message. */
- pj_status_t reason;/**< The reason. */
- } discard_msg;
-
- /** User event. */
- struct
- {
- void *user1; /**< User data 1. */
- void *user2; /**< User data 2. */
- void *user3; /**< User data 3. */
- void *user4; /**< User data 4. */
- } user;
-
- } body;
-};
-
-/**
- * Init timer event.
- */
-#define PJSIP_EVENT_INIT_TIMER(event,pentry) \
- do { \
- (event).type = PJSIP_EVENT_TIMER; \
- (event).body.timer.entry = pentry; \
- } while (0)
-
-/**
- * Init tsx state event.
- */
-#define PJSIP_EVENT_INIT_TSX_STATE(event,ptsx,ptype,pdata) \
- do { \
- (event).type = PJSIP_EVENT_TSX_STATE; \
- (event).body.tsx_state.tsx = ptsx; \
- (event).body.tsx_state.type = ptype; \
- (event).body.tsx_state.src.data = pdata; \
- } while (0)
-
-/**
- * Init tx msg event.
- */
-#define PJSIP_EVENT_INIT_TX_MSG(event,ptsx,ptdata) \
- do { \
- (event).type = PJSIP_EVENT_TX_MSG; \
- (event).body.tx_msg.tsx = ptsx; \
- (event).body.tx_msg.tdata = ptdata; \
- } while (0)
-
-/**
- * Init rx msg event.
- */
-#define PJSIP_EVENT_INIT_RX_MSG(event,ptsx,prdata) \
- do { \
- (event).type = PJSIP_EVENT_RX_MSG; \
- (event).body.rx_msg.tsx = ptsx; \
- (event).body.rx_msg.rdata = prdata; \
- } while (0)
-
-/**
- * Init transport error event.
- */
-#define PJSIP_EVENT_INIT_TRANSPORT_ERROR(event,ptsx,ptdata) \
- do { \
- (event).type = PJSIP_EVENT_TRANSPORT_ERROR; \
- (event).body.tx_error.tsx = ptsx; \
- (event).body.tx_error.tdata = ptdata; \
- } while (0)
-
-/**
- * Init rx 200/INVITE event.
- */
-#define PJSIP_EVENT_INIT_RX_200_MSG(event,prdata) \
- do { \
- (event).type = PJSIP_EVENT_RX_200_MSG; \
- (event).body.rx_200_msg.rdata = prdata; \
- } while (0)
-
-/**
- * Init rx ack msg event.
- */
-#define PJSIP_EVENT_INIT_RX_ACK_MSG(event,prdata) \
- do { \
- (event).type = PJSIP_EVENT_RX_ACK_MSG; \
- (event).body.rx_ack_msg.rdata = prdata; \
- } while (0)
-
-/**
- * Init discard msg event.
- */
-#define PJSIP_EVENT_INIT_DISCARD_MSG(event,prdata,preason) \
- do { \
- (event).type = PJSIP_EVENT_DISCARD_MSG; \
- (event).body.discard_msg.rdata = prdata; \
- (event).body.discard_msg.reason = preason; \
- } while (0)
-
-/**
- * Init user event.
- */
-#define PJSIP_EVENT_INIT_USER(event,u1,u2,u3,u4) \
- do { \
- (event).type = PJSIP_EVENT_USER; \
- (event).body.user.user1 = (void*)u1; \
- (event).body.user.user2 = (void*)u2; \
- (event).body.user.user3 = (void*)u3; \
- (event).body.user.user4 = (void*)u4; \
- } while (0)
-
-/**
- * Init pre tx msg event.
- */
-#define PJSIP_EVENT_INIT_PRE_TX_MSG(event,ptsx,ptdata,pretcnt) \
- do { \
- (event).type = PJSIP_EVENT_PRE_TX_MSG; \
- (event).body.pre_tx_msg.tsx = ptsx; \
- (event).body.pre_tx_msg.tdata = ptdata; \
- (event).body.pre_tx_msg.retcnt = pretcnt; \
- } while (0)
-
-
-/**
- * Get the event string from the event ID.
- * @param e the event ID.
- * @notes defined in sip_util.c
- */
-PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_EVENT_H__ */
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIP_EVENT_H__ +#define __PJSIP_SIP_EVENT_H__ + +/** + * @file sip_event.h + * @brief SIP Event + */ + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_EVENT SIP Event + * @ingroup PJSIP + * @{ + */ +#include <pj/types.h> + + +/** + * Event IDs. + */ +typedef enum pjsip_event_id_e +{ + /** Unidentified event. */ + PJSIP_EVENT_UNKNOWN, + + /** Timer event, normally only used internally in transaction. */ + PJSIP_EVENT_TIMER, + + /** Message transmission event. */ + PJSIP_EVENT_TX_MSG, + + /** Message received event. */ + PJSIP_EVENT_RX_MSG, + + /** Transport error event. */ + PJSIP_EVENT_TRANSPORT_ERROR, + + /** Transaction state changed event. */ + PJSIP_EVENT_TSX_STATE, + + /** 2xx response received event. */ + PJSIP_EVENT_RX_200_MSG, + + /** ACK request received event. */ + PJSIP_EVENT_RX_ACK_MSG, + + /** Message discarded event. */ + PJSIP_EVENT_DISCARD_MSG, + + /** Indicates that the event was triggered by user action. */ + PJSIP_EVENT_USER, + + /** On before transmitting message. */ + PJSIP_EVENT_PRE_TX_MSG, + +} pjsip_event_id_e; + + +/** + * \struct + * \brief Event descriptor to fully identify a SIP event. + * + * Events are the only way for a lower layer object to inform something + * to higher layer objects. Normally this is achieved by means of callback, + * i.e. the higher layer objects register a callback to handle the event on + * the lower layer objects. + * + * This event descriptor is used for example by transactions, to inform + * endpoint about events, and by transports, to inform endpoint about + * unexpected transport error. + */ +struct pjsip_event +{ + /** This is necessary so that we can put events as a list. */ + PJ_DECL_LIST_MEMBER(struct pjsip_event); + + /** The event type, can be any value of \b pjsip_event_id_e. + */ + pjsip_event_id_e type; + + /* + * The event body. + * By convention, the first member of each struct in the union must be + * the pointer which is relevant to the event. + */ + union + { + /** Timer event. */ + struct + { + pj_timer_entry *entry; /**< The timer entry. */ + } timer; + + /** Transaction state has changed event. */ + struct + { + union + { + pjsip_rx_data *rdata; /**< The incoming message. */ + pjsip_tx_data *tdata; /**< The outgoing message. */ + pj_timer_entry *timer; /**< The timer. */ + pj_status_t status;/**< Transport error status. */ + void *data; /**< Generic data. */ + } src; + pjsip_transaction *tsx; /**< The transaction. */ + pjsip_event_id_e type; /**< Type of event source: + * - PJSIP_EVENT_TX_MSG + * - PJSIP_EVENT_RX_MSG, + * - PJSIP_EVENT_TRANSPORT_ERROR + * - PJSIP_EVENT_TIMER + * - PJSIP_EVENT_USER + */ + } tsx_state; + + /** Message transmission event. */ + struct + { + pjsip_tx_data *tdata; /**< The transmit data buffer. */ + pjsip_transaction *tsx; /**< The transaction. */ + + } tx_msg; + + /** Pre-transmission event. */ + struct + { + pjsip_tx_data *tdata; /**< Msg to be transmitted. */ + pjsip_transaction *tsx; /**< The transaction. */ + int retcnt;/**< Retransmission count. */ + } pre_tx_msg; + + /** Transmission error event. */ + struct + { + pjsip_tx_data *tdata; /**< The transmit data. */ + pjsip_transaction *tsx; /**< The transaction. */ + } tx_error; + + /** Message arrival event. */ + struct + { + pjsip_rx_data *rdata; /**< The receive data buffer. */ + pjsip_transaction *tsx; /**< The transaction. */ + } rx_msg; + + /** Receipt of 200/INVITE response. */ + struct + { + pjsip_rx_data *rdata; /**< The 200 response msg. */ + } rx_200_msg; + + /** Receipt of ACK message. */ + struct + { + pjsip_rx_data *rdata; /**< The ack message. */ + } rx_ack_msg; + + /** Notification that endpoint has discarded a message. */ + struct + { + pjsip_rx_data *rdata; /**< The discarded message. */ + pj_status_t reason;/**< The reason. */ + } discard_msg; + + /** User event. */ + struct + { + void *user1; /**< User data 1. */ + void *user2; /**< User data 2. */ + void *user3; /**< User data 3. */ + void *user4; /**< User data 4. */ + } user; + + } body; +}; + +/** + * Init timer event. + */ +#define PJSIP_EVENT_INIT_TIMER(event,pentry) \ + do { \ + (event).type = PJSIP_EVENT_TIMER; \ + (event).body.timer.entry = pentry; \ + } while (0) + +/** + * Init tsx state event. + */ +#define PJSIP_EVENT_INIT_TSX_STATE(event,ptsx,ptype,pdata) \ + do { \ + (event).type = PJSIP_EVENT_TSX_STATE; \ + (event).body.tsx_state.tsx = ptsx; \ + (event).body.tsx_state.type = ptype; \ + (event).body.tsx_state.src.data = pdata; \ + } while (0) + +/** + * Init tx msg event. + */ +#define PJSIP_EVENT_INIT_TX_MSG(event,ptsx,ptdata) \ + do { \ + (event).type = PJSIP_EVENT_TX_MSG; \ + (event).body.tx_msg.tsx = ptsx; \ + (event).body.tx_msg.tdata = ptdata; \ + } while (0) + +/** + * Init rx msg event. + */ +#define PJSIP_EVENT_INIT_RX_MSG(event,ptsx,prdata) \ + do { \ + (event).type = PJSIP_EVENT_RX_MSG; \ + (event).body.rx_msg.tsx = ptsx; \ + (event).body.rx_msg.rdata = prdata; \ + } while (0) + +/** + * Init transport error event. + */ +#define PJSIP_EVENT_INIT_TRANSPORT_ERROR(event,ptsx,ptdata) \ + do { \ + (event).type = PJSIP_EVENT_TRANSPORT_ERROR; \ + (event).body.tx_error.tsx = ptsx; \ + (event).body.tx_error.tdata = ptdata; \ + } while (0) + +/** + * Init rx 200/INVITE event. + */ +#define PJSIP_EVENT_INIT_RX_200_MSG(event,prdata) \ + do { \ + (event).type = PJSIP_EVENT_RX_200_MSG; \ + (event).body.rx_200_msg.rdata = prdata; \ + } while (0) + +/** + * Init rx ack msg event. + */ +#define PJSIP_EVENT_INIT_RX_ACK_MSG(event,prdata) \ + do { \ + (event).type = PJSIP_EVENT_RX_ACK_MSG; \ + (event).body.rx_ack_msg.rdata = prdata; \ + } while (0) + +/** + * Init discard msg event. + */ +#define PJSIP_EVENT_INIT_DISCARD_MSG(event,prdata,preason) \ + do { \ + (event).type = PJSIP_EVENT_DISCARD_MSG; \ + (event).body.discard_msg.rdata = prdata; \ + (event).body.discard_msg.reason = preason; \ + } while (0) + +/** + * Init user event. + */ +#define PJSIP_EVENT_INIT_USER(event,u1,u2,u3,u4) \ + do { \ + (event).type = PJSIP_EVENT_USER; \ + (event).body.user.user1 = (void*)u1; \ + (event).body.user.user2 = (void*)u2; \ + (event).body.user.user3 = (void*)u3; \ + (event).body.user.user4 = (void*)u4; \ + } while (0) + +/** + * Init pre tx msg event. + */ +#define PJSIP_EVENT_INIT_PRE_TX_MSG(event,ptsx,ptdata,pretcnt) \ + do { \ + (event).type = PJSIP_EVENT_PRE_TX_MSG; \ + (event).body.pre_tx_msg.tsx = ptsx; \ + (event).body.pre_tx_msg.tdata = ptdata; \ + (event).body.pre_tx_msg.retcnt = pretcnt; \ + } while (0) + + +/** + * Get the event string from the event ID. + * @param e the event ID. + * @notes defined in sip_util.c + */ +PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_SIP_EVENT_H__ */ diff --git a/pjsip/include/pjsip/sip_module.h b/pjsip/include/pjsip/sip_module.h index bd3bcd87..345be88a 100644 --- a/pjsip/include/pjsip/sip_module.h +++ b/pjsip/include/pjsip/sip_module.h @@ -1,140 +1,140 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_MODULE_H__
-#define __PJSIP_SIP_MODULE_H__
-
-/**
- * @file sip_module.h
- * @brief Module helpers
- */
-#include <pjsip/sip_types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_MOD SIP Modules
- * @ingroup PJSIP
- * @{
- */
-
-/**
- * Module registration structure, which is passed by the module to the
- * endpoint during the module registration process. This structure enables
- * the endpoint to query the module capability and to further communicate
- * with the module.
- */
-struct pjsip_module
-{
- /**
- * Module name.
- */
- pj_str_t name;
-
- /**
- * Flag to indicate the type of interfaces supported by the module.
- */
- pj_uint32_t flag;
-
- /**
- * Integer number to identify module initialization and start order with
- * regard to other modules. Higher number will make the module gets
- * initialized later.
- */
- pj_uint32_t priority;
-
- /**
- * Opaque data which can be used by a module to identify a resource within
- * the module itself.
- */
- void *mod_data;
-
- /**
- * Number of methods supported by this module.
- */
- int method_cnt;
-
- /**
- * Array of methods supported by this module.
- */
- const pjsip_method *methods[8];
-
- /**
- * Pointer to function to be called to initialize the module.
- *
- * @param endpt The endpoint instance.
- * @param mod The module.
- * @param id The unique module ID assigned to this module.
- *
- * @return Module should return zero when initialization succeed.
- */
- pj_status_t (*init_module)(pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id);
-
- /**
- * Pointer to function to be called to start the module.
- *
- * @param mod The module.
- *
- * @return Module should return zero to indicate success.
- */
- pj_status_t (*start_module)(struct pjsip_module *mod);
-
- /**
- * Pointer to function to be called to deinitialize the module before
- * it is unloaded.
- *
- * @param mod The module.
- *
- * @return Module should return zero to indicate success.
- */
- pj_status_t (*deinit_module)(struct pjsip_module *mod);
-
- /**
- * Pointer to function to receive transaction related events.
- * If the module doesn't wish to receive such notification, this member
- * must be set to NULL.
- *
- * @param mod The module.
- * @param event The transaction event.
- */
- void (*tsx_handler)(struct pjsip_module *mod, pjsip_event *event);
-};
-
-
-/**
- * Prototype of function to register static modules (eg modules that are
- * linked staticly with the application). This function must be implemented
- * by any applications that use PJSIP library.
- *
- * @param count [input/output] On input, it contains the maximum number of
- * elements in the array. On output, the function fills with
- * the number of modules to be registered.
- * @param modules [output] array of pointer to modules to be registered.
- */
-pj_status_t register_static_modules( pj_size_t *count,
- pjsip_module **modules );
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_MODULE_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIP_MODULE_H__ +#define __PJSIP_SIP_MODULE_H__ + +/** + * @file sip_module.h + * @brief Module helpers + */ +#include <pjsip/sip_types.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_MOD SIP Modules + * @ingroup PJSIP + * @{ + */ + +/** + * Module registration structure, which is passed by the module to the + * endpoint during the module registration process. This structure enables + * the endpoint to query the module capability and to further communicate + * with the module. + */ +struct pjsip_module +{ + /** + * Module name. + */ + pj_str_t name; + + /** + * Flag to indicate the type of interfaces supported by the module. + */ + pj_uint32_t flag; + + /** + * Integer number to identify module initialization and start order with + * regard to other modules. Higher number will make the module gets + * initialized later. + */ + pj_uint32_t priority; + + /** + * Opaque data which can be used by a module to identify a resource within + * the module itself. + */ + void *mod_data; + + /** + * Number of methods supported by this module. + */ + int method_cnt; + + /** + * Array of methods supported by this module. + */ + const pjsip_method *methods[8]; + + /** + * Pointer to function to be called to initialize the module. + * + * @param endpt The endpoint instance. + * @param mod The module. + * @param id The unique module ID assigned to this module. + * + * @return Module should return zero when initialization succeed. + */ + pj_status_t (*init_module)(pjsip_endpoint *endpt, + struct pjsip_module *mod, pj_uint32_t id); + + /** + * Pointer to function to be called to start the module. + * + * @param mod The module. + * + * @return Module should return zero to indicate success. + */ + pj_status_t (*start_module)(struct pjsip_module *mod); + + /** + * Pointer to function to be called to deinitialize the module before + * it is unloaded. + * + * @param mod The module. + * + * @return Module should return zero to indicate success. + */ + pj_status_t (*deinit_module)(struct pjsip_module *mod); + + /** + * Pointer to function to receive transaction related events. + * If the module doesn't wish to receive such notification, this member + * must be set to NULL. + * + * @param mod The module. + * @param event The transaction event. + */ + void (*tsx_handler)(struct pjsip_module *mod, pjsip_event *event); +}; + + +/** + * Prototype of function to register static modules (eg modules that are + * linked staticly with the application). This function must be implemented + * by any applications that use PJSIP library. + * + * @param count [input/output] On input, it contains the maximum number of + * elements in the array. On output, the function fills with + * the number of modules to be registered. + * @param modules [output] array of pointer to modules to be registered. + */ +pj_status_t register_static_modules( pj_size_t *count, + pjsip_module **modules ); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_SIP_MODULE_H__ */ + diff --git a/pjsip/include/pjsip/sip_msg.h b/pjsip/include/pjsip/sip_msg.h index a547bfa2..836ce37b 100644 --- a/pjsip/include/pjsip/sip_msg.h +++ b/pjsip/include/pjsip/sip_msg.h @@ -1,1527 +1,1527 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_MSG_H__
-#define __PJSIP_SIP_MSG_H__
-
-/**
- * @file sip_msg.h
- * @brief SIP Message Structure.
- */
-
-#include <pjsip/sip_types.h>
-#include <pjsip/sip_uri.h>
-#include <pj/list.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_MSG SIP Message Structure
- * @ingroup PJSIP
- * @{
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_METHOD Methods
- * @brief Method names and manipulation.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * This enumeration declares SIP methods as described by RFC3261. Additional
- * methods do exist, and they are described by corresponding RFCs for the SIP
- * extentensions. Since they won't alter the characteristic of the processing
- * of the message, they don't need to be explicitly mentioned here.
- */
-typedef enum pjsip_method_e
-{
- /** INVITE method, for establishing dialogs. */
- PJSIP_INVITE_METHOD,
-
- /** CANCEL method, for cancelling request. */
- PJSIP_CANCEL_METHOD,
-
- /** ACK method, for acknowledging final response to INVITE. */
- PJSIP_ACK_METHOD,
-
- /** BYE method, for terminating dialog. */
- PJSIP_BYE_METHOD,
-
- /** REGISTER method. */
- PJSIP_REGISTER_METHOD,
-
- /** OPTIONS method, for querying remote capabilities. */
- PJSIP_OPTIONS_METHOD,
-
- /** Other method, which means that the method name itself will be stored
- elsewhere. */
- PJSIP_OTHER_METHOD,
-
-} pjsip_method_e;
-
-
-
-/**
- * This structure represents a SIP method.
- * Application must always use either #pjsip_method_init or #pjsip_method_set
- * to make sure that method name is initialized correctly. This way, the name
- * member will always contain a valid method string regardless whether the ID
- * is recognized or not.
- */
-typedef struct pjsip_method
-{
- pjsip_method_e id; /**< Method ID, from \a pjsip_method_e. */
- pj_str_t name; /**< Method name, which will always contain the
- method string. */
-} pjsip_method;
-
-
-/**
- * Initialize the method structure from a string.
- * This function will check whether the method is a known method then set
- * both the id and name accordingly.
- *
- * @param m The method to initialize.
- * @param pool Pool where memory allocation will be allocated from, if required.
- * @param str The method string.
- */
-PJ_DECL(void) pjsip_method_init( pjsip_method *m,
- pj_pool_t *pool,
- const pj_str_t *str);
-
-/**
- * Initialize the method structure from a string, without cloning the string.
- * See #pjsip_method_init.
- *
- * @param m The method structure to be initialized.
- * @param str The method string.
- */
-PJ_DECL(void) pjsip_method_init_np( pjsip_method *m,
- pj_str_t *str);
-
-/**
- * Set the method with the predefined method ID.
- * This function will also set the name member of the structure to the correct
- * string according to the method.
- *
- * @param m The method structure.
- * @param id The method ID.
- */
-PJ_DECL(void) pjsip_method_set( pjsip_method *m, pjsip_method_e id );
-
-
-/**
- * Copy one method structure to another. If the method is of the known methods,
- * then memory allocation is not required.
- *
- * @param pool Pool to allocate memory from, if required.
- * @param method The destination method to copy to.
- * @param rhs The source method to copy from.
- */
-PJ_DECL(void) pjsip_method_copy( pj_pool_t *pool,
- pjsip_method *method,
- const pjsip_method *rhs );
-
-/**
- * Compare one method with another, and conveniently determine whether the
- * first method is equal, less than, or greater than the second method.
- *
- * @param m1 The first method.
- * @param m2 The second method.
- *
- * @return Zero if equal, otherwise will return -1 if less or +1 if greater.
- */
-PJ_DECL(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR Header Fields General Structure.
- * @brief General Header Fields Structure.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * Header types, as defined by RFC3261.
- */
-typedef enum pjsip_hdr_e
-{
- /*
- * These are the headers documented in RFC3261. Headers not documented
- * there must have type PJSIP_H_OTHER, and the header type itself is
- * recorded in the header name string.
- *
- * DO NOT CHANGE THE VALUE/ORDER OF THE HEADER IDs!!!.
- */
- PJSIP_H_ACCEPT,
- PJSIP_H_ACCEPT_ENCODING_UNIMP,
- PJSIP_H_ACCEPT_LANGUAGE_UNIMP,
- PJSIP_H_ALERT_INFO_UNIMP,
- PJSIP_H_ALLOW,
- PJSIP_H_AUTHENTICATION_INFO_UNIMP,
- PJSIP_H_AUTHORIZATION,
- PJSIP_H_CALL_ID,
- PJSIP_H_CALL_INFO_UNIMP,
- PJSIP_H_CONTACT,
- PJSIP_H_CONTENT_DISPOSITION_UNIMP,
- PJSIP_H_CONTENT_ENCODING_UNIMP,
- PJSIP_H_CONTENT_LANGUAGE_UNIMP,
- PJSIP_H_CONTENT_LENGTH,
- PJSIP_H_CONTENT_TYPE,
- PJSIP_H_CSEQ,
- PJSIP_H_DATE_UNIMP,
- PJSIP_H_ERROR_INFO_UNIMP,
- PJSIP_H_EXPIRES,
- PJSIP_H_FROM,
- PJSIP_H_IN_REPLY_TO_UNIMP,
- PJSIP_H_MAX_FORWARDS,
- PJSIP_H_MIME_VERSION_UNIMP,
- PJSIP_H_MIN_EXPIRES,
- PJSIP_H_ORGANIZATION_UNIMP,
- PJSIP_H_PRIORITY_UNIMP,
- PJSIP_H_PROXY_AUTHENTICATE,
- PJSIP_H_PROXY_AUTHORIZATION,
- PJSIP_H_PROXY_REQUIRE_UNIMP,
- PJSIP_H_RECORD_ROUTE,
- PJSIP_H_REPLY_TO_UNIMP,
- PJSIP_H_REQUIRE,
- PJSIP_H_RETRY_AFTER,
- PJSIP_H_ROUTE,
- PJSIP_H_SERVER_UNIMP,
- PJSIP_H_SUBJECT_UNIMP,
- PJSIP_H_SUPPORTED,
- PJSIP_H_TIMESTAMP_UNIMP,
- PJSIP_H_TO,
- PJSIP_H_UNSUPPORTED,
- PJSIP_H_USER_AGENT_UNIMP,
- PJSIP_H_VIA,
- PJSIP_H_WARNING_UNIMP,
- PJSIP_H_WWW_AUTHENTICATE,
-
- PJSIP_H_OTHER,
-
-} pjsip_hdr_e;
-
-/**
- * This structure provides the pointer to basic functions that are needed
- * for generic header operations. All header fields will have pointer to
- * this structure, so that they can be manipulated uniformly.
- */
-typedef struct pjsip_hdr_vptr
-{
- /**
- * Function to clone the header.
- *
- * @param pool Memory pool to allocate the new header.
- * @param hdr Header to clone.
- *
- * @return A new instance of the header.
- */
- void *(*clone)(pj_pool_t *pool, const void *hdr);
-
- /**
- * Pointer to function to shallow clone the header.
- * Shallow cloning will just make a memory copy of the original header,
- * thus all pointers in original header will be kept intact. Because the
- * function does not need to perform deep copy, the operation should be
- * faster, but the application must make sure that the original header
- * is still valid throughout the lifetime of new header.
- *
- * @param pool Memory pool to allocate the new header.
- * @param hdr The header to clone.
- */
- void *(*shallow_clone)(pj_pool_t *pool, const void *hdr);
-
- /** Pointer to function to print the header to the specified buffer.
- * Returns the length of string written, or -1 if the remaining buffer
- * is not enough to hold the header.
- *
- * @param hdr The header to print.
- * @param buf The buffer.
- * @param len The size of the buffer.
- *
- * @return The size copied to buffer, or -1 if there's not enough space.
- */
- int (*print_on)(void *hdr, char *buf, pj_size_t len);
-
-} pjsip_hdr_vptr;
-
-
-/**
- * Generic fields for all SIP headers are declared using this macro, to make
- * sure that all headers will have exactly the same layout in their start of
- * the storage. This behaves like C++ inheritance actually.
- */
-#define PJSIP_DECL_HDR_MEMBER(hdr) \
- /** List members. */ \
- PJ_DECL_LIST_MEMBER(hdr); \
- /** Header type */ \
- pjsip_hdr_e type; \
- /** Header name. */ \
- pj_str_t name; \
- /** Header short name version. */ \
- pj_str_t sname; \
- /** Virtual function table. */ \
- pjsip_hdr_vptr *vptr
-
-
-/**
- * Generic SIP header structure, for generic manipulation for headers in the
- * message. All header fields can be typecasted to this type.
- */
-struct pjsip_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_hdr);
-};
-
-
-/**
- * This generic function will clone any header, by calling "clone" function
- * in header's virtual function table.
- *
- * @param pool The pool to allocate memory from.
- * @param hdr The header to clone.
- *
- * @return A new instance copied from the original header.
- */
-PJ_DECL(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr );
-
-
-/**
- * This generic function will clone any header, by calling "shallow_clone"
- * function in header's virtual function table.
- *
- * @param pool The pool to allocate memory from.
- * @param hdr The header to clone.
- *
- * @return A new instance copied from the original header.
- */
-PJ_DECL(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr );
-
-/**
- * This generic function will print any header, by calling "print"
- * function in header's virtual function table.
- *
- * @param hdr The header to print.
- * @param buf The buffer.
- * @param len The size of the buffer.
- *
- * @return The size copied to buffer, or -1 if there's not enough space.
- */
-PJ_DECL(int) pjsip_hdr_print_on( void *hdr, char *buf, pj_size_t len);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_LINE Request and Status Line.
- * @brief Request and status line structures and manipulation.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * This structure describes SIP request line.
- */
-typedef struct pjsip_request_line
-{
- pjsip_method method; /**< Method for this request line. */
- pjsip_uri *uri; /**< URI for this request line. */
-} pjsip_request_line;
-
-
-/**
- * This structure describes SIP status line.
- */
-typedef struct pjsip_status_line
-{
- int code; /**< Status code. */
- pj_str_t reason; /**< Reason string. */
-} pjsip_status_line;
-
-
-/**
- * This enumeration lists standard SIP status codes according to RFC 3261.
- * In addition, it also declares new status class 7xx for errors generated
- * by the stack. This status class however should not get transmitted on the
- * wire.
- */
-typedef enum pjsip_status_code
-{
- PJSIP_SC_TRYING = 100,
- PJSIP_SC_RINGING = 180,
- PJSIP_SC_CALL_BEING_FORWARDED = 181,
- PJSIP_SC_QUEUED = 182,
- PJSIP_SC_PROGRESS = 183,
-
- PJSIP_SC_OK = 200,
-
- PJSIP_SC_MULTIPLE_CHOICES = 300,
- PJSIP_SC_MOVED_PERMANENTLY = 301,
- PJSIP_SC_MOVED_TEMPORARILY = 302,
- PJSIP_SC_USE_PROXY = 305,
- PJSIP_SC_ALTERNATIVE_SERVICE = 380,
-
- PJSIP_SC_BAD_REQUEST = 400,
- PJSIP_SC_UNAUTHORIZED = 401,
- PJSIP_SC_PAYMENT_REQUIRED = 402,
- PJSIP_SC_FORBIDDEN = 403,
- PJSIP_SC_NOT_FOUND = 404,
- PJSIP_SC_METHOD_NOT_ALLOWED = 405,
- PJSIP_SC_NOT_ACCEPTABLE = 406,
- PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED = 407,
- PJSIP_SC_REQUEST_TIMEOUT = 408,
- PJSIP_SC_GONE = 410,
- PJSIP_SC_REQUEST_ENTITY_TOO_LARGE = 413,
- PJSIP_SC_REQUEST_URI_TOO_LONG = 414,
- PJSIP_SC_UNSUPPORTED_MEDIA_TYPE = 415,
- PJSIP_SC_UNSUPPORTED_URI_SCHEME = 416,
- PJSIP_SC_BAD_EXTENSION = 420,
- PJSIP_SC_EXTENSION_REQUIRED = 421,
- PJSIP_SC_INTERVAL_TOO_BRIEF = 423,
- PJSIP_SC_TEMPORARILY_UNAVAILABLE = 480,
- PJSIP_SC_CALL_TSX_DOES_NOT_EXIST = 481,
- PJSIP_SC_LOOP_DETECTED = 482,
- PJSIP_SC_TOO_MANY_HOPS = 483,
- PJSIP_SC_ADDRESS_INCOMPLETE = 484,
- PJSIP_AC_AMBIGUOUS = 485,
- PJSIP_SC_BUSY_HERE = 486,
- PJSIP_SC_REQUEST_TERMINATED = 487,
- PJSIP_SC_NOT_ACCEPTABLE_HERE = 488,
- PJSIP_SC_REQUEST_PENDING = 491,
- PJSIP_SC_UNDECIPHERABLE = 493,
-
- PJSIP_SC_INTERNAL_SERVER_ERROR = 500,
- PJSIP_SC_NOT_IMPLEMENTED = 501,
- PJSIP_SC_BAD_GATEWAY = 502,
- PJSIP_SC_SERVICE_UNAVAILABLE = 503,
- PJSIP_SC_SERVER_TIMEOUT = 504,
- PJSIP_SC_VERSION_NOT_SUPPORTED = 505,
- PJSIP_SC_MESSAGE_TOO_LARGE = 513,
-
- PJSIP_SC_BUSY_EVERYWHERE = 600,
- PJSIP_SC_DECLINE = 603,
- PJSIP_SC_DOES_NOT_EXIST_ANYWHERE = 604,
- PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE = 606,
-
- PJSIP_SC_TSX_TIMEOUT = 701,
- PJSIP_SC_TSX_RESOLVE_ERROR = 702,
- PJSIP_SC_TSX_TRANSPORT_ERROR = 703,
-
-} pjsip_status_code;
-
-/**
- * Get the default status text for the status code.
- *
- * @param status_code SIP Status Code
- *
- * @return textual message for the status code.
- */
-PJ_DECL(const pj_str_t*) pjsip_get_status_text(int status_code);
-
-/**
- * This macro returns non-zero (TRUE) if the specified status_code is
- * in the same class as the code_class.
- *
- * @param status_code The status code.
- * @param code_class The status code in the class (for example 100, 200).
- */
-#define PJSIP_IS_STATUS_IN_CLASS(status_code, code_class) \
- (status_code/100 == code_class/100)
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @addtogroup PJSIP_MSG_MEDIA Media Type
- * @brief Media type definitions and manipulations.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * This structure describes SIP media type, as used for example in
- * Accept and Content-Type header..
- */
-typedef struct pjsip_media_type
-{
- pj_str_t type; /**< Media type. */
- pj_str_t subtype; /**< Media subtype. */
- pj_str_t param; /**< Media type parameters (concatenated). */
-} pjsip_media_type;
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @addtogroup PJSIP_MSG_BODY Message Body
- * @brief SIP message body structures and manipulation.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * Generic abstraction to message body.
- * When an incoming message is parsed (pjsip_parse_msg()), the parser fills in
- * all members with the appropriate value. The 'data' and 'len' member will
- * describe portion of incoming packet which denotes the message body.
- * When application needs to attach message body to outgoing SIP message, it
- * must fill in all members of this structure.
- */
-typedef struct pjsip_msg_body
-{
- /** MIME content type.
- * For incoming messages, the parser will fill in this member with the
- * content type found in Content-Type header.
- *
- * For outgoing messages, application must fill in this member with
- * appropriate value, because the stack will generate Content-Type header
- * based on the value specified here.
- */
- pjsip_media_type content_type;
-
- /** Pointer to buffer which holds the message body data.
- * For incoming messages, the parser will fill in this member with the
- * pointer to the body string.
- *
- * When sending outgoing message, this member doesn't need to point to the
- * actual message body string. It can be assigned with arbitrary pointer,
- * because the value will only need to be understood by the print_body()
- * function. The stack itself will not try to interpret this value, but
- * instead will always call the print_body() whenever it needs to get the
- * actual body string.
- */
- void *data;
-
- /** The length of the data.
- * For incoming messages, the parser will fill in this member with the
- * actual length of message body.
- *
- * When sending outgoing message, again just like the "data" member, the
- * "len" member doesn't need to point to the actual length of the body
- * string.
- */
- unsigned len;
-
- /** Pointer to function to print this message body.
- * Application must set a proper function here when sending outgoing
- * message.
- *
- * @param msg_body This structure itself.
- * @param buf The buffer.
- * @param size The buffer size.
- *
- * @return The length of the string printed, or -1 if there is
- * not enough space in the buffer to print the whole
- * message body.
- */
- int (*print_body)(struct pjsip_msg_body *msg_body,
- char *buf, pj_size_t size);
-
-} pjsip_msg_body;
-
-/**
- * General purpose function to textual data in a SIP body. Attach this function
- * in a SIP message body only if the data in pjsip_msg_body is a textual
- * message ready to be embedded in a SIP message. If the data in the message
- * body is not a textual body, then application must supply a custom function
- * to print that body.
- *
- * @param msg_body The message body.
- * @param buf Buffer to copy the message body to.
- * @param size The size of the buffer.
- *
- * @return The length copied to the buffer, or -1.
- */
-PJ_DECL(int) pjsip_print_text_body( pjsip_msg_body *msg_body,
- char *buf, pj_size_t size);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_MSG Message Structure
- * @brief Message structure and operations.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * Message type (request or response).
- */
-typedef enum pjsip_msg_type_e
-{
- PJSIP_REQUEST_MSG, /**< Indicates request message. */
- PJSIP_RESPONSE_MSG, /**< Indicates response message. */
-} pjsip_msg_type_e;
-
-
-/**
- * This structure describes a SIP message.
- */
-struct pjsip_msg
-{
- /** Message type (ie request or response). */
- pjsip_msg_type_e type;
-
- /** The first line of the message can be either request line for request
- * messages, or status line for response messages. It is represented here
- * as a union.
- */
- union
- {
- /** Request Line. */
- struct pjsip_request_line req;
-
- /** Status Line. */
- struct pjsip_status_line status;
- } line;
-
- /** List of message headers. */
- pjsip_hdr hdr;
-
- /** Pointer to message body, or NULL if no message body is attached to
- * this mesage.
- */
- pjsip_msg_body *body;
-};
-
-
-/**
- * Create new request or response message.
- *
- * @param pool The pool.
- * @param type Message type.
- * @return New message, or THROW exception if failed.
- */
-PJ_DECL(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type);
-
-/**
- * Find a header in the message by the header type.
- *
- * @param msg The message.
- * @param type The header type to find.
- * @param start The first header field where the search should begin.
- * If NULL is specified, then the search will begin from the
- * first header, otherwise the search will begin at the
- * specified header.
- *
- * @return The header field, or NULL if no header with the specified
- * type is found.
- */
-PJ_DECL(void*) pjsip_msg_find_hdr( pjsip_msg *msg,
- pjsip_hdr_e type, void *start);
-
-/**
- * Find a header in the message by its name.
- *
- * @param msg The message.
- * @param name The header name to find.
- * @param start The first header field where the search should begin.
- * If NULL is specified, then the search will begin from the
- * first header, otherwise the search will begin at the
- * specified header.
- *
- * @return The header field, or NULL if no header with the specified
- * type is found.
- */
-PJ_DECL(void*) pjsip_msg_find_hdr_by_name( pjsip_msg *msg,
- const pj_str_t *name, void *start);
-
-/**
- * Find and remove a header in the message.
- *
- * @param msg The message.
- * @param hdr The header type to find.
- * @param start The first header field where the search should begin,
- * or NULL to search from the first header in the message.
- *
- * @return The header field, or NULL if not found.
- */
-PJ_DECL(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg,
- pjsip_hdr_e hdr, void *start);
-
-/**
- * Add a header to the message, putting it last in the header list.
- *
- * @param msg The message.
- * @param hdr The header to add.
- *
- * @bug Once the header is put in a list (or message), it can not be put in
- * other list (or message). Otherwise Real Bad Thing will happen.
- */
-PJ_IDECL(void) pjsip_msg_add_hdr( pjsip_msg *msg, pjsip_hdr *hdr );
-
-/**
- * Add header field to the message, putting it in the front of the header list.
- *
- * @param msg The message.
- * @param hdr The header to add.
- *
- * @bug Once the header is put in a list (or message), it can not be put in
- * other list (or message). Otherwise Real Bad Thing will happen.
- */
-PJ_IDECL(void) pjsip_msg_insert_first_hdr( pjsip_msg *msg, pjsip_hdr *hdr );
-
-/**
- * Print the message to the specified buffer.
- *
- * @param msg The message to print.
- * @param buf The buffer
- * @param size The size of the buffer.
- *
- * @return The length of the printed characters (in bytes), or NEGATIVE
- * value if the message is too large for the specified buffer.
- */
-PJ_DECL(pj_ssize_t) pjsip_msg_print(pjsip_msg *msg, char *buf, pj_size_t size);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @addtogroup PJSIP_MSG_HDR_GEN Header Field: Generic
- * @brief Generic header field which contains header name and value.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * Generic SIP header, which contains hname and a string hvalue.
- * Note that this header is not supposed to be used as 'base' class for headers.
- */
-typedef struct pjsip_generic_string_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_string_hdr); /**< Standard header field. */
- pj_str_t hvalue; /**< hvalue */
-} pjsip_generic_string_hdr;
-
-
-/**
- * Create a new instance of generic header. A generic header can have an
- * arbitrary header name.
- *
- * @param pool The pool.
- * @param hname The header name to be assigned to the header, or NULL to
- * assign the header name with some string.
- *
- * @return The header, or THROW exception.
- */
-PJ_DECL(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create( pj_pool_t *pool,
- const pj_str_t *hname );
-
-/**
- * Create a generic header along with the text content.
- *
- * @param pool The pool.
- * @param hname The header name.
- * @param hvalue The header text content.
- *
- * @return The header instance.
- */
-PJ_DECL(pjsip_generic_string_hdr*)
-pjsip_generic_string_hdr_create_with_text( pj_pool_t *pool,
- const pj_str_t *hname,
- const pj_str_t *hvalue);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @addtogroup PJSIP_MSG_HDR_GEN_INT Header Field: Generic Integer
- * @brief Generic header field which contains header name and value.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * Generic SIP header, which contains hname and a string hvalue.
- */
-typedef struct pjsip_generic_int_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_int_hdr); /**< Standard header field. */
- pj_int32_t ivalue; /**< ivalue */
-} pjsip_generic_int_hdr;
-
-
-/**
- * Create a new instance of generic header. A generic header can have an
- * arbitrary header name.
- *
- * @param pool The pool.
- * @param hname The header name to be assigned to the header, or NULL to
- * assign the header name with some string.
- *
- * @return The header, or THROW exception.
- */
-PJ_DECL(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool,
- const pj_str_t *hname );
-
-/**
- * Create a generic header along with the value.
- *
- * @param pool The pool.
- * @param hname The header name.
- * @param value The header value content.
- *
- * @return The header instance.
- */
-PJ_DECL(pjsip_generic_int_hdr*)
-pjsip_generic_int_hdr_create_with_value( pj_pool_t *pool,
- const pj_str_t *hname,
- int value);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_GENERIC_LIST Header Field: Generic string list.
- * @brief Header with list of strings separated with comma
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/** Maximum elements in the header array. */
-#define PJSIP_GENERIC_ARRAY_MAX_COUNT 32
-
-typedef struct pjsip_generic_array_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_array_hdr);
- unsigned count; /**< Number of elements. */
- pj_str_t values[PJSIP_GENERIC_ARRAY_MAX_COUNT]; /**< Elements. */
-} pjsip_generic_array_hdr;
-
-/**
- * Create generic array header.
- *
- * @param pool Pool to allocate memory from.
- *
- * @return New generic array header.
- */
-PJ_DECL(pjsip_generic_array_hdr*) pjsip_generic_array_create(pj_pool_t *pool,
- const pj_str_t *hnames);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_ACCEPT Header Field: Accept
- * @brief Accept header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/** Accept header. */
-typedef pjsip_generic_array_hdr pjsip_accept_hdr;
-
-/** Maximum fields in Accept header. */
-#define PJSIP_MAX_ACCEPT_COUNT PJSIP_GENERIC_ARRAY_MAX_COUNT
-
-/**
- * Create new Accept header instance.
- *
- * @param pool The pool.
- *
- * @return New Accept header instance.
- */
-PJ_DECL(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_ALLOW Header Field: Allow
- * @brief Allow header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-typedef pjsip_generic_array_hdr pjsip_allow_hdr;
-
-/**
- * Create new Allow header instance.
- *
- * @param pool The pool.
- *
- * @return New Allow header instance.
- */
-PJ_DECL(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_CID Header Field: Call-ID
- * @brief Call-ID header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/**
- * Call-ID header.
- */
-typedef struct pjsip_cid_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_cid_hdr);
- pj_str_t id; /**< Call-ID string. */
-} pjsip_cid_hdr;
-
-
-/**
- * Create new Call-ID header.
- *
- * @param pool The pool.
- *
- * @return new Call-ID header.
- */
-PJ_DECL(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool );
-
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_CLEN Header Field: Content-Length
- * @brief Content-Length header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/**
- * Content-Length header.
- */
-typedef struct pjsip_clen_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_clen_hdr);
- int len; /**< Content length. */
-} pjsip_clen_hdr;
-
-/**
- * Create new Content-Length header.
- *
- * @param pool the pool.
- * @return A new Content-Length header instance.
- */
-PJ_DECL(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool );
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_CSEQ Header Field: CSeq
- * @brief CSeq header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/**
- * CSeq header.
- */
-typedef struct pjsip_cseq_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_cseq_hdr);
- int cseq; /**< CSeq number. */
- pjsip_method method; /**< CSeq method. */
-} pjsip_cseq_hdr;
-
-
-/** Create new CSeq header.
- *
- * @param pool The pool.
- * @return A new CSeq header instance.
- */
-PJ_DECL(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool );
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_CONTACT Header Field: Contact
- * @brief Contact header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/**
- * Contact header.
- * In this library, contact header only contains single URI. If a message has
- * multiple URI in the Contact header, the URI will be put in separate Contact
- * headers.
- */
-typedef struct pjsip_contact_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_contact_hdr);
- int star; /**< The contact contains only a '*' character */
- pjsip_uri *uri; /**< URI in the contact. */
- int q1000; /**< The "q" value times 1000 (to avoid float) */
- pj_int32_t expires; /**< Expires parameter, otherwise -1 if not present. */
- pj_str_t other_param; /**< Other parameters, concatenated in a single string. */
-} pjsip_contact_hdr;
-
-
-/**
- * Create a new Contact header.
- *
- * @param pool The pool.
- * @return A new instance of Contact header.
- */
-PJ_DECL(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool );
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_CTYPE Header Field: Content-Type
- * @brief Content-Type header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/**
- * Content-Type.
- */
-typedef struct pjsip_ctype_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_ctype_hdr);
- pjsip_media_type media; /**< Media type. */
-} pjsip_ctype_hdr;
-
-
-/**
- * Create a nwe Content Type header.
- *
- * @param pool The pool.
- * @return A new Content-Type header.
- */
-PJ_DECL(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool );
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_EXPIRES Header Field: Expires
- * @brief Expires header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/** Expires header. */
-typedef pjsip_generic_int_hdr pjsip_expires_hdr;
-
-/**
- * Create a new Expires header.
- *
- * @param pool The pool.
- * @return A new Expires header.
- */
-PJ_DECL(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool );
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_FROMTO Header Field: From/To
- * @brief From and To header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/**
- * To or From header.
- */
-typedef struct pjsip_fromto_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_fromto_hdr);
- pjsip_uri *uri; /**< URI in From/To header. */
- pj_str_t tag; /**< Header "tag" parameter. */
- pj_str_t other_param; /**< Other params, concatenated as a single string. */
-} pjsip_fromto_hdr;
-
-/** Alias for From header. */
-typedef pjsip_fromto_hdr pjsip_from_hdr;
-
-/** Alias for To header. */
-typedef pjsip_fromto_hdr pjsip_to_hdr;
-
-/**
- * Create a From header.
- *
- * @param pool The pool.
- * @return New instance of From header.
- */
-PJ_DECL(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool );
-
-/**
- * Create a To header.
- *
- * @param pool The pool.
- * @return New instance of To header.
- */
-PJ_DECL(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool );
-
-/**
- * Convert the header to a From header.
- *
- * @param pool The pool.
- * @return "From" header.
- */
-PJ_DECL(pjsip_from_hdr*) pjsip_fromto_set_from( pjsip_fromto_hdr *hdr );
-
-/**
- * Convert the header to a To header.
- *
- * @param pool The pool.
- * @return "To" header.
- */
-PJ_DECL(pjsip_to_hdr*) pjsip_fromto_set_to( pjsip_fromto_hdr *hdr );
-
-/**
- * @}
- */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_MAX_FORWARDS Header Field: Max-Forwards
- * @brief Max-Forwards header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-typedef pjsip_generic_int_hdr pjsip_max_forwards_hdr;
-
-/**
- * Create new Max-Forwards header instance.
- *
- * @param pool The pool.
- *
- * @return New Max-Forwards header instance.
- */
-PJ_DECL(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_MIN_EXPIRES Header Field: Min-Expires
- * @brief Min-Expires header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-typedef pjsip_generic_int_hdr pjsip_min_expires_hdr;
-
-/**
- * Create new Max-Forwards header instance.
- *
- * @param pool The pool.
- *
- * @return New Max-Forwards header instance.
- */
-PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_ROUTING Header Field: Record-Route/Route
- * @brief Record-Route and Route header fields.
- * @ingroup PJSIP_MSG
- * @{
- */
-/**
- * Record-Route and Route headers.
- */
-typedef struct pjsip_routing_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_routing_hdr); /**< Generic header fields. */
- pjsip_name_addr name_addr; /**< The URL in the Route/Record-Route header. */
- pj_str_t other_param; /** Other parameter. */
-} pjsip_routing_hdr;
-
-/** Alias for Record-Route header. */
-typedef pjsip_routing_hdr pjsip_rr_hdr;
-
-/** Alias for Route header. */
-typedef pjsip_routing_hdr pjsip_route_hdr;
-
-
-/**
- * Create new Record-Route header from the pool.
- *
- * @param pool The pool.
- * @return A new instance of Record-Route header.
- */
-PJ_DECL(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool );
-
-/**
- * Create new Route header from the pool.
- *
- * @param pool The pool.
- * @return A new instance of "Route" header.
- */
-PJ_DECL(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool );
-
-/**
- * Convert generic routing header to Record-Route header.
- *
- * @param r The generic routing header, or a "Routing" header.
- * @return Record-Route header.
- */
-PJ_DECL(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *r );
-
-/**
- * Convert generic routing header to "Route" header.
- *
- * @param r The generic routing header, or a "Record-Route" header.
- * @return "Route" header.
- */
-PJ_DECL(pjsip_route_hdr*) pjsip_routing_hdr_set_route( pjsip_routing_hdr *r );
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_REQUIRE Header Field: Require
- * @brief Require header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-typedef pjsip_generic_array_hdr pjsip_require_hdr;
-
-/**
- * Create new Require header instance.
- *
- * @param pool The pool.
- *
- * @return New Require header instance.
- */
-PJ_DECL(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_RETRY_AFTER Header Field: Retry-After
- * @brief Retry-After header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-typedef pjsip_generic_int_hdr pjsip_retry_after_hdr;
-
-/**
- * Create new Retry-After header instance.
- *
- * @param pool The pool.
- *
- * @return New Retry-After header instance.
- */
-PJ_DECL(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_SUPPORTED Header Field: Supported
- * @brief Supported header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-typedef pjsip_generic_array_hdr pjsip_supported_hdr;
-
-/**
- * Create new Supported header instance.
- *
- * @param pool The pool.
- *
- * @return New Supported header instance.
- */
-PJ_DECL(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_UNSUPPORTED Header Field: Unsupported
- * @brief Unsupported header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-typedef pjsip_generic_array_hdr pjsip_unsupported_hdr;
-
-/**
- * Create new Unsupported header instance.
- *
- * @param pool The pool.
- *
- * @return New Unsupported header instance.
- */
-PJ_DECL(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_VIA Header Field: Via
- * @brief Via header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/**
- * SIP Via header.
- * In this implementation, Via header can only have one element in each header.
- * If a message arrives with multiple elements in a single Via, then they will
- * be split up into multiple Via headers.
- */
-typedef struct pjsip_via_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_via_hdr);
- pj_str_t transport; /**< Transport type. */
- pjsip_host_port sent_by; /**< Host and optional port */
- int ttl_param; /**< TTL parameter, or -1 if it's not specified. */
- int rport_param; /**< "rport" parameter, 0 to specify without
- port number, -1 means doesn't exist. */
- pj_str_t maddr_param; /**< "maddr" parameter. */
- pj_str_t recvd_param; /**< "received" parameter. */
- pj_str_t branch_param; /**< "branch" parameter. */
- pj_str_t other_param; /**< Other parameters, concatenated as single string. */
- pj_str_t comment; /**< Comment. */
-} pjsip_via_hdr;
-
-/**
- * Create a new Via header.
- *
- * @param pool The pool.
- * @return A new "Via" header instance.
- */
-PJ_DECL(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool );
-
-/**
- * @}
- */
-
-/**
- * @bug Once a header is put in the message, the header CAN NOT be put in
- * other list. Solution:
- * - always clone header in the message.
- * - create a list node for each header in the message.
- */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_UNIMP Unimplemented Header Fields
- * @brief Unimplemented header fields.
- * @ingroup PJSIP_MSG
- * @{
- */
-/** Accept-Encoding header. */
-typedef pjsip_generic_string_hdr pjsip_accept_encoding_hdr;
-
-/** Create Accept-Encoding header. */
-#define pjsip_accept_encoding_hdr_create pjsip_generic_string_hdr_create
-
-/** Accept-Language header. */
-typedef pjsip_generic_string_hdr pjsip_accept_lang_hdr;
-
-/** Create Accept-Language header. */
-#define pjsip_accept_lang_hdr_create pjsip_generic_string_hdr_create
-
-/** Alert-Info header. */
-typedef pjsip_generic_string_hdr pjsip_alert_info_hdr;
-
-/** Create Alert-Info header. */
-#define pjsip_alert_info_hdr_create pjsip_generic_string_hdr_create
-
-/** Authentication-Info header. */
-typedef pjsip_generic_string_hdr pjsip_auth_info_hdr;
-
-/** Create Authentication-Info header. */
-#define pjsip_auth_info_hdr_create pjsip_generic_string_hdr_create
-
-/** Call-Info header. */
-typedef pjsip_generic_string_hdr pjsip_call_info_hdr;
-
-/** Create Call-Info header. */
-#define pjsip_call_info_hdr_create pjsip_generic_string_hdr_create
-
-/** Content-Disposition header. */
-typedef pjsip_generic_string_hdr pjsip_content_disposition_hdr;
-
-/** Create Content-Disposition header. */
-#define pjsip_content_disposition_hdr_create pjsip_generic_string_hdr_create
-
-/** Content-Encoding header. */
-typedef pjsip_generic_string_hdr pjsip_content_encoding_hdr;
-
-/** Create Content-Encoding header. */
-#define pjsip_content_encoding_hdr_create pjsip_generic_string_hdr_create
-
-/** Content-Language header. */
-typedef pjsip_generic_string_hdr pjsip_content_lang_hdr;
-
-/** Create Content-Language header. */
-#define pjsip_content_lang_hdr_create pjsip_generic_string_hdr_create
-
-/** Date header. */
-typedef pjsip_generic_string_hdr pjsip_date_hdr;
-
-/** Create Date header. */
-#define pjsip_date_hdr_create pjsip_generic_string_hdr_create
-
-/** Error-Info header. */
-typedef pjsip_generic_string_hdr pjsip_err_info_hdr;
-
-/** Create Error-Info header. */
-#define pjsip_err_info_hdr_create pjsip_generic_string_hdr_create
-
-/** In-Reply-To header. */
-typedef pjsip_generic_string_hdr pjsip_in_reply_to_hdr;
-
-/** Create In-Reply-To header. */
-#define pjsip_in_reply_to_hdr_create pjsip_generic_string_hdr_create
-
-/** MIME-Version header. */
-typedef pjsip_generic_string_hdr pjsip_mime_version_hdr;
-
-/** Create MIME-Version header. */
-#define pjsip_mime_version_hdr_create pjsip_generic_string_hdr_create
-
-/** Organization header. */
-typedef pjsip_generic_string_hdr pjsip_organization_hdr;
-
-/** Create Organization header. */
-#define pjsip_organization_hdr_create pjsip_genric_string_hdr_create
-
-/** Priority header. */
-typedef pjsip_generic_string_hdr pjsip_priority_hdr;
-
-/** Create Priority header. */
-#define pjsip_priority_hdr_create pjsip_generic_string_hdr_create
-
-/** Proxy-Require header. */
-typedef pjsip_generic_string_hdr pjsip_proxy_require_hdr;
-
-/** Reply-To header. */
-typedef pjsip_generic_string_hdr pjsip_reply_to_hdr;
-
-/** Create Reply-To header. */
-#define pjsip_reply_to_hdr_create pjsip_generic_string_hdr_create
-
-/** Server header. */
-typedef pjsip_generic_string_hdr pjsip_server_hdr;
-
-/** Create Server header. */
-#define pjsip_server_hdr_create pjsip_generic_string_hdr_create
-
-/** Subject header. */
-typedef pjsip_generic_string_hdr pjsip_subject_hdr;
-
-/** Create Subject header. */
-#define pjsip_subject_hdr_create pjsip_generic_string_hdr_create
-
-/** Timestamp header. */
-typedef pjsip_generic_string_hdr pjsip_timestamp_hdr;
-
-/** Create Timestamp header. */
-#define pjsip_timestamp_hdr_create pjsip_generic_string_hdr_create
-
-/** User-Agent header. */
-typedef pjsip_generic_string_hdr pjsip_user_agent_hdr;
-
-/** Create User-Agent header. */
-#define pjsip_user_agent_hdr_create pjsip_generic_string_hdr_create
-
-/** Warning header. */
-typedef pjsip_generic_string_hdr pjsip_warning_hdr;
-
-/** Create Warning header. */
-#define pjsip_warning_hdr_create pjsip_generic_string_hdr_create
-
-/**
- * @}
- */
-
-/**
- * @} // PJSIP_MSG
- */
-
-/*
- * Include inline definitions.
- */
-#if PJ_FUNCTIONS_ARE_INLINED
-# include <pjsip/sip_msg_i.h>
-#endif
-
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_MSG_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIP_MSG_H__ +#define __PJSIP_SIP_MSG_H__ + +/** + * @file sip_msg.h + * @brief SIP Message Structure. + */ + +#include <pjsip/sip_types.h> +#include <pjsip/sip_uri.h> +#include <pj/list.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_MSG SIP Message Structure + * @ingroup PJSIP + * @{ + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_METHOD Methods + * @brief Method names and manipulation. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * This enumeration declares SIP methods as described by RFC3261. Additional + * methods do exist, and they are described by corresponding RFCs for the SIP + * extentensions. Since they won't alter the characteristic of the processing + * of the message, they don't need to be explicitly mentioned here. + */ +typedef enum pjsip_method_e +{ + /** INVITE method, for establishing dialogs. */ + PJSIP_INVITE_METHOD, + + /** CANCEL method, for cancelling request. */ + PJSIP_CANCEL_METHOD, + + /** ACK method, for acknowledging final response to INVITE. */ + PJSIP_ACK_METHOD, + + /** BYE method, for terminating dialog. */ + PJSIP_BYE_METHOD, + + /** REGISTER method. */ + PJSIP_REGISTER_METHOD, + + /** OPTIONS method, for querying remote capabilities. */ + PJSIP_OPTIONS_METHOD, + + /** Other method, which means that the method name itself will be stored + elsewhere. */ + PJSIP_OTHER_METHOD, + +} pjsip_method_e; + + + +/** + * This structure represents a SIP method. + * Application must always use either #pjsip_method_init or #pjsip_method_set + * to make sure that method name is initialized correctly. This way, the name + * member will always contain a valid method string regardless whether the ID + * is recognized or not. + */ +typedef struct pjsip_method +{ + pjsip_method_e id; /**< Method ID, from \a pjsip_method_e. */ + pj_str_t name; /**< Method name, which will always contain the + method string. */ +} pjsip_method; + + +/** + * Initialize the method structure from a string. + * This function will check whether the method is a known method then set + * both the id and name accordingly. + * + * @param m The method to initialize. + * @param pool Pool where memory allocation will be allocated from, if required. + * @param str The method string. + */ +PJ_DECL(void) pjsip_method_init( pjsip_method *m, + pj_pool_t *pool, + const pj_str_t *str); + +/** + * Initialize the method structure from a string, without cloning the string. + * See #pjsip_method_init. + * + * @param m The method structure to be initialized. + * @param str The method string. + */ +PJ_DECL(void) pjsip_method_init_np( pjsip_method *m, + pj_str_t *str); + +/** + * Set the method with the predefined method ID. + * This function will also set the name member of the structure to the correct + * string according to the method. + * + * @param m The method structure. + * @param id The method ID. + */ +PJ_DECL(void) pjsip_method_set( pjsip_method *m, pjsip_method_e id ); + + +/** + * Copy one method structure to another. If the method is of the known methods, + * then memory allocation is not required. + * + * @param pool Pool to allocate memory from, if required. + * @param method The destination method to copy to. + * @param rhs The source method to copy from. + */ +PJ_DECL(void) pjsip_method_copy( pj_pool_t *pool, + pjsip_method *method, + const pjsip_method *rhs ); + +/** + * Compare one method with another, and conveniently determine whether the + * first method is equal, less than, or greater than the second method. + * + * @param m1 The first method. + * @param m2 The second method. + * + * @return Zero if equal, otherwise will return -1 if less or +1 if greater. + */ +PJ_DECL(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR Header Fields General Structure. + * @brief General Header Fields Structure. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * Header types, as defined by RFC3261. + */ +typedef enum pjsip_hdr_e +{ + /* + * These are the headers documented in RFC3261. Headers not documented + * there must have type PJSIP_H_OTHER, and the header type itself is + * recorded in the header name string. + * + * DO NOT CHANGE THE VALUE/ORDER OF THE HEADER IDs!!!. + */ + PJSIP_H_ACCEPT, + PJSIP_H_ACCEPT_ENCODING_UNIMP, + PJSIP_H_ACCEPT_LANGUAGE_UNIMP, + PJSIP_H_ALERT_INFO_UNIMP, + PJSIP_H_ALLOW, + PJSIP_H_AUTHENTICATION_INFO_UNIMP, + PJSIP_H_AUTHORIZATION, + PJSIP_H_CALL_ID, + PJSIP_H_CALL_INFO_UNIMP, + PJSIP_H_CONTACT, + PJSIP_H_CONTENT_DISPOSITION_UNIMP, + PJSIP_H_CONTENT_ENCODING_UNIMP, + PJSIP_H_CONTENT_LANGUAGE_UNIMP, + PJSIP_H_CONTENT_LENGTH, + PJSIP_H_CONTENT_TYPE, + PJSIP_H_CSEQ, + PJSIP_H_DATE_UNIMP, + PJSIP_H_ERROR_INFO_UNIMP, + PJSIP_H_EXPIRES, + PJSIP_H_FROM, + PJSIP_H_IN_REPLY_TO_UNIMP, + PJSIP_H_MAX_FORWARDS, + PJSIP_H_MIME_VERSION_UNIMP, + PJSIP_H_MIN_EXPIRES, + PJSIP_H_ORGANIZATION_UNIMP, + PJSIP_H_PRIORITY_UNIMP, + PJSIP_H_PROXY_AUTHENTICATE, + PJSIP_H_PROXY_AUTHORIZATION, + PJSIP_H_PROXY_REQUIRE_UNIMP, + PJSIP_H_RECORD_ROUTE, + PJSIP_H_REPLY_TO_UNIMP, + PJSIP_H_REQUIRE, + PJSIP_H_RETRY_AFTER, + PJSIP_H_ROUTE, + PJSIP_H_SERVER_UNIMP, + PJSIP_H_SUBJECT_UNIMP, + PJSIP_H_SUPPORTED, + PJSIP_H_TIMESTAMP_UNIMP, + PJSIP_H_TO, + PJSIP_H_UNSUPPORTED, + PJSIP_H_USER_AGENT_UNIMP, + PJSIP_H_VIA, + PJSIP_H_WARNING_UNIMP, + PJSIP_H_WWW_AUTHENTICATE, + + PJSIP_H_OTHER, + +} pjsip_hdr_e; + +/** + * This structure provides the pointer to basic functions that are needed + * for generic header operations. All header fields will have pointer to + * this structure, so that they can be manipulated uniformly. + */ +typedef struct pjsip_hdr_vptr +{ + /** + * Function to clone the header. + * + * @param pool Memory pool to allocate the new header. + * @param hdr Header to clone. + * + * @return A new instance of the header. + */ + void *(*clone)(pj_pool_t *pool, const void *hdr); + + /** + * Pointer to function to shallow clone the header. + * Shallow cloning will just make a memory copy of the original header, + * thus all pointers in original header will be kept intact. Because the + * function does not need to perform deep copy, the operation should be + * faster, but the application must make sure that the original header + * is still valid throughout the lifetime of new header. + * + * @param pool Memory pool to allocate the new header. + * @param hdr The header to clone. + */ + void *(*shallow_clone)(pj_pool_t *pool, const void *hdr); + + /** Pointer to function to print the header to the specified buffer. + * Returns the length of string written, or -1 if the remaining buffer + * is not enough to hold the header. + * + * @param hdr The header to print. + * @param buf The buffer. + * @param len The size of the buffer. + * + * @return The size copied to buffer, or -1 if there's not enough space. + */ + int (*print_on)(void *hdr, char *buf, pj_size_t len); + +} pjsip_hdr_vptr; + + +/** + * Generic fields for all SIP headers are declared using this macro, to make + * sure that all headers will have exactly the same layout in their start of + * the storage. This behaves like C++ inheritance actually. + */ +#define PJSIP_DECL_HDR_MEMBER(hdr) \ + /** List members. */ \ + PJ_DECL_LIST_MEMBER(hdr); \ + /** Header type */ \ + pjsip_hdr_e type; \ + /** Header name. */ \ + pj_str_t name; \ + /** Header short name version. */ \ + pj_str_t sname; \ + /** Virtual function table. */ \ + pjsip_hdr_vptr *vptr + + +/** + * Generic SIP header structure, for generic manipulation for headers in the + * message. All header fields can be typecasted to this type. + */ +struct pjsip_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_hdr); +}; + + +/** + * This generic function will clone any header, by calling "clone" function + * in header's virtual function table. + * + * @param pool The pool to allocate memory from. + * @param hdr The header to clone. + * + * @return A new instance copied from the original header. + */ +PJ_DECL(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr ); + + +/** + * This generic function will clone any header, by calling "shallow_clone" + * function in header's virtual function table. + * + * @param pool The pool to allocate memory from. + * @param hdr The header to clone. + * + * @return A new instance copied from the original header. + */ +PJ_DECL(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr ); + +/** + * This generic function will print any header, by calling "print" + * function in header's virtual function table. + * + * @param hdr The header to print. + * @param buf The buffer. + * @param len The size of the buffer. + * + * @return The size copied to buffer, or -1 if there's not enough space. + */ +PJ_DECL(int) pjsip_hdr_print_on( void *hdr, char *buf, pj_size_t len); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_LINE Request and Status Line. + * @brief Request and status line structures and manipulation. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * This structure describes SIP request line. + */ +typedef struct pjsip_request_line +{ + pjsip_method method; /**< Method for this request line. */ + pjsip_uri *uri; /**< URI for this request line. */ +} pjsip_request_line; + + +/** + * This structure describes SIP status line. + */ +typedef struct pjsip_status_line +{ + int code; /**< Status code. */ + pj_str_t reason; /**< Reason string. */ +} pjsip_status_line; + + +/** + * This enumeration lists standard SIP status codes according to RFC 3261. + * In addition, it also declares new status class 7xx for errors generated + * by the stack. This status class however should not get transmitted on the + * wire. + */ +typedef enum pjsip_status_code +{ + PJSIP_SC_TRYING = 100, + PJSIP_SC_RINGING = 180, + PJSIP_SC_CALL_BEING_FORWARDED = 181, + PJSIP_SC_QUEUED = 182, + PJSIP_SC_PROGRESS = 183, + + PJSIP_SC_OK = 200, + + PJSIP_SC_MULTIPLE_CHOICES = 300, + PJSIP_SC_MOVED_PERMANENTLY = 301, + PJSIP_SC_MOVED_TEMPORARILY = 302, + PJSIP_SC_USE_PROXY = 305, + PJSIP_SC_ALTERNATIVE_SERVICE = 380, + + PJSIP_SC_BAD_REQUEST = 400, + PJSIP_SC_UNAUTHORIZED = 401, + PJSIP_SC_PAYMENT_REQUIRED = 402, + PJSIP_SC_FORBIDDEN = 403, + PJSIP_SC_NOT_FOUND = 404, + PJSIP_SC_METHOD_NOT_ALLOWED = 405, + PJSIP_SC_NOT_ACCEPTABLE = 406, + PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED = 407, + PJSIP_SC_REQUEST_TIMEOUT = 408, + PJSIP_SC_GONE = 410, + PJSIP_SC_REQUEST_ENTITY_TOO_LARGE = 413, + PJSIP_SC_REQUEST_URI_TOO_LONG = 414, + PJSIP_SC_UNSUPPORTED_MEDIA_TYPE = 415, + PJSIP_SC_UNSUPPORTED_URI_SCHEME = 416, + PJSIP_SC_BAD_EXTENSION = 420, + PJSIP_SC_EXTENSION_REQUIRED = 421, + PJSIP_SC_INTERVAL_TOO_BRIEF = 423, + PJSIP_SC_TEMPORARILY_UNAVAILABLE = 480, + PJSIP_SC_CALL_TSX_DOES_NOT_EXIST = 481, + PJSIP_SC_LOOP_DETECTED = 482, + PJSIP_SC_TOO_MANY_HOPS = 483, + PJSIP_SC_ADDRESS_INCOMPLETE = 484, + PJSIP_AC_AMBIGUOUS = 485, + PJSIP_SC_BUSY_HERE = 486, + PJSIP_SC_REQUEST_TERMINATED = 487, + PJSIP_SC_NOT_ACCEPTABLE_HERE = 488, + PJSIP_SC_REQUEST_PENDING = 491, + PJSIP_SC_UNDECIPHERABLE = 493, + + PJSIP_SC_INTERNAL_SERVER_ERROR = 500, + PJSIP_SC_NOT_IMPLEMENTED = 501, + PJSIP_SC_BAD_GATEWAY = 502, + PJSIP_SC_SERVICE_UNAVAILABLE = 503, + PJSIP_SC_SERVER_TIMEOUT = 504, + PJSIP_SC_VERSION_NOT_SUPPORTED = 505, + PJSIP_SC_MESSAGE_TOO_LARGE = 513, + + PJSIP_SC_BUSY_EVERYWHERE = 600, + PJSIP_SC_DECLINE = 603, + PJSIP_SC_DOES_NOT_EXIST_ANYWHERE = 604, + PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE = 606, + + PJSIP_SC_TSX_TIMEOUT = 701, + PJSIP_SC_TSX_RESOLVE_ERROR = 702, + PJSIP_SC_TSX_TRANSPORT_ERROR = 703, + +} pjsip_status_code; + +/** + * Get the default status text for the status code. + * + * @param status_code SIP Status Code + * + * @return textual message for the status code. + */ +PJ_DECL(const pj_str_t*) pjsip_get_status_text(int status_code); + +/** + * This macro returns non-zero (TRUE) if the specified status_code is + * in the same class as the code_class. + * + * @param status_code The status code. + * @param code_class The status code in the class (for example 100, 200). + */ +#define PJSIP_IS_STATUS_IN_CLASS(status_code, code_class) \ + (status_code/100 == code_class/100) + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @addtogroup PJSIP_MSG_MEDIA Media Type + * @brief Media type definitions and manipulations. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * This structure describes SIP media type, as used for example in + * Accept and Content-Type header.. + */ +typedef struct pjsip_media_type +{ + pj_str_t type; /**< Media type. */ + pj_str_t subtype; /**< Media subtype. */ + pj_str_t param; /**< Media type parameters (concatenated). */ +} pjsip_media_type; + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @addtogroup PJSIP_MSG_BODY Message Body + * @brief SIP message body structures and manipulation. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * Generic abstraction to message body. + * When an incoming message is parsed (pjsip_parse_msg()), the parser fills in + * all members with the appropriate value. The 'data' and 'len' member will + * describe portion of incoming packet which denotes the message body. + * When application needs to attach message body to outgoing SIP message, it + * must fill in all members of this structure. + */ +typedef struct pjsip_msg_body +{ + /** MIME content type. + * For incoming messages, the parser will fill in this member with the + * content type found in Content-Type header. + * + * For outgoing messages, application must fill in this member with + * appropriate value, because the stack will generate Content-Type header + * based on the value specified here. + */ + pjsip_media_type content_type; + + /** Pointer to buffer which holds the message body data. + * For incoming messages, the parser will fill in this member with the + * pointer to the body string. + * + * When sending outgoing message, this member doesn't need to point to the + * actual message body string. It can be assigned with arbitrary pointer, + * because the value will only need to be understood by the print_body() + * function. The stack itself will not try to interpret this value, but + * instead will always call the print_body() whenever it needs to get the + * actual body string. + */ + void *data; + + /** The length of the data. + * For incoming messages, the parser will fill in this member with the + * actual length of message body. + * + * When sending outgoing message, again just like the "data" member, the + * "len" member doesn't need to point to the actual length of the body + * string. + */ + unsigned len; + + /** Pointer to function to print this message body. + * Application must set a proper function here when sending outgoing + * message. + * + * @param msg_body This structure itself. + * @param buf The buffer. + * @param size The buffer size. + * + * @return The length of the string printed, or -1 if there is + * not enough space in the buffer to print the whole + * message body. + */ + int (*print_body)(struct pjsip_msg_body *msg_body, + char *buf, pj_size_t size); + +} pjsip_msg_body; + +/** + * General purpose function to textual data in a SIP body. Attach this function + * in a SIP message body only if the data in pjsip_msg_body is a textual + * message ready to be embedded in a SIP message. If the data in the message + * body is not a textual body, then application must supply a custom function + * to print that body. + * + * @param msg_body The message body. + * @param buf Buffer to copy the message body to. + * @param size The size of the buffer. + * + * @return The length copied to the buffer, or -1. + */ +PJ_DECL(int) pjsip_print_text_body( pjsip_msg_body *msg_body, + char *buf, pj_size_t size); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_MSG Message Structure + * @brief Message structure and operations. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * Message type (request or response). + */ +typedef enum pjsip_msg_type_e +{ + PJSIP_REQUEST_MSG, /**< Indicates request message. */ + PJSIP_RESPONSE_MSG, /**< Indicates response message. */ +} pjsip_msg_type_e; + + +/** + * This structure describes a SIP message. + */ +struct pjsip_msg +{ + /** Message type (ie request or response). */ + pjsip_msg_type_e type; + + /** The first line of the message can be either request line for request + * messages, or status line for response messages. It is represented here + * as a union. + */ + union + { + /** Request Line. */ + struct pjsip_request_line req; + + /** Status Line. */ + struct pjsip_status_line status; + } line; + + /** List of message headers. */ + pjsip_hdr hdr; + + /** Pointer to message body, or NULL if no message body is attached to + * this mesage. + */ + pjsip_msg_body *body; +}; + + +/** + * Create new request or response message. + * + * @param pool The pool. + * @param type Message type. + * @return New message, or THROW exception if failed. + */ +PJ_DECL(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type); + +/** + * Find a header in the message by the header type. + * + * @param msg The message. + * @param type The header type to find. + * @param start The first header field where the search should begin. + * If NULL is specified, then the search will begin from the + * first header, otherwise the search will begin at the + * specified header. + * + * @return The header field, or NULL if no header with the specified + * type is found. + */ +PJ_DECL(void*) pjsip_msg_find_hdr( pjsip_msg *msg, + pjsip_hdr_e type, void *start); + +/** + * Find a header in the message by its name. + * + * @param msg The message. + * @param name The header name to find. + * @param start The first header field where the search should begin. + * If NULL is specified, then the search will begin from the + * first header, otherwise the search will begin at the + * specified header. + * + * @return The header field, or NULL if no header with the specified + * type is found. + */ +PJ_DECL(void*) pjsip_msg_find_hdr_by_name( pjsip_msg *msg, + const pj_str_t *name, void *start); + +/** + * Find and remove a header in the message. + * + * @param msg The message. + * @param hdr The header type to find. + * @param start The first header field where the search should begin, + * or NULL to search from the first header in the message. + * + * @return The header field, or NULL if not found. + */ +PJ_DECL(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg, + pjsip_hdr_e hdr, void *start); + +/** + * Add a header to the message, putting it last in the header list. + * + * @param msg The message. + * @param hdr The header to add. + * + * @bug Once the header is put in a list (or message), it can not be put in + * other list (or message). Otherwise Real Bad Thing will happen. + */ +PJ_IDECL(void) pjsip_msg_add_hdr( pjsip_msg *msg, pjsip_hdr *hdr ); + +/** + * Add header field to the message, putting it in the front of the header list. + * + * @param msg The message. + * @param hdr The header to add. + * + * @bug Once the header is put in a list (or message), it can not be put in + * other list (or message). Otherwise Real Bad Thing will happen. + */ +PJ_IDECL(void) pjsip_msg_insert_first_hdr( pjsip_msg *msg, pjsip_hdr *hdr ); + +/** + * Print the message to the specified buffer. + * + * @param msg The message to print. + * @param buf The buffer + * @param size The size of the buffer. + * + * @return The length of the printed characters (in bytes), or NEGATIVE + * value if the message is too large for the specified buffer. + */ +PJ_DECL(pj_ssize_t) pjsip_msg_print(pjsip_msg *msg, char *buf, pj_size_t size); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @addtogroup PJSIP_MSG_HDR_GEN Header Field: Generic + * @brief Generic header field which contains header name and value. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * Generic SIP header, which contains hname and a string hvalue. + * Note that this header is not supposed to be used as 'base' class for headers. + */ +typedef struct pjsip_generic_string_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_string_hdr); /**< Standard header field. */ + pj_str_t hvalue; /**< hvalue */ +} pjsip_generic_string_hdr; + + +/** + * Create a new instance of generic header. A generic header can have an + * arbitrary header name. + * + * @param pool The pool. + * @param hname The header name to be assigned to the header, or NULL to + * assign the header name with some string. + * + * @return The header, or THROW exception. + */ +PJ_DECL(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create( pj_pool_t *pool, + const pj_str_t *hname ); + +/** + * Create a generic header along with the text content. + * + * @param pool The pool. + * @param hname The header name. + * @param hvalue The header text content. + * + * @return The header instance. + */ +PJ_DECL(pjsip_generic_string_hdr*) +pjsip_generic_string_hdr_create_with_text( pj_pool_t *pool, + const pj_str_t *hname, + const pj_str_t *hvalue); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @addtogroup PJSIP_MSG_HDR_GEN_INT Header Field: Generic Integer + * @brief Generic header field which contains header name and value. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * Generic SIP header, which contains hname and a string hvalue. + */ +typedef struct pjsip_generic_int_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_int_hdr); /**< Standard header field. */ + pj_int32_t ivalue; /**< ivalue */ +} pjsip_generic_int_hdr; + + +/** + * Create a new instance of generic header. A generic header can have an + * arbitrary header name. + * + * @param pool The pool. + * @param hname The header name to be assigned to the header, or NULL to + * assign the header name with some string. + * + * @return The header, or THROW exception. + */ +PJ_DECL(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool, + const pj_str_t *hname ); + +/** + * Create a generic header along with the value. + * + * @param pool The pool. + * @param hname The header name. + * @param value The header value content. + * + * @return The header instance. + */ +PJ_DECL(pjsip_generic_int_hdr*) +pjsip_generic_int_hdr_create_with_value( pj_pool_t *pool, + const pj_str_t *hname, + int value); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_GENERIC_LIST Header Field: Generic string list. + * @brief Header with list of strings separated with comma + * @ingroup PJSIP_MSG + * @{ + */ + +/** Maximum elements in the header array. */ +#define PJSIP_GENERIC_ARRAY_MAX_COUNT 32 + +typedef struct pjsip_generic_array_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_array_hdr); + unsigned count; /**< Number of elements. */ + pj_str_t values[PJSIP_GENERIC_ARRAY_MAX_COUNT]; /**< Elements. */ +} pjsip_generic_array_hdr; + +/** + * Create generic array header. + * + * @param pool Pool to allocate memory from. + * + * @return New generic array header. + */ +PJ_DECL(pjsip_generic_array_hdr*) pjsip_generic_array_create(pj_pool_t *pool, + const pj_str_t *hnames); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_ACCEPT Header Field: Accept + * @brief Accept header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** Accept header. */ +typedef pjsip_generic_array_hdr pjsip_accept_hdr; + +/** Maximum fields in Accept header. */ +#define PJSIP_MAX_ACCEPT_COUNT PJSIP_GENERIC_ARRAY_MAX_COUNT + +/** + * Create new Accept header instance. + * + * @param pool The pool. + * + * @return New Accept header instance. + */ +PJ_DECL(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_ALLOW Header Field: Allow + * @brief Allow header field. + * @ingroup PJSIP_MSG + * @{ + */ +typedef pjsip_generic_array_hdr pjsip_allow_hdr; + +/** + * Create new Allow header instance. + * + * @param pool The pool. + * + * @return New Allow header instance. + */ +PJ_DECL(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_CID Header Field: Call-ID + * @brief Call-ID header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** + * Call-ID header. + */ +typedef struct pjsip_cid_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_cid_hdr); + pj_str_t id; /**< Call-ID string. */ +} pjsip_cid_hdr; + + +/** + * Create new Call-ID header. + * + * @param pool The pool. + * + * @return new Call-ID header. + */ +PJ_DECL(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool ); + + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_CLEN Header Field: Content-Length + * @brief Content-Length header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** + * Content-Length header. + */ +typedef struct pjsip_clen_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_clen_hdr); + int len; /**< Content length. */ +} pjsip_clen_hdr; + +/** + * Create new Content-Length header. + * + * @param pool the pool. + * @return A new Content-Length header instance. + */ +PJ_DECL(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool ); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_CSEQ Header Field: CSeq + * @brief CSeq header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** + * CSeq header. + */ +typedef struct pjsip_cseq_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_cseq_hdr); + int cseq; /**< CSeq number. */ + pjsip_method method; /**< CSeq method. */ +} pjsip_cseq_hdr; + + +/** Create new CSeq header. + * + * @param pool The pool. + * @return A new CSeq header instance. + */ +PJ_DECL(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool ); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_CONTACT Header Field: Contact + * @brief Contact header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** + * Contact header. + * In this library, contact header only contains single URI. If a message has + * multiple URI in the Contact header, the URI will be put in separate Contact + * headers. + */ +typedef struct pjsip_contact_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_contact_hdr); + int star; /**< The contact contains only a '*' character */ + pjsip_uri *uri; /**< URI in the contact. */ + int q1000; /**< The "q" value times 1000 (to avoid float) */ + pj_int32_t expires; /**< Expires parameter, otherwise -1 if not present. */ + pjsip_param other_param; /**< Other parameters, concatenated in a single string. */ +} pjsip_contact_hdr; + + +/** + * Create a new Contact header. + * + * @param pool The pool. + * @return A new instance of Contact header. + */ +PJ_DECL(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool ); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_CTYPE Header Field: Content-Type + * @brief Content-Type header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** + * Content-Type. + */ +typedef struct pjsip_ctype_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_ctype_hdr); + pjsip_media_type media; /**< Media type. */ +} pjsip_ctype_hdr; + + +/** + * Create a nwe Content Type header. + * + * @param pool The pool. + * @return A new Content-Type header. + */ +PJ_DECL(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool ); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_EXPIRES Header Field: Expires + * @brief Expires header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** Expires header. */ +typedef pjsip_generic_int_hdr pjsip_expires_hdr; + +/** + * Create a new Expires header. + * + * @param pool The pool. + * @return A new Expires header. + */ +PJ_DECL(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool ); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_FROMTO Header Field: From/To + * @brief From and To header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** + * To or From header. + */ +typedef struct pjsip_fromto_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_fromto_hdr); + pjsip_uri *uri; /**< URI in From/To header. */ + pj_str_t tag; /**< Header "tag" parameter. */ + pjsip_param other_param; /**< Other params, concatenated as a single string. */ +} pjsip_fromto_hdr; + +/** Alias for From header. */ +typedef pjsip_fromto_hdr pjsip_from_hdr; + +/** Alias for To header. */ +typedef pjsip_fromto_hdr pjsip_to_hdr; + +/** + * Create a From header. + * + * @param pool The pool. + * @return New instance of From header. + */ +PJ_DECL(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool ); + +/** + * Create a To header. + * + * @param pool The pool. + * @return New instance of To header. + */ +PJ_DECL(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool ); + +/** + * Convert the header to a From header. + * + * @param pool The pool. + * @return "From" header. + */ +PJ_DECL(pjsip_from_hdr*) pjsip_fromto_set_from( pjsip_fromto_hdr *hdr ); + +/** + * Convert the header to a To header. + * + * @param pool The pool. + * @return "To" header. + */ +PJ_DECL(pjsip_to_hdr*) pjsip_fromto_set_to( pjsip_fromto_hdr *hdr ); + +/** + * @} + */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_MAX_FORWARDS Header Field: Max-Forwards + * @brief Max-Forwards header field. + * @ingroup PJSIP_MSG + * @{ + */ +typedef pjsip_generic_int_hdr pjsip_max_forwards_hdr; + +/** + * Create new Max-Forwards header instance. + * + * @param pool The pool. + * + * @return New Max-Forwards header instance. + */ +PJ_DECL(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_MIN_EXPIRES Header Field: Min-Expires + * @brief Min-Expires header field. + * @ingroup PJSIP_MSG + * @{ + */ +typedef pjsip_generic_int_hdr pjsip_min_expires_hdr; + +/** + * Create new Max-Forwards header instance. + * + * @param pool The pool. + * + * @return New Max-Forwards header instance. + */ +PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_ROUTING Header Field: Record-Route/Route + * @brief Record-Route and Route header fields. + * @ingroup PJSIP_MSG + * @{ + */ +/** + * Record-Route and Route headers. + */ +typedef struct pjsip_routing_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_routing_hdr); /**< Generic header fields. */ + pjsip_name_addr name_addr; /**< The URL in the Route/Record-Route header. */ + pjsip_param other_param; /** Other parameter. */ +} pjsip_routing_hdr; + +/** Alias for Record-Route header. */ +typedef pjsip_routing_hdr pjsip_rr_hdr; + +/** Alias for Route header. */ +typedef pjsip_routing_hdr pjsip_route_hdr; + + +/** + * Create new Record-Route header from the pool. + * + * @param pool The pool. + * @return A new instance of Record-Route header. + */ +PJ_DECL(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool ); + +/** + * Create new Route header from the pool. + * + * @param pool The pool. + * @return A new instance of "Route" header. + */ +PJ_DECL(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool ); + +/** + * Convert generic routing header to Record-Route header. + * + * @param r The generic routing header, or a "Routing" header. + * @return Record-Route header. + */ +PJ_DECL(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *r ); + +/** + * Convert generic routing header to "Route" header. + * + * @param r The generic routing header, or a "Record-Route" header. + * @return "Route" header. + */ +PJ_DECL(pjsip_route_hdr*) pjsip_routing_hdr_set_route( pjsip_routing_hdr *r ); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_REQUIRE Header Field: Require + * @brief Require header field. + * @ingroup PJSIP_MSG + * @{ + */ +typedef pjsip_generic_array_hdr pjsip_require_hdr; + +/** + * Create new Require header instance. + * + * @param pool The pool. + * + * @return New Require header instance. + */ +PJ_DECL(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_RETRY_AFTER Header Field: Retry-After + * @brief Retry-After header field. + * @ingroup PJSIP_MSG + * @{ + */ +typedef pjsip_generic_int_hdr pjsip_retry_after_hdr; + +/** + * Create new Retry-After header instance. + * + * @param pool The pool. + * + * @return New Retry-After header instance. + */ +PJ_DECL(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_SUPPORTED Header Field: Supported + * @brief Supported header field. + * @ingroup PJSIP_MSG + * @{ + */ +typedef pjsip_generic_array_hdr pjsip_supported_hdr; + +/** + * Create new Supported header instance. + * + * @param pool The pool. + * + * @return New Supported header instance. + */ +PJ_DECL(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_UNSUPPORTED Header Field: Unsupported + * @brief Unsupported header field. + * @ingroup PJSIP_MSG + * @{ + */ +typedef pjsip_generic_array_hdr pjsip_unsupported_hdr; + +/** + * Create new Unsupported header instance. + * + * @param pool The pool. + * + * @return New Unsupported header instance. + */ +PJ_DECL(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_VIA Header Field: Via + * @brief Via header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** + * SIP Via header. + * In this implementation, Via header can only have one element in each header. + * If a message arrives with multiple elements in a single Via, then they will + * be split up into multiple Via headers. + */ +typedef struct pjsip_via_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_via_hdr); + pj_str_t transport; /**< Transport type. */ + pjsip_host_port sent_by; /**< Host and optional port */ + int ttl_param; /**< TTL parameter, or -1 if it's not specified. */ + int rport_param; /**< "rport" parameter, 0 to specify without + port number, -1 means doesn't exist. */ + pj_str_t maddr_param; /**< "maddr" parameter. */ + pj_str_t recvd_param; /**< "received" parameter. */ + pj_str_t branch_param; /**< "branch" parameter. */ + pjsip_param other_param; /**< Other parameters, concatenated as single string. */ + pj_str_t comment; /**< Comment. */ +} pjsip_via_hdr; + +/** + * Create a new Via header. + * + * @param pool The pool. + * @return A new "Via" header instance. + */ +PJ_DECL(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool ); + +/** + * @} + */ + +/** + * @bug Once a header is put in the message, the header CAN NOT be put in + * other list. Solution: + * - always clone header in the message. + * - create a list node for each header in the message. + */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_UNIMP Unimplemented Header Fields + * @brief Unimplemented header fields. + * @ingroup PJSIP_MSG + * @{ + */ +/** Accept-Encoding header. */ +typedef pjsip_generic_string_hdr pjsip_accept_encoding_hdr; + +/** Create Accept-Encoding header. */ +#define pjsip_accept_encoding_hdr_create pjsip_generic_string_hdr_create + +/** Accept-Language header. */ +typedef pjsip_generic_string_hdr pjsip_accept_lang_hdr; + +/** Create Accept-Language header. */ +#define pjsip_accept_lang_hdr_create pjsip_generic_string_hdr_create + +/** Alert-Info header. */ +typedef pjsip_generic_string_hdr pjsip_alert_info_hdr; + +/** Create Alert-Info header. */ +#define pjsip_alert_info_hdr_create pjsip_generic_string_hdr_create + +/** Authentication-Info header. */ +typedef pjsip_generic_string_hdr pjsip_auth_info_hdr; + +/** Create Authentication-Info header. */ +#define pjsip_auth_info_hdr_create pjsip_generic_string_hdr_create + +/** Call-Info header. */ +typedef pjsip_generic_string_hdr pjsip_call_info_hdr; + +/** Create Call-Info header. */ +#define pjsip_call_info_hdr_create pjsip_generic_string_hdr_create + +/** Content-Disposition header. */ +typedef pjsip_generic_string_hdr pjsip_content_disposition_hdr; + +/** Create Content-Disposition header. */ +#define pjsip_content_disposition_hdr_create pjsip_generic_string_hdr_create + +/** Content-Encoding header. */ +typedef pjsip_generic_string_hdr pjsip_content_encoding_hdr; + +/** Create Content-Encoding header. */ +#define pjsip_content_encoding_hdr_create pjsip_generic_string_hdr_create + +/** Content-Language header. */ +typedef pjsip_generic_string_hdr pjsip_content_lang_hdr; + +/** Create Content-Language header. */ +#define pjsip_content_lang_hdr_create pjsip_generic_string_hdr_create + +/** Date header. */ +typedef pjsip_generic_string_hdr pjsip_date_hdr; + +/** Create Date header. */ +#define pjsip_date_hdr_create pjsip_generic_string_hdr_create + +/** Error-Info header. */ +typedef pjsip_generic_string_hdr pjsip_err_info_hdr; + +/** Create Error-Info header. */ +#define pjsip_err_info_hdr_create pjsip_generic_string_hdr_create + +/** In-Reply-To header. */ +typedef pjsip_generic_string_hdr pjsip_in_reply_to_hdr; + +/** Create In-Reply-To header. */ +#define pjsip_in_reply_to_hdr_create pjsip_generic_string_hdr_create + +/** MIME-Version header. */ +typedef pjsip_generic_string_hdr pjsip_mime_version_hdr; + +/** Create MIME-Version header. */ +#define pjsip_mime_version_hdr_create pjsip_generic_string_hdr_create + +/** Organization header. */ +typedef pjsip_generic_string_hdr pjsip_organization_hdr; + +/** Create Organization header. */ +#define pjsip_organization_hdr_create pjsip_genric_string_hdr_create + +/** Priority header. */ +typedef pjsip_generic_string_hdr pjsip_priority_hdr; + +/** Create Priority header. */ +#define pjsip_priority_hdr_create pjsip_generic_string_hdr_create + +/** Proxy-Require header. */ +typedef pjsip_generic_string_hdr pjsip_proxy_require_hdr; + +/** Reply-To header. */ +typedef pjsip_generic_string_hdr pjsip_reply_to_hdr; + +/** Create Reply-To header. */ +#define pjsip_reply_to_hdr_create pjsip_generic_string_hdr_create + +/** Server header. */ +typedef pjsip_generic_string_hdr pjsip_server_hdr; + +/** Create Server header. */ +#define pjsip_server_hdr_create pjsip_generic_string_hdr_create + +/** Subject header. */ +typedef pjsip_generic_string_hdr pjsip_subject_hdr; + +/** Create Subject header. */ +#define pjsip_subject_hdr_create pjsip_generic_string_hdr_create + +/** Timestamp header. */ +typedef pjsip_generic_string_hdr pjsip_timestamp_hdr; + +/** Create Timestamp header. */ +#define pjsip_timestamp_hdr_create pjsip_generic_string_hdr_create + +/** User-Agent header. */ +typedef pjsip_generic_string_hdr pjsip_user_agent_hdr; + +/** Create User-Agent header. */ +#define pjsip_user_agent_hdr_create pjsip_generic_string_hdr_create + +/** Warning header. */ +typedef pjsip_generic_string_hdr pjsip_warning_hdr; + +/** Create Warning header. */ +#define pjsip_warning_hdr_create pjsip_generic_string_hdr_create + +/** + * @} + */ + +/** + * @} // PJSIP_MSG + */ + +/* + * Include inline definitions. + */ +#if PJ_FUNCTIONS_ARE_INLINED +# include <pjsip/sip_msg_i.h> +#endif + + +PJ_END_DECL + +#endif /* __PJSIP_SIP_MSG_H__ */ + diff --git a/pjsip/include/pjsip/sip_msg_i.h b/pjsip/include/pjsip/sip_msg_i.h index 4a545ca8..c6c87b9a 100644 --- a/pjsip/include/pjsip/sip_msg_i.h +++ b/pjsip/include/pjsip/sip_msg_i.h @@ -1,29 +1,29 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-PJ_IDEF(void) pjsip_msg_add_hdr( pjsip_msg *msg, pjsip_hdr *hdr )
-{
- pj_list_insert_before(&msg->hdr, hdr);
-}
-
-PJ_IDEF(void) pjsip_msg_insert_first_hdr( pjsip_msg *msg, pjsip_hdr *hdr )
-{
- pj_list_insert_after(&msg->hdr, hdr);
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +PJ_IDEF(void) pjsip_msg_add_hdr( pjsip_msg *msg, pjsip_hdr *hdr ) +{ + pj_list_insert_before(&msg->hdr, hdr); +} + +PJ_IDEF(void) pjsip_msg_insert_first_hdr( pjsip_msg *msg, pjsip_hdr *hdr ) +{ + pj_list_insert_after(&msg->hdr, hdr); +} + diff --git a/pjsip/include/pjsip/sip_parser.h b/pjsip/include/pjsip/sip_parser.h index 6c30e837..2b4e6c14 100644 --- a/pjsip/include/pjsip/sip_parser.h +++ b/pjsip/include/pjsip/sip_parser.h @@ -1,353 +1,353 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_PARSER_H__
-#define __PJSIP_SIP_PARSER_H__
-
-/**
- * @file sip_parser.h
- * @brief SIP Message Parser
- */
-
-#include <pjsip/sip_types.h>
-#include <pjlib-util/scanner.h>
-#include <pj/list.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_PARSER SIP Message Parser
- * @ingroup PJSIP
- * @{
- */
-
-/**
- * URI Parsing options.
- */
-enum
-{
- /** If this option is specified, function #pjsip_parse_uri will return
- * the URI object as pjsip_name_addr instead of the corresponding
- * URI object.
- */
- PJSIP_PARSE_URI_AS_NAMEADDR = 1,
-
- /** If this option is specified, function #pjsip_parse_uri and other
- * internal functions that this function calls will parse URI according
- * to convention for parsing From/To header. For example, when the URI
- * is not enclosed in brackets ("<" and ">"), all parameters will not
- * be stored to the URI (it will be stored to the header).
- */
- PJSIP_PARSE_URI_IN_FROM_TO_HDR = 2,
-};
-
-/**
- * Parser syntax error exception value.
- */
-#define PJSIP_SYN_ERR_EXCEPTION 1
-
-/**
- * This structure is used to get error reporting from parser.
- */
-typedef struct pjsip_parser_err_report
-{
- PJ_DECL_LIST_MEMBER(struct pjsip_parser_err_report);
- int exception_code; /**< Error exception (e.g. PJSIP_SYN_ERR_EXCEPTION) */
- int line; /**< Line number. */
- int col; /**< Column number. */
- pj_str_t hname; /**< Header name, if any. */
-} pjsip_parser_err_report;
-
-
-/**
- * Parsing context, the default argument for parsing functions.
- */
-typedef struct pjsip_parse_ctx
-{
- pj_scanner *scanner; /**< The scanner. */
- pj_pool_t *pool; /**< The pool. */
- pjsip_rx_data *rdata; /**< Optional rdata. */
-} pjsip_parse_ctx;
-
-
-/**
- * Type of function to parse header. The parsing function must follow these
- * specification:
- * - It must not modify the input text.
- * - The hname and HCOLON has been parsed prior to invoking the handler.
- * - It returns the header instance on success.
- * - For error reporting, it must throw PJSIP_SYN_ERR_EXCEPTION exception
- * instead of just returning NULL.
- * When exception is thrown, the return value is ignored.
- * - It must read the header separator after finished reading the header
- * body. The separator types are described below, and if they don't exist,
- * exception must be thrown. Header separator can be a:
- * - newline, such as when the header is part of a SIP message.
- * - ampersand, such as when the header is part of an URI.
- * - for the last header, these separator is optional since parsing
- * can be terminated when seeing EOF.
- */
-typedef pjsip_hdr* (pjsip_parse_hdr_func)(pjsip_parse_ctx *context);
-
-/**
- * Type of function to parse URI scheme.
- * Most of the specification of header parser handler (pjsip_parse_hdr_func)
- * also applies here (except the separator part).
- */
-typedef void* (pjsip_parse_uri_func)(pj_scanner *scanner, pj_pool_t *pool);
-
-/**
- * Register header parser handler. The parser handler MUST follow the
- * specification of header parser handler function. New registration
- * overwrites previous registration with the same name.
- *
- * @param hname The header name.
- * @param hshortname The short header name or NULL.
- * @param fptr The pointer to function to parser the header.
- *
- * @return PJ_SUCCESS if success, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_register_hdr_parser( const char *hname,
- const char *hshortname,
- pjsip_parse_hdr_func *fptr);
-
-/**
- * Unregister previously registered header parser handler.
- * All the arguments MUST exactly equal to the value specified upon
- * registration of the handler.
- *
- * @param hname The header name registered.
- * @param hshortname The short header name registered, or NULL.
- *
- * @return zero if unregistration was successfull.
- */
-PJ_DECL(pj_status_t) pjsip_unregister_hdr_parser( const char *hname,
- const char *hshortname,
- pjsip_parse_hdr_func *fptr);
-
-/**
- * Register URI scheme parser handler.
- *
- * @param scheme The URI scheme registered.
- * @param func The URI parser function.
- *
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_register_uri_parser( const char *scheme,
- pjsip_parse_uri_func *func);
-
-/**
- * Unregister URI scheme parser handler.
- * All the arguments MUST exactly equal to the value specified upon
- * registration of the handler.
- *
- * @param scheme The URI scheme as registered previously.
- * @param func The function handler as registered previously.
- *
- * @return zero if the registration was successfull.
- */
-PJ_DECL(pj_status_t) pjsip_unregister_uri_parser( const char *scheme,
- pjsip_parse_uri_func *func);
-
-/**
- * Parse an URI in the input and return the correct instance of URI.
- *
- * @param pool The pool to get memory allocations.
- * @param buf The input buffer, which size must be at least (size+1)
- * because the function will temporarily put NULL
- * termination at the end of the buffer during parsing.
- * @param size The length of the string (not counting NULL terminator).
- * @param options If no options are given (value is zero), the object
- * returned is dependent on the syntax of the URI,
- * eg. basic SIP URL, TEL URL, or name address.
- * If option PJSIP_PARSE_URI_AS_NAMEADDR is given,
- * then the returned object is always name address object,
- * with the relevant URI object contained in the name
- * address object.
- * @return The URI or NULL when failed. No exception is thrown by
- * this function (or any public parser functions).
- */
-PJ_DECL(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool,
- char *buf, pj_size_t size,
- unsigned option);
-
-/**
- * Parse a packet buffer and build a full SIP message from the packet. This
- * function parses all parts of the message, including request/status line,
- * all headers, and the message body. The message body however is only
- * treated as a text block, ie. the function will not try to parse the content
- * of the body.
- *
- * @param pool The pool to allocate memory.
- * @param buf The input buffer, which size must be at least (size+1)
- * because the function will temporarily put NULL
- * termination at the end of the buffer during parsing.
- * @param size The length of the string (not counting NULL terminator).
- * @param err_list If this parameter is not NULL, then the parser will
- * put error messages during parsing in this list.
- *
- * @return The message or NULL when failed. No exception is thrown
- * by this function (or any public parser functions).
- */
-PJ_DECL(pjsip_msg *) pjsip_parse_msg( pj_pool_t *pool,
- char *buf, pj_size_t size,
- pjsip_parser_err_report *err_list);
-
-
-/**
- * Parse a packet buffer and build a rdata. The resulting message will be
- * stored in \c msg field in the \c rdata. This behaves pretty much like
- * #pjsip_parse_msg(), except that it will also initialize the header fields
- * in the \c rdata.
- *
- * This function is normally called by the transport layer.
- *
- * @param buf The input buffer
- * @param buf The input buffer, which size must be at least (size+1)
- * because the function will temporarily put NULL
- * termination at the end of the buffer during parsing.
- * @param size The length of the string (not counting NULL terminator).
- * @param rdata The receive data buffer to store the message and
- * its elements.
- *
- * @return The message inside the rdata if successfull, or NULL.
- */
-PJ_DECL(pjsip_msg *) pjsip_parse_rdata( char *buf, pj_size_t size,
- pjsip_rx_data *rdata );
-
-/**
- * Check incoming packet to see if a (probably) valid SIP message has been
- * received.
- *
- * @param buf The input buffer, which must be NULL terminated.
- * @param size The buffer size.
- * @param msg_size [out] If message is valid, this parameter will contain
- * the size of the SIP message (including body, if any).
- *
- * @return PJ_SUCCESS if a message is found, or an error code.
- */
-PJ_DECL(pj_status_t) pjsip_find_msg(const char *buf,
- pj_size_t size,
- pj_bool_t is_datagram,
- pj_size_t *msg_size);
-
-/**
- * Parse the content of a header and return the header instance.
- * This function parses the content of a header (ie. part after colon) according
- * to the expected name, and will return the correct instance of header.
- *
- * @param pool Pool to allocate memory for the header.
- * @param hname Header name which is used to find the correct function
- * to parse the header.
- * @param line Header content, which size must be at least size+1.
- * @param size The length of the string (not counting NULL terminator,
- * if any).
- * @param parsed_len If the value is not NULL, then upon return the function
- * will fill the pointer with the length of the string
- * that has been parsed. This is usefull for two purposes,
- * one is when the string may contain more than one header
- * lines, and two when an error happen the value can
- * pinpoint the location of the error in the buffer.
- *
- * @return The instance of the header if parsing was successfull,
- * or otherwise a NULL pointer will be returned.
- */
-PJ_DECL(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname,
- char *line, pj_size_t size,
- int *parsed_len);
-
-/**
- * Parse header line(s). Multiple headers can be parsed by this function.
- * When there are multiple headers, the headers MUST be separated by either
- * a newline (as in SIP message) or ampersand mark (as in URI). This separator
- * however is optional for the last header.
- *
- * @param pool the pool.
- * @param buf the input text to parse.
- * @param size the text length.
- * @param hlist the header list to store the parsed headers. This list must
- * have been initialized before calling this function.
- * @return zero if successfull, or -1 if error is encountered. Upon error,
- * the \a hlist argument MAY contain successfully parsed headers.
- */
-PJ_DECL(pj_status_t) pjsip_parse_headers( pj_pool_t *pool,
- char *input, pj_size_t size,
- pj_list *hlist );
-
-
-/*
- * Various specification used in parsing, exported here as extern for other
- * parsers.
- */
-extern pj_cis_t
- pjsip_HOST_SPEC, /**< For scanning host part. */
- pjsip_DIGIT_SPEC, /**< Decimal digits */
- pjsip_ALPHA_SPEC, /**< Alpha (A-Z, a-z) */
- pjsip_ALNUM_SPEC, /**< Decimal + Alpha. */
- pjsip_TOKEN_SPEC, /**< Token. */
- pjsip_HEX_SPEC, /**< Hexadecimal digits. */
- pjsip_PARAM_CHAR_SPEC, /**< For scanning pname (or pvalue when it's
- not quoted.) */
- pjsip_HDR_CHAR_SPEC, /**< Chars in hname/havalue in URL. */
- pjsip_PROBE_USER_HOST_SPEC, /**< Hostname characters. */
- pjsip_PASSWD_SPEC, /**< Password. */
- pjsip_USER_SPEC, /**< User */
- pjsip_NEWLINE_OR_EOF_SPEC, /**< For eating up header.*/
- pjsip_DISPLAY_SCAN_SPEC; /**< Used when searching for display name. */
-
-/*
- * Various string constants.
- */
-extern const pj_str_t pjsip_USER_STR,
- pjsip_METHOD_STR,
- pjsip_TRANSPORT_STR,
- pjsip_MADDR_STR,
- pjsip_LR_STR,
- pjsip_SIP_STR,
- pjsip_SIPS_STR,
- pjsip_TEL_STR,
- pjsip_BRANCH_STR,
- pjsip_TTL_STR,
- pjsip_PNAME_STR,
- pjsip_Q_STR,
- pjsip_EXPIRES_STR,
- pjsip_TAG_STR;
-
-/*
- * Parser utilities.
- */
-enum
-{
- PJSIP_PARSE_REMOVE_QUOTE = 1,
-};
-
-void pjsip_parse_param_imp( pj_scanner *scanner,
- pj_str_t *pname, pj_str_t *pvalue,
- unsigned opt);
-void pjsip_concat_param_imp( pj_str_t *param, pj_pool_t *pool,
- const pj_str_t *pname, const pj_str_t *pvalue, int sepchar);
-void pjsip_parse_end_hdr_imp ( pj_scanner *scanner );
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_PARSER_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIP_PARSER_H__ +#define __PJSIP_SIP_PARSER_H__ + +/** + * @file sip_parser.h + * @brief SIP Message Parser + */ + +#include <pjsip/sip_types.h> +#include <pjlib-util/scanner.h> +#include <pj/list.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_PARSER SIP Message Parser + * @ingroup PJSIP + * @{ + */ + +/** + * URI Parsing options. + */ +enum +{ + /** If this option is specified, function #pjsip_parse_uri will return + * the URI object as pjsip_name_addr instead of the corresponding + * URI object. + */ + PJSIP_PARSE_URI_AS_NAMEADDR = 1, + + /** If this option is specified, function #pjsip_parse_uri and other + * internal functions that this function calls will parse URI according + * to convention for parsing From/To header. For example, when the URI + * is not enclosed in brackets ("<" and ">"), all parameters will not + * be stored to the URI (it will be stored to the header). + */ + PJSIP_PARSE_URI_IN_FROM_TO_HDR = 2, +}; + +/** + * Parser syntax error exception value. + */ +#define PJSIP_SYN_ERR_EXCEPTION 1 + +/** + * This structure is used to get error reporting from parser. + */ +typedef struct pjsip_parser_err_report +{ + PJ_DECL_LIST_MEMBER(struct pjsip_parser_err_report); + int exception_code; /**< Error exception (e.g. PJSIP_SYN_ERR_EXCEPTION) */ + int line; /**< Line number. */ + int col; /**< Column number. */ + pj_str_t hname; /**< Header name, if any. */ +} pjsip_parser_err_report; + + +/** + * Parsing context, the default argument for parsing functions. + */ +typedef struct pjsip_parse_ctx +{ + pj_scanner *scanner; /**< The scanner. */ + pj_pool_t *pool; /**< The pool. */ + pjsip_rx_data *rdata; /**< Optional rdata. */ +} pjsip_parse_ctx; + + +/** + * Type of function to parse header. The parsing function must follow these + * specification: + * - It must not modify the input text. + * - The hname and HCOLON has been parsed prior to invoking the handler. + * - It returns the header instance on success. + * - For error reporting, it must throw PJSIP_SYN_ERR_EXCEPTION exception + * instead of just returning NULL. + * When exception is thrown, the return value is ignored. + * - It must read the header separator after finished reading the header + * body. The separator types are described below, and if they don't exist, + * exception must be thrown. Header separator can be a: + * - newline, such as when the header is part of a SIP message. + * - ampersand, such as when the header is part of an URI. + * - for the last header, these separator is optional since parsing + * can be terminated when seeing EOF. + */ +typedef pjsip_hdr* (pjsip_parse_hdr_func)(pjsip_parse_ctx *context); + +/** + * Type of function to parse URI scheme. + * Most of the specification of header parser handler (pjsip_parse_hdr_func) + * also applies here (except the separator part). + */ +typedef void* (pjsip_parse_uri_func)(pj_scanner *scanner, pj_pool_t *pool); + +/** + * Register header parser handler. The parser handler MUST follow the + * specification of header parser handler function. New registration + * overwrites previous registration with the same name. + * + * @param hname The header name. + * @param hshortname The short header name or NULL. + * @param fptr The pointer to function to parser the header. + * + * @return PJ_SUCCESS if success, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsip_register_hdr_parser( const char *hname, + const char *hshortname, + pjsip_parse_hdr_func *fptr); + +/** + * Unregister previously registered header parser handler. + * All the arguments MUST exactly equal to the value specified upon + * registration of the handler. + * + * @param hname The header name registered. + * @param hshortname The short header name registered, or NULL. + * + * @return zero if unregistration was successfull. + */ +PJ_DECL(pj_status_t) pjsip_unregister_hdr_parser( const char *hname, + const char *hshortname, + pjsip_parse_hdr_func *fptr); + +/** + * Register URI scheme parser handler. + * + * @param scheme The URI scheme registered. + * @param func The URI parser function. + * + * @return zero on success. + */ +PJ_DECL(pj_status_t) pjsip_register_uri_parser( const char *scheme, + pjsip_parse_uri_func *func); + +/** + * Unregister URI scheme parser handler. + * All the arguments MUST exactly equal to the value specified upon + * registration of the handler. + * + * @param scheme The URI scheme as registered previously. + * @param func The function handler as registered previously. + * + * @return zero if the registration was successfull. + */ +PJ_DECL(pj_status_t) pjsip_unregister_uri_parser( const char *scheme, + pjsip_parse_uri_func *func); + +/** + * Parse an URI in the input and return the correct instance of URI. + * + * @param pool The pool to get memory allocations. + * @param buf The input buffer, which size must be at least (size+1) + * because the function will temporarily put NULL + * termination at the end of the buffer during parsing. + * @param size The length of the string (not counting NULL terminator). + * @param options If no options are given (value is zero), the object + * returned is dependent on the syntax of the URI, + * eg. basic SIP URL, TEL URL, or name address. + * If option PJSIP_PARSE_URI_AS_NAMEADDR is given, + * then the returned object is always name address object, + * with the relevant URI object contained in the name + * address object. + * @return The URI or NULL when failed. No exception is thrown by + * this function (or any public parser functions). + */ +PJ_DECL(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool, + char *buf, pj_size_t size, + unsigned option); + +/** + * Parse a packet buffer and build a full SIP message from the packet. This + * function parses all parts of the message, including request/status line, + * all headers, and the message body. The message body however is only + * treated as a text block, ie. the function will not try to parse the content + * of the body. + * + * @param pool The pool to allocate memory. + * @param buf The input buffer, which size must be at least (size+1) + * because the function will temporarily put NULL + * termination at the end of the buffer during parsing. + * @param size The length of the string (not counting NULL terminator). + * @param err_list If this parameter is not NULL, then the parser will + * put error messages during parsing in this list. + * + * @return The message or NULL when failed. No exception is thrown + * by this function (or any public parser functions). + */ +PJ_DECL(pjsip_msg *) pjsip_parse_msg( pj_pool_t *pool, + char *buf, pj_size_t size, + pjsip_parser_err_report *err_list); + + +/** + * Parse a packet buffer and build a rdata. The resulting message will be + * stored in \c msg field in the \c rdata. This behaves pretty much like + * #pjsip_parse_msg(), except that it will also initialize the header fields + * in the \c rdata. + * + * This function is normally called by the transport layer. + * + * @param buf The input buffer + * @param buf The input buffer, which size must be at least (size+1) + * because the function will temporarily put NULL + * termination at the end of the buffer during parsing. + * @param size The length of the string (not counting NULL terminator). + * @param rdata The receive data buffer to store the message and + * its elements. + * + * @return The message inside the rdata if successfull, or NULL. + */ +PJ_DECL(pjsip_msg *) pjsip_parse_rdata( char *buf, pj_size_t size, + pjsip_rx_data *rdata ); + +/** + * Check incoming packet to see if a (probably) valid SIP message has been + * received. + * + * @param buf The input buffer, which must be NULL terminated. + * @param size The buffer size. + * @param msg_size [out] If message is valid, this parameter will contain + * the size of the SIP message (including body, if any). + * + * @return PJ_SUCCESS if a message is found, or an error code. + */ +PJ_DECL(pj_status_t) pjsip_find_msg(const char *buf, + pj_size_t size, + pj_bool_t is_datagram, + pj_size_t *msg_size); + +/** + * Parse the content of a header and return the header instance. + * This function parses the content of a header (ie. part after colon) according + * to the expected name, and will return the correct instance of header. + * + * @param pool Pool to allocate memory for the header. + * @param hname Header name which is used to find the correct function + * to parse the header. + * @param line Header content, which size must be at least size+1. + * @param size The length of the string (not counting NULL terminator, + * if any). + * @param parsed_len If the value is not NULL, then upon return the function + * will fill the pointer with the length of the string + * that has been parsed. This is usefull for two purposes, + * one is when the string may contain more than one header + * lines, and two when an error happen the value can + * pinpoint the location of the error in the buffer. + * + * @return The instance of the header if parsing was successfull, + * or otherwise a NULL pointer will be returned. + */ +PJ_DECL(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname, + char *line, pj_size_t size, + int *parsed_len); + +/** + * Parse header line(s). Multiple headers can be parsed by this function. + * When there are multiple headers, the headers MUST be separated by either + * a newline (as in SIP message) or ampersand mark (as in URI). This separator + * however is optional for the last header. + * + * @param pool the pool. + * @param buf the input text to parse. + * @param size the text length. + * @param hlist the header list to store the parsed headers. This list must + * have been initialized before calling this function. + * @return zero if successfull, or -1 if error is encountered. Upon error, + * the \a hlist argument MAY contain successfully parsed headers. + */ +PJ_DECL(pj_status_t) pjsip_parse_headers( pj_pool_t *pool, + char *input, pj_size_t size, + pj_list *hlist ); + + +/* + * Various specification used in parsing, exported here as extern for other + * parsers. + */ +extern pj_cis_t + pjsip_HOST_SPEC, /**< For scanning host part. */ + pjsip_DIGIT_SPEC, /**< Decimal digits */ + pjsip_ALPHA_SPEC, /**< Alpha (A-Z, a-z) */ + pjsip_ALNUM_SPEC, /**< Decimal + Alpha. */ + pjsip_TOKEN_SPEC, /**< Token. */ + pjsip_HEX_SPEC, /**< Hexadecimal digits. */ + pjsip_PARAM_CHAR_SPEC, /**< For scanning pname (or pvalue when it's + not quoted.) */ + pjsip_HDR_CHAR_SPEC, /**< Chars in hname/havalue in URL. */ + pjsip_PROBE_USER_HOST_SPEC, /**< Hostname characters. */ + pjsip_PASSWD_SPEC, /**< Password. */ + pjsip_USER_SPEC, /**< User */ + pjsip_NEWLINE_OR_EOF_SPEC, /**< For eating up header.*/ + pjsip_DISPLAY_SCAN_SPEC; /**< Used when searching for display name. */ + +/* + * Various string constants. + */ +extern const pj_str_t pjsip_USER_STR, + pjsip_METHOD_STR, + pjsip_TRANSPORT_STR, + pjsip_MADDR_STR, + pjsip_LR_STR, + pjsip_SIP_STR, + pjsip_SIPS_STR, + pjsip_TEL_STR, + pjsip_BRANCH_STR, + pjsip_TTL_STR, + pjsip_PNAME_STR, + pjsip_Q_STR, + pjsip_EXPIRES_STR, + pjsip_TAG_STR; + +/* + * Parser utilities. + */ +enum +{ + PJSIP_PARSE_REMOVE_QUOTE = 1, +}; + +void pjsip_parse_param_imp( pj_scanner *scanner, pj_pool_t *pool, + pj_str_t *pname, pj_str_t *pvalue, + unsigned opt); +void pjsip_concat_param_imp( pj_str_t *param, pj_pool_t *pool, + const pj_str_t *pname, const pj_str_t *pvalue, int sepchar); +void pjsip_parse_end_hdr_imp ( pj_scanner *scanner ); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_SIP_PARSER_H__ */ + diff --git a/pjsip/include/pjsip/sip_private.h b/pjsip/include/pjsip/sip_private.h index 1c14e607..80417906 100644 --- a/pjsip/include/pjsip/sip_private.h +++ b/pjsip/include/pjsip/sip_private.h @@ -1,46 +1,46 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_PRIVATE_H__
-#define __PJSIP_SIP_PRIVATE_H__
-
-/**
- * @file sip_private.h
- * @brief Private structures and functions for PJSIP Library.
- */
-
-#include <pjsip/sip_types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_PRIVATE Private structures and functions (PJSIP internals)
- * @ingroup PJSIP
- * @{
- */
-
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_PRIVATE_I_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIP_PRIVATE_H__ +#define __PJSIP_SIP_PRIVATE_H__ + +/** + * @file sip_private.h + * @brief Private structures and functions for PJSIP Library. + */ + +#include <pjsip/sip_types.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_PRIVATE Private structures and functions (PJSIP internals) + * @ingroup PJSIP + * @{ + */ + + + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_PRIVATE_I_H__ */ + diff --git a/pjsip/include/pjsip/sip_resolve.h b/pjsip/include/pjsip/sip_resolve.h index efc54ace..673de2a7 100644 --- a/pjsip/include/pjsip/sip_resolve.h +++ b/pjsip/include/pjsip/sip_resolve.h @@ -1,120 +1,120 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_RESOLVE_H__
-#define __PJSIP_SIP_RESOLVE_H__
-
-/**
- * @file sip_resolve.h
- * @brief
- * This module contains the mechanism to resolve server address as specified by
- * RFC 3263 - Locating SIP Servers
- */
-
-#include <pjsip/sip_types.h>
-#include <pj/sock.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_RESOLVE SIP Server Resolver
- * @ingroup PJSIP
- * @{
- */
-
-/**
- * Maximum number of addresses returned by the resolver.
- */
-#define PJSIP_MAX_RESOLVED_ADDRESSES 8
-
-typedef struct pjsip_server_addresses pjsip_server_addresses;
-
-/**
- * The server addresses returned by the resolver.
- */
-struct pjsip_server_addresses
-{
- /** Number of address records. */
- unsigned count;
-
- /** Address records. */
- struct
- {
- /** Preferable transport to be used to contact this address. */
- pjsip_transport_type_e type;
-
- /** The server's address. */
- pj_sockaddr_in addr;
-
- } entry[PJSIP_MAX_RESOLVED_ADDRESSES];
-
-};
-
-/**
- * The type of callback function to be called when resolver finishes the job.
- *
- * @param status The status of the operation, which is zero on success.
- * @param token The token that was associated with the job when application
- * call the resolve function.
- * @param addr The addresses resolved by the operation.
- */
-typedef void pjsip_resolver_callback(pj_status_t status,
- void *token,
- const struct pjsip_server_addresses *addr);
-
-/**
- * Create resolver engine.
- *
- * @param pool The Pool.
- * @return The resolver engine.
- */
-PJ_DECL(pjsip_resolver_t*) pjsip_resolver_create(pj_pool_t *pool);
-
-/**
- * Destroy resolver engine.
- *
- * @param resolver The resolver.
- */
-PJ_DECL(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver);
-
-/**
- * Asynchronously resolve a SIP target host or domain according to rule
- * specified in RFC 3263 (Locating SIP Servers). When the resolving operation
- * has completed, the callback will be called.
- *
- * Note: at the moment we don't have implementation of RFC 3263 yet!
- *
- * @param resolver The resolver engine.
- * @param pool The pool to allocate resolver job.
- * @param target The target specification to be resolved.
- * @param token A user defined token to be passed back to callback function.
- * @param cb The callback function.
- */
-PJ_DECL(void) pjsip_resolve( pjsip_resolver_t *resolver,
- pj_pool_t *pool,
- pjsip_host_port *target,
- void *token,
- pjsip_resolver_callback *cb);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_RESOLVE_H__ */
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIP_RESOLVE_H__ +#define __PJSIP_SIP_RESOLVE_H__ + +/** + * @file sip_resolve.h + * @brief + * This module contains the mechanism to resolve server address as specified by + * RFC 3263 - Locating SIP Servers + */ + +#include <pjsip/sip_types.h> +#include <pj/sock.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_RESOLVE SIP Server Resolver + * @ingroup PJSIP + * @{ + */ + +/** + * Maximum number of addresses returned by the resolver. + */ +#define PJSIP_MAX_RESOLVED_ADDRESSES 8 + +typedef struct pjsip_server_addresses pjsip_server_addresses; + +/** + * The server addresses returned by the resolver. + */ +struct pjsip_server_addresses +{ + /** Number of address records. */ + unsigned count; + + /** Address records. */ + struct + { + /** Preferable transport to be used to contact this address. */ + pjsip_transport_type_e type; + + /** The server's address. */ + pj_sockaddr_in addr; + + } entry[PJSIP_MAX_RESOLVED_ADDRESSES]; + +}; + +/** + * The type of callback function to be called when resolver finishes the job. + * + * @param status The status of the operation, which is zero on success. + * @param token The token that was associated with the job when application + * call the resolve function. + * @param addr The addresses resolved by the operation. + */ +typedef void pjsip_resolver_callback(pj_status_t status, + void *token, + const struct pjsip_server_addresses *addr); + +/** + * Create resolver engine. + * + * @param pool The Pool. + * @return The resolver engine. + */ +PJ_DECL(pjsip_resolver_t*) pjsip_resolver_create(pj_pool_t *pool); + +/** + * Destroy resolver engine. + * + * @param resolver The resolver. + */ +PJ_DECL(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver); + +/** + * Asynchronously resolve a SIP target host or domain according to rule + * specified in RFC 3263 (Locating SIP Servers). When the resolving operation + * has completed, the callback will be called. + * + * Note: at the moment we don't have implementation of RFC 3263 yet! + * + * @param resolver The resolver engine. + * @param pool The pool to allocate resolver job. + * @param target The target specification to be resolved. + * @param token A user defined token to be passed back to callback function. + * @param cb The callback function. + */ +PJ_DECL(void) pjsip_resolve( pjsip_resolver_t *resolver, + pj_pool_t *pool, + pjsip_host_port *target, + void *token, + pjsip_resolver_callback *cb); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_SIP_RESOLVE_H__ */ diff --git a/pjsip/include/pjsip/sip_transaction.h b/pjsip/include/pjsip/sip_transaction.h index e7edceef..e95c66b1 100644 --- a/pjsip/include/pjsip/sip_transaction.h +++ b/pjsip/include/pjsip/sip_transaction.h @@ -1,271 +1,271 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_TRANSACTION_H__
-#define __PJSIP_SIP_TRANSACTION_H__
-
-/**
- * @file sip_transaction.h
- * @brief SIP Transaction
- */
-
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_resolve.h>
-#include <pj/timer.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_TRANSACT SIP Transaction
- * @ingroup PJSIP
- * @{
- */
-
-/* Forward decl. */
-struct pjsip_transaction;
-
-
-/**
- * Transaction state.
- */
-typedef enum pjsip_tsx_state_e
-{
- PJSIP_TSX_STATE_NULL,
- PJSIP_TSX_STATE_CALLING,
- PJSIP_TSX_STATE_TRYING,
- PJSIP_TSX_STATE_PROCEEDING,
- PJSIP_TSX_STATE_COMPLETED,
- PJSIP_TSX_STATE_CONFIRMED,
- PJSIP_TSX_STATE_TERMINATED,
- PJSIP_TSX_STATE_DESTROYED,
- PJSIP_TSX_STATE_MAX,
-} pjsip_tsx_state_e;
-
-
-/**
- * State of the transport in the transaction.
- * The transport is progressing independently of the transaction.
- */
-typedef enum pjsip_tsx_transport_state_e
-{
- PJSIP_TSX_TRANSPORT_STATE_NULL,
- PJSIP_TSX_TRANSPORT_STATE_RESOLVING,
- PJSIP_TSX_TRANSPORT_STATE_CONNECTING,
- PJSIP_TSX_TRANSPORT_STATE_FINAL,
-} pjsip_tsx_transport_state_e;
-
-
-/**
- * Transaction state.
- */
-struct pjsip_transaction
-{
- /*
- * Administrivia
- */
- pj_pool_t *pool; /**< Pool owned by the tsx. */
- pjsip_endpoint *endpt; /**< Endpoint instance. */
- pj_mutex_t *mutex; /**< Mutex for this tsx. */
- char obj_name[PJ_MAX_OBJ_NAME]; /**< Tsx name. */
- int tracing; /**< Tracing enabled? */
-
- /*
- * Transaction identification.
- */
- pjsip_role_e role; /**< Role (UAS or UAC) */
- pjsip_method method; /**< The method. */
- int cseq; /**< The CSeq */
- pj_str_t transaction_key;/**< hash table key. */
- pj_str_t branch; /**< The branch Id. */
-
- /*
- * State and status.
- */
- int status_code; /**< Last status code seen. */
- pjsip_tsx_state_e state; /**< State. */
- int handle_ack; /**< Should we handle ACK? */
-
- /** Handler according to current state. */
- pj_status_t (*state_handler)(struct pjsip_transaction *, pjsip_event *);
-
- /*
- * Transport.
- */
- pjsip_tsx_transport_state_e transport_state;/**< Transport's state. */
- pjsip_host_port dest_name; /**< Destination address. */
- pjsip_server_addresses remote_addr; /**< Addresses resolved. */
- int current_addr; /**< Address currently used. */
-
- pjsip_transport *transport; /**< Transport to use. */
-
- /*
- * Messages and timer.
- */
- pjsip_tx_data *last_tx; /**< Msg kept for retrans. */
- int has_unsent_msg; /**< Non-zero if tsx need to
- transmit msg once resolver
- completes. */
- int retransmit_count;/**< Retransmission count. */
- pj_timer_entry retransmit_timer;/**< Retransmit timer. */
- pj_timer_entry timeout_timer; /**< Timeout timer. */
-
- /** Module specific data. */
- void *module_data[PJSIP_MAX_MODULE];
-};
-
-
-/**
- * Create new transaction. Application would normally use
- * #pjsip_endpt_create_tsx rather than this function.
- *
- * @param pool Pool to use by the transaction.
- * @param endpt Endpoint.
- * @param p_tsx Pointer to return the transaction.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- *
- * @see pjsip_endpt_create_tsx
- *
- */
-PJ_DEF(pj_status_t) pjsip_tsx_create( pj_pool_t *pool,
- pjsip_endpoint *endpt,
- pjsip_transaction **p_tsx);
-
-/**
- * Init transaction as UAC from the specified transmit data (\c tdata).
- * The transmit data must have a valid \c Request-Line and \c CSeq header.
- * If \c Route headers are present, it will be used to calculate remote
- * destination.
- *
- * If \c Via header does not exist, it will be created along with a unique
- * \c branch parameter. If it exists and contains branch parameter, then
- * the \c branch parameter will be used as is as the transaction key.
- *
- * The \c Route headers in the transmit data, if present, are used to
- * calculate remote destination.
- *
- * At the end of the function, the transaction will start resolving the
- * addresses of remote server to contact. Transport will be acquired as soon
- * as the resolving job completes.
- *
- * @param tsx The transaction.
- * @param tdata The transmit data.
- *
- * @return PJ_SUCCESS if successfull.
- */
-PJ_DECL(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx,
- pjsip_tx_data *tdata);
-
-/**
- * Init transaction as UAS.
- *
- * @param tsx The transaction to be initialized.
- * @param rdata The received incoming request.
- *
- * @return PJ_SUCCESS if successfull.
- */
-PJ_DECL(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx,
- pjsip_rx_data *rdata);
-
-/**
- * Process incoming message for this transaction.
- *
- * @param tsx The transaction.
- * @param rdata The incoming message.
- */
-PJ_DECL(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx,
- pjsip_rx_data *rdata);
-
-/**
- * Transmit message with this transaction.
- *
- * @param tsx The transaction.
- * @param tdata The outgoing message.
- */
-PJ_DECL(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx,
- pjsip_tx_data *tdata);
-
-
-/**
- * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for
- * non-2xx/INVITE is automatically sent by the transaction.
- * This operation is only valid if the transaction is configured to handle ACK
- * (tsx->handle_ack is non-zero). If this attribute is not set, then the
- * transaction will comply with RFC-3261, i.e. it will set itself to
- * TERMINATED state when it receives 2xx/INVITE.
- *
- * @param tsx The transaction.
- * @param tdata The ACK request.
- */
-PJ_DECL(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx,
- pjsip_tx_data *tdata);
-
-/**
- * Force terminate transaction.
- *
- * @param tsx The transaction.
- * @param code The status code to report.
- */
-PJ_DECL(void) pjsip_tsx_terminate( pjsip_transaction *tsx,
- int code );
-
-/**
- * Create transaction key, which is used to match incoming requests
- * or response (retransmissions) against transactions.
- *
- * @param pool The pool
- * @param key Output key.
- * @param role The role of the transaction.
- * @param method The method to be put as a key.
- * @param rdata The received data to calculate.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_tsx_create_key( pj_pool_t *pool,
- pj_str_t *key,
- pjsip_role_e role,
- const pjsip_method *method,
- const pjsip_rx_data *rdata );
-
-
-/**
- * @}
- */
-
-/*
- * Internal.
- */
-
-/*
- * Get the string name for the state.
- */
-PJ_DECL(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state);
-
-/*
- * Get the role name.
- */
-PJ_DECL(const char *) pjsip_role_name(pjsip_role_e role);
-
-
-/* Thread Local Storage ID for transaction lock (initialized by endpoint) */
-extern long pjsip_tsx_lock_tls_id;
-
-PJ_END_DECL
-
-#endif /* __PJSIP_TRANSACT_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIP_TRANSACTION_H__ +#define __PJSIP_SIP_TRANSACTION_H__ + +/** + * @file sip_transaction.h + * @brief SIP Transaction + */ + +#include <pjsip/sip_msg.h> +#include <pjsip/sip_resolve.h> +#include <pj/timer.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_TRANSACT SIP Transaction + * @ingroup PJSIP + * @{ + */ + +/* Forward decl. */ +struct pjsip_transaction; + + +/** + * Transaction state. + */ +typedef enum pjsip_tsx_state_e +{ + PJSIP_TSX_STATE_NULL, + PJSIP_TSX_STATE_CALLING, + PJSIP_TSX_STATE_TRYING, + PJSIP_TSX_STATE_PROCEEDING, + PJSIP_TSX_STATE_COMPLETED, + PJSIP_TSX_STATE_CONFIRMED, + PJSIP_TSX_STATE_TERMINATED, + PJSIP_TSX_STATE_DESTROYED, + PJSIP_TSX_STATE_MAX, +} pjsip_tsx_state_e; + + +/** + * State of the transport in the transaction. + * The transport is progressing independently of the transaction. + */ +typedef enum pjsip_tsx_transport_state_e +{ + PJSIP_TSX_TRANSPORT_STATE_NULL, + PJSIP_TSX_TRANSPORT_STATE_RESOLVING, + PJSIP_TSX_TRANSPORT_STATE_CONNECTING, + PJSIP_TSX_TRANSPORT_STATE_FINAL, +} pjsip_tsx_transport_state_e; + + +/** + * Transaction state. + */ +struct pjsip_transaction +{ + /* + * Administrivia + */ + pj_pool_t *pool; /**< Pool owned by the tsx. */ + pjsip_endpoint *endpt; /**< Endpoint instance. */ + pj_mutex_t *mutex; /**< Mutex for this tsx. */ + char obj_name[PJ_MAX_OBJ_NAME]; /**< Tsx name. */ + int tracing; /**< Tracing enabled? */ + + /* + * Transaction identification. + */ + pjsip_role_e role; /**< Role (UAS or UAC) */ + pjsip_method method; /**< The method. */ + int cseq; /**< The CSeq */ + pj_str_t transaction_key;/**< hash table key. */ + pj_str_t branch; /**< The branch Id. */ + + /* + * State and status. + */ + int status_code; /**< Last status code seen. */ + pjsip_tsx_state_e state; /**< State. */ + int handle_ack; /**< Should we handle ACK? */ + + /** Handler according to current state. */ + pj_status_t (*state_handler)(struct pjsip_transaction *, pjsip_event *); + + /* + * Transport. + */ + pjsip_tsx_transport_state_e transport_state;/**< Transport's state. */ + pjsip_host_port dest_name; /**< Destination address. */ + pjsip_server_addresses remote_addr; /**< Addresses resolved. */ + int current_addr; /**< Address currently used. */ + + pjsip_transport *transport; /**< Transport to use. */ + + /* + * Messages and timer. + */ + pjsip_tx_data *last_tx; /**< Msg kept for retrans. */ + int has_unsent_msg; /**< Non-zero if tsx need to + transmit msg once resolver + completes. */ + int retransmit_count;/**< Retransmission count. */ + pj_timer_entry retransmit_timer;/**< Retransmit timer. */ + pj_timer_entry timeout_timer; /**< Timeout timer. */ + + /** Module specific data. */ + void *module_data[PJSIP_MAX_MODULE]; +}; + + +/** + * Create new transaction. Application would normally use + * #pjsip_endpt_create_tsx rather than this function. + * + * @param pool Pool to use by the transaction. + * @param endpt Endpoint. + * @param p_tsx Pointer to return the transaction. + * + * @return PJ_SUCCESS or the appropriate error code. + * + * @see pjsip_endpt_create_tsx + * + */ +PJ_DEF(pj_status_t) pjsip_tsx_create( pj_pool_t *pool, + pjsip_endpoint *endpt, + pjsip_transaction **p_tsx); + +/** + * Init transaction as UAC from the specified transmit data (\c tdata). + * The transmit data must have a valid \c Request-Line and \c CSeq header. + * If \c Route headers are present, it will be used to calculate remote + * destination. + * + * If \c Via header does not exist, it will be created along with a unique + * \c branch parameter. If it exists and contains branch parameter, then + * the \c branch parameter will be used as is as the transaction key. + * + * The \c Route headers in the transmit data, if present, are used to + * calculate remote destination. + * + * At the end of the function, the transaction will start resolving the + * addresses of remote server to contact. Transport will be acquired as soon + * as the resolving job completes. + * + * @param tsx The transaction. + * @param tdata The transmit data. + * + * @return PJ_SUCCESS if successfull. + */ +PJ_DECL(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx, + pjsip_tx_data *tdata); + +/** + * Init transaction as UAS. + * + * @param tsx The transaction to be initialized. + * @param rdata The received incoming request. + * + * @return PJ_SUCCESS if successfull. + */ +PJ_DECL(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx, + pjsip_rx_data *rdata); + +/** + * Process incoming message for this transaction. + * + * @param tsx The transaction. + * @param rdata The incoming message. + */ +PJ_DECL(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx, + pjsip_rx_data *rdata); + +/** + * Transmit message with this transaction. + * + * @param tsx The transaction. + * @param tdata The outgoing message. + */ +PJ_DECL(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx, + pjsip_tx_data *tdata); + + +/** + * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for + * non-2xx/INVITE is automatically sent by the transaction. + * This operation is only valid if the transaction is configured to handle ACK + * (tsx->handle_ack is non-zero). If this attribute is not set, then the + * transaction will comply with RFC-3261, i.e. it will set itself to + * TERMINATED state when it receives 2xx/INVITE. + * + * @param tsx The transaction. + * @param tdata The ACK request. + */ +PJ_DECL(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx, + pjsip_tx_data *tdata); + +/** + * Force terminate transaction. + * + * @param tsx The transaction. + * @param code The status code to report. + */ +PJ_DECL(void) pjsip_tsx_terminate( pjsip_transaction *tsx, + int code ); + +/** + * Create transaction key, which is used to match incoming requests + * or response (retransmissions) against transactions. + * + * @param pool The pool + * @param key Output key. + * @param role The role of the transaction. + * @param method The method to be put as a key. + * @param rdata The received data to calculate. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsip_tsx_create_key( pj_pool_t *pool, + pj_str_t *key, + pjsip_role_e role, + const pjsip_method *method, + const pjsip_rx_data *rdata ); + + +/** + * @} + */ + +/* + * Internal. + */ + +/* + * Get the string name for the state. + */ +PJ_DECL(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state); + +/* + * Get the role name. + */ +PJ_DECL(const char *) pjsip_role_name(pjsip_role_e role); + + +/* Thread Local Storage ID for transaction lock (initialized by endpoint) */ +extern long pjsip_tsx_lock_tls_id; + +PJ_END_DECL + +#endif /* __PJSIP_TRANSACT_H__ */ + diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h index 869641bd..dac608ff 100644 --- a/pjsip/include/pjsip/sip_transport.h +++ b/pjsip/include/pjsip/sip_transport.h @@ -1,635 +1,635 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_TRANSPORT_H__
-#define __PJSIP_SIP_TRANSPORT_H__
-
-/**
- * @file sip_transport.h
- * @brief SIP Transport
- */
-
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_parser.h>
-#include <pj/sock.h>
-#include <pj/list.h>
-#include <pj/ioqueue.h>
-#include <pj/timer.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_TRANSPORT SIP Transport
- * @ingroup PJSIP
- *
- * This is the low-level transport layer. Application normally won't need to
- * use this function, but instead can use transaction or higher layer API to
- * send and receive messages.
- *
- * @{
- */
-
-/*****************************************************************************
- *
- * GENERAL TRANSPORT (NAMES, TYPES, ETC.)
- *
- *****************************************************************************/
-
-/**
- * Flags for SIP transports.
- */
-enum pjsip_transport_flags_e
-{
- PJSIP_TRANSPORT_RELIABLE = 1, /**< Transport is reliable. */
- PJSIP_TRANSPORT_SECURE = 2, /**< Transport is secure. */
- PJSIP_TRANSPORT_DATAGRAM = 4, /**< Datagram based transport. */
-};
-
-/**
- * Check if transport tp is reliable.
- */
-#define PJSIP_TRANSPORT_IS_RELIABLE(tp) \
- ((tp)->flag & PJSIP_TRANSPORT_RELIABLE)
-
-/**
- * Get the transport type from the transport name.
- *
- * @param name Transport name, such as "TCP", or "UDP".
- *
- * @return The transport type, or PJSIP_TRANSPORT_UNSPECIFIED if
- * the name is not recognized as the name of supported
- * transport.
- */
-PJ_DECL(pjsip_transport_type_e)
-pjsip_transport_get_type_from_name(const pj_str_t *name);
-
-/**
- * Get the transport type for the specified flags.
- *
- * @param flag The transport flag.
- *
- * @return Transport type.
- */
-PJ_DECL(pjsip_transport_type_e)
-pjsip_transport_get_type_from_flag(unsigned flag);
-
-/**
- * Get transport flag from type.
- *
- * @param type Transport type.
- *
- * @return Transport flags.
- */
-PJ_DECL(unsigned)
-pjsip_transport_get_flag_from_type( pjsip_transport_type_e type );
-
-/**
- * Get the default SIP port number for the specified type.
- *
- * @param type Transport type.
- *
- * @return The port number, which is the default SIP port number for
- * the specified type.
- */
-PJ_DECL(int)
-pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type);
-
-
-/*****************************************************************************
- *
- * RECEIVE DATA BUFFER.
- *
- *****************************************************************************/
-
-/**
- * A customized ioqueue async operation key which is used by transport
- * to locate rdata when a pending read operation completes.
- */
-typedef struct pjsip_rx_data_op_key
-{
- pj_ioqueue_op_key_t op_key;
- pjsip_rx_data *rdata;
-} pjsip_rx_data_op_key;
-
-
-/**
- * Incoming message buffer.
- * This structure keep all the information regarding the received message. This
- * buffer lifetime is only very short, normally after the transaction has been
- * called, this buffer will be deleted/recycled. So care must be taken when
- * allocating storage from the pool of this buffer.
- */
-struct pjsip_rx_data
-{
-
- /**
- * tp_info is part of rdata that remains static for the duration of the
- * buffer. It is initialized when the buffer was created by transport.
- */
- struct
- {
- /** Memory pool for this buffer. */
- pj_pool_t *pool;
-
- /** The transport object which received this packet. */
- pjsip_transport *transport;
-
- /** Ioqueue key. */
- pjsip_rx_data_op_key op_key;
-
- } tp_info;
-
-
- /**
- * pkt_info is initialized by transport when it receives an incoming
- * packet.
- */
- struct
- {
- /** Time when the message was received. */
- pj_time_val timestamp;
-
- /** Pointer to the original packet. */
- char packet[PJSIP_MAX_PKT_LEN];
-
- /** Zero termination for the packet. */
- pj_uint32_t zero;
-
- /** The length of the packet received. */
- int len;
-
- /** The source address from which the packet was received. */
- pj_sockaddr_in addr;
-
- /** The length of the source address. */
- int addr_len;
-
- } pkt_info;
-
-
- /**
- * msg_info is initialized by transport mgr (tpmgr) before this buffer
- * is passed to endpoint.
- */
- struct
- {
- /** Start of msg buffer. */
- char *msg_buf;
-
- /** Length fo message. */
- int len;
-
- /** The parsed message, if any. */
- pjsip_msg *msg;
-
- /** The Call-ID header as found in the message. */
- pj_str_t call_id;
-
- /** The From header as found in the message. */
- pjsip_from_hdr *from;
-
- /** The To header as found in the message. */
- pjsip_to_hdr *to;
-
- /** The topmost Via header as found in the message. */
- pjsip_via_hdr *via;
-
- /** The CSeq header as found in the message. */
- pjsip_cseq_hdr *cseq;
-
- /** Max forwards header. */
- pjsip_max_forwards_hdr *max_fwd;
-
- /** The first route header. */
- pjsip_route_hdr *route;
-
- /** The first record-route header. */
- pjsip_rr_hdr *record_route;
-
- /** Content-type header. */
- pjsip_ctype_hdr *ctype;
-
- /** Content-length header. */
- pjsip_clen_hdr *clen;
-
- /** The first Require header. */
- pjsip_require_hdr *require;
-
- /** The list of error generated by the parser when parsing
- this message.
- */
- pjsip_parser_err_report parse_err;
-
- } msg_info;
-
-
- /**
- * endpt_info is initialized by endpoint after this buffer reaches
- * endpoint.
- */
- struct
- {
- /**
- * This the transaction key generated for the message.
- */
- pj_str_t key;
-
- } endpt_info;
-
-};
-
-
-/*****************************************************************************
- *
- * TRANSMIT DATA BUFFER MANIPULATION.
- *
- *****************************************************************************/
-
-/** Customized ioqueue async operation key, used by transport to keep
- * callback parameters.
- */
-typedef struct pjsip_tx_data_op_key
-{
- pj_ioqueue_op_key_t key;
- pjsip_tx_data *tdata;
- void *token;
- void (*callback)(pjsip_transport*,void*,pj_ssize_t);
-} pjsip_tx_data_op_key;
-
-
-/**
- * Data structure for sending outgoing message. Application normally creates
- * this buffer by calling #pjsip_endpt_create_tdata.
- *
- * The lifetime of this buffer is controlled by the reference counter in this
- * structure, which is manipulated by calling #pjsip_tx_data_add_ref and
- * #pjsip_tx_data_dec_ref. When the reference counter has reached zero, then
- * this buffer will be destroyed.
- *
- * A transaction object normally will add reference counter to this buffer
- * when application calls #pjsip_tsx_on_tx_msg, because it needs to keep the
- * message for retransmission. The transaction will release the reference
- * counter once its state has reached final state.
- */
-struct pjsip_tx_data
-{
- /** This is for transmission queue; it's managed by transports. */
- PJ_DECL_LIST_MEMBER(struct pjsip_tx_data);
-
- /** Memory pool for this buffer. */
- pj_pool_t *pool;
-
- /** A name to identify this buffer. */
- char obj_name[PJ_MAX_OBJ_NAME];
-
- /** For response message, this contains the reference to timestamp when
- * the original request message was received. The value of this field
- * is set when application creates response message to a request by
- * calling #pjsip_endpt_create_response.
- */
- pj_time_val rx_timestamp;
-
- /** The transport manager for this buffer. */
- pjsip_tpmgr *mgr;
-
- /** Ioqueue asynchronous operation key. */
- pjsip_tx_data_op_key op_key;
-
- /** Lock object. */
- pj_lock_t *lock;
-
- /** The message in this buffer. */
- pjsip_msg *msg;
-
- /** Buffer to the printed text representation of the message. When the
- * content of this buffer is set, then the transport will send the content
- * of this buffer instead of re-printing the message structure. If the
- * message structure has changed, then application must invalidate this
- * buffer by calling #pjsip_tx_data_invalidate_msg.
- */
- pjsip_buffer buf;
-
- /** Reference counter. */
- pj_atomic_t *ref_cnt;
-
- /** Being processed by transport? */
- int is_pending;
-
- /** Transport manager internal. */
- void *token;
- void (*cb)(void*, pjsip_tx_data*, pj_ssize_t);
-};
-
-
-/**
- * Create a new, blank transmit buffer. The reference count is initialized
- * to zero.
- *
- * @param mgr The transport manager.
- * @param tdata Pointer to receive transmit data.
- *
- * @return PJ_SUCCESS, or the appropriate error code.
- *
- * @see pjsip_endpt_create_tdata
- */
-pj_status_t pjsip_tx_data_create( pjsip_tpmgr *mgr,
- pjsip_tx_data **tdata );
-
-/**
- * Add reference counter to the transmit buffer. The reference counter controls
- * the life time of the buffer, ie. when the counter reaches zero, then it
- * will be destroyed.
- *
- * @param tdata The transmit buffer.
- */
-PJ_DECL(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata );
-
-/**
- * Decrement reference counter of the transmit buffer.
- * When the transmit buffer is no longer used, it will be destroyed.
- *
- * @param tdata The transmit buffer data.
- */
-PJ_DECL(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata );
-
-/**
- * Check if transmit data buffer contains a valid message.
- *
- * @param tdata The transmit buffer.
- */
-PJ_DECL(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata );
-
-/**
- * Invalidate the print buffer to force message to be re-printed. Call
- * when the message has changed after it has been printed to buffer. The
- * message is printed to buffer normally by transport when it is about to be
- * sent to the wire. Subsequent sending of the message will not cause
- * the message to be re-printed, unless application invalidates the buffer
- * by calling this function.
- *
- * @param tdata The transmit buffer.
- */
-PJ_DECL(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata );
-
-
-/*****************************************************************************
- *
- * TRANSPORT
- *
- *****************************************************************************/
-
-/**
- * This structure represent the "public" interface of a SIP transport.
- * Applications normally extend this structure to include transport
- * specific members.
- */
-typedef struct pjsip_transport
-{
- char obj_name[PJ_MAX_OBJ_NAME]; /**< Name. */
-
- pj_pool_t *pool; /**< Pool used by transport. */
- pj_atomic_t *ref_cnt; /**< Reference counter. */
- pj_lock_t *lock; /**< Lock object. */
- int tracing; /**< Tracing enabled? */
-
- pjsip_transport_type_e type; /**< Transport type. */
- char type_name[8]; /**< Type name. */
- unsigned flag; /**< #pjsip_transport_flags_e */
-
- pj_sockaddr_in local_addr; /**< Bound address. */
- pj_sockaddr_in public_addr; /**< STUN addres. */
- pj_sockaddr_in rem_addr; /**< Remote addr (zero for UDP) */
-
- pjsip_endpoint *endpt; /**< Endpoint instance. */
- pjsip_tpmgr *tpmgr; /**< Transport manager. */
- pj_timer_entry idle_timer; /**< Timer when ref cnt is zero.*/
-
- /**
- * Function to be called by transport manager to send SIP message.
- *
- * @param transport The transport to send the message.
- * @param packet The buffer to send.
- * @param length The length of the buffer to send.
- * @param op_key Completion token, which will be supplied to
- * caller when pending send operation completes.
- * @param rem_addr The remote destination address.
- * @param callback If supplied, the callback will be called
- * once a pending transmission has completed. If
- * the function completes immediately (i.e. return
- * code is not PJ_EPENDING), the callback will not
- * be called.
- *
- * @return Should return PJ_SUCCESS only if data has been
- * succesfully queued to operating system for
- * transmission. Otherwise it may return PJ_EPENDING
- * if the underlying transport can not send the
- * data immediately and will send it later, which in
- * this case caller doesn't have to do anything
- * except wait the calback to be called, if it
- * supplies one.
- * Other return values indicate the error code.
- */
- pj_status_t (*send_msg)(pjsip_transport *transport,
- pjsip_tx_data *tdata,
- const pj_sockaddr_in *rem_addr,
- void *token,
- void (*callback)(pjsip_transport *transport,
- void *token,
- pj_ssize_t sent_bytes));
-
- /**
- * Destroy this transport.
- */
- pj_status_t (*destroy)(pjsip_transport *transport);
-
- /*
- * Application may extend this structure..
- */
-} pjsip_transport;
-
-
-/**
- * Register a transport.
- */
-PJ_DECL(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr,
- pjsip_transport *tp );
-
-
-/**
- * Unregister transport. This will eventually call the transport to
- * destroy itself.
- */
-PJ_DECL(pj_status_t) pjsip_transport_unregister( pjsip_tpmgr *mgr,
- pjsip_transport *tp);
-
-/**
- * Add ref.
- */
-PJ_DECL(pj_status_t) pjsip_transport_add_ref( pjsip_transport *tp );
-
-/**
- * Dec ref.
- */
-PJ_DECL(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp );
-
-
-/**
- * Call for incoming message.
- */
-PJ_DECL(pj_ssize_t) pjsip_tpmgr_receive_packet(pjsip_tpmgr *mgr,
- pjsip_rx_data *rdata);
-
-
-/*****************************************************************************
- *
- * TRANSPORT FACTORY
- *
- *****************************************************************************/
-
-
-/**
- * Transport factory.
- */
-typedef struct pjsip_tpfactory pjsip_tpfactory;
-
-/**
- * Transport factory.
- */
-struct pjsip_tpfactory
-{
- /* This list is managed by transport manager. */
- PJ_DECL_LIST_MEMBER(struct pjsip_tpfactory);
-
- pj_pool_t *pool;
- pj_lock_t *lock;
-
- pjsip_transport_type_e type;
- char type_name[8];
- unsigned flag;
-
- pj_sockaddr_in local_addr;
- pj_sockaddr_in public_addr;
-
- /**
- * Create new outbound connection.
- */
- pj_status_t (*create_transport)(pjsip_tpfactory *factory,
- pjsip_tpmgr *mgr,
- pjsip_endpoint *endpt,
- const pj_sockaddr_in *rem_addr,
- pjsip_transport **transport);
-
- /*
- * Application may extend this structure..
- */
-};
-
-
-
-/**
- * Register a transport factory.
- *
- * @param mgr The transport manager.
- * @param factory Transport factory.
- *
- * @return PJ_SUCCESS if listener was successfully created.
- */
-PJ_DECL(pj_status_t) pjsip_tpmgr_register_tpfactory(pjsip_tpmgr *mgr,
- pjsip_tpfactory *tpf);
-
-/**
- * Unregister factory.
- */
-PJ_DECL(pj_status_t) pjsip_tpmgr_unregister_tpfactory(pjsip_tpmgr *mgr,
- pjsip_tpfactory *tpf);
-
-
-/*****************************************************************************
- *
- * TRANSPORT MANAGER
- *
- *****************************************************************************/
-
-/**
- * Create a new transport manager.
- *
- * @param pool Pool.
- * @param endpt Endpoint instance.
- * @param cb Callback to receive incoming message.
- * @param p_mgr Pointer to receive the new transport manager.
- *
- * @return PJ_SUCCESS or the appropriate error code on error.
- */
-PJ_DECL(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool,
- pjsip_endpoint * endpt,
- void (*cb)(pjsip_endpoint*,
- pj_status_t,
- pjsip_rx_data *),
- pjsip_tpmgr **p_mgr);
-
-
-/**
- * Destroy transport manager.
- */
-PJ_DECL(pj_status_t) pjsip_tpmgr_destroy(pjsip_tpmgr *mgr);
-
-
-/**
- * Dump transport info.
- */
-PJ_DECL(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr);
-
-
-/*****************************************************************************
- *
- * PUBLIC API
- *
- *****************************************************************************/
-
-
-/**
- * Find transport to be used to send message to remote destination. If no
- * suitable transport is found, a new one will be created.
- */
-PJ_DECL(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr,
- pjsip_transport_type_e type,
- const pj_sockaddr_in *remote,
- pjsip_transport **p_transport );
-
-
-/**
- * Send a SIP message using the specified transport.
- */
-PJ_DECL(pj_status_t) pjsip_transport_send( pjsip_transport *tr,
- pjsip_tx_data *tdata,
- const pj_sockaddr_in *addr,
- void *token,
- void (*cb)(void *token,
- pjsip_tx_data *tdata,
- pj_ssize_t bytes_sent));
-
-
-/**
- * @}
- */
-
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_TRANSPORT_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIP_TRANSPORT_H__ +#define __PJSIP_SIP_TRANSPORT_H__ + +/** + * @file sip_transport.h + * @brief SIP Transport + */ + +#include <pjsip/sip_msg.h> +#include <pjsip/sip_parser.h> +#include <pj/sock.h> +#include <pj/list.h> +#include <pj/ioqueue.h> +#include <pj/timer.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_TRANSPORT SIP Transport + * @ingroup PJSIP + * + * This is the low-level transport layer. Application normally won't need to + * use this function, but instead can use transaction or higher layer API to + * send and receive messages. + * + * @{ + */ + +/***************************************************************************** + * + * GENERAL TRANSPORT (NAMES, TYPES, ETC.) + * + *****************************************************************************/ + +/** + * Flags for SIP transports. + */ +enum pjsip_transport_flags_e +{ + PJSIP_TRANSPORT_RELIABLE = 1, /**< Transport is reliable. */ + PJSIP_TRANSPORT_SECURE = 2, /**< Transport is secure. */ + PJSIP_TRANSPORT_DATAGRAM = 4, /**< Datagram based transport. */ +}; + +/** + * Check if transport tp is reliable. + */ +#define PJSIP_TRANSPORT_IS_RELIABLE(tp) \ + ((tp)->flag & PJSIP_TRANSPORT_RELIABLE) + +/** + * Get the transport type from the transport name. + * + * @param name Transport name, such as "TCP", or "UDP". + * + * @return The transport type, or PJSIP_TRANSPORT_UNSPECIFIED if + * the name is not recognized as the name of supported + * transport. + */ +PJ_DECL(pjsip_transport_type_e) +pjsip_transport_get_type_from_name(const pj_str_t *name); + +/** + * Get the transport type for the specified flags. + * + * @param flag The transport flag. + * + * @return Transport type. + */ +PJ_DECL(pjsip_transport_type_e) +pjsip_transport_get_type_from_flag(unsigned flag); + +/** + * Get transport flag from type. + * + * @param type Transport type. + * + * @return Transport flags. + */ +PJ_DECL(unsigned) +pjsip_transport_get_flag_from_type( pjsip_transport_type_e type ); + +/** + * Get the default SIP port number for the specified type. + * + * @param type Transport type. + * + * @return The port number, which is the default SIP port number for + * the specified type. + */ +PJ_DECL(int) +pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type); + + +/***************************************************************************** + * + * RECEIVE DATA BUFFER. + * + *****************************************************************************/ + +/** + * A customized ioqueue async operation key which is used by transport + * to locate rdata when a pending read operation completes. + */ +typedef struct pjsip_rx_data_op_key +{ + pj_ioqueue_op_key_t op_key; + pjsip_rx_data *rdata; +} pjsip_rx_data_op_key; + + +/** + * Incoming message buffer. + * This structure keep all the information regarding the received message. This + * buffer lifetime is only very short, normally after the transaction has been + * called, this buffer will be deleted/recycled. So care must be taken when + * allocating storage from the pool of this buffer. + */ +struct pjsip_rx_data +{ + + /** + * tp_info is part of rdata that remains static for the duration of the + * buffer. It is initialized when the buffer was created by transport. + */ + struct + { + /** Memory pool for this buffer. */ + pj_pool_t *pool; + + /** The transport object which received this packet. */ + pjsip_transport *transport; + + /** Ioqueue key. */ + pjsip_rx_data_op_key op_key; + + } tp_info; + + + /** + * pkt_info is initialized by transport when it receives an incoming + * packet. + */ + struct + { + /** Time when the message was received. */ + pj_time_val timestamp; + + /** Pointer to the original packet. */ + char packet[PJSIP_MAX_PKT_LEN]; + + /** Zero termination for the packet. */ + pj_uint32_t zero; + + /** The length of the packet received. */ + int len; + + /** The source address from which the packet was received. */ + pj_sockaddr_in addr; + + /** The length of the source address. */ + int addr_len; + + } pkt_info; + + + /** + * msg_info is initialized by transport mgr (tpmgr) before this buffer + * is passed to endpoint. + */ + struct + { + /** Start of msg buffer. */ + char *msg_buf; + + /** Length fo message. */ + int len; + + /** The parsed message, if any. */ + pjsip_msg *msg; + + /** The Call-ID header as found in the message. */ + pj_str_t call_id; + + /** The From header as found in the message. */ + pjsip_from_hdr *from; + + /** The To header as found in the message. */ + pjsip_to_hdr *to; + + /** The topmost Via header as found in the message. */ + pjsip_via_hdr *via; + + /** The CSeq header as found in the message. */ + pjsip_cseq_hdr *cseq; + + /** Max forwards header. */ + pjsip_max_forwards_hdr *max_fwd; + + /** The first route header. */ + pjsip_route_hdr *route; + + /** The first record-route header. */ + pjsip_rr_hdr *record_route; + + /** Content-type header. */ + pjsip_ctype_hdr *ctype; + + /** Content-length header. */ + pjsip_clen_hdr *clen; + + /** The first Require header. */ + pjsip_require_hdr *require; + + /** The list of error generated by the parser when parsing + this message. + */ + pjsip_parser_err_report parse_err; + + } msg_info; + + + /** + * endpt_info is initialized by endpoint after this buffer reaches + * endpoint. + */ + struct + { + /** + * This the transaction key generated for the message. + */ + pj_str_t key; + + } endpt_info; + +}; + + +/***************************************************************************** + * + * TRANSMIT DATA BUFFER MANIPULATION. + * + *****************************************************************************/ + +/** Customized ioqueue async operation key, used by transport to keep + * callback parameters. + */ +typedef struct pjsip_tx_data_op_key +{ + pj_ioqueue_op_key_t key; + pjsip_tx_data *tdata; + void *token; + void (*callback)(pjsip_transport*,void*,pj_ssize_t); +} pjsip_tx_data_op_key; + + +/** + * Data structure for sending outgoing message. Application normally creates + * this buffer by calling #pjsip_endpt_create_tdata. + * + * The lifetime of this buffer is controlled by the reference counter in this + * structure, which is manipulated by calling #pjsip_tx_data_add_ref and + * #pjsip_tx_data_dec_ref. When the reference counter has reached zero, then + * this buffer will be destroyed. + * + * A transaction object normally will add reference counter to this buffer + * when application calls #pjsip_tsx_on_tx_msg, because it needs to keep the + * message for retransmission. The transaction will release the reference + * counter once its state has reached final state. + */ +struct pjsip_tx_data +{ + /** This is for transmission queue; it's managed by transports. */ + PJ_DECL_LIST_MEMBER(struct pjsip_tx_data); + + /** Memory pool for this buffer. */ + pj_pool_t *pool; + + /** A name to identify this buffer. */ + char obj_name[PJ_MAX_OBJ_NAME]; + + /** For response message, this contains the reference to timestamp when + * the original request message was received. The value of this field + * is set when application creates response message to a request by + * calling #pjsip_endpt_create_response. + */ + pj_time_val rx_timestamp; + + /** The transport manager for this buffer. */ + pjsip_tpmgr *mgr; + + /** Ioqueue asynchronous operation key. */ + pjsip_tx_data_op_key op_key; + + /** Lock object. */ + pj_lock_t *lock; + + /** The message in this buffer. */ + pjsip_msg *msg; + + /** Buffer to the printed text representation of the message. When the + * content of this buffer is set, then the transport will send the content + * of this buffer instead of re-printing the message structure. If the + * message structure has changed, then application must invalidate this + * buffer by calling #pjsip_tx_data_invalidate_msg. + */ + pjsip_buffer buf; + + /** Reference counter. */ + pj_atomic_t *ref_cnt; + + /** Being processed by transport? */ + int is_pending; + + /** Transport manager internal. */ + void *token; + void (*cb)(void*, pjsip_tx_data*, pj_ssize_t); +}; + + +/** + * Create a new, blank transmit buffer. The reference count is initialized + * to zero. + * + * @param mgr The transport manager. + * @param tdata Pointer to receive transmit data. + * + * @return PJ_SUCCESS, or the appropriate error code. + * + * @see pjsip_endpt_create_tdata + */ +pj_status_t pjsip_tx_data_create( pjsip_tpmgr *mgr, + pjsip_tx_data **tdata ); + +/** + * Add reference counter to the transmit buffer. The reference counter controls + * the life time of the buffer, ie. when the counter reaches zero, then it + * will be destroyed. + * + * @param tdata The transmit buffer. + */ +PJ_DECL(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata ); + +/** + * Decrement reference counter of the transmit buffer. + * When the transmit buffer is no longer used, it will be destroyed. + * + * @param tdata The transmit buffer data. + */ +PJ_DECL(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata ); + +/** + * Check if transmit data buffer contains a valid message. + * + * @param tdata The transmit buffer. + */ +PJ_DECL(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata ); + +/** + * Invalidate the print buffer to force message to be re-printed. Call + * when the message has changed after it has been printed to buffer. The + * message is printed to buffer normally by transport when it is about to be + * sent to the wire. Subsequent sending of the message will not cause + * the message to be re-printed, unless application invalidates the buffer + * by calling this function. + * + * @param tdata The transmit buffer. + */ +PJ_DECL(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata ); + + +/***************************************************************************** + * + * TRANSPORT + * + *****************************************************************************/ + +/** + * This structure represent the "public" interface of a SIP transport. + * Applications normally extend this structure to include transport + * specific members. + */ +typedef struct pjsip_transport +{ + char obj_name[PJ_MAX_OBJ_NAME]; /**< Name. */ + + pj_pool_t *pool; /**< Pool used by transport. */ + pj_atomic_t *ref_cnt; /**< Reference counter. */ + pj_lock_t *lock; /**< Lock object. */ + int tracing; /**< Tracing enabled? */ + + pjsip_transport_type_e type; /**< Transport type. */ + char type_name[8]; /**< Type name. */ + unsigned flag; /**< #pjsip_transport_flags_e */ + + pj_sockaddr_in local_addr; /**< Bound address. */ + pj_sockaddr_in public_addr; /**< STUN addres. */ + pj_sockaddr_in rem_addr; /**< Remote addr (zero for UDP) */ + + pjsip_endpoint *endpt; /**< Endpoint instance. */ + pjsip_tpmgr *tpmgr; /**< Transport manager. */ + pj_timer_entry idle_timer; /**< Timer when ref cnt is zero.*/ + + /** + * Function to be called by transport manager to send SIP message. + * + * @param transport The transport to send the message. + * @param packet The buffer to send. + * @param length The length of the buffer to send. + * @param op_key Completion token, which will be supplied to + * caller when pending send operation completes. + * @param rem_addr The remote destination address. + * @param callback If supplied, the callback will be called + * once a pending transmission has completed. If + * the function completes immediately (i.e. return + * code is not PJ_EPENDING), the callback will not + * be called. + * + * @return Should return PJ_SUCCESS only if data has been + * succesfully queued to operating system for + * transmission. Otherwise it may return PJ_EPENDING + * if the underlying transport can not send the + * data immediately and will send it later, which in + * this case caller doesn't have to do anything + * except wait the calback to be called, if it + * supplies one. + * Other return values indicate the error code. + */ + pj_status_t (*send_msg)(pjsip_transport *transport, + pjsip_tx_data *tdata, + const pj_sockaddr_in *rem_addr, + void *token, + void (*callback)(pjsip_transport *transport, + void *token, + pj_ssize_t sent_bytes)); + + /** + * Destroy this transport. + */ + pj_status_t (*destroy)(pjsip_transport *transport); + + /* + * Application may extend this structure.. + */ +} pjsip_transport; + + +/** + * Register a transport. + */ +PJ_DECL(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr, + pjsip_transport *tp ); + + +/** + * Unregister transport. This will eventually call the transport to + * destroy itself. + */ +PJ_DECL(pj_status_t) pjsip_transport_unregister( pjsip_tpmgr *mgr, + pjsip_transport *tp); + +/** + * Add ref. + */ +PJ_DECL(pj_status_t) pjsip_transport_add_ref( pjsip_transport *tp ); + +/** + * Dec ref. + */ +PJ_DECL(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp ); + + +/** + * Call for incoming message. + */ +PJ_DECL(pj_ssize_t) pjsip_tpmgr_receive_packet(pjsip_tpmgr *mgr, + pjsip_rx_data *rdata); + + +/***************************************************************************** + * + * TRANSPORT FACTORY + * + *****************************************************************************/ + + +/** + * Transport factory. + */ +typedef struct pjsip_tpfactory pjsip_tpfactory; + +/** + * Transport factory. + */ +struct pjsip_tpfactory +{ + /* This list is managed by transport manager. */ + PJ_DECL_LIST_MEMBER(struct pjsip_tpfactory); + + pj_pool_t *pool; + pj_lock_t *lock; + + pjsip_transport_type_e type; + char type_name[8]; + unsigned flag; + + pj_sockaddr_in local_addr; + pj_sockaddr_in public_addr; + + /** + * Create new outbound connection. + */ + pj_status_t (*create_transport)(pjsip_tpfactory *factory, + pjsip_tpmgr *mgr, + pjsip_endpoint *endpt, + const pj_sockaddr_in *rem_addr, + pjsip_transport **transport); + + /* + * Application may extend this structure.. + */ +}; + + + +/** + * Register a transport factory. + * + * @param mgr The transport manager. + * @param factory Transport factory. + * + * @return PJ_SUCCESS if listener was successfully created. + */ +PJ_DECL(pj_status_t) pjsip_tpmgr_register_tpfactory(pjsip_tpmgr *mgr, + pjsip_tpfactory *tpf); + +/** + * Unregister factory. + */ +PJ_DECL(pj_status_t) pjsip_tpmgr_unregister_tpfactory(pjsip_tpmgr *mgr, + pjsip_tpfactory *tpf); + + +/***************************************************************************** + * + * TRANSPORT MANAGER + * + *****************************************************************************/ + +/** + * Create a new transport manager. + * + * @param pool Pool. + * @param endpt Endpoint instance. + * @param cb Callback to receive incoming message. + * @param p_mgr Pointer to receive the new transport manager. + * + * @return PJ_SUCCESS or the appropriate error code on error. + */ +PJ_DECL(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool, + pjsip_endpoint * endpt, + void (*cb)(pjsip_endpoint*, + pj_status_t, + pjsip_rx_data *), + pjsip_tpmgr **p_mgr); + + +/** + * Destroy transport manager. + */ +PJ_DECL(pj_status_t) pjsip_tpmgr_destroy(pjsip_tpmgr *mgr); + + +/** + * Dump transport info. + */ +PJ_DECL(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr); + + +/***************************************************************************** + * + * PUBLIC API + * + *****************************************************************************/ + + +/** + * Find transport to be used to send message to remote destination. If no + * suitable transport is found, a new one will be created. + */ +PJ_DECL(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr, + pjsip_transport_type_e type, + const pj_sockaddr_in *remote, + pjsip_transport **p_transport ); + + +/** + * Send a SIP message using the specified transport. + */ +PJ_DECL(pj_status_t) pjsip_transport_send( pjsip_transport *tr, + pjsip_tx_data *tdata, + const pj_sockaddr_in *addr, + void *token, + void (*cb)(void *token, + pjsip_tx_data *tdata, + pj_ssize_t bytes_sent)); + + +/** + * @} + */ + + +PJ_END_DECL + +#endif /* __PJSIP_SIP_TRANSPORT_H__ */ + diff --git a/pjsip/include/pjsip/sip_transport_udp.h b/pjsip/include/pjsip/sip_transport_udp.h index 10ace7fc..99f5ab67 100644 --- a/pjsip/include/pjsip/sip_transport_udp.h +++ b/pjsip/include/pjsip/sip_transport_udp.h @@ -1,68 +1,68 @@ -/* $Id: $ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_TRANSPORT_UDP_H__
-#define __PJSIP_TRANSPORT_UDP_H__
-
-#include <pjsip/sip_transport.h>
-
-PJ_DECL
-
-/**
- * Start UDP transport.
- *
- * @param endpt The SIP endpoint.
- * @param local Local address to bind.
- * @param pub_addr Public address to advertise.
- * @param async_cnt Number of simultaneous async operations.
- * @param p_transport Pointer to receive the transport.
- *
- * @return PJ_SUCCESS when the transport has been successfully
- * started and registered to transport manager, or
- * the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_udp_transport_start(pjsip_endpoint *endpt,
- const pj_sockaddr_in *local,
- const pj_sockaddr_in *pub_addr,
- unsigned async_cnt,
- pjsip_transport **p_transport);
-
-/**
- * Attach UDP socket as a new transport and start the transport.
- *
- * @param endpt The SIP endpoint.
- * @param sock UDP socket to use.
- * @param pub_addr Public address to advertise.
- * @param async_cnt Number of simultaneous async operations.
- * @param p_transport Pointer to receive the transport.
- *
- * @return PJ_SUCCESS when the transport has been successfully
- * started and registered to transport manager, or
- * the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_udp_transport_attach(pjsip_endpoint *endpt,
- pj_sock_t sock,
- const pj_sockaddr_in *pub_addr,
- unsigned async_cnt,
- pjsip_transport **p_transport);
-
-
-PJ_END_DECL
-
-
-#endif /* __PJSIP_TRANSPORT_UDP_H__ */
+/* $Id: $ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_TRANSPORT_UDP_H__ +#define __PJSIP_TRANSPORT_UDP_H__ + +#include <pjsip/sip_transport.h> + +PJ_DECL + +/** + * Start UDP transport. + * + * @param endpt The SIP endpoint. + * @param local Local address to bind. + * @param pub_addr Public address to advertise. + * @param async_cnt Number of simultaneous async operations. + * @param p_transport Pointer to receive the transport. + * + * @return PJ_SUCCESS when the transport has been successfully + * started and registered to transport manager, or + * the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsip_udp_transport_start(pjsip_endpoint *endpt, + const pj_sockaddr_in *local, + const pj_sockaddr_in *pub_addr, + unsigned async_cnt, + pjsip_transport **p_transport); + +/** + * Attach UDP socket as a new transport and start the transport. + * + * @param endpt The SIP endpoint. + * @param sock UDP socket to use. + * @param pub_addr Public address to advertise. + * @param async_cnt Number of simultaneous async operations. + * @param p_transport Pointer to receive the transport. + * + * @return PJ_SUCCESS when the transport has been successfully + * started and registered to transport manager, or + * the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsip_udp_transport_attach(pjsip_endpoint *endpt, + pj_sock_t sock, + const pj_sockaddr_in *pub_addr, + unsigned async_cnt, + pjsip_transport **p_transport); + + +PJ_END_DECL + + +#endif /* __PJSIP_TRANSPORT_UDP_H__ */ diff --git a/pjsip/include/pjsip/sip_types.h b/pjsip/include/pjsip/sip_types.h index 6554b4ce..2f8f2d13 100644 --- a/pjsip/include/pjsip/sip_types.h +++ b/pjsip/include/pjsip/sip_types.h @@ -1,175 +1,175 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_TYPES_H__
-#define __PJSIP_SIP_TYPES_H__
-
-#include <pjsip/sip_config.h>
-#include <pj/types.h>
-
-/**
- * Opaque data structure for transports (sip_transport.h).
- */
-typedef struct pjsip_transport pjsip_transport;
-
-/**
- * Opaque data type for transport manager (sip_transport.h).
- */
-typedef struct pjsip_tpmgr pjsip_tpmgr;
-
-/**
- * Transport types.
- */
-typedef enum pjsip_transport_type_e
-{
- /** Unspecified. */
- PJSIP_TRANSPORT_UNSPECIFIED,
-
- /** UDP. */
- PJSIP_TRANSPORT_UDP,
-
- /** TCP. */
- PJSIP_TRANSPORT_TCP,
-
- /** TLS. */
- PJSIP_TRANSPORT_TLS,
-
- /** SCTP. */
- PJSIP_TRANSPORT_SCTP,
-
-} pjsip_transport_type_e;
-
-
-/**
- * Forward declaration for endpoint (sip_endpoint.h).
- */
-typedef struct pjsip_endpoint pjsip_endpoint;
-
-/**
- * Forward declaration for transactions (sip_transaction.h).
- */
-typedef struct pjsip_transaction pjsip_transaction;
-
-/**
- * Forward declaration for events (sip_event.h).
- */
-typedef struct pjsip_event pjsip_event;
-
-/**
- * Forward declaration for transmit data/buffer (sip_transport.h).
- */
-typedef struct pjsip_tx_data pjsip_tx_data;
-
-/**
- * Forward declaration for receive data/buffer (sip_transport.h).
- */
-typedef struct pjsip_rx_data pjsip_rx_data;
-
-/**
- * Forward declaration for message (sip_msg.h).
- */
-typedef struct pjsip_msg pjsip_msg;
-
-/**
- * Forward declaration for header field (sip_msg.h).
- */
-typedef struct pjsip_hdr pjsip_hdr;
-
-/**
- * Forward declaration for URI (sip_uri.h).
- */
-typedef struct pjsip_uri pjsip_uri;
-
-/**
- * Opaque data type for the resolver engine (sip_resolve.h).
- */
-typedef struct pjsip_resolver_t pjsip_resolver_t;
-
-/**
- * Forward declaration for credential.
- */
-typedef struct pjsip_cred_info pjsip_cred_info;
-
-
-/**
- * Forward declaration for module (sip_module.h).
- */
-typedef struct pjsip_module pjsip_module;
-
-/**
- * Transaction role.
- */
-typedef enum pjsip_role_e
-{
- PJSIP_ROLE_UAC, /**< Transaction role is UAC. */
- PJSIP_ROLE_UAS, /**< Transaction role is UAS. */
-} pjsip_role_e;
-
-
-/**
- * General purpose buffer.
- */
-typedef struct pjsip_buffer
-{
- /** The start of the buffer. */
- char *start;
-
- /** Pointer to current end of the buffer, which also indicates the position
- of subsequent buffer write.
- */
- char *cur;
-
- /** The absolute end of the buffer. */
- char *end;
-
-} pjsip_buffer;
-
-
-/**
- * General host:port pair, used for example as Via sent-by.
- */
-typedef struct pjsip_host_port
-{
- unsigned flag; /**< Flags of pjsip_transport_flags_e (not used in Via). */
- unsigned type; /**< Transport type (pjsip_transport_type_e), or zero. */
- pj_str_t host; /**< Host part. */
- int port; /**< Port number. */
-} pjsip_host_port;
-
-
-/**
- * Convert exception ID into pj_status_t status.
- *
- * @param exception_id Exception Id.
- *
- * @return Error code for the specified exception Id.
- */
-PJ_DECL(pj_status_t) pjsip_exception_to_status(int exception_id);
-
-/**
- * Return standard pj_status_t status from current exception.
- */
-#define PJSIP_RETURN_EXCEPTION() pjsip_exception_to_status(PJ_GET_EXCEPTION())
-
-/**
- * Attributes to inform that the function may throw exceptions.
- */
-#define PJSIP_THROW_SPEC(list)
-
-#endif /* __PJSIP_SIP_TYPES_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIP_TYPES_H__ +#define __PJSIP_SIP_TYPES_H__ + +#include <pjsip/sip_config.h> +#include <pj/types.h> + +/** + * Opaque data structure for transports (sip_transport.h). + */ +typedef struct pjsip_transport pjsip_transport; + +/** + * Opaque data type for transport manager (sip_transport.h). + */ +typedef struct pjsip_tpmgr pjsip_tpmgr; + +/** + * Transport types. + */ +typedef enum pjsip_transport_type_e +{ + /** Unspecified. */ + PJSIP_TRANSPORT_UNSPECIFIED, + + /** UDP. */ + PJSIP_TRANSPORT_UDP, + + /** TCP. */ + PJSIP_TRANSPORT_TCP, + + /** TLS. */ + PJSIP_TRANSPORT_TLS, + + /** SCTP. */ + PJSIP_TRANSPORT_SCTP, + +} pjsip_transport_type_e; + + +/** + * Forward declaration for endpoint (sip_endpoint.h). + */ +typedef struct pjsip_endpoint pjsip_endpoint; + +/** + * Forward declaration for transactions (sip_transaction.h). + */ +typedef struct pjsip_transaction pjsip_transaction; + +/** + * Forward declaration for events (sip_event.h). + */ +typedef struct pjsip_event pjsip_event; + +/** + * Forward declaration for transmit data/buffer (sip_transport.h). + */ +typedef struct pjsip_tx_data pjsip_tx_data; + +/** + * Forward declaration for receive data/buffer (sip_transport.h). + */ +typedef struct pjsip_rx_data pjsip_rx_data; + +/** + * Forward declaration for message (sip_msg.h). + */ +typedef struct pjsip_msg pjsip_msg; + +/** + * Forward declaration for header field (sip_msg.h). + */ +typedef struct pjsip_hdr pjsip_hdr; + +/** + * Forward declaration for URI (sip_uri.h). + */ +typedef struct pjsip_uri pjsip_uri; + +/** + * Opaque data type for the resolver engine (sip_resolve.h). + */ +typedef struct pjsip_resolver_t pjsip_resolver_t; + +/** + * Forward declaration for credential. + */ +typedef struct pjsip_cred_info pjsip_cred_info; + + +/** + * Forward declaration for module (sip_module.h). + */ +typedef struct pjsip_module pjsip_module; + +/** + * Transaction role. + */ +typedef enum pjsip_role_e +{ + PJSIP_ROLE_UAC, /**< Transaction role is UAC. */ + PJSIP_ROLE_UAS, /**< Transaction role is UAS. */ +} pjsip_role_e; + + +/** + * General purpose buffer. + */ +typedef struct pjsip_buffer +{ + /** The start of the buffer. */ + char *start; + + /** Pointer to current end of the buffer, which also indicates the position + of subsequent buffer write. + */ + char *cur; + + /** The absolute end of the buffer. */ + char *end; + +} pjsip_buffer; + + +/** + * General host:port pair, used for example as Via sent-by. + */ +typedef struct pjsip_host_port +{ + unsigned flag; /**< Flags of pjsip_transport_flags_e (not used in Via). */ + unsigned type; /**< Transport type (pjsip_transport_type_e), or zero. */ + pj_str_t host; /**< Host part. */ + int port; /**< Port number. */ +} pjsip_host_port; + + +/** + * Convert exception ID into pj_status_t status. + * + * @param exception_id Exception Id. + * + * @return Error code for the specified exception Id. + */ +PJ_DECL(pj_status_t) pjsip_exception_to_status(int exception_id); + +/** + * Return standard pj_status_t status from current exception. + */ +#define PJSIP_RETURN_EXCEPTION() pjsip_exception_to_status(PJ_GET_EXCEPTION()) + +/** + * Attributes to inform that the function may throw exceptions. + */ +#define PJSIP_THROW_SPEC(list) + +#endif /* __PJSIP_SIP_TYPES_H__ */ + diff --git a/pjsip/include/pjsip/sip_uri.h b/pjsip/include/pjsip/sip_uri.h index b72721f2..0e13b300 100644 --- a/pjsip/include/pjsip/sip_uri.h +++ b/pjsip/include/pjsip/sip_uri.h @@ -1,359 +1,384 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_URI_H__
-#define __PJSIP_SIP_URI_H__
-
-/**
- * @file sip_uri.h
- * @brief SIP URL Structures and Manipulations
- */
-
-#include <pjsip/sip_types.h>
-#include <pjsip/sip_config.h>
-#include <pj/list.h>
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJSIP_URL URL Structures
- * @brief SIP Url, tel: Url, and generic URI.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * Generic parameter, normally used in other_param or header_param.
- */
-typedef struct pjsip_param
-{
- PJ_DECL_LIST_MEMBER(struct pjsip_param); /**< Generic list member. */
- pj_str_t name; /**< Param/header name. */
- pj_str_t value; /**< Param/header value. */
-} pjsip_param;
-
-
-/**
- * Find the specified parameter name in the list. The name will be compared
- * in case-insensitive comparison.
- *
- * @param param_list List of parameters to find.
- * @param name Parameter/header name to find.
- *
- * @return The parameter if found, or NULL.
- */
-PJ_DECL(pjsip_param*) pjsip_param_find( pjsip_param *param_list,
- const pj_str_t *name );
-
-
-/**
- * Find the specified parameter name in the list. The name will be compared
- * in case-insensitive comparison.
- *
- * @param param_list List of parameters to find.
- * @param name Parameter/header name to find.
- *
- * @return The parameter if found, or NULL.
- */
-PJ_DECL(const pjsip_param*) pjsip_param_cfind(const pjsip_param *param_list,
- const pj_str_t *name );
-
-
-/**
- * Duplicate the parameters.
- *
- * @param pool Pool to allocate memory from.
- * @param dst_list Destination list.
- * @param src_list Source list.
- */
-PJ_DECL(void) pjsip_param_clone(pj_pool_t *pool, pjsip_param *dst_list,
- const pjsip_param *src_list);
-
-/**
- * URI context.
- */
-typedef enum pjsip_uri_context_e
-{
- PJSIP_URI_IN_REQ_URI, /**< The URI is in Request URI. */
- PJSIP_URI_IN_FROMTO_HDR, /**< The URI is in From/To header. */
- PJSIP_URI_IN_CONTACT_HDR, /**< The URI is in Contact header. */
- PJSIP_URI_IN_ROUTING_HDR, /**< The URI is in Route/Record-Route header. */
- PJSIP_URI_IN_OTHER, /**< Other context (web page, business card, etc.) */
-} pjsip_uri_context_e;
-
-/**
- * URI 'virtual' function table.
- * All types of URI in this library (such as sip:, sips:, tel:, and name-addr)
- * will have pointer to this table as their first struct member. This table
- * provides polimorphic behaviour to the URI.
- */
-typedef struct pjsip_uri_vptr
-{
- /**
- * Get URI scheme.
- * @param uri the URI (self).
- * @return the URI scheme.
- */
- const pj_str_t* (*p_get_scheme)(const void *uri);
-
- /**
- * Get the URI object contained by this URI, or the URI itself if
- * it doesn't contain another URI.
- * @param uri the URI (self).
- */
- void* (*p_get_uri)(void *uri);
-
- /**
- * Print URI components to the buffer, following the rule of which
- * components are allowed for the context.
- * @param context the context where the URI will be placed.
- * @param uri the URI (self).
- * @param buf the buffer.
- * @param size the size of the buffer.
- * @return the length printed.
- */
- int (*p_print)(pjsip_uri_context_e context,
- const void *uri,
- char *buf, pj_size_t size);
-
- /**
- * Compare two URIs according to the context.
- * @param context the context.
- * @param uri1 the first URI (self).
- * @param uri2 the second URI.
- * @return PJ_SUCCESS if equal, or otherwise the error status which
- * should point to the mismatch part.
- */
- pj_status_t (*p_compare)(pjsip_uri_context_e context,
- const void *uri1, const void *uri2);
-
- /**
- * Clone URI.
- * @param pool the pool.
- * @param the URI to clone (self).
- * @return new URI.
- */
- void *(*p_clone)(pj_pool_t *pool, const void *uri);
-
-} pjsip_uri_vptr;
-
-
-/**
- * The declaration of 'base class' for all URI scheme.
- */
-struct pjsip_uri
-{
- /** All URIs must have URI virtual function table as their first member. */
- pjsip_uri_vptr *vptr;
-};
-
-/**
- * This macro checks that the URL is a "sip:" or "sips:" URL.
- * @param url The URL (pointer to)
- * @return non-zero if TRUE.
- */
-#define PJSIP_URI_SCHEME_IS_SIP(url) \
- (pj_strnicmp2(pjsip_uri_get_scheme(url), "sip", 3)==0)
-
-/**
- * This macro checks that the URL is a "sips:" URL (not SIP).
- * @param url The URL (pointer to)
- * @return non-zero if TRUE.
- */
-#define PJSIP_URI_SCHEME_IS_SIPS(url) \
- (pj_strnicmp2(pjsip_uri_get_scheme(url), "sips", 4)==0)
-
-/**
- * This macro checks that the URL is a "tel:" URL.
- * @param url The URL (pointer to)
- * @return non-zero if TRUE.
- */
-#define PJSIP_URI_SCHEME_IS_TEL(url) \
- (pj_strnicmp2(pjsip_uri_get_scheme(url), "tel", 3)==0)
-
-
-/**
- * SIP and SIPS URL scheme.
- */
-typedef struct pjsip_url
-{
- pjsip_uri_vptr *vptr; /**< Pointer to virtual function table.*/
- pj_str_t user; /**< Optional user part. */
- pj_str_t passwd; /**< Optional password part. */
- pj_str_t host; /**< Host part, always exists. */
- int port; /**< Optional port number, or zero. */
- pj_str_t user_param; /**< Optional user parameter */
- pj_str_t method_param; /**< Optional method parameter. */
- pj_str_t transport_param; /**< Optional transport parameter. */
- int ttl_param; /**< Optional TTL param, or -1. */
- int lr_param; /**< Optional loose routing param, or zero */
- pj_str_t maddr_param; /**< Optional maddr param */
- pjsip_param other_param; /**< Other parameters grouped together. */
- pjsip_param header_param; /**< Optional header parameter. */
-} pjsip_url;
-
-
-/**
- * SIP name-addr, which typically appear in From, To, and Contact header.
- * The SIP name-addr contains a generic URI and a display name.
- */
-typedef struct pjsip_name_addr
-{
- /** Pointer to virtual function table. */
- pjsip_uri_vptr *vptr;
-
- /** Optional display name. */
- pj_str_t display;
-
- /** URI part. */
- pjsip_uri *uri;
-
-} pjsip_name_addr;
-
-
-/**
- * Generic function to get the URI scheme.
- * @param uri the URI object.
- * @return the URI scheme.
- */
-PJ_INLINE(const pj_str_t*) pjsip_uri_get_scheme(const void *uri)
-{
- return (*((pjsip_uri*)uri)->vptr->p_get_scheme)(uri);
-}
-
-/**
- * Generic function to get the URI object contained by this URI, or the URI
- * itself if it doesn't contain another URI.
- *
- * @param uri the URI.
- * @return the URI.
- */
-PJ_INLINE(void*) pjsip_uri_get_uri(void *uri)
-{
- return (*((pjsip_uri*)uri)->vptr->p_get_uri)(uri);
-}
-
-/**
- * Generic function to compare two URIs.
- *
- * @param context Comparison context.
- * @param uri1 The first URI.
- * @param uri2 The second URI.
- * @return PJ_SUCCESS if equal, or otherwise the error status which
- * should point to the mismatch part.
- */
-PJ_INLINE(pj_status_t) pjsip_uri_cmp(pjsip_uri_context_e context,
- const void *uri1, const void *uri2)
-{
- return (*((const pjsip_uri*)uri1)->vptr->p_compare)(context, uri1, uri2);
-}
-
-/**
- * Generic function to print an URI object.
- *
- * @param context Print context.
- * @param uri The URI to print.
- * @param buf The buffer.
- * @param size Size of the buffer.
- * @return Length printed.
- */
-PJ_INLINE(int) pjsip_uri_print(pjsip_uri_context_e context,
- const void *uri,
- char *buf, pj_size_t size)
-{
- return (*((const pjsip_uri*)uri)->vptr->p_print)(context, uri, buf, size);
-}
-
-/**
- * Generic function to clone an URI object.
- *
- * @param pool Pool.
- * @param uri URI to clone.
- * @return New URI.
- */
-PJ_INLINE(void*) pjsip_uri_clone( pj_pool_t *pool, const void *uri )
-{
- return (*((const pjsip_uri*)uri)->vptr->p_clone)(pool, uri);
-}
-
-
-/**
- * Create new SIP URL and initialize all fields with zero or NULL.
- * @param pool The pool.
- * @param secure Tlag to indicate whether secure transport should be used.
- * @return SIP URL.
- */
-PJ_DECL(pjsip_url*) pjsip_url_create( pj_pool_t *pool, int secure );
-
-/**
- * Create new SIPS URL and initialize all fields with zero or NULL.
- * @param pool The pool.
- * @return SIPS URL.
- */
-PJ_DECL(pjsip_url*) pjsips_url_create( pj_pool_t *pool );
-
-/**
- * Initialize SIP URL (all fields are set to NULL or zero).
- * @param url The URL.
- */
-PJ_DECL(void) pjsip_url_init(pjsip_url *url, int secure);
-
-/**
- * Perform full assignment to the SIP URL.
- * @param pool The pool.
- * @param url Destination URL.
- * @param rhs The source URL.
- */
-PJ_DECL(void) pjsip_url_assign(pj_pool_t *pool, pjsip_url *url, const pjsip_url *rhs);
-
-/**
- * Create new instance of name address and initialize all fields with zero or
- * NULL.
- * @param pool The pool.
- * @return New SIP name address.
- */
-PJ_DECL(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool);
-
-/**
- * Initialize with default value.
- * @param name_addr The name address.
- */
-PJ_DECL(void) pjsip_name_addr_init(pjsip_name_addr *name_addr);
-
-/**
- * Perform full assignment to the name address.
- * @param pool The pool.
- * @param addr The destination name address.
- * @param rhs The source name address.
- */
-PJ_DECL(void) pjsip_name_addr_assign(pj_pool_t *pool,
- pjsip_name_addr *addr,
- const pjsip_name_addr *rhs);
-
-
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_URL_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIP_URI_H__ +#define __PJSIP_SIP_URI_H__ + +/** + * @file sip_uri.h + * @brief SIP URL Structures and Manipulations + */ + +#include <pjsip/sip_types.h> +#include <pjsip/sip_config.h> +#include <pj/list.h> + +PJ_BEGIN_DECL + + +/** + * @defgroup PJSIP_URL URL Structures + * @brief SIP Url, tel: Url, and generic URI. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * Generic parameter, normally used in other_param or header_param. + */ +typedef struct pjsip_param +{ + PJ_DECL_LIST_MEMBER(struct pjsip_param); /**< Generic list member. */ + pj_str_t name; /**< Param/header name. */ + pj_str_t value; /**< Param/header value. */ +} pjsip_param; + + +/** + * Find the specified parameter name in the list. The name will be compared + * in case-insensitive comparison. + * + * @param param_list List of parameters to find. + * @param name Parameter/header name to find. + * + * @return The parameter if found, or NULL. + */ +PJ_DECL(pjsip_param*) pjsip_param_find( pjsip_param *param_list, + const pj_str_t *name ); + + +/** + * Find the specified parameter name in the list. The name will be compared + * in case-insensitive comparison. + * + * @param param_list List of parameters to find. + * @param name Parameter/header name to find. + * + * @return The parameter if found, or NULL. + */ +PJ_DECL(const pjsip_param*) pjsip_param_cfind(const pjsip_param *param_list, + const pj_str_t *name ); + + +/** + * Duplicate the parameters. + * + * @param pool Pool to allocate memory from. + * @param dst_list Destination list. + * @param src_list Source list. + */ +PJ_DECL(void) pjsip_param_clone(pj_pool_t *pool, pjsip_param *dst_list, + const pjsip_param *src_list); + +/** + * Duplicate the parameters. + * + * @param pool Pool to allocate memory from. + * @param dst_list Destination list. + * @param src_list Source list. + */ +PJ_DECL(void) pjsip_param_shallow_clone(pj_pool_t *pool, + pjsip_param *dst_list, + const pjsip_param *src_list); + +/** + * Print parameters. + * + * @param param_list The parameter list. + * @param buf Buffer. + * @param size Size of buffer. + * @param sep Separator character (either ';' or ','). + * + * @return The number of bytes printed, or -1 on errr. + */ +PJ_DECL(pj_ssize_t) pjsip_param_print_on(const pjsip_param *param_list, + char *buf, pj_size_t size, + int sep); + +/** + * URI context. + */ +typedef enum pjsip_uri_context_e +{ + PJSIP_URI_IN_REQ_URI, /**< The URI is in Request URI. */ + PJSIP_URI_IN_FROMTO_HDR, /**< The URI is in From/To header. */ + PJSIP_URI_IN_CONTACT_HDR, /**< The URI is in Contact header. */ + PJSIP_URI_IN_ROUTING_HDR, /**< The URI is in Route/Record-Route header. */ + PJSIP_URI_IN_OTHER, /**< Other context (web page, business card, etc.) */ +} pjsip_uri_context_e; + +/** + * URI 'virtual' function table. + * All types of URI in this library (such as sip:, sips:, tel:, and name-addr) + * will have pointer to this table as their first struct member. This table + * provides polimorphic behaviour to the URI. + */ +typedef struct pjsip_uri_vptr +{ + /** + * Get URI scheme. + * @param uri the URI (self). + * @return the URI scheme. + */ + const pj_str_t* (*p_get_scheme)(const void *uri); + + /** + * Get the URI object contained by this URI, or the URI itself if + * it doesn't contain another URI. + * @param uri the URI (self). + */ + void* (*p_get_uri)(void *uri); + + /** + * Print URI components to the buffer, following the rule of which + * components are allowed for the context. + * @param context the context where the URI will be placed. + * @param uri the URI (self). + * @param buf the buffer. + * @param size the size of the buffer. + * @return the length printed. + */ + int (*p_print)(pjsip_uri_context_e context, + const void *uri, + char *buf, pj_size_t size); + + /** + * Compare two URIs according to the context. + * @param context the context. + * @param uri1 the first URI (self). + * @param uri2 the second URI. + * @return PJ_SUCCESS if equal, or otherwise the error status which + * should point to the mismatch part. + */ + pj_status_t (*p_compare)(pjsip_uri_context_e context, + const void *uri1, const void *uri2); + + /** + * Clone URI. + * @param pool the pool. + * @param the URI to clone (self). + * @return new URI. + */ + void *(*p_clone)(pj_pool_t *pool, const void *uri); + +} pjsip_uri_vptr; + + +/** + * The declaration of 'base class' for all URI scheme. + */ +struct pjsip_uri +{ + /** All URIs must have URI virtual function table as their first member. */ + pjsip_uri_vptr *vptr; +}; + +/** + * This macro checks that the URL is a "sip:" or "sips:" URL. + * @param url The URL (pointer to) + * @return non-zero if TRUE. + */ +#define PJSIP_URI_SCHEME_IS_SIP(url) \ + (pj_strnicmp2(pjsip_uri_get_scheme(url), "sip", 3)==0) + +/** + * This macro checks that the URL is a "sips:" URL (not SIP). + * @param url The URL (pointer to) + * @return non-zero if TRUE. + */ +#define PJSIP_URI_SCHEME_IS_SIPS(url) \ + (pj_strnicmp2(pjsip_uri_get_scheme(url), "sips", 4)==0) + +/** + * This macro checks that the URL is a "tel:" URL. + * @param url The URL (pointer to) + * @return non-zero if TRUE. + */ +#define PJSIP_URI_SCHEME_IS_TEL(url) \ + (pj_strnicmp2(pjsip_uri_get_scheme(url), "tel", 3)==0) + + +/** + * SIP and SIPS URL scheme. + */ +typedef struct pjsip_url +{ + pjsip_uri_vptr *vptr; /**< Pointer to virtual function table.*/ + pj_str_t user; /**< Optional user part. */ + pj_str_t passwd; /**< Optional password part. */ + pj_str_t host; /**< Host part, always exists. */ + int port; /**< Optional port number, or zero. */ + pj_str_t user_param; /**< Optional user parameter */ + pj_str_t method_param; /**< Optional method parameter. */ + pj_str_t transport_param; /**< Optional transport parameter. */ + int ttl_param; /**< Optional TTL param, or -1. */ + int lr_param; /**< Optional loose routing param, or zero */ + pj_str_t maddr_param; /**< Optional maddr param */ + pjsip_param other_param; /**< Other parameters grouped together. */ + pjsip_param header_param; /**< Optional header parameter. */ +} pjsip_url; + + +/** + * SIP name-addr, which typically appear in From, To, and Contact header. + * The SIP name-addr contains a generic URI and a display name. + */ +typedef struct pjsip_name_addr +{ + /** Pointer to virtual function table. */ + pjsip_uri_vptr *vptr; + + /** Optional display name. */ + pj_str_t display; + + /** URI part. */ + pjsip_uri *uri; + +} pjsip_name_addr; + + +/** + * Generic function to get the URI scheme. + * @param uri the URI object. + * @return the URI scheme. + */ +PJ_INLINE(const pj_str_t*) pjsip_uri_get_scheme(const void *uri) +{ + return (*((pjsip_uri*)uri)->vptr->p_get_scheme)(uri); +} + +/** + * Generic function to get the URI object contained by this URI, or the URI + * itself if it doesn't contain another URI. + * + * @param uri the URI. + * @return the URI. + */ +PJ_INLINE(void*) pjsip_uri_get_uri(void *uri) +{ + return (*((pjsip_uri*)uri)->vptr->p_get_uri)(uri); +} + +/** + * Generic function to compare two URIs. + * + * @param context Comparison context. + * @param uri1 The first URI. + * @param uri2 The second URI. + * @return PJ_SUCCESS if equal, or otherwise the error status which + * should point to the mismatch part. + */ +PJ_INLINE(pj_status_t) pjsip_uri_cmp(pjsip_uri_context_e context, + const void *uri1, const void *uri2) +{ + return (*((const pjsip_uri*)uri1)->vptr->p_compare)(context, uri1, uri2); +} + +/** + * Generic function to print an URI object. + * + * @param context Print context. + * @param uri The URI to print. + * @param buf The buffer. + * @param size Size of the buffer. + * @return Length printed. + */ +PJ_INLINE(int) pjsip_uri_print(pjsip_uri_context_e context, + const void *uri, + char *buf, pj_size_t size) +{ + return (*((const pjsip_uri*)uri)->vptr->p_print)(context, uri, buf, size); +} + +/** + * Generic function to clone an URI object. + * + * @param pool Pool. + * @param uri URI to clone. + * @return New URI. + */ +PJ_INLINE(void*) pjsip_uri_clone( pj_pool_t *pool, const void *uri ) +{ + return (*((const pjsip_uri*)uri)->vptr->p_clone)(pool, uri); +} + + +/** + * Create new SIP URL and initialize all fields with zero or NULL. + * @param pool The pool. + * @param secure Tlag to indicate whether secure transport should be used. + * @return SIP URL. + */ +PJ_DECL(pjsip_url*) pjsip_url_create( pj_pool_t *pool, int secure ); + +/** + * Create new SIPS URL and initialize all fields with zero or NULL. + * @param pool The pool. + * @return SIPS URL. + */ +PJ_DECL(pjsip_url*) pjsips_url_create( pj_pool_t *pool ); + +/** + * Initialize SIP URL (all fields are set to NULL or zero). + * @param url The URL. + */ +PJ_DECL(void) pjsip_url_init(pjsip_url *url, int secure); + +/** + * Perform full assignment to the SIP URL. + * @param pool The pool. + * @param url Destination URL. + * @param rhs The source URL. + */ +PJ_DECL(void) pjsip_url_assign(pj_pool_t *pool, pjsip_url *url, const pjsip_url *rhs); + +/** + * Create new instance of name address and initialize all fields with zero or + * NULL. + * @param pool The pool. + * @return New SIP name address. + */ +PJ_DECL(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool); + +/** + * Initialize with default value. + * @param name_addr The name address. + */ +PJ_DECL(void) pjsip_name_addr_init(pjsip_name_addr *name_addr); + +/** + * Perform full assignment to the name address. + * @param pool The pool. + * @param addr The destination name address. + * @param rhs The source name address. + */ +PJ_DECL(void) pjsip_name_addr_assign(pj_pool_t *pool, + pjsip_name_addr *addr, + const pjsip_name_addr *rhs); + + + + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_URL_H__ */ + diff --git a/pjsip/include/pjsip/sip_util.h b/pjsip/include/pjsip/sip_util.h index f8685e54..e158212a 100644 --- a/pjsip/include/pjsip/sip_util.h +++ b/pjsip/include/pjsip/sip_util.h @@ -1,192 +1,192 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_MISC_H__
-#define __PJSIP_SIP_MISC_H__
-
-#include <pjsip/sip_msg.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_ENDPT SIP Endpoint
- * @ingroup PJSIP
- * @{
- */
-
-/**
- * Create an independent request message. This can be used to build any
- * request outside a dialog, such as OPTIONS, MESSAGE, etc. To create a request
- * inside a dialog, application should use #pjsip_dlg_create_request.
- *
- * Once a transmit data is created, the reference counter is initialized to 1.
- *
- * @param endpt Endpoint instance.
- * @param method SIP Method.
- * @param target Target URI.
- * @param from URL to put in From header.
- * @param to URL to put in To header.
- * @param contact URL to put in Contact header.
- * @param call_id Optional Call-ID (put NULL to generate unique Call-ID).
- * @param cseq Optional CSeq (put -1 to generate random CSeq).
- * @param text Optional text body (put NULL to omit body).
- * @param p_tdata Pointer to receive the transmit data.
- *
- * @return PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt,
- const pjsip_method *method,
- const pj_str_t *target,
- const pj_str_t *from,
- const pj_str_t *to,
- const pj_str_t *contact,
- const pj_str_t *call_id,
- int cseq,
- const pj_str_t *text,
- pjsip_tx_data **p_tdata);
-
-/**
- * Create an independent request message from the specified headers. This
- * function will shallow clone the headers and put them in the request.
- *
- * Once a transmit data is created, the reference counter is initialized to 1.
- *
- * @param endpt Endpoint instance.
- * @param method SIP Method.
- * @param target Target URI.
- * @param from URL to put in From header.
- * @param to URL to put in To header.
- * @param contact URL to put in Contact header.
- * @param call_id Optional Call-ID (put NULL to generate unique Call-ID).
- * @param cseq Optional CSeq (put -1 to generate random CSeq).
- * @param text Optional text body (put NULL to omit body).
- * @param p_tdata Pointer to receive the transmit data.
- *
- * @return PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t)
-pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
- const pjsip_method *method,
- const pjsip_uri *target,
- const pjsip_from_hdr *from,
- const pjsip_to_hdr *to,
- const pjsip_contact_hdr *contact,
- const pjsip_cid_hdr *call_id,
- int cseq,
- const pj_str_t *text,
- pjsip_tx_data **p_tdata);
-
-/**
- * Send outgoing request and initiate UAC transaction for the request.
- * This is an auxiliary function to be used by application to send arbitrary
- * requests outside a dialog. To send a request within a dialog, application
- * should use #pjsip_dlg_send_msg instead.
- *
- * @param endpt The endpoint instance.
- * @param tdata The transmit data to be sent.
- * @param timeout Optional timeout for final response to be received, or -1
- * if the transaction should not have a timeout restriction.
- * @param token Optional token to be associated with the transaction, and
- * to be passed to the callback.
- * @param cb Optional callback to be called when the transaction has
- * received a final response. The callback will be called with
- * the previously registered token and the event that triggers
- * the completion of the transaction.
- *
- * @return PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- int timeout,
- void *token,
- void (*cb)(void*,pjsip_event*));
-
-/**
- * Construct a minimal response message for the received request. This function
- * will construct all the Via, Record-Route, Call-ID, From, To, CSeq, and
- * Call-ID headers from the request.
- *
- * Note: the txdata reference counter is set to ZERO!.
- *
- * @param endpt The endpoint.
- * @param rdata The request receive data.
- * @param code Status code to be put in the response.
- * @param p_tdata Pointer to receive the transmit data.
- *
- * @return PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
- const pjsip_rx_data *rdata,
- int code,
- pjsip_tx_data **p_tdata);
-
-/**
- * Construct a full ACK request for the received non-2xx final response.
- * This utility function is normally called by the transaction to construct
- * an ACK request to 3xx-6xx final response.
- * The generation of ACK message for 2xx final response is different than
- * this one.
- *
- * @param endpt The endpoint.
- * @param tdata On input, this contains the original INVITE request, and on
- * output, it contains the ACK message.
- * @param rdata The final response message.
- */
-PJ_DECL(void) pjsip_endpt_create_ack( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- const pjsip_rx_data *rdata );
-
-
-/**
- * Construct CANCEL request for the previously sent request.
- *
- * @param endpt The endpoint.
- * @param tdata The transmit buffer for the request being cancelled.
- * @param p_tdata Pointer to receive the transmit data.
- *
- * @return PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- pjsip_tx_data **p_tdata);
-
-
-/**
- * Get the address parameters (host, port, flag, TTL, etc) to send the
- * response.
- *
- * @param pool The pool.
- * @param tr The transport where the request was received.
- * @param via The top-most Via header of the request.
- * @param addr The send address concluded from the calculation.
- *
- * @return zero (PJ_OK) if successfull.
- */
-PJ_DECL(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool,
- const pjsip_transport *tr,
- const pjsip_via_hdr *via,
- pjsip_host_port *addr);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_MISC_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_SIP_MISC_H__ +#define __PJSIP_SIP_MISC_H__ + +#include <pjsip/sip_msg.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_ENDPT SIP Endpoint + * @ingroup PJSIP + * @{ + */ + +/** + * Create an independent request message. This can be used to build any + * request outside a dialog, such as OPTIONS, MESSAGE, etc. To create a request + * inside a dialog, application should use #pjsip_dlg_create_request. + * + * Once a transmit data is created, the reference counter is initialized to 1. + * + * @param endpt Endpoint instance. + * @param method SIP Method. + * @param target Target URI. + * @param from URL to put in From header. + * @param to URL to put in To header. + * @param contact URL to put in Contact header. + * @param call_id Optional Call-ID (put NULL to generate unique Call-ID). + * @param cseq Optional CSeq (put -1 to generate random CSeq). + * @param text Optional text body (put NULL to omit body). + * @param p_tdata Pointer to receive the transmit data. + * + * @return PJ_SUCCESS, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt, + const pjsip_method *method, + const pj_str_t *target, + const pj_str_t *from, + const pj_str_t *to, + const pj_str_t *contact, + const pj_str_t *call_id, + int cseq, + const pj_str_t *text, + pjsip_tx_data **p_tdata); + +/** + * Create an independent request message from the specified headers. This + * function will shallow clone the headers and put them in the request. + * + * Once a transmit data is created, the reference counter is initialized to 1. + * + * @param endpt Endpoint instance. + * @param method SIP Method. + * @param target Target URI. + * @param from URL to put in From header. + * @param to URL to put in To header. + * @param contact URL to put in Contact header. + * @param call_id Optional Call-ID (put NULL to generate unique Call-ID). + * @param cseq Optional CSeq (put -1 to generate random CSeq). + * @param text Optional text body (put NULL to omit body). + * @param p_tdata Pointer to receive the transmit data. + * + * @return PJ_SUCCESS, or the appropriate error code. + */ +PJ_DECL(pj_status_t) +pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt, + const pjsip_method *method, + const pjsip_uri *target, + const pjsip_from_hdr *from, + const pjsip_to_hdr *to, + const pjsip_contact_hdr *contact, + const pjsip_cid_hdr *call_id, + int cseq, + const pj_str_t *text, + pjsip_tx_data **p_tdata); + +/** + * Send outgoing request and initiate UAC transaction for the request. + * This is an auxiliary function to be used by application to send arbitrary + * requests outside a dialog. To send a request within a dialog, application + * should use #pjsip_dlg_send_msg instead. + * + * @param endpt The endpoint instance. + * @param tdata The transmit data to be sent. + * @param timeout Optional timeout for final response to be received, or -1 + * if the transaction should not have a timeout restriction. + * @param token Optional token to be associated with the transaction, and + * to be passed to the callback. + * @param cb Optional callback to be called when the transaction has + * received a final response. The callback will be called with + * the previously registered token and the event that triggers + * the completion of the transaction. + * + * @return PJ_SUCCESS, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt, + pjsip_tx_data *tdata, + int timeout, + void *token, + void (*cb)(void*,pjsip_event*)); + +/** + * Construct a minimal response message for the received request. This function + * will construct all the Via, Record-Route, Call-ID, From, To, CSeq, and + * Call-ID headers from the request. + * + * Note: the txdata reference counter is set to ZERO!. + * + * @param endpt The endpoint. + * @param rdata The request receive data. + * @param code Status code to be put in the response. + * @param p_tdata Pointer to receive the transmit data. + * + * @return PJ_SUCCESS, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt, + const pjsip_rx_data *rdata, + int code, + pjsip_tx_data **p_tdata); + +/** + * Construct a full ACK request for the received non-2xx final response. + * This utility function is normally called by the transaction to construct + * an ACK request to 3xx-6xx final response. + * The generation of ACK message for 2xx final response is different than + * this one. + * + * @param endpt The endpoint. + * @param tdata On input, this contains the original INVITE request, and on + * output, it contains the ACK message. + * @param rdata The final response message. + */ +PJ_DECL(void) pjsip_endpt_create_ack( pjsip_endpoint *endpt, + pjsip_tx_data *tdata, + const pjsip_rx_data *rdata ); + + +/** + * Construct CANCEL request for the previously sent request. + * + * @param endpt The endpoint. + * @param tdata The transmit buffer for the request being cancelled. + * @param p_tdata Pointer to receive the transmit data. + * + * @return PJ_SUCCESS, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt, + pjsip_tx_data *tdata, + pjsip_tx_data **p_tdata); + + +/** + * Get the address parameters (host, port, flag, TTL, etc) to send the + * response. + * + * @param pool The pool. + * @param tr The transport where the request was received. + * @param via The top-most Via header of the request. + * @param addr The send address concluded from the calculation. + * + * @return zero (PJ_OK) if successfull. + */ +PJ_DECL(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool, + const pjsip_transport *tr, + const pjsip_via_hdr *via, + pjsip_host_port *addr); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_SIP_MISC_H__ */ + diff --git a/pjsip/src/pjsip-simple/event_notify.c b/pjsip/src/pjsip-simple/event_notify.c index 4879f884..25869c40 100644 --- a/pjsip/src/pjsip-simple/event_notify.c +++ b/pjsip/src/pjsip-simple/event_notify.c @@ -1,1644 +1,1644 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_simple/event_notify.h>
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_event.h>
-#include <pj/pool.h>
-#include <pj/timer.h>
-#include <pj/string.h>
-#include <pj/hash.h>
-#include <pj/os.h>
-#include <pj/except.h>
-#include <pj/log.h>
-#include <pj/guid.h>
-
-#define THIS_FILE "event_sub"
-
-/* String names for state.
- * The names here should be compliant with sub_state names in RFC3265.
- */
-static const pj_str_t state[] = {
- { "null", 4 },
- { "active", 6 },
- { "pending", 7 },
- { "terminated", 10 },
- { "unknown", 7 }
-};
-
-/* Timer IDs */
-#define TIMER_ID_REFRESH 1
-#define TIMER_ID_UAS_EXPIRY 2
-
-/* Static configuration. */
-#define SECONDS_BEFORE_EXPIRY 10
-#define MGR_POOL_SIZE 512
-#define MGR_POOL_INC 0
-#define SUB_POOL_SIZE 2048
-#define SUB_POOL_INC 0
-#define HASH_TABLE_SIZE 32
-
-/* Static vars. */
-static int mod_id;
-static const pjsip_method SUBSCRIBE = { PJSIP_OTHER_METHOD, {"SUBSCRIBE", 9}};
-static const pjsip_method NOTIFY = { PJSIP_OTHER_METHOD, { "NOTIFY", 6}};
-
-typedef struct package
-{
- PJ_DECL_LIST_MEMBER(struct package)
- pj_str_t event;
- int accept_cnt;
- pj_str_t *accept;
- pjsip_event_sub_pkg_cb cb;
-} package;
-
-/* Event subscription manager singleton instance. */
-static struct pjsip_event_sub_mgr
-{
- pj_pool_t *pool;
- pj_hash_table_t *ht;
- pjsip_endpoint *endpt;
- pj_mutex_t *mutex;
- pjsip_allow_events_hdr *allow_events;
- package pkg_list;
-} mgr;
-
-/* Fordward declarations for static functions. */
-static pj_status_t mod_init(pjsip_endpoint *, pjsip_module *, pj_uint32_t);
-static pj_status_t mod_deinit(pjsip_module*);
-static void tsx_handler(pjsip_module*, pjsip_event*);
-static pjsip_event_sub *find_sub(pjsip_rx_data *);
-static void on_subscribe_request(pjsip_transaction*, pjsip_rx_data*);
-static void on_subscribe_response(void *, pjsip_event*);
-static void on_notify_request(pjsip_transaction *, pjsip_rx_data*);
-static void on_notify_response(void *, pjsip_event *);
-static void refresh_timer_cb(pj_timer_heap_t*, pj_timer_entry*);
-static void uas_expire_timer_cb(pj_timer_heap_t*, pj_timer_entry*);
-static pj_status_t send_sub_refresh( pjsip_event_sub *sub );
-
-/* Module descriptor. */
-static pjsip_module event_sub_module =
-{
- {"EventSub", 8}, /* Name. */
- 0, /* Flag */
- 128, /* Priority */
- &mgr, /* User data. */
- 2, /* Number of methods supported . */
- { &SUBSCRIBE, &NOTIFY }, /* Array of methods */
- &mod_init, /* init_module() */
- NULL, /* start_module() */
- &mod_deinit, /* deinit_module() */
- &tsx_handler, /* tsx_handler() */
-};
-
-/*
- * Module initialization.
- * This will be called by endpoint when it initializes all modules.
- */
-static pj_status_t mod_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id )
-{
- pj_pool_t *pool;
-
- pool = pjsip_endpt_create_pool(endpt, "esubmgr", MGR_POOL_SIZE, MGR_POOL_INC);
- if (!pool)
- return -1;
-
- /* Manager initialization: create hash table and mutex. */
- mgr.pool = pool;
- mgr.endpt = endpt;
- mgr.ht = pj_hash_create(pool, HASH_TABLE_SIZE);
- if (!mgr.ht)
- return -1;
-
- mgr.mutex = pj_mutex_create(pool, "esubmgr", PJ_MUTEX_SIMPLE);
- if (!mgr.mutex)
- return -1;
-
- /* Attach manager to module. */
- mod->mod_data = &mgr;
-
- /* Init package list. */
- pj_list_init(&mgr.pkg_list);
-
- /* Init Allow-Events header. */
- mgr.allow_events = pjsip_allow_events_hdr_create(mgr.pool);
-
- /* Save the module ID. */
- mod_id = id;
-
- pjsip_event_notify_init_parser();
- return 0;
-}
-
-/*
- * Module deinitialization.
- * Called by endpoint.
- */
-static pj_status_t mod_deinit( struct pjsip_module *mod )
-{
- pj_mutex_lock(mgr.mutex);
- pj_mutex_destroy(mgr.mutex);
- pjsip_endpt_destroy_pool(mgr.endpt, mgr.pool);
- return 0;
-}
-
-/*
- * This public function is called by application to register callback.
- * In exchange, the instance of the module is returned.
- */
-PJ_DEF(pjsip_module*) pjsip_event_sub_get_module(void)
-{
- return &event_sub_module;
-}
-
-/*
- * Register event package.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event,
- int accept_cnt,
- const pj_str_t accept[],
- const pjsip_event_sub_pkg_cb *cb )
-{
- package *pkg;
- int i;
-
- pj_mutex_lock(mgr.mutex);
-
- /* Create and register new package. */
- pkg = pj_pool_alloc(mgr.pool, sizeof(*pkg));
- pj_strdup(mgr.pool, &pkg->event, event);
- pj_list_insert_before(&mgr.pkg_list, pkg);
-
- /* Save Accept specification. */
- pkg->accept_cnt = accept_cnt;
- pkg->accept = pj_pool_alloc(mgr.pool, accept_cnt*sizeof(pj_str_t));
- for (i=0; i<accept_cnt; ++i) {
- pj_strdup(mgr.pool, &pkg->accept[i], &accept[i]);
- }
-
- /* Copy callback. */
- pj_memcpy(&pkg->cb, cb, sizeof(*cb));
-
- /* Update Allow-Events header. */
- pj_assert(mgr.allow_events->event_cnt < PJSIP_MAX_ALLOW_EVENTS);
- mgr.allow_events->events[mgr.allow_events->event_cnt++] = pkg->event;
-
- pj_mutex_unlock(mgr.mutex);
- return 0;
-}
-
-/*
- * Create subscription key (for hash table).
- */
-static void create_subscriber_key( pj_str_t *key, pj_pool_t *pool,
- pjsip_role_e role,
- const pj_str_t *call_id, const pj_str_t *from_tag)
-{
- char *p;
-
- p = key->ptr = pj_pool_alloc(pool, call_id->slen + from_tag->slen + 3);
- *p++ = (role == PJSIP_ROLE_UAS ? 'S' : 'C');
- *p++ = '$';
- pj_memcpy(p, call_id->ptr, call_id->slen);
- p += call_id->slen;
- *p++ = '$';
- pj_memcpy(p, from_tag->ptr, from_tag->slen);
- p += from_tag->slen;
-
- key->slen = p - key->ptr;
-}
-
-
-/*
- * Create UAC subscription.
- */
-PJ_DEF(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt,
- const pj_str_t *from,
- const pj_str_t *to,
- const pj_str_t *event,
- int expires,
- int accept_cnt,
- const pj_str_t accept[],
- void *user_data,
- const pjsip_event_sub_cb *cb)
-{
- pjsip_tx_data *tdata;
- pj_pool_t *pool;
- const pjsip_hdr *hdr;
- pjsip_event_sub *sub;
- PJ_USE_EXCEPTION;
-
- PJ_LOG(5,(THIS_FILE, "Creating event subscription %.*s to %.*s",
- event->slen, event->ptr, to->slen, to->ptr));
-
- /* Create pool for the event subscription. */
- pool = pjsip_endpt_create_pool(endpt, "esub", SUB_POOL_SIZE, SUB_POOL_INC);
- if (!pool) {
- return NULL;
- }
-
- /* Init subscription. */
- sub = pj_pool_calloc(pool, 1, sizeof(*sub));
- sub->pool = pool;
- sub->endpt = endpt;
- sub->role = PJSIP_ROLE_UAC;
- sub->state = PJSIP_EVENT_SUB_STATE_PENDING;
- sub->state_str = state[sub->state];
- sub->user_data = user_data;
- sub->timer.id = 0;
- sub->default_interval = expires;
- pj_memcpy(&sub->cb, cb, sizeof(*cb));
- pj_list_init(&sub->auth_sess);
- pj_list_init(&sub->route_set);
- sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE);
- if (!sub->mutex) {
- pjsip_endpt_destroy_pool(endpt, pool);
- return NULL;
- }
-
- /* The easiest way to parse the parameters is to create a dummy request! */
- tdata = pjsip_endpt_create_request( endpt, &SUBSCRIBE, to, from, to, from,
- NULL, -1, NULL);
- if (!tdata) {
- pj_mutex_destroy(sub->mutex);
- pjsip_endpt_destroy_pool(endpt, pool);
- return NULL;
- }
-
- /*
- * Duplicate headers in the request to our structure.
- */
- PJ_TRY {
- int i;
-
- /* From */
- hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL);
- pj_assert(hdr != NULL);
- sub->from = pjsip_hdr_clone(pool, hdr);
-
- /* To */
- hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL);
- pj_assert(hdr != NULL);
- sub->to = pjsip_hdr_clone(pool, hdr);
-
- /* Contact. */
- sub->contact = pjsip_contact_hdr_create(pool);
- sub->contact->uri = sub->from->uri;
-
- /* Call-ID */
- hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CALL_ID, NULL);
- pj_assert(hdr != NULL);
- sub->call_id = pjsip_hdr_clone(pool, hdr);
-
- /* CSeq */
- sub->cseq = pj_rand() % 0xFFFF;
-
- /* Event. */
- sub->event = pjsip_event_hdr_create(sub->pool);
- pj_strdup(pool, &sub->event->event_type, event);
-
- /* Expires. */
- sub->uac_expires = pjsip_expires_hdr_create(pool);
- sub->uac_expires->ivalue = expires;
-
- /* Accept. */
- sub->local_accept = pjsip_accept_hdr_create(pool);
- for (i=0; i<accept_cnt && i < PJSIP_MAX_ACCEPT_COUNT; ++i) {
- sub->local_accept->count++;
- pj_strdup(sub->pool, &sub->local_accept->values[i], &accept[i]);
- }
-
- /* Register to hash table. */
- create_subscriber_key( &sub->key, pool, PJSIP_ROLE_UAC,
- &sub->call_id->id, &sub->from->tag);
- pj_mutex_lock( mgr.mutex );
- pj_hash_set( pool, mgr.ht, sub->key.ptr, sub->key.slen, sub);
- pj_mutex_unlock( mgr.mutex );
-
- }
- PJ_DEFAULT {
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): caught exception %d during init",
- sub, state[sub->state].ptr, PJ_GET_EXCEPTION()));
-
- pjsip_tx_data_dec_ref(tdata);
- pj_mutex_destroy(sub->mutex);
- pjsip_endpt_destroy_pool(endpt, sub->pool);
- return NULL;
- }
- PJ_END;
-
- /* All set, delete temporary transmit data as we don't need it. */
- pjsip_tx_data_dec_ref(tdata);
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): client created, target=%.*s, event=%.*s",
- sub, state[sub->state].ptr,
- to->slen, to->ptr, event->slen, event->ptr));
-
- return sub;
-}
-
-/*
- * Set credentials.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub,
- int count,
- const pjsip_cred_info cred[])
-{
- pj_mutex_lock(sub->mutex);
- if (count > 0) {
- sub->cred_info = pj_pool_alloc(sub->pool, count*sizeof(pjsip_cred_info));
- pj_memcpy( sub->cred_info, cred, count*sizeof(pjsip_cred_info));
- }
- sub->cred_cnt = count;
- pj_mutex_unlock(sub->mutex);
- return 0;
-}
-
-/*
- * Set route-set.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub,
- const pjsip_route_hdr *route_set )
-{
- const pjsip_route_hdr *hdr;
-
- pj_mutex_lock(sub->mutex);
-
- /* Clear existing route set. */
- pj_list_init(&sub->route_set);
-
- /* Duplicate route headers. */
- hdr = route_set->next;
- while (hdr != route_set) {
- pjsip_route_hdr *new_hdr = pjsip_hdr_clone(sub->pool, hdr);
- pj_list_insert_before(&sub->route_set, new_hdr);
- hdr = hdr->next;
- }
-
- pj_mutex_unlock(sub->mutex);
-
- return 0;
-}
-
-/*
- * Send subscribe request.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub )
-{
- pj_status_t status;
-
- pj_mutex_lock(sub->mutex);
- status = send_sub_refresh(sub);
- pj_mutex_unlock(sub->mutex);
-
- return status;
-}
-
-/*
- * Destroy subscription.
- * If there are pending transactions, then this will just set the flag.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub)
-{
- pj_assert(sub != NULL);
- if (sub == NULL)
- return -1;
-
- /* Application must terminate the subscription first. */
- pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_NULL ||
- sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED);
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): about to be destroyed",
- sub, state[sub->state].ptr));
-
- pj_mutex_lock(mgr.mutex);
- pj_mutex_lock(sub->mutex);
-
- /* Set delete flag. */
- sub->delete_flag = 1;
-
- /* Unregister timer, if any. */
- if (sub->timer.id != 0) {
- pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
- sub->timer.id = 0;
- }
-
- if (sub->pending_tsx > 0) {
- pj_mutex_unlock(sub->mutex);
- pj_mutex_unlock(mgr.mutex);
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): has %d pending, will destroy later",
- sub, state[sub->state].ptr,
- sub->pending_tsx));
- return 1;
- }
-
- /* Unregister from hash table. */
- pj_hash_set(sub->pool, mgr.ht, sub->key.ptr, sub->key.slen, NULL);
-
- /* Destroy. */
- pj_mutex_destroy(sub->mutex);
- pjsip_endpt_destroy_pool(sub->endpt, sub->pool);
-
- pj_mutex_unlock(mgr.mutex);
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p: destroyed", sub));
- return 0;
-}
-
-/* Change state. */
-static void sub_set_state( pjsip_event_sub *sub, int new_state)
-{
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): changed state to %s",
- sub, state[sub->state].ptr, state[new_state].ptr));
- sub->state = new_state;
- sub->state_str = state[new_state];
-}
-
-/*
- * Refresh subscription.
- */
-static pj_status_t send_sub_refresh( pjsip_event_sub *sub )
-{
- pjsip_tx_data *tdata;
- pj_status_t status;
- const pjsip_route_hdr *route;
-
- pj_assert(sub->role == PJSIP_ROLE_UAC);
- pj_assert(sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED);
- if (sub->role != PJSIP_ROLE_UAC ||
- sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED)
- {
- return -1;
- }
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refreshing subscription",
- sub, state[sub->state].ptr));
-
- /* Create request. */
- tdata = pjsip_endpt_create_request_from_hdr( sub->endpt,
- &SUBSCRIBE,
- sub->to->uri,
- sub->from, sub->to,
- sub->contact, sub->call_id,
- sub->cseq++,
- NULL);
-
- if (!tdata) {
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh: unable to create tx data!",
- sub, state[sub->state].ptr));
- return -1;
- }
-
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, sub->event));
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires));
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, sub->local_accept));
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
-
- /* Authentication */
- pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
- sub->cred_cnt, sub->cred_info);
-
- /* Route set. */
- route = sub->route_set.next;
- while (route != &sub->route_set) {
- pj_list_insert_before( &tdata->msg->hdr,
- pjsip_hdr_shallow_clone(tdata->pool, route));
- route = route->next;
- }
-
- /* Send */
- status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub,
- &on_subscribe_response);
- if (status == 0) {
- sub->pending_tsx++;
- } else {
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to refresh subscription!",
- sub, state[sub->state].ptr));
- }
-
- return status;
-}
-
-/*
- * Stop subscription.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub )
-{
- pjsip_tx_data *tdata;
- const pjsip_route_hdr *route;
- pj_status_t status;
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): unsubscribing...",
- sub, state[sub->state].ptr));
-
- /* Lock subscription. */
- pj_mutex_lock(sub->mutex);
-
- pj_assert(sub->role == PJSIP_ROLE_UAC);
-
- /* Kill refresh timer, if any. */
- if (sub->timer.id != 0) {
- sub->timer.id = 0;
- pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
- }
-
- /* Create request. */
- tdata = pjsip_endpt_create_request_from_hdr( sub->endpt,
- &SUBSCRIBE,
- sub->to->uri,
- sub->from, sub->to,
- sub->contact, sub->call_id,
- sub->cseq++,
- NULL);
-
- if (!tdata) {
- pj_mutex_unlock(sub->mutex);
- return -1;
- }
-
- /* Add headers to request. */
- pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event));
- sub->uac_expires->ivalue = 0;
- pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires));
-
- /* Add authentication. */
- pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
- sub->cred_cnt, sub->cred_info);
-
-
- /* Route set. */
- route = sub->route_set.next;
- while (route != &sub->route_set) {
- pj_list_insert_before( &tdata->msg->hdr,
- pjsip_hdr_shallow_clone(tdata->pool, route));
- route = route->next;
- }
-
- /* Prevent timer from refreshing itself. */
- sub->default_interval = 0;
-
- /* Set state. */
- sub_set_state( sub, PJSIP_EVENT_SUB_STATE_TERMINATED );
-
- /* Send the request. */
- status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub,
- &on_subscribe_response);
- if (status == 0) {
- sub->pending_tsx++;
- }
-
- pj_mutex_unlock(sub->mutex);
-
- if (status != 0) {
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to unsubscribe!",
- sub, state[sub->state].ptr));
- }
-
- return status;
-}
-
-/*
- * Send notify.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_notify(pjsip_event_sub *sub,
- pjsip_event_sub_state new_state,
- const pj_str_t *reason,
- pjsip_msg_body *body)
-{
- pjsip_tx_data *tdata;
- pjsip_sub_state_hdr *ss_hdr;
- const pjsip_route_hdr *route;
- pj_time_val now;
- pj_status_t status;
- pjsip_event_sub_state old_state = sub->state;
-
- pj_gettimeofday(&now);
-
- pj_assert(sub->role == PJSIP_ROLE_UAS);
- if (sub->role != PJSIP_ROLE_UAS)
- return -1;
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sending NOTIFY",
- sub, state[new_state].ptr));
-
- /* Lock subscription. */
- pj_mutex_lock(sub->mutex);
-
- /* Can not send NOTIFY if current state is NULL. We can accept TERMINATED. */
- if (sub->state==PJSIP_EVENT_SUB_STATE_NULL) {
- pj_assert(0);
- pj_mutex_unlock(sub->mutex);
- return -1;
- }
-
- /* Update state no matter what. */
- sub_set_state(sub, new_state);
-
- /* Create transmit data. */
- tdata = pjsip_endpt_create_request_from_hdr( sub->endpt,
- &NOTIFY,
- sub->to->uri,
- sub->from, sub->to,
- sub->contact, sub->call_id,
- sub->cseq++,
- NULL);
- if (!tdata) {
- pj_mutex_unlock(sub->mutex);
- return -1;
- }
-
- /* Add Event header. */
- pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event));
-
- /* Add Subscription-State header. */
- ss_hdr = pjsip_sub_state_hdr_create(tdata->pool);
- ss_hdr->sub_state = state[new_state];
- ss_hdr->expires_param = sub->expiry_time.sec - now.sec;
- if (ss_hdr->expires_param < 0)
- ss_hdr->expires_param = 0;
- if (reason)
- pj_strdup(tdata->pool, &ss_hdr->reason_param, reason);
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)ss_hdr);
-
- /* Add Allow-Events header. */
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
-
- /* Add authentication */
- pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
- sub->cred_cnt, sub->cred_info);
-
- /* Route set. */
- route = sub->route_set.next;
- while (route != &sub->route_set) {
- pj_list_insert_before( &tdata->msg->hdr,
- pjsip_hdr_shallow_clone(tdata->pool, route));
- route = route->next;
- }
-
- /* Attach body. */
- tdata->msg->body = body;
-
- /* That's it, send! */
- status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, &on_notify_response);
- if (status == 0)
- sub->pending_tsx++;
-
- /* If terminated notify application. */
- if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
- if (sub->cb.on_sub_terminated) {
- sub->pending_tsx++;
- (*sub->cb.on_sub_terminated)(sub, reason);
- sub->pending_tsx--;
- }
- }
-
- /* Unlock subscription. */
- pj_mutex_unlock(sub->mutex);
-
- if (status != 0) {
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): failed to send NOTIFY",
- sub, state[sub->state].ptr));
- }
-
- if (sub->delete_flag && sub->pending_tsx <= 0) {
- pjsip_event_sub_destroy(sub);
- }
- return status;
-}
-
-
-/* If this timer callback is called, it means subscriber hasn't refreshed its
- * subscription on-time. Set the state to terminated. This will also send
- * NOTIFY with Subscription-State set to terminated.
- */
-static void uas_expire_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry)
-{
- pjsip_event_sub *sub = entry->user_data;
- pj_str_t reason = { "timeout", 7 };
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): UAS subscription expired!",
- sub, state[sub->state].ptr));
-
- pj_mutex_lock(sub->mutex);
- sub->timer.id = 0;
-
- if (sub->cb.on_sub_terminated && sub->state!=PJSIP_EVENT_SUB_STATE_TERMINATED) {
- /* Notify application, but prevent app from destroying the sub. */
- ++sub->pending_tsx;
- (*sub->cb.on_sub_terminated)(sub, &reason);
- --sub->pending_tsx;
- }
- //pjsip_event_sub_notify( sub, PJSIP_EVENT_SUB_STATE_TERMINATED,
- // &reason, NULL);
- pj_mutex_unlock(sub->mutex);
-
-}
-
-/* Schedule notifier expiration. */
-static void sub_schedule_uas_expire( pjsip_event_sub *sub, int sec_delay)
-{
- pj_time_val delay = { 0, 0 };
- pj_parsed_time pt;
-
- if (sub->timer.id != 0)
- pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
-
- pj_gettimeofday(&sub->expiry_time);
- sub->expiry_time.sec += sec_delay;
-
- sub->timer.id = TIMER_ID_UAS_EXPIRY;
- sub->timer.user_data = sub;
- sub->timer.cb = &uas_expire_timer_cb;
- delay.sec = sec_delay;
- pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay);
-
- pj_time_decode(&sub->expiry_time, &pt);
- PJ_LOG(4,(THIS_FILE,
- "event_sub%p (%s)(UAS): will expire at %02d:%02d:%02d (in %d secs)",
- sub, state[sub->state].ptr, pt.hour, pt.min, pt.sec, sec_delay));
-}
-
-/* This timer is called for UAC to refresh the subscription. */
-static void refresh_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry)
-{
- pjsip_event_sub *sub = entry->user_data;
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh subscription timer",
- sub, state[sub->state].ptr));
-
- pj_mutex_lock(sub->mutex);
- sub->timer.id = 0;
- send_sub_refresh(sub);
- pj_mutex_unlock(sub->mutex);
-}
-
-
-/* This will update the UAC's refresh schedule. */
-static void update_next_refresh(pjsip_event_sub *sub, int interval)
-{
- pj_time_val delay = {0, 0};
- pj_parsed_time pt;
-
- if (interval < SECONDS_BEFORE_EXPIRY) {
- PJ_LOG(4,(THIS_FILE,
- "event_sub%p (%s): expiration delay too short (%d sec)! updated.",
- sub, state[sub->state].ptr, interval));
- interval = SECONDS_BEFORE_EXPIRY;
- }
-
- if (sub->timer.id != 0)
- pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
-
- sub->timer.id = TIMER_ID_REFRESH;
- sub->timer.user_data = sub;
- sub->timer.cb = &refresh_timer_cb;
- pj_gettimeofday(&sub->expiry_time);
- delay.sec = interval - SECONDS_BEFORE_EXPIRY;
- sub->expiry_time.sec += delay.sec;
-
- pj_time_decode(&sub->expiry_time, &pt);
- PJ_LOG(4,(THIS_FILE,
- "event_sub%p (%s): will send SUBSCRIBE at %02d:%02d:%02d (in %d secs)",
- sub, state[sub->state].ptr,
- pt.hour, pt.min, pt.sec,
- delay.sec));
-
- pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay );
-}
-
-
-/* Find subscription in the hash table.
- * If found, lock the subscription before returning to caller.
- */
-static pjsip_event_sub *find_sub(pjsip_rx_data *rdata)
-{
- pj_str_t key;
- pjsip_role_e role;
- pjsip_event_sub *sub;
- pjsip_method *method = &rdata->msg->line.req.method;
- pj_str_t *tag;
-
- if (rdata->msg->type == PJSIP_REQUEST_MSG) {
- if (pjsip_method_cmp(method, &SUBSCRIBE)==0) {
- role = PJSIP_ROLE_UAS;
- tag = &rdata->to_tag;
- } else {
- pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0);
- role = PJSIP_ROLE_UAC;
- tag = &rdata->to_tag;
- }
- } else {
- if (pjsip_method_cmp(&rdata->cseq->method, &SUBSCRIBE)==0) {
- role = PJSIP_ROLE_UAC;
- tag = &rdata->from_tag;
- } else {
- pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0);
- role = PJSIP_ROLE_UAS;
- tag = &rdata->from_tag;
- }
- }
- create_subscriber_key( &key, rdata->pool, role, &rdata->call_id, tag);
-
- pj_mutex_lock(mgr.mutex);
- sub = pj_hash_get(mgr.ht, key.ptr, key.slen);
- if (sub)
- pj_mutex_lock(sub->mutex);
- pj_mutex_unlock(mgr.mutex);
-
- return sub;
-}
-
-
-/* This function is called when we receive SUBSCRIBE request message
- * to refresh existing subscription.
- */
-static void on_received_sub_refresh( pjsip_event_sub *sub,
- pjsip_transaction *tsx, pjsip_rx_data *rdata)
-{
- pjsip_event_hdr *e;
- pjsip_expires_hdr *expires;
- pj_str_t hname;
- int status = 200;
- pj_str_t reason_phrase = { NULL, 0 };
- int new_state = sub->state;
- int old_state = sub->state;
- int new_interval = 0;
- pjsip_tx_data *tdata;
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received target refresh",
- sub, state[sub->state].ptr));
-
- /* Check that the event matches. */
- hname = pj_str("Event");
- e = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL);
- if (!e) {
- status = 400;
- reason_phrase = pj_str("Missing Event header");
- goto send_response;
- }
- if (pj_stricmp(&e->event_type, &sub->event->event_type) != 0 ||
- pj_stricmp(&e->id_param, &sub->event->id_param) != 0)
- {
- status = 481;
- reason_phrase = pj_str("Subscription does not exist");
- goto send_response;
- }
-
- /* Check server state. */
- if (sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) {
- status = 481;
- reason_phrase = pj_str("Subscription does not exist");
- goto send_response;
- }
-
- /* Check expires header. */
- expires = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_EXPIRES, NULL);
- if (!expires) {
- /*
- status = 400;
- reason_phrase = pj_str("Missing Expires header");
- goto send_response;
- */
- new_interval = sub->default_interval;
- } else {
- /* Check that interval is not too short.
- * Note that expires time may be zero (for unsubscription).
- */
- new_interval = expires->ivalue;
- if (new_interval != 0 && new_interval < SECONDS_BEFORE_EXPIRY) {
- status = PJSIP_SC_INTERVAL_TOO_BRIEF;
- goto send_response;
- }
- }
-
- /* Update interval. */
- sub->default_interval = new_interval;
- pj_gettimeofday(&sub->expiry_time);
- sub->expiry_time.sec += new_interval;
-
- /* Update timer only if this is not unsubscription. */
- if (new_interval > 0) {
- sub->default_interval = new_interval;
- sub_schedule_uas_expire( sub, new_interval );
-
- /* Call callback. */
- if (sub->cb.on_received_refresh) {
- sub->pending_tsx++;
- (*sub->cb.on_received_refresh)(sub, rdata);
- sub->pending_tsx--;
- }
- }
-
-send_response:
- tdata = pjsip_endpt_create_response( sub->endpt, rdata, status);
- if (tdata) {
- if (reason_phrase.slen)
- tdata->msg->line.status.reason = reason_phrase;
-
- /* Add Expires header. */
- expires = pjsip_expires_hdr_create(tdata->pool);
- expires->ivalue = sub->default_interval;
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires);
-
- if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
- pjsip_msg_add_hdr(tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
- }
- /* Send down to transaction. */
- pjsip_tsx_on_tx_msg(tsx, tdata);
- }
-
- if (sub->default_interval==0 || !PJSIP_IS_STATUS_IN_CLASS(status,200)) {
- /* Notify application if sub is terminated. */
- new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
- sub_set_state(sub, new_state);
- if (new_state!=old_state && sub->cb.on_sub_terminated) {
- pj_str_t reason = {"", 0};
- if (reason_phrase.slen) reason = reason_phrase;
- else reason = *pjsip_get_status_text(status);
-
- sub->pending_tsx++;
- (*sub->cb.on_sub_terminated)(sub, &reason);
- sub->pending_tsx--;
- }
- }
-
- pj_mutex_unlock(sub->mutex);
-
- /* Prefer to call log when we're not holding the mutex. */
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sent refresh response %s, status=%d",
- sub, state[sub->state].ptr,
- (tdata ? tdata->obj_name : "null"), status));
-
- /* Check if application has requested deletion. */
- if (sub->delete_flag && sub->pending_tsx <= 0) {
- pjsip_event_sub_destroy(sub);
- }
-
-}
-
-
-/* This function is called when we receive SUBSCRIBE request message for
- * a new subscription.
- */
-static void on_new_subscription( pjsip_transaction *tsx, pjsip_rx_data *rdata )
-{
- package *pkg;
- pj_pool_t *pool;
- pjsip_event_sub *sub = NULL;
- pj_str_t hname;
- int status = 200;
- pj_str_t reason = { NULL, 0 };
- pjsip_tx_data *tdata;
- pjsip_expires_hdr *expires;
- pjsip_accept_hdr *accept;
- pjsip_event_hdr *evhdr;
-
- /* Get the Event header. */
- hname = pj_str("Event");
- evhdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL);
- if (!evhdr) {
- status = 400;
- reason = pj_str("No Event header in request");
- goto send_response;
- }
-
- /* Find corresponding package.
- * We don't lock the manager's mutex since we assume the package list
- * won't change once the application is running!
- */
- pkg = mgr.pkg_list.next;
- while (pkg != &mgr.pkg_list) {
- if (pj_stricmp(&pkg->event, &evhdr->event_type) == 0)
- break;
- pkg = pkg->next;
- }
-
- if (pkg == &mgr.pkg_list) {
- /* Event type is not supported by any packages! */
- status = 489;
- reason = pj_str("Bad Event");
- goto send_response;
- }
-
- /* First check that the Accept specification matches the
- * package's Accept types.
- */
- accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL);
- if (accept) {
- unsigned i;
- pj_str_t *content_type = NULL;
-
- for (i=0; i<accept->count && !content_type; ++i) {
- int j;
- for (j=0; j<pkg->accept_cnt; ++j) {
- if (pj_stricmp(&accept->values[i], &pkg->accept[j])==0) {
- content_type = &pkg->accept[j];
- break;
- }
- }
- }
-
- if (!content_type) {
- status = PJSIP_SC_NOT_ACCEPTABLE_HERE;
- goto send_response;
- }
- }
-
- /* Check whether the package wants to accept the subscription. */
- pj_assert(pkg->cb.on_query_subscribe != NULL);
- (*pkg->cb.on_query_subscribe)(rdata, &status);
- if (!PJSIP_IS_STATUS_IN_CLASS(status,200))
- goto send_response;
-
- /* Create new subscription record. */
- pool = pjsip_endpt_create_pool(tsx->endpt, "esub",
- SUB_POOL_SIZE, SUB_POOL_INC);
- if (!pool) {
- status = 500;
- goto send_response;
- }
- sub = pj_pool_calloc(pool, 1, sizeof(*sub));
- sub->pool = pool;
- sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE);
- if (!sub->mutex) {
- status = 500;
- goto send_response;
- }
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p: notifier is created.", sub));
-
- /* Start locking mutex. */
- pj_mutex_lock(sub->mutex);
-
- /* Init UAS subscription */
- sub->endpt = tsx->endpt;
- sub->role = PJSIP_ROLE_UAS;
- sub->state = PJSIP_EVENT_SUB_STATE_PENDING;
- sub->state_str = state[sub->state];
- pj_list_init(&sub->auth_sess);
- pj_list_init(&sub->route_set);
- sub->from = pjsip_hdr_clone(pool, rdata->to);
- pjsip_fromto_set_from(sub->from);
- if (sub->from->tag.slen == 0) {
- pj_create_unique_string(pool, &sub->from->tag);
- rdata->to->tag = sub->from->tag;
- }
- sub->to = pjsip_hdr_clone(pool, rdata->from);
- pjsip_fromto_set_to(sub->to);
- sub->contact = pjsip_contact_hdr_create(pool);
- sub->contact->uri = sub->from->uri;
- sub->call_id = pjsip_cid_hdr_create(pool);
- pj_strdup(pool, &sub->call_id->id, &rdata->call_id);
- sub->cseq = pj_rand() % 0xFFFF;
-
- expires = pjsip_msg_find_hdr( rdata->msg, PJSIP_H_EXPIRES, NULL);
- if (expires) {
- sub->default_interval = expires->ivalue;
- if (sub->default_interval > 0 &&
- sub->default_interval < SECONDS_BEFORE_EXPIRY)
- {
- status = 423; /* Interval too short. */
- goto send_response;
- }
- } else {
- sub->default_interval = 600;
- }
-
- /* Clone Event header. */
- sub->event = pjsip_hdr_clone(pool, evhdr);
-
- /* Register to hash table. */
- create_subscriber_key(&sub->key, pool, PJSIP_ROLE_UAS, &sub->call_id->id,
- &sub->from->tag);
- pj_mutex_lock(mgr.mutex);
- pj_hash_set(pool, mgr.ht, sub->key.ptr, sub->key.slen, sub);
- pj_mutex_unlock(mgr.mutex);
-
- /* Set timer where subscription will expire only when expires<>0.
- * Subscriber may send new subscription with expires==0.
- */
- if (sub->default_interval != 0) {
- sub_schedule_uas_expire( sub, sub->default_interval-SECONDS_BEFORE_EXPIRY);
- }
-
- /* Notify application. */
- if (pkg->cb.on_subscribe) {
- pjsip_event_sub_cb *cb = NULL;
- sub->pending_tsx++;
- (*pkg->cb.on_subscribe)(sub, rdata, &cb, &sub->default_interval);
- sub->pending_tsx--;
- if (cb == NULL)
- pj_memset(&sub->cb, 0, sizeof(*cb));
- else
- pj_memcpy(&sub->cb, cb, sizeof(*cb));
- }
-
-
-send_response:
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s)(UAS): status=%d",
- sub, state[sub->state].ptr, status));
-
- tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status);
- if (tdata) {
- if (reason.slen) {
- /* Customize reason text. */
- tdata->msg->line.status.reason = reason;
- }
- if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
- /* Add Expires header. */
- pjsip_expires_hdr *hdr;
-
- hdr = pjsip_expires_hdr_create(tdata->pool);
- hdr->ivalue = sub->default_interval;
- pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr );
- }
- if (status == 423) {
- /* Add Min-Expires header. */
- pjsip_min_expires_hdr *hdr;
-
- hdr = pjsip_min_expires_hdr_create(tdata->pool);
- hdr->ivalue = SECONDS_BEFORE_EXPIRY;
- pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr);
- }
- if (status == 489 ||
- status==PJSIP_SC_NOT_ACCEPTABLE_HERE ||
- PJSIP_IS_STATUS_IN_CLASS(status,200))
- {
- /* Add Allow-Events header. */
- pjsip_hdr *hdr;
- hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events);
- pjsip_msg_add_hdr(tdata->msg, hdr);
-
- /* Should add Accept header?. */
- }
-
- pjsip_tsx_on_tx_msg(tsx, tdata);
- }
-
- /* If received new subscription with expires=0, terminate. */
- if (sub && sub->default_interval == 0) {
- pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED);
- if (sub->cb.on_sub_terminated) {
- pj_str_t reason = { "timeout", 7 };
- (*sub->cb.on_sub_terminated)(sub, &reason);
- }
- }
-
- if (!PJSIP_IS_STATUS_IN_CLASS(status,200) || (sub && sub->delete_flag)) {
- if (sub && sub->mutex) {
- pjsip_event_sub_destroy(sub);
- } else if (sub) {
- pjsip_endpt_destroy_pool(tsx->endpt, sub->pool);
- }
- } else {
- pj_assert(status >= 200);
- pj_mutex_unlock(sub->mutex);
- }
-}
-
-/* This is the main callback when SUBSCRIBE request is received. */
-static void on_subscribe_request(pjsip_transaction *tsx, pjsip_rx_data *rdata)
-{
- pjsip_event_sub *sub = find_sub(rdata);
-
- if (sub)
- on_received_sub_refresh(sub, tsx, rdata);
- else
- on_new_subscription(tsx, rdata);
-}
-
-
-/* This callback is called when response to SUBSCRIBE is received. */
-static void on_subscribe_response(void *token, pjsip_event *event)
-{
- pjsip_event_sub *sub = token;
- pjsip_transaction *tsx = event->obj.tsx;
- int new_state, old_state = sub->state;
-
- pj_assert(tsx->status_code >= 200);
- if (tsx->status_code < 200)
- return;
-
- pj_assert(sub->role == PJSIP_ROLE_UAC);
-
- /* Lock mutex. */
- pj_mutex_lock(sub->mutex);
-
- /* If request failed with 401/407 error, silently retry the request. */
- if (tsx->status_code==401 || tsx->status_code==407) {
- pjsip_tx_data *tdata;
- tdata = pjsip_auth_reinit_req(sub->endpt,
- sub->pool, &sub->auth_sess,
- sub->cred_cnt, sub->cred_info,
- tsx->last_tx, event->src.rdata );
- if (tdata) {
- int status;
- pjsip_cseq_hdr *cseq;
- cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
- cseq->cseq = sub->cseq++;
- status = pjsip_endpt_send_request( sub->endpt, tdata,
- -1, sub,
- &on_subscribe_response);
- if (status == 0) {
- pj_mutex_unlock(sub->mutex);
- return;
- }
- }
- }
-
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) {
- /* Update To tag. */
- if (sub->to->tag.slen == 0)
- pj_strdup(sub->pool, &sub->to->tag, &event->src.rdata->to_tag);
-
- new_state = sub->state;
-
- } else if (tsx->status_code == 481) {
- new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
-
- } else if (tsx->status_code >= 300) {
- /* RFC 3265 Section 3.1.4.2:
- * If a SUBSCRIBE request to refresh a subscription fails
- * with a non-481 response, the original subscription is still
- * considered valid for the duration of original exires.
- *
- * Note:
- * Since we normally send SUBSCRIBE for refreshing the subscription,
- * it means the subscription already expired anyway. So we terminate
- * the subscription now.
- */
- if (sub->state != PJSIP_EVENT_SUB_STATE_ACTIVE) {
- new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
- } else {
- /* Use this to be compliant with Section 3.1.4.2
- new_state = sub->state;
- */
- new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
- }
- } else {
- pj_assert(0);
- new_state = sub->state;
- }
-
- if (new_state != sub->state && sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) {
- sub_set_state(sub, new_state);
- }
-
- if (sub->state == PJSIP_EVENT_SUB_STATE_ACTIVE ||
- sub->state == PJSIP_EVENT_SUB_STATE_PENDING)
- {
- /*
- * Register timer for next subscription refresh, but only when
- * we're not unsubscribing. Also update default_interval and Expires
- * header.
- */
- if (sub->default_interval > 0 && !sub->delete_flag) {
- pjsip_expires_hdr *exp = NULL;
-
- /* Could be transaction timeout. */
- if (event->src_type == PJSIP_EVENT_RX_MSG) {
- exp = pjsip_msg_find_hdr(event->src.rdata->msg,
- PJSIP_H_EXPIRES, NULL);
- }
-
- if (exp) {
- int delay = exp->ivalue;
- if (delay > 0) {
- pj_time_val new_expiry;
- pj_gettimeofday(&new_expiry);
- new_expiry.sec += delay;
- if (sub->timer.id==0 ||
- new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2)
- {
- //if (delay > 0 && delay < sub->default_interval) {
- sub->default_interval = delay;
- sub->uac_expires->ivalue = delay;
- update_next_refresh(sub, delay);
- }
- }
- }
- }
- }
-
- /* Call callback. */
- if (!sub->delete_flag) {
- if (sub->cb.on_received_sub_response) {
- (*sub->cb.on_received_sub_response)(sub, event);
- }
- }
-
- /* Notify application if we're terminated. */
- if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
- if (sub->cb.on_sub_terminated) {
- pj_str_t reason;
- if (event->src_type == PJSIP_EVENT_RX_MSG)
- reason = event->src.rdata->msg->line.status.reason;
- else
- reason = *pjsip_get_status_text(tsx->status_code);
-
- (*sub->cb.on_sub_terminated)(sub, &reason);
- }
- }
-
- /* Decrement pending tsx count. */
- --sub->pending_tsx;
- pj_assert(sub->pending_tsx >= 0);
-
- if (sub->delete_flag && sub->pending_tsx <= 0) {
- pjsip_event_sub_destroy(sub);
- } else {
- pj_mutex_unlock(sub->mutex);
- }
-
- /* DO NOT ACCESS sub FROM NOW ON! IT MIGHT HAVE BEEN DELETED */
-}
-
-/*
- * This callback called when we receive incoming NOTIFY request.
- */
-static void on_notify_request(pjsip_transaction *tsx, pjsip_rx_data *rdata)
-{
- pjsip_event_sub *sub;
- pjsip_tx_data *tdata;
- int status = 200;
- int old_state;
- pj_str_t reason = { NULL, 0 };
- pj_str_t reason_phrase = { NULL, 0 };
- int new_state = PJSIP_EVENT_SUB_STATE_NULL;
-
- /* Find subscription based on Call-ID and From tag.
- * This will also automatically lock the subscription, if it's found.
- */
- sub = find_sub(rdata);
- if (!sub) {
- /* RFC 3265: Section 3.2 Description of NOTIFY Behavior:
- * Answer with 481 Subscription does not exist.
- */
- PJ_LOG(4,(THIS_FILE, "Unable to find subscription for incoming NOTIFY!"));
- status = 481;
- reason_phrase = pj_str("Subscription does not exist");
-
- } else {
- pj_assert(sub->role == PJSIP_ROLE_UAC);
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received NOTIFY",
- sub, state[sub->state].ptr));
-
- }
-
- new_state = old_state = sub->state;
-
- /* RFC 3265: Section 3.2.1
- * Check that the Event header match the subscription.
- */
- if (status == 200) {
- pjsip_event_hdr *hdr;
- pj_str_t hname = { "Event", 5 };
-
- hdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL);
- if (!hdr) {
- status = PJSIP_SC_BAD_REQUEST;
- reason_phrase = pj_str("No Event header found");
- } else if (pj_stricmp(&hdr->event_type, &sub->event->event_type) != 0 ||
- pj_stricmp(&hdr->id_param, &sub->event->id_param) != 0)
- {
- status = 481;
- reason_phrase = pj_str("Subscription does not exist");
- }
- }
-
- /* Update subscription state and timer. */
- if (status == 200) {
- pjsip_sub_state_hdr *hdr;
- const pj_str_t hname = { "Subscription-State", 18 };
- const pj_str_t state_active = { "active", 6 },
- state_pending = { "pending", 7},
- state_terminated = { "terminated", 10 };
-
- hdr = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL);
- if (!hdr) {
- status = PJSIP_SC_BAD_REQUEST;
- reason_phrase = pj_str("No Subscription-State header found");
- goto process;
- }
-
- /*
- * Update subscription state.
- */
- if (pj_stricmp(&hdr->sub_state, &state_active) == 0) {
- if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
- new_state = PJSIP_EVENT_SUB_STATE_ACTIVE;
- } else if (pj_stricmp(&hdr->sub_state, &state_pending) == 0) {
- if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
- new_state = PJSIP_EVENT_SUB_STATE_PENDING;
- } else if (pj_stricmp(&hdr->sub_state, &state_terminated) == 0) {
- new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
- } else {
- new_state = PJSIP_EVENT_SUB_STATE_UNKNOWN;
- }
-
- reason = hdr->reason_param;
-
- if (new_state != sub->state && new_state != PJSIP_EVENT_SUB_STATE_NULL &&
- sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
- {
- sub_set_state(sub, new_state);
- if (new_state == PJSIP_EVENT_SUB_STATE_UNKNOWN) {
- pj_strdup_with_null(sub->pool, &sub->state_str, &hdr->sub_state);
- } else {
- sub->state_str = state[new_state];
- }
- }
-
- /*
- * Update timeout timer in required, just in case notifier changed the
- * expiration to shorter time.
- * Section 3.2.2: the expires param can only shorten the interval.
- */
- if ((sub->state==PJSIP_EVENT_SUB_STATE_ACTIVE ||
- sub->state==PJSIP_EVENT_SUB_STATE_PENDING) && hdr->expires_param > 0)
- {
- pj_time_val now, new_expiry;
-
- pj_gettimeofday(&now);
- new_expiry.sec = now.sec + hdr->expires_param;
- if (sub->timer.id==0 ||
- new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2)
- {
- update_next_refresh(sub, hdr->expires_param);
- }
- }
- }
-
-process:
- /* Note: here we sub MAY BE NULL! */
-
- /* Send response to NOTIFY */
- tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status );
- if (tdata) {
- if (reason_phrase.slen)
- tdata->msg->line.status.reason = reason_phrase;
-
- if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
- pjsip_hdr *hdr;
- hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events);
- pjsip_msg_add_hdr( tdata->msg, hdr);
- }
-
- pjsip_tsx_on_tx_msg(tsx, tdata);
- }
-
- /* Call NOTIFY callback, if any. */
- if (sub && PJSIP_IS_STATUS_IN_CLASS(status,200) && sub->cb.on_received_notify) {
- sub->pending_tsx++;
- (*sub->cb.on_received_notify)(sub, rdata);
- sub->pending_tsx--;
- }
-
- /* Check if subscription is terminated and call callback. */
- if (sub && new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
- if (sub->cb.on_sub_terminated) {
- sub->pending_tsx++;
- (*sub->cb.on_sub_terminated)(sub, &reason);
- sub->pending_tsx--;
- }
- }
-
- /* Check if application has requested deletion. */
- if (sub && sub->delete_flag && sub->pending_tsx <= 0) {
- pjsip_event_sub_destroy(sub);
- } else if (sub) {
- pj_mutex_unlock(sub->mutex);
- }
-}
-
-/* This callback is called when we received NOTIFY response. */
-static void on_notify_response(void *token, pjsip_event *event)
-{
- pjsip_event_sub *sub = token;
- pjsip_event_sub_state old_state = sub->state;
- pjsip_transaction *tsx = event->obj.tsx;
-
- /* Lock the subscription. */
- pj_mutex_lock(sub->mutex);
-
- pj_assert(sub->role == PJSIP_ROLE_UAS);
-
- /* If request failed with authorization failure, silently retry. */
- if (tsx->status_code==401 || tsx->status_code==407) {
- pjsip_tx_data *tdata;
- tdata = pjsip_auth_reinit_req(sub->endpt,
- sub->pool, &sub->auth_sess,
- sub->cred_cnt, sub->cred_info,
- tsx->last_tx, event->src.rdata );
- if (tdata) {
- int status;
- pjsip_cseq_hdr *cseq;
- cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
- cseq->cseq = sub->cseq++;
- status = pjsip_endpt_send_request( sub->endpt, tdata,
- -1, sub,
- &on_notify_response);
- if (status == 0) {
- pj_mutex_unlock(sub->mutex);
- return;
- }
- }
- }
-
- /* Notify application. */
- if (sub->cb.on_received_notify_response)
- (*sub->cb.on_received_notify_response)(sub, event);
-
- /* Check for response 481. */
- if (event->obj.tsx->status_code == 481) {
- /* Remote says that the subscription does not exist!
- * Terminate subscription!
- */
- sub_set_state(sub, PJSIP_EVENT_SUB_STATE_TERMINATED);
- if (sub->timer.id) {
- pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
- sub->timer.id = 0;
- }
-
- PJ_LOG(4, (THIS_FILE,
- "event_sub%p (%s): got 481 response to NOTIFY. Terminating...",
- sub, state[sub->state].ptr));
-
- /* Notify app. */
- if (sub->state!=old_state && sub->cb.on_sub_terminated)
- (*sub->cb.on_sub_terminated)(sub, &event->src.rdata->msg->line.status.reason);
- }
-
- /* Decrement pending transaction count. */
- --sub->pending_tsx;
- pj_assert(sub->pending_tsx >= 0);
-
- /* Check that the subscription is marked for deletion. */
- if (sub->delete_flag && sub->pending_tsx <= 0) {
- pjsip_event_sub_destroy(sub);
- } else {
- pj_mutex_unlock(sub->mutex);
- }
-
- /* DO NOT ACCESS sub, IT MIGHT HAVE BEEN DESTROYED! */
-}
-
-
-/* This is the transaction handler for incoming SUBSCRIBE and NOTIFY
- * requests.
- */
-static void tsx_handler( struct pjsip_module *mod, pjsip_event *event )
-{
- pjsip_msg *msg;
- pjsip_rx_data *rdata;
-
- /* Only want incoming message events. */
- if (event->src_type != PJSIP_EVENT_RX_MSG)
- return;
-
- rdata = event->src.rdata;
- msg = rdata->msg;
-
- /* Only want to process request messages. */
- if (msg->type != PJSIP_REQUEST_MSG)
- return;
-
- /* Only want the first notification. */
- if (event->obj.tsx && event->obj.tsx->status_code >= 100)
- return;
-
- if (pjsip_method_cmp(&msg->line.req.method, &SUBSCRIBE)==0) {
- /* Process incoming SUBSCRIBE request. */
- on_subscribe_request( event->obj.tsx, rdata );
- } else if (pjsip_method_cmp(&msg->line.req.method, &NOTIFY)==0) {
- /* Process incoming NOTIFY request. */
- on_notify_request( event->obj.tsx, rdata );
- }
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip_simple/event_notify.h> +#include <pjsip/sip_msg.h> +#include <pjsip/sip_util.h> +#include <pjsip/sip_endpoint.h> +#include <pjsip/sip_module.h> +#include <pjsip/sip_transaction.h> +#include <pjsip/sip_event.h> +#include <pj/pool.h> +#include <pj/timer.h> +#include <pj/string.h> +#include <pj/hash.h> +#include <pj/os.h> +#include <pj/except.h> +#include <pj/log.h> +#include <pj/guid.h> + +#define THIS_FILE "event_sub" + +/* String names for state. + * The names here should be compliant with sub_state names in RFC3265. + */ +static const pj_str_t state[] = { + { "null", 4 }, + { "active", 6 }, + { "pending", 7 }, + { "terminated", 10 }, + { "unknown", 7 } +}; + +/* Timer IDs */ +#define TIMER_ID_REFRESH 1 +#define TIMER_ID_UAS_EXPIRY 2 + +/* Static configuration. */ +#define SECONDS_BEFORE_EXPIRY 10 +#define MGR_POOL_SIZE 512 +#define MGR_POOL_INC 0 +#define SUB_POOL_SIZE 2048 +#define SUB_POOL_INC 0 +#define HASH_TABLE_SIZE 32 + +/* Static vars. */ +static int mod_id; +static const pjsip_method SUBSCRIBE = { PJSIP_OTHER_METHOD, {"SUBSCRIBE", 9}}; +static const pjsip_method NOTIFY = { PJSIP_OTHER_METHOD, { "NOTIFY", 6}}; + +typedef struct package +{ + PJ_DECL_LIST_MEMBER(struct package) + pj_str_t event; + int accept_cnt; + pj_str_t *accept; + pjsip_event_sub_pkg_cb cb; +} package; + +/* Event subscription manager singleton instance. */ +static struct pjsip_event_sub_mgr +{ + pj_pool_t *pool; + pj_hash_table_t *ht; + pjsip_endpoint *endpt; + pj_mutex_t *mutex; + pjsip_allow_events_hdr *allow_events; + package pkg_list; +} mgr; + +/* Fordward declarations for static functions. */ +static pj_status_t mod_init(pjsip_endpoint *, pjsip_module *, pj_uint32_t); +static pj_status_t mod_deinit(pjsip_module*); +static void tsx_handler(pjsip_module*, pjsip_event*); +static pjsip_event_sub *find_sub(pjsip_rx_data *); +static void on_subscribe_request(pjsip_transaction*, pjsip_rx_data*); +static void on_subscribe_response(void *, pjsip_event*); +static void on_notify_request(pjsip_transaction *, pjsip_rx_data*); +static void on_notify_response(void *, pjsip_event *); +static void refresh_timer_cb(pj_timer_heap_t*, pj_timer_entry*); +static void uas_expire_timer_cb(pj_timer_heap_t*, pj_timer_entry*); +static pj_status_t send_sub_refresh( pjsip_event_sub *sub ); + +/* Module descriptor. */ +static pjsip_module event_sub_module = +{ + {"EventSub", 8}, /* Name. */ + 0, /* Flag */ + 128, /* Priority */ + &mgr, /* User data. */ + 2, /* Number of methods supported . */ + { &SUBSCRIBE, &NOTIFY }, /* Array of methods */ + &mod_init, /* init_module() */ + NULL, /* start_module() */ + &mod_deinit, /* deinit_module() */ + &tsx_handler, /* tsx_handler() */ +}; + +/* + * Module initialization. + * This will be called by endpoint when it initializes all modules. + */ +static pj_status_t mod_init( pjsip_endpoint *endpt, + struct pjsip_module *mod, pj_uint32_t id ) +{ + pj_pool_t *pool; + + pool = pjsip_endpt_create_pool(endpt, "esubmgr", MGR_POOL_SIZE, MGR_POOL_INC); + if (!pool) + return -1; + + /* Manager initialization: create hash table and mutex. */ + mgr.pool = pool; + mgr.endpt = endpt; + mgr.ht = pj_hash_create(pool, HASH_TABLE_SIZE); + if (!mgr.ht) + return -1; + + mgr.mutex = pj_mutex_create(pool, "esubmgr", PJ_MUTEX_SIMPLE); + if (!mgr.mutex) + return -1; + + /* Attach manager to module. */ + mod->mod_data = &mgr; + + /* Init package list. */ + pj_list_init(&mgr.pkg_list); + + /* Init Allow-Events header. */ + mgr.allow_events = pjsip_allow_events_hdr_create(mgr.pool); + + /* Save the module ID. */ + mod_id = id; + + pjsip_event_notify_init_parser(); + return 0; +} + +/* + * Module deinitialization. + * Called by endpoint. + */ +static pj_status_t mod_deinit( struct pjsip_module *mod ) +{ + pj_mutex_lock(mgr.mutex); + pj_mutex_destroy(mgr.mutex); + pjsip_endpt_destroy_pool(mgr.endpt, mgr.pool); + return 0; +} + +/* + * This public function is called by application to register callback. + * In exchange, the instance of the module is returned. + */ +PJ_DEF(pjsip_module*) pjsip_event_sub_get_module(void) +{ + return &event_sub_module; +} + +/* + * Register event package. + */ +PJ_DEF(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event, + int accept_cnt, + const pj_str_t accept[], + const pjsip_event_sub_pkg_cb *cb ) +{ + package *pkg; + int i; + + pj_mutex_lock(mgr.mutex); + + /* Create and register new package. */ + pkg = pj_pool_alloc(mgr.pool, sizeof(*pkg)); + pj_strdup(mgr.pool, &pkg->event, event); + pj_list_insert_before(&mgr.pkg_list, pkg); + + /* Save Accept specification. */ + pkg->accept_cnt = accept_cnt; + pkg->accept = pj_pool_alloc(mgr.pool, accept_cnt*sizeof(pj_str_t)); + for (i=0; i<accept_cnt; ++i) { + pj_strdup(mgr.pool, &pkg->accept[i], &accept[i]); + } + + /* Copy callback. */ + pj_memcpy(&pkg->cb, cb, sizeof(*cb)); + + /* Update Allow-Events header. */ + pj_assert(mgr.allow_events->event_cnt < PJSIP_MAX_ALLOW_EVENTS); + mgr.allow_events->events[mgr.allow_events->event_cnt++] = pkg->event; + + pj_mutex_unlock(mgr.mutex); + return 0; +} + +/* + * Create subscription key (for hash table). + */ +static void create_subscriber_key( pj_str_t *key, pj_pool_t *pool, + pjsip_role_e role, + const pj_str_t *call_id, const pj_str_t *from_tag) +{ + char *p; + + p = key->ptr = pj_pool_alloc(pool, call_id->slen + from_tag->slen + 3); + *p++ = (role == PJSIP_ROLE_UAS ? 'S' : 'C'); + *p++ = '$'; + pj_memcpy(p, call_id->ptr, call_id->slen); + p += call_id->slen; + *p++ = '$'; + pj_memcpy(p, from_tag->ptr, from_tag->slen); + p += from_tag->slen; + + key->slen = p - key->ptr; +} + + +/* + * Create UAC subscription. + */ +PJ_DEF(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt, + const pj_str_t *from, + const pj_str_t *to, + const pj_str_t *event, + int expires, + int accept_cnt, + const pj_str_t accept[], + void *user_data, + const pjsip_event_sub_cb *cb) +{ + pjsip_tx_data *tdata; + pj_pool_t *pool; + const pjsip_hdr *hdr; + pjsip_event_sub *sub; + PJ_USE_EXCEPTION; + + PJ_LOG(5,(THIS_FILE, "Creating event subscription %.*s to %.*s", + event->slen, event->ptr, to->slen, to->ptr)); + + /* Create pool for the event subscription. */ + pool = pjsip_endpt_create_pool(endpt, "esub", SUB_POOL_SIZE, SUB_POOL_INC); + if (!pool) { + return NULL; + } + + /* Init subscription. */ + sub = pj_pool_calloc(pool, 1, sizeof(*sub)); + sub->pool = pool; + sub->endpt = endpt; + sub->role = PJSIP_ROLE_UAC; + sub->state = PJSIP_EVENT_SUB_STATE_PENDING; + sub->state_str = state[sub->state]; + sub->user_data = user_data; + sub->timer.id = 0; + sub->default_interval = expires; + pj_memcpy(&sub->cb, cb, sizeof(*cb)); + pj_list_init(&sub->auth_sess); + pj_list_init(&sub->route_set); + sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE); + if (!sub->mutex) { + pjsip_endpt_destroy_pool(endpt, pool); + return NULL; + } + + /* The easiest way to parse the parameters is to create a dummy request! */ + tdata = pjsip_endpt_create_request( endpt, &SUBSCRIBE, to, from, to, from, + NULL, -1, NULL); + if (!tdata) { + pj_mutex_destroy(sub->mutex); + pjsip_endpt_destroy_pool(endpt, pool); + return NULL; + } + + /* + * Duplicate headers in the request to our structure. + */ + PJ_TRY { + int i; + + /* From */ + hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL); + pj_assert(hdr != NULL); + sub->from = pjsip_hdr_clone(pool, hdr); + + /* To */ + hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL); + pj_assert(hdr != NULL); + sub->to = pjsip_hdr_clone(pool, hdr); + + /* Contact. */ + sub->contact = pjsip_contact_hdr_create(pool); + sub->contact->uri = sub->from->uri; + + /* Call-ID */ + hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CALL_ID, NULL); + pj_assert(hdr != NULL); + sub->call_id = pjsip_hdr_clone(pool, hdr); + + /* CSeq */ + sub->cseq = pj_rand() % 0xFFFF; + + /* Event. */ + sub->event = pjsip_event_hdr_create(sub->pool); + pj_strdup(pool, &sub->event->event_type, event); + + /* Expires. */ + sub->uac_expires = pjsip_expires_hdr_create(pool); + sub->uac_expires->ivalue = expires; + + /* Accept. */ + sub->local_accept = pjsip_accept_hdr_create(pool); + for (i=0; i<accept_cnt && i < PJSIP_MAX_ACCEPT_COUNT; ++i) { + sub->local_accept->count++; + pj_strdup(sub->pool, &sub->local_accept->values[i], &accept[i]); + } + + /* Register to hash table. */ + create_subscriber_key( &sub->key, pool, PJSIP_ROLE_UAC, + &sub->call_id->id, &sub->from->tag); + pj_mutex_lock( mgr.mutex ); + pj_hash_set( pool, mgr.ht, sub->key.ptr, sub->key.slen, sub); + pj_mutex_unlock( mgr.mutex ); + + } + PJ_DEFAULT { + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): caught exception %d during init", + sub, state[sub->state].ptr, PJ_GET_EXCEPTION())); + + pjsip_tx_data_dec_ref(tdata); + pj_mutex_destroy(sub->mutex); + pjsip_endpt_destroy_pool(endpt, sub->pool); + return NULL; + } + PJ_END; + + /* All set, delete temporary transmit data as we don't need it. */ + pjsip_tx_data_dec_ref(tdata); + + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): client created, target=%.*s, event=%.*s", + sub, state[sub->state].ptr, + to->slen, to->ptr, event->slen, event->ptr)); + + return sub; +} + +/* + * Set credentials. + */ +PJ_DEF(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub, + int count, + const pjsip_cred_info cred[]) +{ + pj_mutex_lock(sub->mutex); + if (count > 0) { + sub->cred_info = pj_pool_alloc(sub->pool, count*sizeof(pjsip_cred_info)); + pj_memcpy( sub->cred_info, cred, count*sizeof(pjsip_cred_info)); + } + sub->cred_cnt = count; + pj_mutex_unlock(sub->mutex); + return 0; +} + +/* + * Set route-set. + */ +PJ_DEF(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub, + const pjsip_route_hdr *route_set ) +{ + const pjsip_route_hdr *hdr; + + pj_mutex_lock(sub->mutex); + + /* Clear existing route set. */ + pj_list_init(&sub->route_set); + + /* Duplicate route headers. */ + hdr = route_set->next; + while (hdr != route_set) { + pjsip_route_hdr *new_hdr = pjsip_hdr_clone(sub->pool, hdr); + pj_list_insert_before(&sub->route_set, new_hdr); + hdr = hdr->next; + } + + pj_mutex_unlock(sub->mutex); + + return 0; +} + +/* + * Send subscribe request. + */ +PJ_DEF(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub ) +{ + pj_status_t status; + + pj_mutex_lock(sub->mutex); + status = send_sub_refresh(sub); + pj_mutex_unlock(sub->mutex); + + return status; +} + +/* + * Destroy subscription. + * If there are pending transactions, then this will just set the flag. + */ +PJ_DEF(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub) +{ + pj_assert(sub != NULL); + if (sub == NULL) + return -1; + + /* Application must terminate the subscription first. */ + pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_NULL || + sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED); + + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): about to be destroyed", + sub, state[sub->state].ptr)); + + pj_mutex_lock(mgr.mutex); + pj_mutex_lock(sub->mutex); + + /* Set delete flag. */ + sub->delete_flag = 1; + + /* Unregister timer, if any. */ + if (sub->timer.id != 0) { + pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); + sub->timer.id = 0; + } + + if (sub->pending_tsx > 0) { + pj_mutex_unlock(sub->mutex); + pj_mutex_unlock(mgr.mutex); + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): has %d pending, will destroy later", + sub, state[sub->state].ptr, + sub->pending_tsx)); + return 1; + } + + /* Unregister from hash table. */ + pj_hash_set(sub->pool, mgr.ht, sub->key.ptr, sub->key.slen, NULL); + + /* Destroy. */ + pj_mutex_destroy(sub->mutex); + pjsip_endpt_destroy_pool(sub->endpt, sub->pool); + + pj_mutex_unlock(mgr.mutex); + + PJ_LOG(4,(THIS_FILE, "event_sub%p: destroyed", sub)); + return 0; +} + +/* Change state. */ +static void sub_set_state( pjsip_event_sub *sub, int new_state) +{ + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): changed state to %s", + sub, state[sub->state].ptr, state[new_state].ptr)); + sub->state = new_state; + sub->state_str = state[new_state]; +} + +/* + * Refresh subscription. + */ +static pj_status_t send_sub_refresh( pjsip_event_sub *sub ) +{ + pjsip_tx_data *tdata; + pj_status_t status; + const pjsip_route_hdr *route; + + pj_assert(sub->role == PJSIP_ROLE_UAC); + pj_assert(sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED); + if (sub->role != PJSIP_ROLE_UAC || + sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) + { + return -1; + } + + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refreshing subscription", + sub, state[sub->state].ptr)); + + /* Create request. */ + tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, + &SUBSCRIBE, + sub->to->uri, + sub->from, sub->to, + sub->contact, sub->call_id, + sub->cseq++, + NULL); + + if (!tdata) { + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh: unable to create tx data!", + sub, state[sub->state].ptr)); + return -1; + } + + pjsip_msg_add_hdr( tdata->msg, + pjsip_hdr_shallow_clone(tdata->pool, sub->event)); + pjsip_msg_add_hdr( tdata->msg, + pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires)); + pjsip_msg_add_hdr( tdata->msg, + pjsip_hdr_shallow_clone(tdata->pool, sub->local_accept)); + pjsip_msg_add_hdr( tdata->msg, + pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events)); + + /* Authentication */ + pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess, + sub->cred_cnt, sub->cred_info); + + /* Route set. */ + route = sub->route_set.next; + while (route != &sub->route_set) { + pj_list_insert_before( &tdata->msg->hdr, + pjsip_hdr_shallow_clone(tdata->pool, route)); + route = route->next; + } + + /* Send */ + status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, + &on_subscribe_response); + if (status == 0) { + sub->pending_tsx++; + } else { + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to refresh subscription!", + sub, state[sub->state].ptr)); + } + + return status; +} + +/* + * Stop subscription. + */ +PJ_DEF(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub ) +{ + pjsip_tx_data *tdata; + const pjsip_route_hdr *route; + pj_status_t status; + + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): unsubscribing...", + sub, state[sub->state].ptr)); + + /* Lock subscription. */ + pj_mutex_lock(sub->mutex); + + pj_assert(sub->role == PJSIP_ROLE_UAC); + + /* Kill refresh timer, if any. */ + if (sub->timer.id != 0) { + sub->timer.id = 0; + pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); + } + + /* Create request. */ + tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, + &SUBSCRIBE, + sub->to->uri, + sub->from, sub->to, + sub->contact, sub->call_id, + sub->cseq++, + NULL); + + if (!tdata) { + pj_mutex_unlock(sub->mutex); + return -1; + } + + /* Add headers to request. */ + pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event)); + sub->uac_expires->ivalue = 0; + pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires)); + + /* Add authentication. */ + pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess, + sub->cred_cnt, sub->cred_info); + + + /* Route set. */ + route = sub->route_set.next; + while (route != &sub->route_set) { + pj_list_insert_before( &tdata->msg->hdr, + pjsip_hdr_shallow_clone(tdata->pool, route)); + route = route->next; + } + + /* Prevent timer from refreshing itself. */ + sub->default_interval = 0; + + /* Set state. */ + sub_set_state( sub, PJSIP_EVENT_SUB_STATE_TERMINATED ); + + /* Send the request. */ + status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, + &on_subscribe_response); + if (status == 0) { + sub->pending_tsx++; + } + + pj_mutex_unlock(sub->mutex); + + if (status != 0) { + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to unsubscribe!", + sub, state[sub->state].ptr)); + } + + return status; +} + +/* + * Send notify. + */ +PJ_DEF(pj_status_t) pjsip_event_sub_notify(pjsip_event_sub *sub, + pjsip_event_sub_state new_state, + const pj_str_t *reason, + pjsip_msg_body *body) +{ + pjsip_tx_data *tdata; + pjsip_sub_state_hdr *ss_hdr; + const pjsip_route_hdr *route; + pj_time_val now; + pj_status_t status; + pjsip_event_sub_state old_state = sub->state; + + pj_gettimeofday(&now); + + pj_assert(sub->role == PJSIP_ROLE_UAS); + if (sub->role != PJSIP_ROLE_UAS) + return -1; + + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sending NOTIFY", + sub, state[new_state].ptr)); + + /* Lock subscription. */ + pj_mutex_lock(sub->mutex); + + /* Can not send NOTIFY if current state is NULL. We can accept TERMINATED. */ + if (sub->state==PJSIP_EVENT_SUB_STATE_NULL) { + pj_assert(0); + pj_mutex_unlock(sub->mutex); + return -1; + } + + /* Update state no matter what. */ + sub_set_state(sub, new_state); + + /* Create transmit data. */ + tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, + &NOTIFY, + sub->to->uri, + sub->from, sub->to, + sub->contact, sub->call_id, + sub->cseq++, + NULL); + if (!tdata) { + pj_mutex_unlock(sub->mutex); + return -1; + } + + /* Add Event header. */ + pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event)); + + /* Add Subscription-State header. */ + ss_hdr = pjsip_sub_state_hdr_create(tdata->pool); + ss_hdr->sub_state = state[new_state]; + ss_hdr->expires_param = sub->expiry_time.sec - now.sec; + if (ss_hdr->expires_param < 0) + ss_hdr->expires_param = 0; + if (reason) + pj_strdup(tdata->pool, &ss_hdr->reason_param, reason); + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)ss_hdr); + + /* Add Allow-Events header. */ + pjsip_msg_add_hdr( tdata->msg, + pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events)); + + /* Add authentication */ + pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess, + sub->cred_cnt, sub->cred_info); + + /* Route set. */ + route = sub->route_set.next; + while (route != &sub->route_set) { + pj_list_insert_before( &tdata->msg->hdr, + pjsip_hdr_shallow_clone(tdata->pool, route)); + route = route->next; + } + + /* Attach body. */ + tdata->msg->body = body; + + /* That's it, send! */ + status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, &on_notify_response); + if (status == 0) + sub->pending_tsx++; + + /* If terminated notify application. */ + if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) { + if (sub->cb.on_sub_terminated) { + sub->pending_tsx++; + (*sub->cb.on_sub_terminated)(sub, reason); + sub->pending_tsx--; + } + } + + /* Unlock subscription. */ + pj_mutex_unlock(sub->mutex); + + if (status != 0) { + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): failed to send NOTIFY", + sub, state[sub->state].ptr)); + } + + if (sub->delete_flag && sub->pending_tsx <= 0) { + pjsip_event_sub_destroy(sub); + } + return status; +} + + +/* If this timer callback is called, it means subscriber hasn't refreshed its + * subscription on-time. Set the state to terminated. This will also send + * NOTIFY with Subscription-State set to terminated. + */ +static void uas_expire_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry) +{ + pjsip_event_sub *sub = entry->user_data; + pj_str_t reason = { "timeout", 7 }; + + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): UAS subscription expired!", + sub, state[sub->state].ptr)); + + pj_mutex_lock(sub->mutex); + sub->timer.id = 0; + + if (sub->cb.on_sub_terminated && sub->state!=PJSIP_EVENT_SUB_STATE_TERMINATED) { + /* Notify application, but prevent app from destroying the sub. */ + ++sub->pending_tsx; + (*sub->cb.on_sub_terminated)(sub, &reason); + --sub->pending_tsx; + } + //pjsip_event_sub_notify( sub, PJSIP_EVENT_SUB_STATE_TERMINATED, + // &reason, NULL); + pj_mutex_unlock(sub->mutex); + +} + +/* Schedule notifier expiration. */ +static void sub_schedule_uas_expire( pjsip_event_sub *sub, int sec_delay) +{ + pj_time_val delay = { 0, 0 }; + pj_parsed_time pt; + + if (sub->timer.id != 0) + pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); + + pj_gettimeofday(&sub->expiry_time); + sub->expiry_time.sec += sec_delay; + + sub->timer.id = TIMER_ID_UAS_EXPIRY; + sub->timer.user_data = sub; + sub->timer.cb = &uas_expire_timer_cb; + delay.sec = sec_delay; + pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay); + + pj_time_decode(&sub->expiry_time, &pt); + PJ_LOG(4,(THIS_FILE, + "event_sub%p (%s)(UAS): will expire at %02d:%02d:%02d (in %d secs)", + sub, state[sub->state].ptr, pt.hour, pt.min, pt.sec, sec_delay)); +} + +/* This timer is called for UAC to refresh the subscription. */ +static void refresh_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry) +{ + pjsip_event_sub *sub = entry->user_data; + + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh subscription timer", + sub, state[sub->state].ptr)); + + pj_mutex_lock(sub->mutex); + sub->timer.id = 0; + send_sub_refresh(sub); + pj_mutex_unlock(sub->mutex); +} + + +/* This will update the UAC's refresh schedule. */ +static void update_next_refresh(pjsip_event_sub *sub, int interval) +{ + pj_time_val delay = {0, 0}; + pj_parsed_time pt; + + if (interval < SECONDS_BEFORE_EXPIRY) { + PJ_LOG(4,(THIS_FILE, + "event_sub%p (%s): expiration delay too short (%d sec)! updated.", + sub, state[sub->state].ptr, interval)); + interval = SECONDS_BEFORE_EXPIRY; + } + + if (sub->timer.id != 0) + pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); + + sub->timer.id = TIMER_ID_REFRESH; + sub->timer.user_data = sub; + sub->timer.cb = &refresh_timer_cb; + pj_gettimeofday(&sub->expiry_time); + delay.sec = interval - SECONDS_BEFORE_EXPIRY; + sub->expiry_time.sec += delay.sec; + + pj_time_decode(&sub->expiry_time, &pt); + PJ_LOG(4,(THIS_FILE, + "event_sub%p (%s): will send SUBSCRIBE at %02d:%02d:%02d (in %d secs)", + sub, state[sub->state].ptr, + pt.hour, pt.min, pt.sec, + delay.sec)); + + pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay ); +} + + +/* Find subscription in the hash table. + * If found, lock the subscription before returning to caller. + */ +static pjsip_event_sub *find_sub(pjsip_rx_data *rdata) +{ + pj_str_t key; + pjsip_role_e role; + pjsip_event_sub *sub; + pjsip_method *method = &rdata->msg->line.req.method; + pj_str_t *tag; + + if (rdata->msg->type == PJSIP_REQUEST_MSG) { + if (pjsip_method_cmp(method, &SUBSCRIBE)==0) { + role = PJSIP_ROLE_UAS; + tag = &rdata->to_tag; + } else { + pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0); + role = PJSIP_ROLE_UAC; + tag = &rdata->to_tag; + } + } else { + if (pjsip_method_cmp(&rdata->cseq->method, &SUBSCRIBE)==0) { + role = PJSIP_ROLE_UAC; + tag = &rdata->from_tag; + } else { + pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0); + role = PJSIP_ROLE_UAS; + tag = &rdata->from_tag; + } + } + create_subscriber_key( &key, rdata->pool, role, &rdata->call_id, tag); + + pj_mutex_lock(mgr.mutex); + sub = pj_hash_get(mgr.ht, key.ptr, key.slen); + if (sub) + pj_mutex_lock(sub->mutex); + pj_mutex_unlock(mgr.mutex); + + return sub; +} + + +/* This function is called when we receive SUBSCRIBE request message + * to refresh existing subscription. + */ +static void on_received_sub_refresh( pjsip_event_sub *sub, + pjsip_transaction *tsx, pjsip_rx_data *rdata) +{ + pjsip_event_hdr *e; + pjsip_expires_hdr *expires; + pj_str_t hname; + int status = 200; + pj_str_t reason_phrase = { NULL, 0 }; + int new_state = sub->state; + int old_state = sub->state; + int new_interval = 0; + pjsip_tx_data *tdata; + + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received target refresh", + sub, state[sub->state].ptr)); + + /* Check that the event matches. */ + hname = pj_str("Event"); + e = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL); + if (!e) { + status = 400; + reason_phrase = pj_str("Missing Event header"); + goto send_response; + } + if (pj_stricmp(&e->event_type, &sub->event->event_type) != 0 || + pj_stricmp(&e->id_param, &sub->event->id_param) != 0) + { + status = 481; + reason_phrase = pj_str("Subscription does not exist"); + goto send_response; + } + + /* Check server state. */ + if (sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) { + status = 481; + reason_phrase = pj_str("Subscription does not exist"); + goto send_response; + } + + /* Check expires header. */ + expires = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_EXPIRES, NULL); + if (!expires) { + /* + status = 400; + reason_phrase = pj_str("Missing Expires header"); + goto send_response; + */ + new_interval = sub->default_interval; + } else { + /* Check that interval is not too short. + * Note that expires time may be zero (for unsubscription). + */ + new_interval = expires->ivalue; + if (new_interval != 0 && new_interval < SECONDS_BEFORE_EXPIRY) { + status = PJSIP_SC_INTERVAL_TOO_BRIEF; + goto send_response; + } + } + + /* Update interval. */ + sub->default_interval = new_interval; + pj_gettimeofday(&sub->expiry_time); + sub->expiry_time.sec += new_interval; + + /* Update timer only if this is not unsubscription. */ + if (new_interval > 0) { + sub->default_interval = new_interval; + sub_schedule_uas_expire( sub, new_interval ); + + /* Call callback. */ + if (sub->cb.on_received_refresh) { + sub->pending_tsx++; + (*sub->cb.on_received_refresh)(sub, rdata); + sub->pending_tsx--; + } + } + +send_response: + tdata = pjsip_endpt_create_response( sub->endpt, rdata, status); + if (tdata) { + if (reason_phrase.slen) + tdata->msg->line.status.reason = reason_phrase; + + /* Add Expires header. */ + expires = pjsip_expires_hdr_create(tdata->pool); + expires->ivalue = sub->default_interval; + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires); + + if (PJSIP_IS_STATUS_IN_CLASS(status,200)) { + pjsip_msg_add_hdr(tdata->msg, + pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events)); + } + /* Send down to transaction. */ + pjsip_tsx_on_tx_msg(tsx, tdata); + } + + if (sub->default_interval==0 || !PJSIP_IS_STATUS_IN_CLASS(status,200)) { + /* Notify application if sub is terminated. */ + new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; + sub_set_state(sub, new_state); + if (new_state!=old_state && sub->cb.on_sub_terminated) { + pj_str_t reason = {"", 0}; + if (reason_phrase.slen) reason = reason_phrase; + else reason = *pjsip_get_status_text(status); + + sub->pending_tsx++; + (*sub->cb.on_sub_terminated)(sub, &reason); + sub->pending_tsx--; + } + } + + pj_mutex_unlock(sub->mutex); + + /* Prefer to call log when we're not holding the mutex. */ + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sent refresh response %s, status=%d", + sub, state[sub->state].ptr, + (tdata ? tdata->obj_name : "null"), status)); + + /* Check if application has requested deletion. */ + if (sub->delete_flag && sub->pending_tsx <= 0) { + pjsip_event_sub_destroy(sub); + } + +} + + +/* This function is called when we receive SUBSCRIBE request message for + * a new subscription. + */ +static void on_new_subscription( pjsip_transaction *tsx, pjsip_rx_data *rdata ) +{ + package *pkg; + pj_pool_t *pool; + pjsip_event_sub *sub = NULL; + pj_str_t hname; + int status = 200; + pj_str_t reason = { NULL, 0 }; + pjsip_tx_data *tdata; + pjsip_expires_hdr *expires; + pjsip_accept_hdr *accept; + pjsip_event_hdr *evhdr; + + /* Get the Event header. */ + hname = pj_str("Event"); + evhdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL); + if (!evhdr) { + status = 400; + reason = pj_str("No Event header in request"); + goto send_response; + } + + /* Find corresponding package. + * We don't lock the manager's mutex since we assume the package list + * won't change once the application is running! + */ + pkg = mgr.pkg_list.next; + while (pkg != &mgr.pkg_list) { + if (pj_stricmp(&pkg->event, &evhdr->event_type) == 0) + break; + pkg = pkg->next; + } + + if (pkg == &mgr.pkg_list) { + /* Event type is not supported by any packages! */ + status = 489; + reason = pj_str("Bad Event"); + goto send_response; + } + + /* First check that the Accept specification matches the + * package's Accept types. + */ + accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL); + if (accept) { + unsigned i; + pj_str_t *content_type = NULL; + + for (i=0; i<accept->count && !content_type; ++i) { + int j; + for (j=0; j<pkg->accept_cnt; ++j) { + if (pj_stricmp(&accept->values[i], &pkg->accept[j])==0) { + content_type = &pkg->accept[j]; + break; + } + } + } + + if (!content_type) { + status = PJSIP_SC_NOT_ACCEPTABLE_HERE; + goto send_response; + } + } + + /* Check whether the package wants to accept the subscription. */ + pj_assert(pkg->cb.on_query_subscribe != NULL); + (*pkg->cb.on_query_subscribe)(rdata, &status); + if (!PJSIP_IS_STATUS_IN_CLASS(status,200)) + goto send_response; + + /* Create new subscription record. */ + pool = pjsip_endpt_create_pool(tsx->endpt, "esub", + SUB_POOL_SIZE, SUB_POOL_INC); + if (!pool) { + status = 500; + goto send_response; + } + sub = pj_pool_calloc(pool, 1, sizeof(*sub)); + sub->pool = pool; + sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE); + if (!sub->mutex) { + status = 500; + goto send_response; + } + + PJ_LOG(4,(THIS_FILE, "event_sub%p: notifier is created.", sub)); + + /* Start locking mutex. */ + pj_mutex_lock(sub->mutex); + + /* Init UAS subscription */ + sub->endpt = tsx->endpt; + sub->role = PJSIP_ROLE_UAS; + sub->state = PJSIP_EVENT_SUB_STATE_PENDING; + sub->state_str = state[sub->state]; + pj_list_init(&sub->auth_sess); + pj_list_init(&sub->route_set); + sub->from = pjsip_hdr_clone(pool, rdata->to); + pjsip_fromto_set_from(sub->from); + if (sub->from->tag.slen == 0) { + pj_create_unique_string(pool, &sub->from->tag); + rdata->to->tag = sub->from->tag; + } + sub->to = pjsip_hdr_clone(pool, rdata->from); + pjsip_fromto_set_to(sub->to); + sub->contact = pjsip_contact_hdr_create(pool); + sub->contact->uri = sub->from->uri; + sub->call_id = pjsip_cid_hdr_create(pool); + pj_strdup(pool, &sub->call_id->id, &rdata->call_id); + sub->cseq = pj_rand() % 0xFFFF; + + expires = pjsip_msg_find_hdr( rdata->msg, PJSIP_H_EXPIRES, NULL); + if (expires) { + sub->default_interval = expires->ivalue; + if (sub->default_interval > 0 && + sub->default_interval < SECONDS_BEFORE_EXPIRY) + { + status = 423; /* Interval too short. */ + goto send_response; + } + } else { + sub->default_interval = 600; + } + + /* Clone Event header. */ + sub->event = pjsip_hdr_clone(pool, evhdr); + + /* Register to hash table. */ + create_subscriber_key(&sub->key, pool, PJSIP_ROLE_UAS, &sub->call_id->id, + &sub->from->tag); + pj_mutex_lock(mgr.mutex); + pj_hash_set(pool, mgr.ht, sub->key.ptr, sub->key.slen, sub); + pj_mutex_unlock(mgr.mutex); + + /* Set timer where subscription will expire only when expires<>0. + * Subscriber may send new subscription with expires==0. + */ + if (sub->default_interval != 0) { + sub_schedule_uas_expire( sub, sub->default_interval-SECONDS_BEFORE_EXPIRY); + } + + /* Notify application. */ + if (pkg->cb.on_subscribe) { + pjsip_event_sub_cb *cb = NULL; + sub->pending_tsx++; + (*pkg->cb.on_subscribe)(sub, rdata, &cb, &sub->default_interval); + sub->pending_tsx--; + if (cb == NULL) + pj_memset(&sub->cb, 0, sizeof(*cb)); + else + pj_memcpy(&sub->cb, cb, sizeof(*cb)); + } + + +send_response: + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s)(UAS): status=%d", + sub, state[sub->state].ptr, status)); + + tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status); + if (tdata) { + if (reason.slen) { + /* Customize reason text. */ + tdata->msg->line.status.reason = reason; + } + if (PJSIP_IS_STATUS_IN_CLASS(status,200)) { + /* Add Expires header. */ + pjsip_expires_hdr *hdr; + + hdr = pjsip_expires_hdr_create(tdata->pool); + hdr->ivalue = sub->default_interval; + pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr ); + } + if (status == 423) { + /* Add Min-Expires header. */ + pjsip_min_expires_hdr *hdr; + + hdr = pjsip_min_expires_hdr_create(tdata->pool); + hdr->ivalue = SECONDS_BEFORE_EXPIRY; + pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr); + } + if (status == 489 || + status==PJSIP_SC_NOT_ACCEPTABLE_HERE || + PJSIP_IS_STATUS_IN_CLASS(status,200)) + { + /* Add Allow-Events header. */ + pjsip_hdr *hdr; + hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events); + pjsip_msg_add_hdr(tdata->msg, hdr); + + /* Should add Accept header?. */ + } + + pjsip_tsx_on_tx_msg(tsx, tdata); + } + + /* If received new subscription with expires=0, terminate. */ + if (sub && sub->default_interval == 0) { + pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED); + if (sub->cb.on_sub_terminated) { + pj_str_t reason = { "timeout", 7 }; + (*sub->cb.on_sub_terminated)(sub, &reason); + } + } + + if (!PJSIP_IS_STATUS_IN_CLASS(status,200) || (sub && sub->delete_flag)) { + if (sub && sub->mutex) { + pjsip_event_sub_destroy(sub); + } else if (sub) { + pjsip_endpt_destroy_pool(tsx->endpt, sub->pool); + } + } else { + pj_assert(status >= 200); + pj_mutex_unlock(sub->mutex); + } +} + +/* This is the main callback when SUBSCRIBE request is received. */ +static void on_subscribe_request(pjsip_transaction *tsx, pjsip_rx_data *rdata) +{ + pjsip_event_sub *sub = find_sub(rdata); + + if (sub) + on_received_sub_refresh(sub, tsx, rdata); + else + on_new_subscription(tsx, rdata); +} + + +/* This callback is called when response to SUBSCRIBE is received. */ +static void on_subscribe_response(void *token, pjsip_event *event) +{ + pjsip_event_sub *sub = token; + pjsip_transaction *tsx = event->obj.tsx; + int new_state, old_state = sub->state; + + pj_assert(tsx->status_code >= 200); + if (tsx->status_code < 200) + return; + + pj_assert(sub->role == PJSIP_ROLE_UAC); + + /* Lock mutex. */ + pj_mutex_lock(sub->mutex); + + /* If request failed with 401/407 error, silently retry the request. */ + if (tsx->status_code==401 || tsx->status_code==407) { + pjsip_tx_data *tdata; + tdata = pjsip_auth_reinit_req(sub->endpt, + sub->pool, &sub->auth_sess, + sub->cred_cnt, sub->cred_info, + tsx->last_tx, event->src.rdata ); + if (tdata) { + int status; + pjsip_cseq_hdr *cseq; + cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); + cseq->cseq = sub->cseq++; + status = pjsip_endpt_send_request( sub->endpt, tdata, + -1, sub, + &on_subscribe_response); + if (status == 0) { + pj_mutex_unlock(sub->mutex); + return; + } + } + } + + if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) { + /* Update To tag. */ + if (sub->to->tag.slen == 0) + pj_strdup(sub->pool, &sub->to->tag, &event->src.rdata->to_tag); + + new_state = sub->state; + + } else if (tsx->status_code == 481) { + new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; + + } else if (tsx->status_code >= 300) { + /* RFC 3265 Section 3.1.4.2: + * If a SUBSCRIBE request to refresh a subscription fails + * with a non-481 response, the original subscription is still + * considered valid for the duration of original exires. + * + * Note: + * Since we normally send SUBSCRIBE for refreshing the subscription, + * it means the subscription already expired anyway. So we terminate + * the subscription now. + */ + if (sub->state != PJSIP_EVENT_SUB_STATE_ACTIVE) { + new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; + } else { + /* Use this to be compliant with Section 3.1.4.2 + new_state = sub->state; + */ + new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; + } + } else { + pj_assert(0); + new_state = sub->state; + } + + if (new_state != sub->state && sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) { + sub_set_state(sub, new_state); + } + + if (sub->state == PJSIP_EVENT_SUB_STATE_ACTIVE || + sub->state == PJSIP_EVENT_SUB_STATE_PENDING) + { + /* + * Register timer for next subscription refresh, but only when + * we're not unsubscribing. Also update default_interval and Expires + * header. + */ + if (sub->default_interval > 0 && !sub->delete_flag) { + pjsip_expires_hdr *exp = NULL; + + /* Could be transaction timeout. */ + if (event->src_type == PJSIP_EVENT_RX_MSG) { + exp = pjsip_msg_find_hdr(event->src.rdata->msg, + PJSIP_H_EXPIRES, NULL); + } + + if (exp) { + int delay = exp->ivalue; + if (delay > 0) { + pj_time_val new_expiry; + pj_gettimeofday(&new_expiry); + new_expiry.sec += delay; + if (sub->timer.id==0 || + new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2) + { + //if (delay > 0 && delay < sub->default_interval) { + sub->default_interval = delay; + sub->uac_expires->ivalue = delay; + update_next_refresh(sub, delay); + } + } + } + } + } + + /* Call callback. */ + if (!sub->delete_flag) { + if (sub->cb.on_received_sub_response) { + (*sub->cb.on_received_sub_response)(sub, event); + } + } + + /* Notify application if we're terminated. */ + if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) { + if (sub->cb.on_sub_terminated) { + pj_str_t reason; + if (event->src_type == PJSIP_EVENT_RX_MSG) + reason = event->src.rdata->msg->line.status.reason; + else + reason = *pjsip_get_status_text(tsx->status_code); + + (*sub->cb.on_sub_terminated)(sub, &reason); + } + } + + /* Decrement pending tsx count. */ + --sub->pending_tsx; + pj_assert(sub->pending_tsx >= 0); + + if (sub->delete_flag && sub->pending_tsx <= 0) { + pjsip_event_sub_destroy(sub); + } else { + pj_mutex_unlock(sub->mutex); + } + + /* DO NOT ACCESS sub FROM NOW ON! IT MIGHT HAVE BEEN DELETED */ +} + +/* + * This callback called when we receive incoming NOTIFY request. + */ +static void on_notify_request(pjsip_transaction *tsx, pjsip_rx_data *rdata) +{ + pjsip_event_sub *sub; + pjsip_tx_data *tdata; + int status = 200; + int old_state; + pj_str_t reason = { NULL, 0 }; + pj_str_t reason_phrase = { NULL, 0 }; + int new_state = PJSIP_EVENT_SUB_STATE_NULL; + + /* Find subscription based on Call-ID and From tag. + * This will also automatically lock the subscription, if it's found. + */ + sub = find_sub(rdata); + if (!sub) { + /* RFC 3265: Section 3.2 Description of NOTIFY Behavior: + * Answer with 481 Subscription does not exist. + */ + PJ_LOG(4,(THIS_FILE, "Unable to find subscription for incoming NOTIFY!")); + status = 481; + reason_phrase = pj_str("Subscription does not exist"); + + } else { + pj_assert(sub->role == PJSIP_ROLE_UAC); + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received NOTIFY", + sub, state[sub->state].ptr)); + + } + + new_state = old_state = sub->state; + + /* RFC 3265: Section 3.2.1 + * Check that the Event header match the subscription. + */ + if (status == 200) { + pjsip_event_hdr *hdr; + pj_str_t hname = { "Event", 5 }; + + hdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL); + if (!hdr) { + status = PJSIP_SC_BAD_REQUEST; + reason_phrase = pj_str("No Event header found"); + } else if (pj_stricmp(&hdr->event_type, &sub->event->event_type) != 0 || + pj_stricmp(&hdr->id_param, &sub->event->id_param) != 0) + { + status = 481; + reason_phrase = pj_str("Subscription does not exist"); + } + } + + /* Update subscription state and timer. */ + if (status == 200) { + pjsip_sub_state_hdr *hdr; + const pj_str_t hname = { "Subscription-State", 18 }; + const pj_str_t state_active = { "active", 6 }, + state_pending = { "pending", 7}, + state_terminated = { "terminated", 10 }; + + hdr = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL); + if (!hdr) { + status = PJSIP_SC_BAD_REQUEST; + reason_phrase = pj_str("No Subscription-State header found"); + goto process; + } + + /* + * Update subscription state. + */ + if (pj_stricmp(&hdr->sub_state, &state_active) == 0) { + if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) + new_state = PJSIP_EVENT_SUB_STATE_ACTIVE; + } else if (pj_stricmp(&hdr->sub_state, &state_pending) == 0) { + if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) + new_state = PJSIP_EVENT_SUB_STATE_PENDING; + } else if (pj_stricmp(&hdr->sub_state, &state_terminated) == 0) { + new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; + } else { + new_state = PJSIP_EVENT_SUB_STATE_UNKNOWN; + } + + reason = hdr->reason_param; + + if (new_state != sub->state && new_state != PJSIP_EVENT_SUB_STATE_NULL && + sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) + { + sub_set_state(sub, new_state); + if (new_state == PJSIP_EVENT_SUB_STATE_UNKNOWN) { + pj_strdup_with_null(sub->pool, &sub->state_str, &hdr->sub_state); + } else { + sub->state_str = state[new_state]; + } + } + + /* + * Update timeout timer in required, just in case notifier changed the + * expiration to shorter time. + * Section 3.2.2: the expires param can only shorten the interval. + */ + if ((sub->state==PJSIP_EVENT_SUB_STATE_ACTIVE || + sub->state==PJSIP_EVENT_SUB_STATE_PENDING) && hdr->expires_param > 0) + { + pj_time_val now, new_expiry; + + pj_gettimeofday(&now); + new_expiry.sec = now.sec + hdr->expires_param; + if (sub->timer.id==0 || + new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2) + { + update_next_refresh(sub, hdr->expires_param); + } + } + } + +process: + /* Note: here we sub MAY BE NULL! */ + + /* Send response to NOTIFY */ + tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status ); + if (tdata) { + if (reason_phrase.slen) + tdata->msg->line.status.reason = reason_phrase; + + if (PJSIP_IS_STATUS_IN_CLASS(status,200)) { + pjsip_hdr *hdr; + hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events); + pjsip_msg_add_hdr( tdata->msg, hdr); + } + + pjsip_tsx_on_tx_msg(tsx, tdata); + } + + /* Call NOTIFY callback, if any. */ + if (sub && PJSIP_IS_STATUS_IN_CLASS(status,200) && sub->cb.on_received_notify) { + sub->pending_tsx++; + (*sub->cb.on_received_notify)(sub, rdata); + sub->pending_tsx--; + } + + /* Check if subscription is terminated and call callback. */ + if (sub && new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) { + if (sub->cb.on_sub_terminated) { + sub->pending_tsx++; + (*sub->cb.on_sub_terminated)(sub, &reason); + sub->pending_tsx--; + } + } + + /* Check if application has requested deletion. */ + if (sub && sub->delete_flag && sub->pending_tsx <= 0) { + pjsip_event_sub_destroy(sub); + } else if (sub) { + pj_mutex_unlock(sub->mutex); + } +} + +/* This callback is called when we received NOTIFY response. */ +static void on_notify_response(void *token, pjsip_event *event) +{ + pjsip_event_sub *sub = token; + pjsip_event_sub_state old_state = sub->state; + pjsip_transaction *tsx = event->obj.tsx; + + /* Lock the subscription. */ + pj_mutex_lock(sub->mutex); + + pj_assert(sub->role == PJSIP_ROLE_UAS); + + /* If request failed with authorization failure, silently retry. */ + if (tsx->status_code==401 || tsx->status_code==407) { + pjsip_tx_data *tdata; + tdata = pjsip_auth_reinit_req(sub->endpt, + sub->pool, &sub->auth_sess, + sub->cred_cnt, sub->cred_info, + tsx->last_tx, event->src.rdata ); + if (tdata) { + int status; + pjsip_cseq_hdr *cseq; + cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); + cseq->cseq = sub->cseq++; + status = pjsip_endpt_send_request( sub->endpt, tdata, + -1, sub, + &on_notify_response); + if (status == 0) { + pj_mutex_unlock(sub->mutex); + return; + } + } + } + + /* Notify application. */ + if (sub->cb.on_received_notify_response) + (*sub->cb.on_received_notify_response)(sub, event); + + /* Check for response 481. */ + if (event->obj.tsx->status_code == 481) { + /* Remote says that the subscription does not exist! + * Terminate subscription! + */ + sub_set_state(sub, PJSIP_EVENT_SUB_STATE_TERMINATED); + if (sub->timer.id) { + pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); + sub->timer.id = 0; + } + + PJ_LOG(4, (THIS_FILE, + "event_sub%p (%s): got 481 response to NOTIFY. Terminating...", + sub, state[sub->state].ptr)); + + /* Notify app. */ + if (sub->state!=old_state && sub->cb.on_sub_terminated) + (*sub->cb.on_sub_terminated)(sub, &event->src.rdata->msg->line.status.reason); + } + + /* Decrement pending transaction count. */ + --sub->pending_tsx; + pj_assert(sub->pending_tsx >= 0); + + /* Check that the subscription is marked for deletion. */ + if (sub->delete_flag && sub->pending_tsx <= 0) { + pjsip_event_sub_destroy(sub); + } else { + pj_mutex_unlock(sub->mutex); + } + + /* DO NOT ACCESS sub, IT MIGHT HAVE BEEN DESTROYED! */ +} + + +/* This is the transaction handler for incoming SUBSCRIBE and NOTIFY + * requests. + */ +static void tsx_handler( struct pjsip_module *mod, pjsip_event *event ) +{ + pjsip_msg *msg; + pjsip_rx_data *rdata; + + /* Only want incoming message events. */ + if (event->src_type != PJSIP_EVENT_RX_MSG) + return; + + rdata = event->src.rdata; + msg = rdata->msg; + + /* Only want to process request messages. */ + if (msg->type != PJSIP_REQUEST_MSG) + return; + + /* Only want the first notification. */ + if (event->obj.tsx && event->obj.tsx->status_code >= 100) + return; + + if (pjsip_method_cmp(&msg->line.req.method, &SUBSCRIBE)==0) { + /* Process incoming SUBSCRIBE request. */ + on_subscribe_request( event->obj.tsx, rdata ); + } else if (pjsip_method_cmp(&msg->line.req.method, &NOTIFY)==0) { + /* Process incoming NOTIFY request. */ + on_notify_request( event->obj.tsx, rdata ); + } +} + diff --git a/pjsip/src/pjsip-simple/event_notify_msg.c b/pjsip/src/pjsip-simple/event_notify_msg.c index 55758052..a5f946ef 100644 --- a/pjsip/src/pjsip-simple/event_notify_msg.c +++ b/pjsip/src/pjsip-simple/event_notify_msg.c @@ -1,322 +1,322 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_simple/event_notify_msg.h>
-#include <pjsip/print.h>
-#include <pjsip/sip_parser.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/except.h>
-
-static int pjsip_event_hdr_print( pjsip_event_hdr *hdr,
- char *buf, pj_size_t size);
-static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool,
- const pjsip_event_hdr *hdr);
-static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_event_hdr*);
-
-static pjsip_hdr_vptr event_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_event_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_event_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_event_hdr_print,
-};
-
-
-PJ_DEF(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool)
-{
- pj_str_t event = { "Event", 5 };
- pjsip_event_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- hdr->type = PJSIP_H_OTHER;
- hdr->name = hdr->sname = event;
- hdr->vptr = &event_hdr_vptr;
- pj_list_init(hdr);
- return hdr;
-}
-
-static int pjsip_event_hdr_print( pjsip_event_hdr *hdr,
- char *buf, pj_size_t size)
-{
- char *p = buf;
- char *endbuf = buf+size;
- int printed;
-
- copy_advance(p, hdr->name);
- *p++ = ':';
- *p++ = ' ';
-
- copy_advance(p, hdr->event_type);
- copy_advance_pair(p, ";id=", 4, hdr->id_param);
- if (hdr->other_param.slen)
- copy_advance(p, hdr->other_param);
- return p - buf;
-}
-
-static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool,
- const pjsip_event_hdr *rhs)
-{
- pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool);
- pj_strdup(pool, &hdr->event_type, &rhs->event_type);
- pj_strdup(pool, &hdr->id_param, &rhs->id_param);
- pj_strdup(pool, &hdr->other_param, &rhs->other_param);
- return hdr;
-}
-
-static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_event_hdr *rhs )
-{
- pjsip_event_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-
-static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr,
- char *buf, pj_size_t size);
-static pjsip_allow_events_hdr*
-pjsip_allow_events_hdr_clone(pj_pool_t *pool,
- const pjsip_allow_events_hdr *hdr);
-static pjsip_allow_events_hdr*
-pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool,
- const pjsip_allow_events_hdr*);
-
-static pjsip_hdr_vptr allow_event_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_allow_events_hdr_print,
-};
-
-
-PJ_DEF(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool)
-{
- pj_str_t allow_events = { "Allow-Events", 12 };
- pjsip_allow_events_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- hdr->type = PJSIP_H_OTHER;
- hdr->name = hdr->sname = allow_events;
- hdr->vptr = &allow_event_hdr_vptr;
- pj_list_init(hdr);
- return hdr;
-}
-
-static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr,
- char *buf, pj_size_t size)
-{
- char *p = buf;
- char *endbuf = buf+size;
- int printed;
-
- copy_advance(p, hdr->name);
- *p++ = ':';
- *p++ = ' ';
-
- if (hdr->event_cnt > 0) {
- int i;
- copy_advance(p, hdr->events[0]);
- for (i=1; i<hdr->event_cnt; ++i) {
- copy_advance_pair(p, ",", 1, hdr->events[i]);
- }
- }
-
- return p - buf;
-}
-
-static pjsip_allow_events_hdr*
-pjsip_allow_events_hdr_clone(pj_pool_t *pool,
- const pjsip_allow_events_hdr *rhs)
-{
- int i;
-
- pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool);
- hdr->event_cnt = rhs->event_cnt;
- for (i=0; i<rhs->event_cnt; ++i) {
- pj_strdup(pool, &hdr->events[i], &rhs->events[i]);
- }
- return hdr;
-}
-
-static pjsip_allow_events_hdr*
-pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool,
- const pjsip_allow_events_hdr *rhs)
-{
- pjsip_allow_events_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-
-static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr,
- char *buf, pj_size_t size);
-static pjsip_sub_state_hdr*
-pjsip_sub_state_hdr_clone(pj_pool_t *pool,
- const pjsip_sub_state_hdr *hdr);
-static pjsip_sub_state_hdr*
-pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool,
- const pjsip_sub_state_hdr*);
-
-static pjsip_hdr_vptr sub_state_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_sub_state_hdr_print,
-};
-
-
-PJ_DEF(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool)
-{
- pj_str_t sub_state = { "Subscription-State", 18 };
- pjsip_sub_state_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- hdr->type = PJSIP_H_OTHER;
- hdr->name = hdr->sname = sub_state;
- hdr->vptr = &sub_state_hdr_vptr;
- hdr->expires_param = -1;
- hdr->retry_after = -1;
- pj_list_init(hdr);
- return hdr;
-}
-
-static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr,
- char *buf, pj_size_t size)
-{
- char *p = buf;
- char *endbuf = buf+size;
- int printed;
-
- copy_advance(p, hdr->name);
- *p++ = ':';
- *p++ = ' ';
-
- copy_advance(p, hdr->sub_state);
- copy_advance_pair(p, ";reason=", 8, hdr->reason_param);
- if (hdr->expires_param >= 0) {
- pj_memcpy(p, ";expires=", 9);
- p += 9;
- printed = pj_utoa(hdr->expires_param, p);
- p += printed;
- }
- if (hdr->retry_after >= 0) {
- pj_memcpy(p, ";retry-after=", 13);
- p += 9;
- printed = pj_utoa(hdr->retry_after, p);
- p += printed;
- }
- if (hdr->other_param.slen)
- copy_advance(p, hdr->other_param);
-
- return p - buf;
-}
-
-static pjsip_sub_state_hdr*
-pjsip_sub_state_hdr_clone(pj_pool_t *pool,
- const pjsip_sub_state_hdr *rhs)
-{
- pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool);
- pj_strdup(pool, &hdr->sub_state, &rhs->sub_state);
- pj_strdup(pool, &hdr->reason_param, &rhs->reason_param);
- hdr->retry_after = rhs->retry_after;
- hdr->expires_param = rhs->expires_param;
- pj_strdup(pool, &hdr->other_param, &rhs->other_param);
- return hdr;
-}
-
-static pjsip_sub_state_hdr*
-pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool,
- const pjsip_sub_state_hdr *rhs)
-{
- pjsip_sub_state_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-static pjsip_event_hdr *parse_hdr_event(pj_scanner *scanner,
- pj_pool_t *pool)
-{
- pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool);
- const pj_str_t id_param = { "id", 2 };
-
- pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->event_type);
-
- while (*scanner->current == ';') {
- pj_str_t pname, pvalue;
- pj_scan_get_char(scanner);
- pjsip_parse_param_imp(scanner, &pname, &pvalue, 0);
- if (pj_stricmp(&pname, &id_param)==0) {
- hdr->id_param = pvalue;
- } else {
- pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';');
- }
- }
- pjsip_parse_end_hdr_imp( scanner );
- return hdr;
-}
-
-static pjsip_allow_events_hdr *parse_hdr_allow_events(pj_scanner *scanner,
- pj_pool_t *pool)
-{
- pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool);
-
- pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[0]);
- hdr->event_cnt = 1;
-
- while (*scanner->current == ',') {
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[hdr->event_cnt++]);
- if (hdr->event_cnt == PJSIP_MAX_ALLOW_EVENTS) {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- }
- }
-
- pjsip_parse_end_hdr_imp( scanner );
- return hdr;
-}
-
-static pjsip_sub_state_hdr *parse_hdr_sub_state(pj_scanner *scanner,
- pj_pool_t *pool)
-{
- pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool);
- const pj_str_t reason = { "reason", 6 },
- expires = { "expires", 7 },
- retry_after = { "retry-after", 11 };
- pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->sub_state);
-
- while (*scanner->current == ';') {
- pj_str_t pname, pvalue;
-
- pj_scan_get_char(scanner);
- pjsip_parse_param_imp(scanner, &pname, &pvalue, 0);
- if (pj_stricmp(&pname, &reason) == 0) {
- hdr->reason_param = pvalue;
- } else if (pj_stricmp(&pname, &expires) == 0) {
- hdr->expires_param = pj_strtoul(&pvalue);
- } else if (pj_stricmp(&pname, &retry_after) == 0) {
- hdr->retry_after = pj_strtoul(&pvalue);
- } else {
- pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';');
- }
- }
-
- pjsip_parse_end_hdr_imp( scanner );
- return hdr;
-}
-
-PJ_DEF(void) pjsip_event_notify_init_parser(void)
-{
- pjsip_register_hdr_parser( "Event", NULL, (pjsip_parse_hdr_func*) &parse_hdr_event);
- pjsip_register_hdr_parser( "Allow-Events", NULL, (pjsip_parse_hdr_func*) &parse_hdr_allow_events);
- pjsip_register_hdr_parser( "Subscription-State", NULL, (pjsip_parse_hdr_func*) &parse_hdr_sub_state);
-}
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip_simple/event_notify_msg.h> +#include <pjsip/print.h> +#include <pjsip/sip_parser.h> +#include <pj/pool.h> +#include <pj/string.h> +#include <pj/except.h> + +static int pjsip_event_hdr_print( pjsip_event_hdr *hdr, + char *buf, pj_size_t size); +static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool, + const pjsip_event_hdr *hdr); +static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_event_hdr*); + +static pjsip_hdr_vptr event_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_event_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_event_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_event_hdr_print, +}; + + +PJ_DEF(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool) +{ + pj_str_t event = { "Event", 5 }; + pjsip_event_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + hdr->type = PJSIP_H_OTHER; + hdr->name = hdr->sname = event; + hdr->vptr = &event_hdr_vptr; + pj_list_init(hdr); + return hdr; +} + +static int pjsip_event_hdr_print( pjsip_event_hdr *hdr, + char *buf, pj_size_t size) +{ + char *p = buf; + char *endbuf = buf+size; + int printed; + + copy_advance(p, hdr->name); + *p++ = ':'; + *p++ = ' '; + + copy_advance(p, hdr->event_type); + copy_advance_pair(p, ";id=", 4, hdr->id_param); + if (hdr->other_param.slen) + copy_advance(p, hdr->other_param); + return p - buf; +} + +static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool, + const pjsip_event_hdr *rhs) +{ + pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool); + pj_strdup(pool, &hdr->event_type, &rhs->event_type); + pj_strdup(pool, &hdr->id_param, &rhs->id_param); + pj_strdup(pool, &hdr->other_param, &rhs->other_param); + return hdr; +} + +static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_event_hdr *rhs ) +{ + pjsip_event_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + + +static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr, + char *buf, pj_size_t size); +static pjsip_allow_events_hdr* +pjsip_allow_events_hdr_clone(pj_pool_t *pool, + const pjsip_allow_events_hdr *hdr); +static pjsip_allow_events_hdr* +pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool, + const pjsip_allow_events_hdr*); + +static pjsip_hdr_vptr allow_event_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_allow_events_hdr_print, +}; + + +PJ_DEF(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool) +{ + pj_str_t allow_events = { "Allow-Events", 12 }; + pjsip_allow_events_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + hdr->type = PJSIP_H_OTHER; + hdr->name = hdr->sname = allow_events; + hdr->vptr = &allow_event_hdr_vptr; + pj_list_init(hdr); + return hdr; +} + +static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr, + char *buf, pj_size_t size) +{ + char *p = buf; + char *endbuf = buf+size; + int printed; + + copy_advance(p, hdr->name); + *p++ = ':'; + *p++ = ' '; + + if (hdr->event_cnt > 0) { + int i; + copy_advance(p, hdr->events[0]); + for (i=1; i<hdr->event_cnt; ++i) { + copy_advance_pair(p, ",", 1, hdr->events[i]); + } + } + + return p - buf; +} + +static pjsip_allow_events_hdr* +pjsip_allow_events_hdr_clone(pj_pool_t *pool, + const pjsip_allow_events_hdr *rhs) +{ + int i; + + pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool); + hdr->event_cnt = rhs->event_cnt; + for (i=0; i<rhs->event_cnt; ++i) { + pj_strdup(pool, &hdr->events[i], &rhs->events[i]); + } + return hdr; +} + +static pjsip_allow_events_hdr* +pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool, + const pjsip_allow_events_hdr *rhs) +{ + pjsip_allow_events_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + + +static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr, + char *buf, pj_size_t size); +static pjsip_sub_state_hdr* +pjsip_sub_state_hdr_clone(pj_pool_t *pool, + const pjsip_sub_state_hdr *hdr); +static pjsip_sub_state_hdr* +pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool, + const pjsip_sub_state_hdr*); + +static pjsip_hdr_vptr sub_state_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_sub_state_hdr_print, +}; + + +PJ_DEF(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool) +{ + pj_str_t sub_state = { "Subscription-State", 18 }; + pjsip_sub_state_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + hdr->type = PJSIP_H_OTHER; + hdr->name = hdr->sname = sub_state; + hdr->vptr = &sub_state_hdr_vptr; + hdr->expires_param = -1; + hdr->retry_after = -1; + pj_list_init(hdr); + return hdr; +} + +static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr, + char *buf, pj_size_t size) +{ + char *p = buf; + char *endbuf = buf+size; + int printed; + + copy_advance(p, hdr->name); + *p++ = ':'; + *p++ = ' '; + + copy_advance(p, hdr->sub_state); + copy_advance_pair(p, ";reason=", 8, hdr->reason_param); + if (hdr->expires_param >= 0) { + pj_memcpy(p, ";expires=", 9); + p += 9; + printed = pj_utoa(hdr->expires_param, p); + p += printed; + } + if (hdr->retry_after >= 0) { + pj_memcpy(p, ";retry-after=", 13); + p += 9; + printed = pj_utoa(hdr->retry_after, p); + p += printed; + } + if (hdr->other_param.slen) + copy_advance(p, hdr->other_param); + + return p - buf; +} + +static pjsip_sub_state_hdr* +pjsip_sub_state_hdr_clone(pj_pool_t *pool, + const pjsip_sub_state_hdr *rhs) +{ + pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool); + pj_strdup(pool, &hdr->sub_state, &rhs->sub_state); + pj_strdup(pool, &hdr->reason_param, &rhs->reason_param); + hdr->retry_after = rhs->retry_after; + hdr->expires_param = rhs->expires_param; + pj_strdup(pool, &hdr->other_param, &rhs->other_param); + return hdr; +} + +static pjsip_sub_state_hdr* +pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool, + const pjsip_sub_state_hdr *rhs) +{ + pjsip_sub_state_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + +static pjsip_event_hdr *parse_hdr_event(pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool); + const pj_str_t id_param = { "id", 2 }; + + pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->event_type); + + while (*scanner->current == ';') { + pj_str_t pname, pvalue; + pj_scan_get_char(scanner); + pjsip_parse_param_imp(scanner, &pname, &pvalue, 0); + if (pj_stricmp(&pname, &id_param)==0) { + hdr->id_param = pvalue; + } else { + pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';'); + } + } + pjsip_parse_end_hdr_imp( scanner ); + return hdr; +} + +static pjsip_allow_events_hdr *parse_hdr_allow_events(pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool); + + pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[0]); + hdr->event_cnt = 1; + + while (*scanner->current == ',') { + pj_scan_get_char(scanner); + pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[hdr->event_cnt++]); + if (hdr->event_cnt == PJSIP_MAX_ALLOW_EVENTS) { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + } + } + + pjsip_parse_end_hdr_imp( scanner ); + return hdr; +} + +static pjsip_sub_state_hdr *parse_hdr_sub_state(pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool); + const pj_str_t reason = { "reason", 6 }, + expires = { "expires", 7 }, + retry_after = { "retry-after", 11 }; + pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->sub_state); + + while (*scanner->current == ';') { + pj_str_t pname, pvalue; + + pj_scan_get_char(scanner); + pjsip_parse_param_imp(scanner, &pname, &pvalue, 0); + if (pj_stricmp(&pname, &reason) == 0) { + hdr->reason_param = pvalue; + } else if (pj_stricmp(&pname, &expires) == 0) { + hdr->expires_param = pj_strtoul(&pvalue); + } else if (pj_stricmp(&pname, &retry_after) == 0) { + hdr->retry_after = pj_strtoul(&pvalue); + } else { + pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';'); + } + } + + pjsip_parse_end_hdr_imp( scanner ); + return hdr; +} + +PJ_DEF(void) pjsip_event_notify_init_parser(void) +{ + pjsip_register_hdr_parser( "Event", NULL, (pjsip_parse_hdr_func*) &parse_hdr_event); + pjsip_register_hdr_parser( "Allow-Events", NULL, (pjsip_parse_hdr_func*) &parse_hdr_allow_events); + pjsip_register_hdr_parser( "Subscription-State", NULL, (pjsip_parse_hdr_func*) &parse_hdr_sub_state); +} diff --git a/pjsip/src/pjsip-simple/messaging.c b/pjsip/src/pjsip-simple/messaging.c index d43acc0c..6e08af68 100644 --- a/pjsip/src/pjsip-simple/messaging.c +++ b/pjsip/src/pjsip-simple/messaging.c @@ -1,352 +1,352 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_simple/messaging.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_parser.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_util.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-#include <pj/guid.h>
-#include <pj/string.h>
-#include <pj/log.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#define THIS_FILE "messaging"
-
-struct messaging_data
-{
- void *token;
- pjsip_messaging_cb cb;
-};
-
-struct pjsip_messaging_session
-{
- pj_pool_t *pool;
- pjsip_endpoint *endpt;
- pjsip_from_hdr *from;
- pjsip_to_hdr *to;
- pjsip_cid_hdr *call_id;
- pjsip_cseq_hdr *cseq;
-};
-
-static int module_id;
-static pjsip_on_new_msg_cb incoming_cb;
-static pjsip_method message_method;
-
-
-/*
- * Set global callback to receive incoming message.
- */
-PJ_DEF(pjsip_on_new_msg_cb)
-pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb)
-{
- pjsip_on_new_msg_cb prev_cb = incoming_cb;
- incoming_cb = cb;
- return prev_cb;
-}
-
-
-/*
- * Create an independent message (ie. not associated with a session).
- */
-PJ_DEF(pjsip_tx_data*)
-pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt,
- const pjsip_uri *target,
- const pjsip_from_hdr *param_from,
- const pjsip_to_hdr *param_to,
- const pjsip_cid_hdr *param_call_id,
- int param_cseq,
- const pj_str_t *param_text)
-{
- return pjsip_endpt_create_request_from_hdr( endpt, &message_method,
- target,
- param_from, param_to,
- NULL, param_call_id,
- param_cseq, param_text );
-}
-
-/*
- * Create independent message from string (instead of from header).
- */
-PJ_DEF(pjsip_tx_data*)
-pjsip_messaging_create_msg( pjsip_endpoint *endpt,
- const pj_str_t *target,
- const pj_str_t *param_from,
- const pj_str_t *param_to,
- const pj_str_t *param_call_id,
- int param_cseq,
- const pj_str_t *param_text)
-{
- return pjsip_endpt_create_request( endpt, &message_method, target,
- param_from, param_to, NULL, param_call_id,
- param_cseq, param_text);
-}
-
-/*
- * Initiate transaction to send outgoing message.
- */
-PJ_DEF(pj_status_t)
-pjsip_messaging_send_msg( pjsip_endpoint *endpt, pjsip_tx_data *tdata,
- void *token, pjsip_messaging_cb cb )
-{
- pjsip_transaction *tsx;
- struct messaging_data *msg_data;
-
- /* Create transaction. */
- tsx = pjsip_endpt_create_tsx(endpt);
- if (!tsx) {
- pjsip_tx_data_dec_ref(tdata);
- return -1;
- }
-
- /* Save parameters to messaging data and attach to tsx. */
- msg_data = pj_pool_calloc(tsx->pool, 1, sizeof(struct messaging_data));
- msg_data->cb = cb;
- msg_data->token = token;
-
- /* Init transaction. */
- tsx->module_data[module_id] = msg_data;
- if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
- pjsip_tx_data_dec_ref(tdata);
- pjsip_endpt_destroy_tsx(endpt, tsx);
- return -1;
- }
-
- pjsip_endpt_register_tsx(endpt, tsx);
-
- /*
- * Instruct transaction to send message.
- * Further events will be received via transaction's event.
- */
- pjsip_tsx_on_tx_msg(tsx, tdata);
-
- /* Decrement reference counter. */
- pjsip_tx_data_dec_ref(tdata);
- return 0;
-}
-
-
-/*
- * Create 'IM session'.
- */
-PJ_DEF(pjsip_messaging_session*)
-pjsip_messaging_create_session( pjsip_endpoint *endpt, const pj_str_t *param_from,
- const pj_str_t *param_to )
-{
- pj_pool_t *pool;
- pjsip_messaging_session *ses;
- pj_str_t tmp, to;
-
- pool = pjsip_endpt_create_pool(endpt, "imsess", 1024, 1024);
- if (!pool)
- return NULL;
-
- ses = pj_pool_calloc(pool, 1, sizeof(pjsip_messaging_session));
- ses->pool = pool;
- ses->endpt = endpt;
-
- ses->call_id = pjsip_cid_hdr_create(pool);
- pj_create_unique_string(pool, &ses->call_id->id);
-
- ses->cseq = pjsip_cseq_hdr_create(pool);
- ses->cseq->cseq = pj_rand();
- ses->cseq->method = message_method;
-
- ses->from = pjsip_from_hdr_create(pool);
- pj_strdup_with_null(pool, &tmp, param_from);
- ses->from->uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
- if (ses->from->uri == NULL) {
- pjsip_endpt_destroy_pool(endpt, pool);
- return NULL;
- }
- pj_create_unique_string(pool, &ses->from->tag);
-
- ses->to = pjsip_to_hdr_create(pool);
- pj_strdup_with_null(pool, &to, param_from);
- ses->to->uri = pjsip_parse_uri(pool, to.ptr, to.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
- if (ses->to->uri == NULL) {
- pjsip_endpt_destroy_pool(endpt, pool);
- return NULL;
- }
-
- PJ_LOG(4,(THIS_FILE, "IM session created: recipient=%s", to.ptr));
- return ses;
-}
-
-
-/*
- * Send IM message using identification from 'IM session'.
- */
-PJ_DEF(pjsip_tx_data*)
-pjsip_messaging_session_create_msg( pjsip_messaging_session *ses, const pj_str_t *text )
-{
- return pjsip_endpt_create_request_from_hdr( ses->endpt,
- &message_method,
- ses->to->uri,
- ses->from,
- ses->to,
- NULL,
- ses->call_id,
- ses->cseq->cseq++,
- text);
-}
-
-
-/*
- * Destroy 'IM session'.
- */
-PJ_DEF(pj_status_t)
-pjsip_messaging_destroy_session( pjsip_messaging_session *ses )
-{
- /*
- * NOTE ABOUT POSSIBLE BUG HERE...
- *
- * We don't check number of pending transaction before destroying IM
- * session. As the result, the headers in the txdata of pending transaction
- * wil be INVALID once the IM session is deleted (because we only
- * shallo_clone()-ed them).
- *
- * This normally should be okay, because once the message is
- * submitted to transaction, the transaction (or rather the transport)
- * will 'print' the message to a buffer, and once it is printed, it
- * won't try to access the original message again. So even when the
- * original message has a dangling pointer, we should be safe.
- *
- * However, it will cause a problem if:
- * - resolving completes asynchronously and with a substantial delay,
- * and before the resolver/transport finished its job the user
- * destroy the IM session.
- * - if the transmit data is invalidated after the IM session is
- * destroyed.
- */
-
- pjsip_endpt_destroy_pool(ses->endpt, ses->pool);
- return 0;
-}
-
-
-static pj_status_t messaging_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id )
-{
- PJ_UNUSED_ARG(endpt)
- PJ_UNUSED_ARG(mod)
-
- module_id = id;
- return 0;
-}
-
-static pj_status_t messaging_start( struct pjsip_module *mod )
-{
- PJ_UNUSED_ARG(mod)
- return 0;
-}
-
-static pj_status_t messaging_deinit( struct pjsip_module *mod )
-{
- PJ_UNUSED_ARG(mod)
- return 0;
-}
-
-static void messaging_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
-{
- pjsip_transaction *tsx = event->obj.tsx;
- struct messaging_data *mod_data;
-
- PJ_UNUSED_ARG(mod)
-
- /* Ignore non transaction event */
- if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED || tsx == NULL)
- return;
-
- /* If this is an incoming message, inform application. */
- if (tsx->role == PJSIP_ROLE_UAS) {
- int status = 100;
- pjsip_tx_data *tdata;
-
- /* Check if we already answered this request. */
- if (tsx->status_code >= 200)
- return;
-
- /* Only handle MESSAGE requests!. */
- if (pjsip_method_cmp(&tsx->method, &message_method) != 0)
- return;
-
- /* Call application callback. */
- if (incoming_cb)
- status = (*incoming_cb)(event->src.rdata);
-
- if (status < 200 || status >= 700)
- status = PJSIP_SC_INTERNAL_SERVER_ERROR;
-
- /* Respond request. */
- tdata = pjsip_endpt_create_response(tsx->endpt, event->src.rdata, status );
- if (tdata)
- pjsip_tsx_on_tx_msg(tsx, tdata);
-
- return;
- }
-
- /* Ignore if it's not something that came from messaging module. */
- mod_data = tsx->module_data[ module_id ];
- if (mod_data == NULL)
- return;
-
- /* Ignore non final response. */
- if (tsx->status_code < 200)
- return;
-
- /* Don't want to call the callback more than once. */
- tsx->module_data[ module_id ] = NULL;
-
- /* Now call the callback. */
- if (mod_data->cb) {
- (*mod_data->cb)(mod_data->token, tsx->status_code);
- }
-}
-
-static pjsip_module messaging_module =
-{
- { "Messaging", 9}, /* Name. */
- 0, /* Flag */
- 128, /* Priority */
- NULL, /* User agent instance, initialized by APP. */
- 0, /* Number of methods supported (will be initialized later). */
- { 0 }, /* Array of methods (will be initialized later) */
- &messaging_init, /* init_module() */
- &messaging_start, /* start_module() */
- &messaging_deinit, /* deinit_module() */
- &messaging_tsx_handler, /* tsx_handler() */
-};
-
-PJ_DEF(pjsip_module*) pjsip_messaging_get_module()
-{
- static pj_str_t method_str = { "MESSAGE", 7 };
-
- pjsip_method_init_np( &message_method, &method_str);
-
- messaging_module.method_cnt = 1;
- messaging_module.methods[0] = &message_method;
-
- return &messaging_module;
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip_simple/messaging.h> +#include <pjsip/sip_endpoint.h> +#include <pjsip/sip_parser.h> +#include <pjsip/sip_transaction.h> +#include <pjsip/sip_event.h> +#include <pjsip/sip_module.h> +#include <pjsip/sip_util.h> +#include <pj/string.h> +#include <pj/pool.h> +#include <pj/guid.h> +#include <pj/string.h> +#include <pj/log.h> +#include <stdio.h> +#include <stdlib.h> + +#define THIS_FILE "messaging" + +struct messaging_data +{ + void *token; + pjsip_messaging_cb cb; +}; + +struct pjsip_messaging_session +{ + pj_pool_t *pool; + pjsip_endpoint *endpt; + pjsip_from_hdr *from; + pjsip_to_hdr *to; + pjsip_cid_hdr *call_id; + pjsip_cseq_hdr *cseq; +}; + +static int module_id; +static pjsip_on_new_msg_cb incoming_cb; +static pjsip_method message_method; + + +/* + * Set global callback to receive incoming message. + */ +PJ_DEF(pjsip_on_new_msg_cb) +pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb) +{ + pjsip_on_new_msg_cb prev_cb = incoming_cb; + incoming_cb = cb; + return prev_cb; +} + + +/* + * Create an independent message (ie. not associated with a session). + */ +PJ_DEF(pjsip_tx_data*) +pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt, + const pjsip_uri *target, + const pjsip_from_hdr *param_from, + const pjsip_to_hdr *param_to, + const pjsip_cid_hdr *param_call_id, + int param_cseq, + const pj_str_t *param_text) +{ + return pjsip_endpt_create_request_from_hdr( endpt, &message_method, + target, + param_from, param_to, + NULL, param_call_id, + param_cseq, param_text ); +} + +/* + * Create independent message from string (instead of from header). + */ +PJ_DEF(pjsip_tx_data*) +pjsip_messaging_create_msg( pjsip_endpoint *endpt, + const pj_str_t *target, + const pj_str_t *param_from, + const pj_str_t *param_to, + const pj_str_t *param_call_id, + int param_cseq, + const pj_str_t *param_text) +{ + return pjsip_endpt_create_request( endpt, &message_method, target, + param_from, param_to, NULL, param_call_id, + param_cseq, param_text); +} + +/* + * Initiate transaction to send outgoing message. + */ +PJ_DEF(pj_status_t) +pjsip_messaging_send_msg( pjsip_endpoint *endpt, pjsip_tx_data *tdata, + void *token, pjsip_messaging_cb cb ) +{ + pjsip_transaction *tsx; + struct messaging_data *msg_data; + + /* Create transaction. */ + tsx = pjsip_endpt_create_tsx(endpt); + if (!tsx) { + pjsip_tx_data_dec_ref(tdata); + return -1; + } + + /* Save parameters to messaging data and attach to tsx. */ + msg_data = pj_pool_calloc(tsx->pool, 1, sizeof(struct messaging_data)); + msg_data->cb = cb; + msg_data->token = token; + + /* Init transaction. */ + tsx->module_data[module_id] = msg_data; + if (pjsip_tsx_init_uac(tsx, tdata) != 0) { + pjsip_tx_data_dec_ref(tdata); + pjsip_endpt_destroy_tsx(endpt, tsx); + return -1; + } + + pjsip_endpt_register_tsx(endpt, tsx); + + /* + * Instruct transaction to send message. + * Further events will be received via transaction's event. + */ + pjsip_tsx_on_tx_msg(tsx, tdata); + + /* Decrement reference counter. */ + pjsip_tx_data_dec_ref(tdata); + return 0; +} + + +/* + * Create 'IM session'. + */ +PJ_DEF(pjsip_messaging_session*) +pjsip_messaging_create_session( pjsip_endpoint *endpt, const pj_str_t *param_from, + const pj_str_t *param_to ) +{ + pj_pool_t *pool; + pjsip_messaging_session *ses; + pj_str_t tmp, to; + + pool = pjsip_endpt_create_pool(endpt, "imsess", 1024, 1024); + if (!pool) + return NULL; + + ses = pj_pool_calloc(pool, 1, sizeof(pjsip_messaging_session)); + ses->pool = pool; + ses->endpt = endpt; + + ses->call_id = pjsip_cid_hdr_create(pool); + pj_create_unique_string(pool, &ses->call_id->id); + + ses->cseq = pjsip_cseq_hdr_create(pool); + ses->cseq->cseq = pj_rand(); + ses->cseq->method = message_method; + + ses->from = pjsip_from_hdr_create(pool); + pj_strdup_with_null(pool, &tmp, param_from); + ses->from->uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR); + if (ses->from->uri == NULL) { + pjsip_endpt_destroy_pool(endpt, pool); + return NULL; + } + pj_create_unique_string(pool, &ses->from->tag); + + ses->to = pjsip_to_hdr_create(pool); + pj_strdup_with_null(pool, &to, param_from); + ses->to->uri = pjsip_parse_uri(pool, to.ptr, to.slen, PJSIP_PARSE_URI_AS_NAMEADDR); + if (ses->to->uri == NULL) { + pjsip_endpt_destroy_pool(endpt, pool); + return NULL; + } + + PJ_LOG(4,(THIS_FILE, "IM session created: recipient=%s", to.ptr)); + return ses; +} + + +/* + * Send IM message using identification from 'IM session'. + */ +PJ_DEF(pjsip_tx_data*) +pjsip_messaging_session_create_msg( pjsip_messaging_session *ses, const pj_str_t *text ) +{ + return pjsip_endpt_create_request_from_hdr( ses->endpt, + &message_method, + ses->to->uri, + ses->from, + ses->to, + NULL, + ses->call_id, + ses->cseq->cseq++, + text); +} + + +/* + * Destroy 'IM session'. + */ +PJ_DEF(pj_status_t) +pjsip_messaging_destroy_session( pjsip_messaging_session *ses ) +{ + /* + * NOTE ABOUT POSSIBLE BUG HERE... + * + * We don't check number of pending transaction before destroying IM + * session. As the result, the headers in the txdata of pending transaction + * wil be INVALID once the IM session is deleted (because we only + * shallo_clone()-ed them). + * + * This normally should be okay, because once the message is + * submitted to transaction, the transaction (or rather the transport) + * will 'print' the message to a buffer, and once it is printed, it + * won't try to access the original message again. So even when the + * original message has a dangling pointer, we should be safe. + * + * However, it will cause a problem if: + * - resolving completes asynchronously and with a substantial delay, + * and before the resolver/transport finished its job the user + * destroy the IM session. + * - if the transmit data is invalidated after the IM session is + * destroyed. + */ + + pjsip_endpt_destroy_pool(ses->endpt, ses->pool); + return 0; +} + + +static pj_status_t messaging_init( pjsip_endpoint *endpt, + struct pjsip_module *mod, pj_uint32_t id ) +{ + PJ_UNUSED_ARG(endpt) + PJ_UNUSED_ARG(mod) + + module_id = id; + return 0; +} + +static pj_status_t messaging_start( struct pjsip_module *mod ) +{ + PJ_UNUSED_ARG(mod) + return 0; +} + +static pj_status_t messaging_deinit( struct pjsip_module *mod ) +{ + PJ_UNUSED_ARG(mod) + return 0; +} + +static void messaging_tsx_handler( struct pjsip_module *mod, pjsip_event *event ) +{ + pjsip_transaction *tsx = event->obj.tsx; + struct messaging_data *mod_data; + + PJ_UNUSED_ARG(mod) + + /* Ignore non transaction event */ + if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED || tsx == NULL) + return; + + /* If this is an incoming message, inform application. */ + if (tsx->role == PJSIP_ROLE_UAS) { + int status = 100; + pjsip_tx_data *tdata; + + /* Check if we already answered this request. */ + if (tsx->status_code >= 200) + return; + + /* Only handle MESSAGE requests!. */ + if (pjsip_method_cmp(&tsx->method, &message_method) != 0) + return; + + /* Call application callback. */ + if (incoming_cb) + status = (*incoming_cb)(event->src.rdata); + + if (status < 200 || status >= 700) + status = PJSIP_SC_INTERNAL_SERVER_ERROR; + + /* Respond request. */ + tdata = pjsip_endpt_create_response(tsx->endpt, event->src.rdata, status ); + if (tdata) + pjsip_tsx_on_tx_msg(tsx, tdata); + + return; + } + + /* Ignore if it's not something that came from messaging module. */ + mod_data = tsx->module_data[ module_id ]; + if (mod_data == NULL) + return; + + /* Ignore non final response. */ + if (tsx->status_code < 200) + return; + + /* Don't want to call the callback more than once. */ + tsx->module_data[ module_id ] = NULL; + + /* Now call the callback. */ + if (mod_data->cb) { + (*mod_data->cb)(mod_data->token, tsx->status_code); + } +} + +static pjsip_module messaging_module = +{ + { "Messaging", 9}, /* Name. */ + 0, /* Flag */ + 128, /* Priority */ + NULL, /* User agent instance, initialized by APP. */ + 0, /* Number of methods supported (will be initialized later). */ + { 0 }, /* Array of methods (will be initialized later) */ + &messaging_init, /* init_module() */ + &messaging_start, /* start_module() */ + &messaging_deinit, /* deinit_module() */ + &messaging_tsx_handler, /* tsx_handler() */ +}; + +PJ_DEF(pjsip_module*) pjsip_messaging_get_module() +{ + static pj_str_t method_str = { "MESSAGE", 7 }; + + pjsip_method_init_np( &message_method, &method_str); + + messaging_module.method_cnt = 1; + messaging_module.methods[0] = &message_method; + + return &messaging_module; +} + diff --git a/pjsip/src/pjsip-simple/pidf.c b/pjsip/src/pjsip-simple/pidf.c index 8e4a7c38..7ababcad 100644 --- a/pjsip/src/pjsip-simple/pidf.c +++ b/pjsip/src/pjsip-simple/pidf.c @@ -1,350 +1,350 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_simple/pidf.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-
-struct pjpidf_op_desc pjpidf_op =
-{
- {
- &pjpidf_pres_construct,
- &pjpidf_pres_add_tuple,
- &pjpidf_pres_get_first_tuple,
- &pjpidf_pres_get_next_tuple,
- &pjpidf_pres_find_tuple,
- &pjpidf_pres_remove_tuple,
- &pjpidf_pres_add_note,
- &pjpidf_pres_get_first_note,
- &pjpidf_pres_get_next_note
- },
- {
- &pjpidf_tuple_construct,
- &pjpidf_tuple_get_id,
- &pjpidf_tuple_set_id,
- &pjpidf_tuple_get_status,
- &pjpidf_tuple_get_contact,
- &pjpidf_tuple_set_contact,
- &pjpidf_tuple_set_contact_prio,
- &pjpidf_tuple_get_contact_prio,
- &pjpidf_tuple_add_note,
- &pjpidf_tuple_get_first_note,
- &pjpidf_tuple_get_next_note,
- &pjpidf_tuple_get_timestamp,
- &pjpidf_tuple_set_timestamp,
- &pjpidf_tuple_set_timestamp_np
- },
- {
- &pjpidf_status_construct,
- &pjpidf_status_is_basic_open,
- &pjpidf_status_set_basic_open
- }
-};
-
-static pj_str_t PRESENCE = { "presence", 8 };
-static pj_str_t ENTITY = { "entity", 6};
-static pj_str_t TUPLE = { "tuple", 5 };
-static pj_str_t ID = { "id", 2 };
-static pj_str_t NOTE = { "note", 4 };
-static pj_str_t STATUS = { "status", 6 };
-static pj_str_t CONTACT = { "contact", 7 };
-static pj_str_t PRIORITY = { "priority", 8 };
-static pj_str_t TIMESTAMP = { "timestamp", 9 };
-static pj_str_t BASIC = { "basic", 5 };
-static pj_str_t OPEN = { "open", 4 };
-static pj_str_t CLOSED = { "closed", 6 };
-static pj_str_t EMPTY_STRING = { NULL, 0 };
-
-static void xml_init_node(pj_pool_t *pool, pj_xml_node *node,
- pj_str_t *name, const pj_str_t *value)
-{
- pj_list_init(&node->attr_head);
- pj_list_init(&node->node_head);
- node->name = *name;
- if (value) pj_strdup(pool, &node->content, value);
- else node->content.ptr=NULL, node->content.slen=0;
-}
-
-static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name,
- const pj_str_t *value)
-{
- pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr));
- attr->name = *name;
- pj_strdup(pool, &attr->value, value);
- return attr;
-}
-
-/* Presence */
-PJ_DEF(void) pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres,
- const pj_str_t *entity)
-{
- pj_xml_attr *attr;
-
- xml_init_node(pool, pres, &PRESENCE, NULL);
- attr = xml_create_attr(pool, &ENTITY, entity);
- pj_xml_add_attr(pres, attr);
-}
-
-PJ_DEF(pjpidf_tuple*) pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres,
- const pj_str_t *id)
-{
- pjpidf_tuple *t = pj_pool_alloc(pool, sizeof(*t));
- pjpidf_tuple_construct(pool, t, id);
- pj_xml_add_node(pres, t);
- return t;
-}
-
-PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_first_tuple(pjpidf_pres *pres)
-{
- return pj_xml_find_node(pres, &TUPLE);
-}
-
-PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_next_tuple(pjpidf_pres *pres,
- pjpidf_tuple *tuple)
-{
- return pj_xml_find_next_node(pres, tuple, &TUPLE);
-}
-
-static pj_bool_t find_tuple_by_id(pj_xml_node *node, const void *id)
-{
- return pj_xml_find_attr(node, &ID, id) != NULL;
-}
-
-PJ_DEF(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres, const pj_str_t *id)
-{
- return pj_xml_find(pres, &TUPLE, id, &find_tuple_by_id);
-}
-
-PJ_DEF(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres, pjpidf_tuple *t)
-{
- PJ_UNUSED_ARG(pres)
- pj_list_erase(t);
-}
-
-PJ_DEF(pjpidf_note*) pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres,
- const pj_str_t *text)
-{
- pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note));
- xml_init_node(pool, note, &NOTE, text);
- pj_xml_add_node(pres, note);
- return note;
-}
-
-PJ_DEF(pjpidf_note*) pjpidf_pres_get_first_note(pjpidf_pres *pres)
-{
- return pj_xml_find_node( pres, &NOTE);
-}
-
-PJ_DEF(pjpidf_note*) pjpidf_pres_get_next_note(pjpidf_pres *t, pjpidf_note *note)
-{
- return pj_xml_find_next_node(t, note, &NOTE);
-}
-
-
-/* Tuple */
-PJ_DEF(void) pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *id)
-{
- pj_xml_attr *attr;
- pjpidf_status *st;
-
- xml_init_node(pool, t, &TUPLE, NULL);
- attr = xml_create_attr(pool, &ID, id);
- pj_xml_add_attr(t, attr);
- st = pj_pool_alloc(pool, sizeof(*st));
- pjpidf_status_construct(pool, st);
- pj_xml_add_node(t, st);
-}
-
-PJ_DEF(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t)
-{
- const pj_xml_attr *attr = pj_xml_find_attr((pj_xml_node*)t, &ID, NULL);
- pj_assert(attr);
- return &attr->value;
-}
-
-PJ_DEF(void) pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *id)
-{
- pj_xml_attr *attr = pj_xml_find_attr(t, &ID, NULL);
- pj_assert(attr);
- pj_strdup(pool, &attr->value, id);
-}
-
-
-PJ_DEF(pjpidf_status*) pjpidf_tuple_get_status(pjpidf_tuple *t)
-{
- pjpidf_status *st = (pjpidf_status*)pj_xml_find_node(t, &STATUS);
- pj_assert(st);
- return st;
-}
-
-
-PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t)
-{
- pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT);
- if (!node)
- return &EMPTY_STRING;
- return &node->content;
-}
-
-PJ_DEF(void) pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *contact)
-{
- pj_xml_node *node = pj_xml_find_node(t, &CONTACT);
- if (!node) {
- node = pj_pool_alloc(pool, sizeof(*node));
- xml_init_node(pool, node, &CONTACT, contact);
- pj_xml_add_node(t, node);
- } else {
- pj_strdup(pool, &node->content, contact);
- }
-}
-
-PJ_DEF(void) pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *prio)
-{
- pj_xml_node *node = pj_xml_find_node(t, &CONTACT);
- pj_xml_attr *attr;
-
- if (!node) {
- node = pj_pool_alloc(pool, sizeof(*node));
- xml_init_node(pool, node, &CONTACT, NULL);
- pj_xml_add_node(t, node);
- }
- attr = pj_xml_find_attr(node, &PRIORITY, NULL);
- if (!attr) {
- attr = xml_create_attr(pool, &PRIORITY, prio);
- pj_xml_add_attr(node, attr);
- } else {
- pj_strdup(pool, &attr->value, prio);
- }
-}
-
-PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t)
-{
- pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT);
- pj_xml_attr *attr;
-
- if (!node)
- return &EMPTY_STRING;
- attr = pj_xml_find_attr(node, &PRIORITY, NULL);
- if (!attr)
- return &EMPTY_STRING;
- return &attr->value;
-}
-
-
-PJ_DEF(pjpidf_note*) pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *text)
-{
- pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note));
- xml_init_node(pool, note, &NOTE, text);
- pj_xml_add_node(t, note);
- return note;
-}
-
-PJ_DEF(pjpidf_note*) pjpidf_tuple_get_first_note(pjpidf_tuple *t)
-{
- return pj_xml_find_node(t, &NOTE);
-}
-
-PJ_DEF(pjpidf_note*) pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n)
-{
- return pj_xml_find_next_node(t, n, &NOTE);
-}
-
-
-PJ_DEF(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t)
-{
- pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &TIMESTAMP);
- return node ? &node->content : &EMPTY_STRING;
-}
-
-PJ_DEF(void) pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *ts)
-{
- pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP);
- if (!node) {
- node = pj_pool_alloc(pool, sizeof(*node));
- xml_init_node(pool, node, &TIMESTAMP, ts);
- } else {
- pj_strdup(pool, &node->content, ts);
- }
-}
-
-
-PJ_DEF(void) pjpidf_tuple_set_timestamp_np(pj_pool_t *pool, pjpidf_tuple *t,
- pj_str_t *ts)
-{
- pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP);
- if (!node) {
- node = pj_pool_alloc(pool, sizeof(*node));
- xml_init_node(pool, node, &TIMESTAMP, ts);
- } else {
- node->content = *ts;
- }
-}
-
-
-/* Status */
-PJ_DEF(void) pjpidf_status_construct(pj_pool_t *pool, pjpidf_status *st)
-{
- pj_xml_node *node;
-
- xml_init_node(pool, st, &STATUS, NULL);
- node = pj_pool_alloc(pool, sizeof(*node));
- xml_init_node(pool, node, &BASIC, &CLOSED);
- pj_xml_add_node(st, node);
-}
-
-PJ_DEF(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status *st)
-{
- pj_xml_node *node = pj_xml_find_node((pj_xml_node*)st, &BASIC);
- pj_assert(node != NULL);
- return pj_stricmp(&node->content, &OPEN)==0;
-}
-
-PJ_DEF(void) pjpidf_status_set_basic_open(pjpidf_status *st, pj_bool_t open)
-{
- pj_xml_node *node = pj_xml_find_node(st, &BASIC);
- pj_assert(node != NULL);
- node->content = open ? OPEN : CLOSED;
-}
-
-PJ_DEF(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity)
-{
- pjpidf_pres *pres = pj_pool_alloc(pool, sizeof(*pres));
- pjpidf_pres_construct(pool, pres, entity);
- return pres;
-}
-
-PJ_DEF(pjpidf_pres*) pjpidf_parse(pj_pool_t *pool, char *text, int len)
-{
- pjpidf_pres *pres = pj_xml_parse(pool, text, len);
- if (pres) {
- if (pj_stricmp(&pres->name, &PRESENCE) != 0)
- return NULL;
- }
- return pres;
-}
-
-PJ_DEF(int) pjpidf_print(const pjpidf_pres* pres, char *buf, int len)
-{
- return pj_xml_print(pres, buf, len, PJ_TRUE);
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip_simple/pidf.h> +#include <pj/string.h> +#include <pj/pool.h> + +struct pjpidf_op_desc pjpidf_op = +{ + { + &pjpidf_pres_construct, + &pjpidf_pres_add_tuple, + &pjpidf_pres_get_first_tuple, + &pjpidf_pres_get_next_tuple, + &pjpidf_pres_find_tuple, + &pjpidf_pres_remove_tuple, + &pjpidf_pres_add_note, + &pjpidf_pres_get_first_note, + &pjpidf_pres_get_next_note + }, + { + &pjpidf_tuple_construct, + &pjpidf_tuple_get_id, + &pjpidf_tuple_set_id, + &pjpidf_tuple_get_status, + &pjpidf_tuple_get_contact, + &pjpidf_tuple_set_contact, + &pjpidf_tuple_set_contact_prio, + &pjpidf_tuple_get_contact_prio, + &pjpidf_tuple_add_note, + &pjpidf_tuple_get_first_note, + &pjpidf_tuple_get_next_note, + &pjpidf_tuple_get_timestamp, + &pjpidf_tuple_set_timestamp, + &pjpidf_tuple_set_timestamp_np + }, + { + &pjpidf_status_construct, + &pjpidf_status_is_basic_open, + &pjpidf_status_set_basic_open + } +}; + +static pj_str_t PRESENCE = { "presence", 8 }; +static pj_str_t ENTITY = { "entity", 6}; +static pj_str_t TUPLE = { "tuple", 5 }; +static pj_str_t ID = { "id", 2 }; +static pj_str_t NOTE = { "note", 4 }; +static pj_str_t STATUS = { "status", 6 }; +static pj_str_t CONTACT = { "contact", 7 }; +static pj_str_t PRIORITY = { "priority", 8 }; +static pj_str_t TIMESTAMP = { "timestamp", 9 }; +static pj_str_t BASIC = { "basic", 5 }; +static pj_str_t OPEN = { "open", 4 }; +static pj_str_t CLOSED = { "closed", 6 }; +static pj_str_t EMPTY_STRING = { NULL, 0 }; + +static void xml_init_node(pj_pool_t *pool, pj_xml_node *node, + pj_str_t *name, const pj_str_t *value) +{ + pj_list_init(&node->attr_head); + pj_list_init(&node->node_head); + node->name = *name; + if (value) pj_strdup(pool, &node->content, value); + else node->content.ptr=NULL, node->content.slen=0; +} + +static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name, + const pj_str_t *value) +{ + pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr)); + attr->name = *name; + pj_strdup(pool, &attr->value, value); + return attr; +} + +/* Presence */ +PJ_DEF(void) pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres, + const pj_str_t *entity) +{ + pj_xml_attr *attr; + + xml_init_node(pool, pres, &PRESENCE, NULL); + attr = xml_create_attr(pool, &ENTITY, entity); + pj_xml_add_attr(pres, attr); +} + +PJ_DEF(pjpidf_tuple*) pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres, + const pj_str_t *id) +{ + pjpidf_tuple *t = pj_pool_alloc(pool, sizeof(*t)); + pjpidf_tuple_construct(pool, t, id); + pj_xml_add_node(pres, t); + return t; +} + +PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_first_tuple(pjpidf_pres *pres) +{ + return pj_xml_find_node(pres, &TUPLE); +} + +PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_next_tuple(pjpidf_pres *pres, + pjpidf_tuple *tuple) +{ + return pj_xml_find_next_node(pres, tuple, &TUPLE); +} + +static pj_bool_t find_tuple_by_id(pj_xml_node *node, const void *id) +{ + return pj_xml_find_attr(node, &ID, id) != NULL; +} + +PJ_DEF(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres, const pj_str_t *id) +{ + return pj_xml_find(pres, &TUPLE, id, &find_tuple_by_id); +} + +PJ_DEF(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres, pjpidf_tuple *t) +{ + PJ_UNUSED_ARG(pres) + pj_list_erase(t); +} + +PJ_DEF(pjpidf_note*) pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres, + const pj_str_t *text) +{ + pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note)); + xml_init_node(pool, note, &NOTE, text); + pj_xml_add_node(pres, note); + return note; +} + +PJ_DEF(pjpidf_note*) pjpidf_pres_get_first_note(pjpidf_pres *pres) +{ + return pj_xml_find_node( pres, &NOTE); +} + +PJ_DEF(pjpidf_note*) pjpidf_pres_get_next_note(pjpidf_pres *t, pjpidf_note *note) +{ + return pj_xml_find_next_node(t, note, &NOTE); +} + + +/* Tuple */ +PJ_DEF(void) pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *id) +{ + pj_xml_attr *attr; + pjpidf_status *st; + + xml_init_node(pool, t, &TUPLE, NULL); + attr = xml_create_attr(pool, &ID, id); + pj_xml_add_attr(t, attr); + st = pj_pool_alloc(pool, sizeof(*st)); + pjpidf_status_construct(pool, st); + pj_xml_add_node(t, st); +} + +PJ_DEF(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t) +{ + const pj_xml_attr *attr = pj_xml_find_attr((pj_xml_node*)t, &ID, NULL); + pj_assert(attr); + return &attr->value; +} + +PJ_DEF(void) pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *id) +{ + pj_xml_attr *attr = pj_xml_find_attr(t, &ID, NULL); + pj_assert(attr); + pj_strdup(pool, &attr->value, id); +} + + +PJ_DEF(pjpidf_status*) pjpidf_tuple_get_status(pjpidf_tuple *t) +{ + pjpidf_status *st = (pjpidf_status*)pj_xml_find_node(t, &STATUS); + pj_assert(st); + return st; +} + + +PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t) +{ + pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT); + if (!node) + return &EMPTY_STRING; + return &node->content; +} + +PJ_DEF(void) pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *contact) +{ + pj_xml_node *node = pj_xml_find_node(t, &CONTACT); + if (!node) { + node = pj_pool_alloc(pool, sizeof(*node)); + xml_init_node(pool, node, &CONTACT, contact); + pj_xml_add_node(t, node); + } else { + pj_strdup(pool, &node->content, contact); + } +} + +PJ_DEF(void) pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *prio) +{ + pj_xml_node *node = pj_xml_find_node(t, &CONTACT); + pj_xml_attr *attr; + + if (!node) { + node = pj_pool_alloc(pool, sizeof(*node)); + xml_init_node(pool, node, &CONTACT, NULL); + pj_xml_add_node(t, node); + } + attr = pj_xml_find_attr(node, &PRIORITY, NULL); + if (!attr) { + attr = xml_create_attr(pool, &PRIORITY, prio); + pj_xml_add_attr(node, attr); + } else { + pj_strdup(pool, &attr->value, prio); + } +} + +PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t) +{ + pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT); + pj_xml_attr *attr; + + if (!node) + return &EMPTY_STRING; + attr = pj_xml_find_attr(node, &PRIORITY, NULL); + if (!attr) + return &EMPTY_STRING; + return &attr->value; +} + + +PJ_DEF(pjpidf_note*) pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *text) +{ + pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note)); + xml_init_node(pool, note, &NOTE, text); + pj_xml_add_node(t, note); + return note; +} + +PJ_DEF(pjpidf_note*) pjpidf_tuple_get_first_note(pjpidf_tuple *t) +{ + return pj_xml_find_node(t, &NOTE); +} + +PJ_DEF(pjpidf_note*) pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n) +{ + return pj_xml_find_next_node(t, n, &NOTE); +} + + +PJ_DEF(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t) +{ + pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &TIMESTAMP); + return node ? &node->content : &EMPTY_STRING; +} + +PJ_DEF(void) pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *ts) +{ + pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP); + if (!node) { + node = pj_pool_alloc(pool, sizeof(*node)); + xml_init_node(pool, node, &TIMESTAMP, ts); + } else { + pj_strdup(pool, &node->content, ts); + } +} + + +PJ_DEF(void) pjpidf_tuple_set_timestamp_np(pj_pool_t *pool, pjpidf_tuple *t, + pj_str_t *ts) +{ + pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP); + if (!node) { + node = pj_pool_alloc(pool, sizeof(*node)); + xml_init_node(pool, node, &TIMESTAMP, ts); + } else { + node->content = *ts; + } +} + + +/* Status */ +PJ_DEF(void) pjpidf_status_construct(pj_pool_t *pool, pjpidf_status *st) +{ + pj_xml_node *node; + + xml_init_node(pool, st, &STATUS, NULL); + node = pj_pool_alloc(pool, sizeof(*node)); + xml_init_node(pool, node, &BASIC, &CLOSED); + pj_xml_add_node(st, node); +} + +PJ_DEF(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status *st) +{ + pj_xml_node *node = pj_xml_find_node((pj_xml_node*)st, &BASIC); + pj_assert(node != NULL); + return pj_stricmp(&node->content, &OPEN)==0; +} + +PJ_DEF(void) pjpidf_status_set_basic_open(pjpidf_status *st, pj_bool_t open) +{ + pj_xml_node *node = pj_xml_find_node(st, &BASIC); + pj_assert(node != NULL); + node->content = open ? OPEN : CLOSED; +} + +PJ_DEF(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity) +{ + pjpidf_pres *pres = pj_pool_alloc(pool, sizeof(*pres)); + pjpidf_pres_construct(pool, pres, entity); + return pres; +} + +PJ_DEF(pjpidf_pres*) pjpidf_parse(pj_pool_t *pool, char *text, int len) +{ + pjpidf_pres *pres = pj_xml_parse(pool, text, len); + if (pres) { + if (pj_stricmp(&pres->name, &PRESENCE) != 0) + return NULL; + } + return pres; +} + +PJ_DEF(int) pjpidf_print(const pjpidf_pres* pres, char *buf, int len) +{ + return pj_xml_print(pres, buf, len, PJ_TRUE); +} + diff --git a/pjsip/src/pjsip-simple/presence.c b/pjsip/src/pjsip-simple/presence.c index 6f822568..a9cc6108 100644 --- a/pjsip/src/pjsip-simple/presence.c +++ b/pjsip/src/pjsip-simple/presence.c @@ -1,399 +1,399 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_simple/presence.h>
-#include <pjsip/sip_transport.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/guid.h>
-#include <pj/os.h>
-#include <stdio.h>
-
-/* Forward declarations. */
-static void on_query_subscribe(pjsip_rx_data *rdata, int *status);
-static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata,
- pjsip_event_sub_cb **cb, int *expires);
-static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason);
-static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata);
-static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata);
-
-/* Some string constants. */
-static pj_str_t PRESENCE_EVENT = { "presence", 8 };
-
-/* Accept types. */
-static pj_str_t accept_names[] = {
- { "application/pidf+xml", 20 },
- { "application/xpidf+xml", 21 }
-};
-static pjsip_media_type accept_types[] = {
- {
- { "application", 11 },
- { "pidf+xml", 8 }
- },
- {
- { "application", 11 },
- { "xpidf+xml", 9 }
- }
-};
-
-/* Callback that is registered by application. */
-static pjsip_presence_cb cb;
-
-/* Package callback to be register to event_notify */
-static pjsip_event_sub_pkg_cb pkg_cb = { &on_query_subscribe,
- &on_subscribe };
-
-/* Global/static callback to be registered to event_notify */
-static pjsip_event_sub_cb sub_cb = { &on_sub_terminated,
- &on_sub_received_refresh,
- NULL,
- &on_received_notify,
- NULL };
-
-/*
- * Initialize presence module.
- * This will register event package "presence" to event framework.
- */
-PJ_DEF(void) pjsip_presence_init(const pjsip_presence_cb *pcb)
-{
- pj_memcpy(&cb, pcb, sizeof(*pcb));
- pjsip_event_sub_register_pkg( &PRESENCE_EVENT,
- sizeof(accept_names)/sizeof(accept_names[0]),
- accept_names,
- &pkg_cb);
-}
-
-/*
- * Create presence subscription.
- */
-PJ_DEF(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt,
- const pj_str_t *local_url,
- const pj_str_t *remote_url,
- int expires,
- void *user_data )
-{
- pjsip_event_sub *sub;
- pjsip_presentity *pres;
-
- if (expires < 0)
- expires = 300;
-
- /* Create event subscription */
- sub = pjsip_event_sub_create(endpt, local_url, remote_url, &PRESENCE_EVENT,
- expires,
- sizeof(accept_names)/sizeof(accept_names[0]),
- accept_names,
- NULL, &sub_cb);
- if (!sub)
- return NULL;
-
- /* Allocate presence descriptor. */
- pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres));
- pres->sub = sub;
- pres->user_data = user_data;
- sub->user_data = pres;
-
- return pres;
-}
-
-/*
- * Send SUBSCRIBE.
- */
-PJ_DEF(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres )
-{
- return pjsip_event_sub_subscribe( pres->sub );
-}
-
-/*
- * Set credentials to be used for outgoing requests.
- */
-PJ_DEF(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres,
- int count,
- const pjsip_cred_info cred[])
-{
- return pjsip_event_sub_set_credentials(pres->sub, count, cred);
-}
-
-/*
- * Set route-set.
- */
-PJ_DEF(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres,
- const pjsip_route_hdr *hdr )
-{
- return pjsip_event_sub_set_route_set( pres->sub, hdr );
-}
-
-/*
- * Unsubscribe.
- */
-PJ_DEF(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres )
-{
- return pjsip_event_sub_unsubscribe(pres->sub);
-}
-
-/*
- * This is the pjsip_msg_body callback to print XML body.
- */
-static int print_xml(pjsip_msg_body *body, char *buf, pj_size_t size)
-{
- return pj_xml_print( body->data, buf, size, PJ_TRUE );
-}
-
-/*
- * Create and initialize PIDF document and msg body (notifier only).
- */
-static pj_status_t init_presence_info( pjsip_presentity *pres )
-{
- pj_str_t uri;
- pj_pool_t *pool = pres->sub->pool;
- char tmp[PJSIP_MAX_URL_SIZE];
- pjpidf_tuple *tuple;
- const pjsip_media_type *content_type = NULL;
-
- pj_assert(pres->uas_body == NULL);
-
- /* Make entity_id */
- uri.ptr = tmp;
- uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->from->uri,
- tmp, sizeof(tmp));
- if (uri.slen < 0)
- return -1;
-
- if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) {
- pj_str_t s;
-
- /* Create <presence>. */
- pres->uas_data.pidf = pjpidf_create(pool, &s);
-
- /* Create <tuple> */
- pj_create_unique_string(pool, &s);
- tuple = pjpidf_pres_add_tuple(pool, pres->uas_data.pidf, &s);
-
- /* Set <contact> */
- s.ptr = tmp;
- s.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->contact->uri, tmp, sizeof(tmp));
- if (s.slen < 0)
- return -1;
- pjpidf_tuple_set_contact(pool, tuple, &s);
-
- /* Content-Type */
- content_type = &accept_types[PJSIP_PRES_TYPE_PIDF];
-
- } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) {
-
- /* Create XPIDF */
- pres->uas_data.xpidf = pjxpidf_create(pool, &uri);
-
- /* Content-Type. */
- content_type = &accept_types[PJSIP_PRES_TYPE_XPIDF];
- }
-
- /* Create message body */
- pres->uas_body = pj_pool_alloc(pool, sizeof(pjsip_msg_body));
- pres->uas_body->content_type = *content_type;
- pres->uas_body->data = pres->uas_data.pidf;
- pres->uas_body->len = 0;
- pres->uas_body->print_body = &print_xml;
-
- return 0;
-}
-
-/*
- * Send NOTIFY and set subscription state.
- */
-PJ_DEF(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres,
- pjsip_event_sub_state state,
- pj_bool_t is_online )
-{
- pj_str_t reason = { "", 0 };
-
- if (pres->uas_data.pidf == NULL) {
- if (init_presence_info(pres) != 0)
- return -1;
- }
-
- /* Update basic status in PIDF/XPIDF document. */
- if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) {
- pjpidf_tuple *first;
- pjpidf_status *status;
- pj_time_val now;
- pj_parsed_time pnow;
-
- first = pjpidf_op.pres.get_first_tuple(pres->uas_data.pidf);
- pj_assert(first);
- status = pjpidf_op.tuple.get_status(first);
- pj_assert(status);
- pjpidf_op.status.set_basic_open(status, is_online);
-
- /* Update timestamp. */
- if (pres->timestamp.ptr == 0) {
- pres->timestamp.ptr = pj_pool_alloc(pres->sub->pool, 24);
- }
- pj_gettimeofday(&now);
- pj_time_decode(&now, &pnow);
- pres->timestamp.slen = sprintf(pres->timestamp.ptr,
- "%04d-%02d-%02dT%02d:%02d:%02dZ",
- pnow.year, pnow.mon, pnow.day,
- pnow.hour, pnow.min, pnow.sec);
- pjpidf_op.tuple.set_timestamp_np(pres->sub->pool, first, &pres->timestamp);
-
- } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) {
- pjxpidf_set_status( pres->uas_data.xpidf, is_online );
-
- } else {
- pj_assert(0);
- }
-
- /* Send notify. */
- return pjsip_event_sub_notify( pres->sub, state, &reason, pres->uas_body);
-}
-
-/*
- * Destroy subscription (can be called for both subscriber and notifier).
- */
-PJ_DEF(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres )
-{
- return pjsip_event_sub_destroy(pres->sub);
-}
-
-/*
- * This callback is called by event framework to query whether we want to
- * accept an incoming subscription.
- */
-static void on_query_subscribe(pjsip_rx_data *rdata, int *status)
-{
- if (cb.accept_presence) {
- (*cb.accept_presence)(rdata, status);
- }
-}
-
-/*
- * This callback is called by event framework after we accept the incoming
- * subscription, to notify about the new subscription instance.
- */
-static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata,
- pjsip_event_sub_cb **set_sub_cb, int *expires)
-{
- pjsip_presentity *pres;
- pjsip_accept_hdr *accept;
-
- pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres));
- pres->sub = sub;
- pres->pres_type = PJSIP_PRES_TYPE_PIDF;
- sub->user_data = pres;
- *set_sub_cb = &sub_cb;
-
- accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL);
- if (accept) {
- unsigned i;
- int found = 0;
- for (i=0; i<accept->count && !found; ++i) {
- int j;
- for (j=0; j<sizeof(accept_names)/sizeof(accept_names[0]); ++j) {
- if (!pj_stricmp(&accept->values[i], &accept_names[j])) {
- pres->pres_type = j;
- found = 1;
- break;
- }
- }
- }
- pj_assert(found );
- }
-
- (*cb.on_received_request)(pres, rdata, expires);
-}
-
-/*
- * This callback is called by event framework when the subscription is
- * terminated.
- */
-static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason)
-{
- pjsip_presentity *pres = sub->user_data;
- if (cb.on_terminated)
- (*cb.on_terminated)(pres, reason);
-}
-
-/*
- * This callback is called by event framework when it receives incoming
- * SUBSCRIBE request to refresh the subscription.
- */
-static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata)
-{
- pjsip_presentity *pres = sub->user_data;
- if (cb.on_received_refresh)
- (*cb.on_received_refresh)(pres, rdata);
-}
-
-/*
- * This callback is called by event framework when it receives incoming
- * NOTIFY request.
- */
-static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata)
-{
- pjsip_presentity *pres = sub->user_data;
-
- if (cb.on_received_update) {
- pj_status_t is_open;
- pjsip_msg_body *body;
- int i;
-
- body = rdata->msg->body;
- if (!body)
- return;
-
- for (i=0; i<sizeof(accept_types)/sizeof(accept_types[0]); ++i) {
- if (!pj_stricmp(&body->content_type.type, &accept_types[i].type) &&
- !pj_stricmp(&body->content_type.subtype, &accept_types[i].subtype))
- {
- break;
- }
- }
-
- if (i==PJSIP_PRES_TYPE_PIDF) {
- pjpidf_pres *pres;
- pjpidf_tuple *tuple;
- pjpidf_status *status;
-
- pres = pjpidf_parse(rdata->pool, body->data, body->len);
- if (!pres)
- return;
- tuple = pjpidf_pres_get_first_tuple(pres);
- if (!tuple)
- return;
- status = pjpidf_tuple_get_status(tuple);
- if (!status)
- return;
- is_open = pjpidf_status_is_basic_open(status);
-
- } else if (i==PJSIP_PRES_TYPE_XPIDF) {
- pjxpidf_pres *pres;
-
- pres = pjxpidf_parse(rdata->pool, body->data, body->len);
- if (!pres)
- return;
- is_open = pjxpidf_get_status(pres);
-
- } else {
- return;
- }
-
- (*cb.on_received_update)(pres, is_open);
- }
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip_simple/presence.h> +#include <pjsip/sip_transport.h> +#include <pj/pool.h> +#include <pj/string.h> +#include <pj/guid.h> +#include <pj/os.h> +#include <stdio.h> + +/* Forward declarations. */ +static void on_query_subscribe(pjsip_rx_data *rdata, int *status); +static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata, + pjsip_event_sub_cb **cb, int *expires); +static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason); +static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata); +static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata); + +/* Some string constants. */ +static pj_str_t PRESENCE_EVENT = { "presence", 8 }; + +/* Accept types. */ +static pj_str_t accept_names[] = { + { "application/pidf+xml", 20 }, + { "application/xpidf+xml", 21 } +}; +static pjsip_media_type accept_types[] = { + { + { "application", 11 }, + { "pidf+xml", 8 } + }, + { + { "application", 11 }, + { "xpidf+xml", 9 } + } +}; + +/* Callback that is registered by application. */ +static pjsip_presence_cb cb; + +/* Package callback to be register to event_notify */ +static pjsip_event_sub_pkg_cb pkg_cb = { &on_query_subscribe, + &on_subscribe }; + +/* Global/static callback to be registered to event_notify */ +static pjsip_event_sub_cb sub_cb = { &on_sub_terminated, + &on_sub_received_refresh, + NULL, + &on_received_notify, + NULL }; + +/* + * Initialize presence module. + * This will register event package "presence" to event framework. + */ +PJ_DEF(void) pjsip_presence_init(const pjsip_presence_cb *pcb) +{ + pj_memcpy(&cb, pcb, sizeof(*pcb)); + pjsip_event_sub_register_pkg( &PRESENCE_EVENT, + sizeof(accept_names)/sizeof(accept_names[0]), + accept_names, + &pkg_cb); +} + +/* + * Create presence subscription. + */ +PJ_DEF(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt, + const pj_str_t *local_url, + const pj_str_t *remote_url, + int expires, + void *user_data ) +{ + pjsip_event_sub *sub; + pjsip_presentity *pres; + + if (expires < 0) + expires = 300; + + /* Create event subscription */ + sub = pjsip_event_sub_create(endpt, local_url, remote_url, &PRESENCE_EVENT, + expires, + sizeof(accept_names)/sizeof(accept_names[0]), + accept_names, + NULL, &sub_cb); + if (!sub) + return NULL; + + /* Allocate presence descriptor. */ + pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres)); + pres->sub = sub; + pres->user_data = user_data; + sub->user_data = pres; + + return pres; +} + +/* + * Send SUBSCRIBE. + */ +PJ_DEF(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres ) +{ + return pjsip_event_sub_subscribe( pres->sub ); +} + +/* + * Set credentials to be used for outgoing requests. + */ +PJ_DEF(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres, + int count, + const pjsip_cred_info cred[]) +{ + return pjsip_event_sub_set_credentials(pres->sub, count, cred); +} + +/* + * Set route-set. + */ +PJ_DEF(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres, + const pjsip_route_hdr *hdr ) +{ + return pjsip_event_sub_set_route_set( pres->sub, hdr ); +} + +/* + * Unsubscribe. + */ +PJ_DEF(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres ) +{ + return pjsip_event_sub_unsubscribe(pres->sub); +} + +/* + * This is the pjsip_msg_body callback to print XML body. + */ +static int print_xml(pjsip_msg_body *body, char *buf, pj_size_t size) +{ + return pj_xml_print( body->data, buf, size, PJ_TRUE ); +} + +/* + * Create and initialize PIDF document and msg body (notifier only). + */ +static pj_status_t init_presence_info( pjsip_presentity *pres ) +{ + pj_str_t uri; + pj_pool_t *pool = pres->sub->pool; + char tmp[PJSIP_MAX_URL_SIZE]; + pjpidf_tuple *tuple; + const pjsip_media_type *content_type = NULL; + + pj_assert(pres->uas_body == NULL); + + /* Make entity_id */ + uri.ptr = tmp; + uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->from->uri, + tmp, sizeof(tmp)); + if (uri.slen < 0) + return -1; + + if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) { + pj_str_t s; + + /* Create <presence>. */ + pres->uas_data.pidf = pjpidf_create(pool, &s); + + /* Create <tuple> */ + pj_create_unique_string(pool, &s); + tuple = pjpidf_pres_add_tuple(pool, pres->uas_data.pidf, &s); + + /* Set <contact> */ + s.ptr = tmp; + s.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->contact->uri, tmp, sizeof(tmp)); + if (s.slen < 0) + return -1; + pjpidf_tuple_set_contact(pool, tuple, &s); + + /* Content-Type */ + content_type = &accept_types[PJSIP_PRES_TYPE_PIDF]; + + } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) { + + /* Create XPIDF */ + pres->uas_data.xpidf = pjxpidf_create(pool, &uri); + + /* Content-Type. */ + content_type = &accept_types[PJSIP_PRES_TYPE_XPIDF]; + } + + /* Create message body */ + pres->uas_body = pj_pool_alloc(pool, sizeof(pjsip_msg_body)); + pres->uas_body->content_type = *content_type; + pres->uas_body->data = pres->uas_data.pidf; + pres->uas_body->len = 0; + pres->uas_body->print_body = &print_xml; + + return 0; +} + +/* + * Send NOTIFY and set subscription state. + */ +PJ_DEF(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres, + pjsip_event_sub_state state, + pj_bool_t is_online ) +{ + pj_str_t reason = { "", 0 }; + + if (pres->uas_data.pidf == NULL) { + if (init_presence_info(pres) != 0) + return -1; + } + + /* Update basic status in PIDF/XPIDF document. */ + if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) { + pjpidf_tuple *first; + pjpidf_status *status; + pj_time_val now; + pj_parsed_time pnow; + + first = pjpidf_op.pres.get_first_tuple(pres->uas_data.pidf); + pj_assert(first); + status = pjpidf_op.tuple.get_status(first); + pj_assert(status); + pjpidf_op.status.set_basic_open(status, is_online); + + /* Update timestamp. */ + if (pres->timestamp.ptr == 0) { + pres->timestamp.ptr = pj_pool_alloc(pres->sub->pool, 24); + } + pj_gettimeofday(&now); + pj_time_decode(&now, &pnow); + pres->timestamp.slen = sprintf(pres->timestamp.ptr, + "%04d-%02d-%02dT%02d:%02d:%02dZ", + pnow.year, pnow.mon, pnow.day, + pnow.hour, pnow.min, pnow.sec); + pjpidf_op.tuple.set_timestamp_np(pres->sub->pool, first, &pres->timestamp); + + } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) { + pjxpidf_set_status( pres->uas_data.xpidf, is_online ); + + } else { + pj_assert(0); + } + + /* Send notify. */ + return pjsip_event_sub_notify( pres->sub, state, &reason, pres->uas_body); +} + +/* + * Destroy subscription (can be called for both subscriber and notifier). + */ +PJ_DEF(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres ) +{ + return pjsip_event_sub_destroy(pres->sub); +} + +/* + * This callback is called by event framework to query whether we want to + * accept an incoming subscription. + */ +static void on_query_subscribe(pjsip_rx_data *rdata, int *status) +{ + if (cb.accept_presence) { + (*cb.accept_presence)(rdata, status); + } +} + +/* + * This callback is called by event framework after we accept the incoming + * subscription, to notify about the new subscription instance. + */ +static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata, + pjsip_event_sub_cb **set_sub_cb, int *expires) +{ + pjsip_presentity *pres; + pjsip_accept_hdr *accept; + + pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres)); + pres->sub = sub; + pres->pres_type = PJSIP_PRES_TYPE_PIDF; + sub->user_data = pres; + *set_sub_cb = &sub_cb; + + accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL); + if (accept) { + unsigned i; + int found = 0; + for (i=0; i<accept->count && !found; ++i) { + int j; + for (j=0; j<sizeof(accept_names)/sizeof(accept_names[0]); ++j) { + if (!pj_stricmp(&accept->values[i], &accept_names[j])) { + pres->pres_type = j; + found = 1; + break; + } + } + } + pj_assert(found ); + } + + (*cb.on_received_request)(pres, rdata, expires); +} + +/* + * This callback is called by event framework when the subscription is + * terminated. + */ +static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason) +{ + pjsip_presentity *pres = sub->user_data; + if (cb.on_terminated) + (*cb.on_terminated)(pres, reason); +} + +/* + * This callback is called by event framework when it receives incoming + * SUBSCRIBE request to refresh the subscription. + */ +static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata) +{ + pjsip_presentity *pres = sub->user_data; + if (cb.on_received_refresh) + (*cb.on_received_refresh)(pres, rdata); +} + +/* + * This callback is called by event framework when it receives incoming + * NOTIFY request. + */ +static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata) +{ + pjsip_presentity *pres = sub->user_data; + + if (cb.on_received_update) { + pj_status_t is_open; + pjsip_msg_body *body; + int i; + + body = rdata->msg->body; + if (!body) + return; + + for (i=0; i<sizeof(accept_types)/sizeof(accept_types[0]); ++i) { + if (!pj_stricmp(&body->content_type.type, &accept_types[i].type) && + !pj_stricmp(&body->content_type.subtype, &accept_types[i].subtype)) + { + break; + } + } + + if (i==PJSIP_PRES_TYPE_PIDF) { + pjpidf_pres *pres; + pjpidf_tuple *tuple; + pjpidf_status *status; + + pres = pjpidf_parse(rdata->pool, body->data, body->len); + if (!pres) + return; + tuple = pjpidf_pres_get_first_tuple(pres); + if (!tuple) + return; + status = pjpidf_tuple_get_status(tuple); + if (!status) + return; + is_open = pjpidf_status_is_basic_open(status); + + } else if (i==PJSIP_PRES_TYPE_XPIDF) { + pjxpidf_pres *pres; + + pres = pjxpidf_parse(rdata->pool, body->data, body->len); + if (!pres) + return; + is_open = pjxpidf_get_status(pres); + + } else { + return; + } + + (*cb.on_received_update)(pres, is_open); + } +} + diff --git a/pjsip/src/pjsip-simple/xpidf.c b/pjsip/src/pjsip-simple/xpidf.c index 8717466f..7cc377ba 100644 --- a/pjsip/src/pjsip-simple/xpidf.c +++ b/pjsip/src/pjsip-simple/xpidf.c @@ -1,294 +1,294 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_simple/xpidf.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/guid.h>
-
-static pj_str_t PRESENCE = { "presence", 8 };
-static pj_str_t STATUS = { "status", 6 };
-static pj_str_t OPEN = { "open", 4 };
-static pj_str_t CLOSED = { "closed", 6 };
-static pj_str_t URI = { "uri", 3 };
-static pj_str_t ATOM = { "atom", 4 };
-static pj_str_t ATOMID = { "atomid", 6 };
-static pj_str_t ADDRESS = { "address", 7 };
-static pj_str_t SUBSCRIBE_PARAM = { ";method=SUBSCRIBE", 17 };
-static pj_str_t PRESENTITY = { "presentity", 10 };
-static pj_str_t EMPTY_STRING = { NULL, 0 };
-
-static pj_xml_node* xml_create_node(pj_pool_t *pool,
- pj_str_t *name, const pj_str_t *value)
-{
- pj_xml_node *node;
-
- node = pj_pool_alloc(pool, sizeof(pj_xml_node));
- pj_list_init(&node->attr_head);
- pj_list_init(&node->node_head);
- node->name = *name;
- if (value) pj_strdup(pool, &node->content, value);
- else node->content.ptr=NULL, node->content.slen=0;
-
- return node;
-}
-
-static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name,
- const pj_str_t *value)
-{
- pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr));
- attr->name = *name;
- pj_strdup(pool, &attr->value, value);
- return attr;
-}
-
-
-PJ_DEF(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri_cstr)
-{
- pjxpidf_pres *pres;
- pj_xml_node *presentity;
- pj_xml_node *atom;
- pj_xml_node *addr;
- pj_xml_node *status;
- pj_xml_attr *attr;
- pj_str_t uri;
- pj_str_t tmp;
-
- /* <presence> */
- pres = xml_create_node(pool, &PRESENCE, NULL);
-
- /* <presentity> */
- presentity = xml_create_node(pool, &PRESENTITY, NULL);
- pj_xml_add_node(pres, presentity);
-
- /* uri attribute */
- uri.ptr = pj_pool_alloc(pool, uri_cstr->slen + SUBSCRIBE_PARAM.slen);
- pj_strcpy( &uri, uri_cstr);
- pj_strcat( &uri, &SUBSCRIBE_PARAM);
- attr = xml_create_attr(pool, &URI, &uri);
- pj_xml_add_attr(presentity, attr);
-
- /* <atom> */
- atom = xml_create_node(pool, &ATOM, NULL);
- pj_xml_add_node(pres, atom);
-
- /* atom id */
- pj_create_unique_string(pool, &tmp);
- attr = xml_create_attr(pool, &ATOMID, &tmp);
- pj_xml_add_attr(atom, attr);
-
- /* address */
- addr = xml_create_node(pool, &ADDRESS, NULL);
- pj_xml_add_node(atom, addr);
-
- /* address'es uri */
- attr = xml_create_attr(pool, &URI, uri_cstr);
- pj_xml_add_attr(addr, attr);
-
- /* status */
- status = xml_create_node(pool, &STATUS, NULL);
- pj_xml_add_node(addr, status);
-
- /* status attr */
- attr = xml_create_attr(pool, &STATUS, &OPEN);
- pj_xml_add_attr(status, attr);
-
- return pres;
-}
-
-
-
-PJ_DEF(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len)
-{
- pjxpidf_pres *pres;
- pj_xml_node *node;
-
- pres = pj_xml_parse(pool, text, len);
- if (!pres)
- return NULL;
-
- /* Validate <presence> */
- if (pj_stricmp(&pres->name, &PRESENCE) != 0)
- return NULL;
- if (pj_xml_find_attr(pres, &URI, NULL) == NULL)
- return NULL;
-
- /* Validate <presentity> */
- node = pj_xml_find_node(pres, &PRESENTITY);
- if (node == NULL)
- return NULL;
-
- /* Validate <atom> */
- node = pj_xml_find_node(pres, &ATOM);
- if (node == NULL)
- return NULL;
- if (pj_xml_find_attr(node, &ATOMID, NULL) == NULL)
- return NULL;
-
- /* Address */
- node = pj_xml_find_node(node, &ADDRESS);
- if (node == NULL)
- return NULL;
- if (pj_xml_find_attr(node, &URI, NULL) == NULL)
- return NULL;
-
-
- /* Status */
- node = pj_xml_find_node(node, &STATUS);
- if (node == NULL)
- return NULL;
- if (pj_xml_find_attr(node, &STATUS, NULL) == NULL)
- return NULL;
-
- return pres;
-}
-
-
-PJ_DEF(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len)
-{
- return pj_xml_print(pres, text, len, PJ_TRUE);
-}
-
-
-PJ_DEF(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres)
-{
- pj_xml_node *presentity;
- pj_xml_attr *attr;
-
- presentity = pj_xml_find_node(pres, &PRESENTITY);
- if (!presentity)
- return &EMPTY_STRING;
-
- attr = pj_xml_find_attr(presentity, &URI, NULL);
- if (!attr)
- return &EMPTY_STRING;
-
- return &attr->value;
-}
-
-
-PJ_DEF(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres,
- const pj_str_t *uri)
-{
- pj_xml_node *presentity;
- pj_xml_node *atom;
- pj_xml_node *addr;
- pj_xml_attr *attr;
- pj_str_t dup_uri;
-
- presentity = pj_xml_find_node(pres, &PRESENTITY);
- if (!presentity) {
- pj_assert(0);
- return -1;
- }
- atom = pj_xml_find_node(pres, &ATOM);
- if (!atom) {
- pj_assert(0);
- return -1;
- }
- addr = pj_xml_find_node(atom, &ADDRESS);
- if (!addr) {
- pj_assert(0);
- return -1;
- }
-
- /* Set uri in presentity */
- attr = pj_xml_find_attr(presentity, &URI, NULL);
- if (!attr) {
- pj_assert(0);
- return -1;
- }
- pj_strdup(pool, &dup_uri, uri);
- attr->value = dup_uri;
-
- /* Set uri in address. */
- attr = pj_xml_find_attr(addr, &URI, NULL);
- if (!attr) {
- pj_assert(0);
- return -1;
- }
- attr->value = dup_uri;
-
- return 0;
-}
-
-
-PJ_DEF(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres)
-{
- pj_xml_node *atom;
- pj_xml_node *addr;
- pj_xml_node *status;
- pj_xml_attr *attr;
-
- atom = pj_xml_find_node(pres, &ATOM);
- if (!atom) {
- pj_assert(0);
- return PJ_FALSE;
- }
- addr = pj_xml_find_node(atom, &ADDRESS);
- if (!addr) {
- pj_assert(0);
- return PJ_FALSE;
- }
- status = pj_xml_find_node(atom, &STATUS);
- if (!status) {
- pj_assert(0);
- return PJ_FALSE;
- }
- attr = pj_xml_find_attr(status, &STATUS, NULL);
- if (!attr) {
- pj_assert(0);
- return PJ_FALSE;
- }
-
- return pj_stricmp(&attr->value, &OPEN) ? PJ_TRUE : PJ_FALSE;
-}
-
-
-PJ_DEF(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t online_status)
-{
- pj_xml_node *atom;
- pj_xml_node *addr;
- pj_xml_node *status;
- pj_xml_attr *attr;
-
- atom = pj_xml_find_node(pres, &ATOM);
- if (!atom) {
- pj_assert(0);
- return -1;
- }
- addr = pj_xml_find_node(atom, &ADDRESS);
- if (!addr) {
- pj_assert(0);
- return -1;
- }
- status = pj_xml_find_node(addr, &STATUS);
- if (!status) {
- pj_assert(0);
- return -1;
- }
- attr = pj_xml_find_attr(status, &STATUS, NULL);
- if (!attr) {
- pj_assert(0);
- return -1;
- }
-
- attr->value = ( online_status ? OPEN : CLOSED );
- return 0;
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip_simple/xpidf.h> +#include <pj/pool.h> +#include <pj/string.h> +#include <pj/guid.h> + +static pj_str_t PRESENCE = { "presence", 8 }; +static pj_str_t STATUS = { "status", 6 }; +static pj_str_t OPEN = { "open", 4 }; +static pj_str_t CLOSED = { "closed", 6 }; +static pj_str_t URI = { "uri", 3 }; +static pj_str_t ATOM = { "atom", 4 }; +static pj_str_t ATOMID = { "atomid", 6 }; +static pj_str_t ADDRESS = { "address", 7 }; +static pj_str_t SUBSCRIBE_PARAM = { ";method=SUBSCRIBE", 17 }; +static pj_str_t PRESENTITY = { "presentity", 10 }; +static pj_str_t EMPTY_STRING = { NULL, 0 }; + +static pj_xml_node* xml_create_node(pj_pool_t *pool, + pj_str_t *name, const pj_str_t *value) +{ + pj_xml_node *node; + + node = pj_pool_alloc(pool, sizeof(pj_xml_node)); + pj_list_init(&node->attr_head); + pj_list_init(&node->node_head); + node->name = *name; + if (value) pj_strdup(pool, &node->content, value); + else node->content.ptr=NULL, node->content.slen=0; + + return node; +} + +static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name, + const pj_str_t *value) +{ + pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr)); + attr->name = *name; + pj_strdup(pool, &attr->value, value); + return attr; +} + + +PJ_DEF(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri_cstr) +{ + pjxpidf_pres *pres; + pj_xml_node *presentity; + pj_xml_node *atom; + pj_xml_node *addr; + pj_xml_node *status; + pj_xml_attr *attr; + pj_str_t uri; + pj_str_t tmp; + + /* <presence> */ + pres = xml_create_node(pool, &PRESENCE, NULL); + + /* <presentity> */ + presentity = xml_create_node(pool, &PRESENTITY, NULL); + pj_xml_add_node(pres, presentity); + + /* uri attribute */ + uri.ptr = pj_pool_alloc(pool, uri_cstr->slen + SUBSCRIBE_PARAM.slen); + pj_strcpy( &uri, uri_cstr); + pj_strcat( &uri, &SUBSCRIBE_PARAM); + attr = xml_create_attr(pool, &URI, &uri); + pj_xml_add_attr(presentity, attr); + + /* <atom> */ + atom = xml_create_node(pool, &ATOM, NULL); + pj_xml_add_node(pres, atom); + + /* atom id */ + pj_create_unique_string(pool, &tmp); + attr = xml_create_attr(pool, &ATOMID, &tmp); + pj_xml_add_attr(atom, attr); + + /* address */ + addr = xml_create_node(pool, &ADDRESS, NULL); + pj_xml_add_node(atom, addr); + + /* address'es uri */ + attr = xml_create_attr(pool, &URI, uri_cstr); + pj_xml_add_attr(addr, attr); + + /* status */ + status = xml_create_node(pool, &STATUS, NULL); + pj_xml_add_node(addr, status); + + /* status attr */ + attr = xml_create_attr(pool, &STATUS, &OPEN); + pj_xml_add_attr(status, attr); + + return pres; +} + + + +PJ_DEF(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len) +{ + pjxpidf_pres *pres; + pj_xml_node *node; + + pres = pj_xml_parse(pool, text, len); + if (!pres) + return NULL; + + /* Validate <presence> */ + if (pj_stricmp(&pres->name, &PRESENCE) != 0) + return NULL; + if (pj_xml_find_attr(pres, &URI, NULL) == NULL) + return NULL; + + /* Validate <presentity> */ + node = pj_xml_find_node(pres, &PRESENTITY); + if (node == NULL) + return NULL; + + /* Validate <atom> */ + node = pj_xml_find_node(pres, &ATOM); + if (node == NULL) + return NULL; + if (pj_xml_find_attr(node, &ATOMID, NULL) == NULL) + return NULL; + + /* Address */ + node = pj_xml_find_node(node, &ADDRESS); + if (node == NULL) + return NULL; + if (pj_xml_find_attr(node, &URI, NULL) == NULL) + return NULL; + + + /* Status */ + node = pj_xml_find_node(node, &STATUS); + if (node == NULL) + return NULL; + if (pj_xml_find_attr(node, &STATUS, NULL) == NULL) + return NULL; + + return pres; +} + + +PJ_DEF(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len) +{ + return pj_xml_print(pres, text, len, PJ_TRUE); +} + + +PJ_DEF(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres) +{ + pj_xml_node *presentity; + pj_xml_attr *attr; + + presentity = pj_xml_find_node(pres, &PRESENTITY); + if (!presentity) + return &EMPTY_STRING; + + attr = pj_xml_find_attr(presentity, &URI, NULL); + if (!attr) + return &EMPTY_STRING; + + return &attr->value; +} + + +PJ_DEF(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres, + const pj_str_t *uri) +{ + pj_xml_node *presentity; + pj_xml_node *atom; + pj_xml_node *addr; + pj_xml_attr *attr; + pj_str_t dup_uri; + + presentity = pj_xml_find_node(pres, &PRESENTITY); + if (!presentity) { + pj_assert(0); + return -1; + } + atom = pj_xml_find_node(pres, &ATOM); + if (!atom) { + pj_assert(0); + return -1; + } + addr = pj_xml_find_node(atom, &ADDRESS); + if (!addr) { + pj_assert(0); + return -1; + } + + /* Set uri in presentity */ + attr = pj_xml_find_attr(presentity, &URI, NULL); + if (!attr) { + pj_assert(0); + return -1; + } + pj_strdup(pool, &dup_uri, uri); + attr->value = dup_uri; + + /* Set uri in address. */ + attr = pj_xml_find_attr(addr, &URI, NULL); + if (!attr) { + pj_assert(0); + return -1; + } + attr->value = dup_uri; + + return 0; +} + + +PJ_DEF(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres) +{ + pj_xml_node *atom; + pj_xml_node *addr; + pj_xml_node *status; + pj_xml_attr *attr; + + atom = pj_xml_find_node(pres, &ATOM); + if (!atom) { + pj_assert(0); + return PJ_FALSE; + } + addr = pj_xml_find_node(atom, &ADDRESS); + if (!addr) { + pj_assert(0); + return PJ_FALSE; + } + status = pj_xml_find_node(atom, &STATUS); + if (!status) { + pj_assert(0); + return PJ_FALSE; + } + attr = pj_xml_find_attr(status, &STATUS, NULL); + if (!attr) { + pj_assert(0); + return PJ_FALSE; + } + + return pj_stricmp(&attr->value, &OPEN) ? PJ_TRUE : PJ_FALSE; +} + + +PJ_DEF(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t online_status) +{ + pj_xml_node *atom; + pj_xml_node *addr; + pj_xml_node *status; + pj_xml_attr *attr; + + atom = pj_xml_find_node(pres, &ATOM); + if (!atom) { + pj_assert(0); + return -1; + } + addr = pj_xml_find_node(atom, &ADDRESS); + if (!addr) { + pj_assert(0); + return -1; + } + status = pj_xml_find_node(addr, &STATUS); + if (!status) { + pj_assert(0); + return -1; + } + attr = pj_xml_find_attr(status, &STATUS, NULL); + if (!attr) { + pj_assert(0); + return -1; + } + + attr->value = ( online_status ? OPEN : CLOSED ); + return 0; +} + diff --git a/pjsip/src/pjsip-ua/sip_dialog.c b/pjsip/src/pjsip-ua/sip_dialog.c index ac110412..bb4861b8 100644 --- a/pjsip/src/pjsip-ua/sip_dialog.c +++ b/pjsip/src/pjsip-ua/sip_dialog.c @@ -1,1802 +1,1802 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_mod_ua/sip_dialog.h>
-#include <pjsip_mod_ua/sip_ua.h>
-#include <pjsip_mod_ua/sip_ua_private.h>
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_types.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_uri.h>
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_parser.h>
-#include <pj/string.h>
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/guid.h>
-#include <pj/except.h>
-#include <pj/pool.h>
-
-/* TLS to keep dialog lock record (initialized by UA) */
-int pjsip_dlg_lock_tls_id;
-
-struct dialog_lock_data
-{
- struct dialog_lock_data *prev;
- pjsip_dlg *dlg;
- int is_alive;
-};
-
-/*
- * Static function prototypes.
- */
-static void dlg_create_request_throw( pjsip_tx_data **p_tdata,
- pjsip_dlg *dlg,
- const pjsip_method *method,
- int cseq );
-static int dlg_on_all_state_pre( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_all_state_post( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_null( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_incoming( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_calling( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_proceeding( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_connecting( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_established( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_disconnected( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_terminated( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-
-/*
- * Dialog state handlers.
- */
-static int (*dlg_state_handlers[])(struct pjsip_dlg *, pjsip_transaction *,
- pjsip_event *) =
-{
- &dlg_on_state_null,
- &dlg_on_state_incoming,
- &dlg_on_state_calling,
- &dlg_on_state_proceeding,
- &dlg_on_state_connecting,
- &dlg_on_state_established,
- &dlg_on_state_disconnected,
- &dlg_on_state_terminated,
-};
-
-/*
- * Dialog state names.
- */
-static const char* dlg_state_names[] =
-{
- "STATE_NULL",
- "STATE_INCOMING",
- "STATE_CALLING",
- "STATE_PROCEEDING",
- "STATE_CONNECTING",
- "STATE_ESTABLISHED",
- "STATE_DISCONNECTED",
- "STATE_TERMINATED",
-};
-
-
-/*
- * Get the dialog string state, normally for logging purpose.
- */
-const char *pjsip_dlg_state_str(pjsip_dlg_state_e state)
-{
- return dlg_state_names[state];
-}
-
-/* Lock dialog mutex. */
-static void lock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck)
-{
- struct dialog_lock_data *prev;
-
- pj_mutex_lock(dlg->mutex);
- prev = pj_thread_local_get(pjsip_dlg_lock_tls_id);
- lck->prev = prev;
- lck->dlg = dlg;
- lck->is_alive = 1;
- pj_thread_local_set(pjsip_dlg_lock_tls_id, lck);
-}
-
-/* Carefully unlock dialog mutex, protect against situation when the dialog
- * has already been destroyed.
- */
-static pj_status_t unlock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck)
-{
- pj_assert(pj_thread_local_get(pjsip_dlg_lock_tls_id) == lck);
- pj_assert(dlg == lck->dlg);
-
- pj_thread_local_set(pjsip_dlg_lock_tls_id, lck->prev);
- if (lck->is_alive)
- pj_mutex_unlock(dlg->mutex);
-
- return lck->is_alive ? 0 : -1;
-}
-
-/*
- * This is called by dialog's FSM to change dialog's state.
- */
-static void dlg_set_state( pjsip_dlg *dlg, pjsip_dlg_state_e state,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(event);
-
- PJ_LOG(4, (dlg->obj_name, "State %s-->%s (ev=%s, src=%s, data=%p)",
- pjsip_dlg_state_str(dlg->state), pjsip_dlg_state_str(state),
- event ? pjsip_event_str(event->type) : "",
- event ? pjsip_event_str(event->src_type) : "",
- event ? event->src.data : NULL));
-
- dlg->state = state;
- dlg->handle_tsx_event = dlg_state_handlers[state];
-}
-
-/*
- * Invoke dialog's callback.
- * This function is called by dialog's FSM, and interpret the event and call
- * the corresponding callback registered by application.
- */
-static void dlg_call_dlg_callback( pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event,
- pjsip_event *event )
-{
- pjsip_dlg_callback *cb = dlg->ua->dlg_cb;
- if (!cb) {
- PJ_LOG(4,(dlg->obj_name, "Can not call callback (none registered)."));
- return;
- }
-
- /* Low level event: call the all-events callback. */
- if (cb->on_all_events) {
- (*cb->on_all_events)(dlg, dlg_event, event);
- }
-
- /* Low level event: call the tx/rx callback if this is a tx/rx event. */
- if (event->type == PJSIP_EVENT_BEFORE_TX && cb->on_before_tx)
- {
- (*cb->on_before_tx)(dlg, event->obj.tsx, event->src.tdata, event->data.long_data);
- }
- else if (event->type == PJSIP_EVENT_TX_MSG &&
- event->src_type == PJSIP_EVENT_TX_MSG && cb->on_tx_msg)
- {
- (*cb->on_tx_msg)(dlg, event->obj.tsx, event->src.tdata);
- }
- else if (event->type == PJSIP_EVENT_RX_MSG &&
- event->src_type == PJSIP_EVENT_RX_MSG && cb->on_rx_msg) {
- (*cb->on_rx_msg)(dlg, event->obj.tsx, event->src.rdata);
- }
-
- /* Now trigger high level events.
- * High level event should only occurs when dialog's state has changed,
- * except for on_provisional, which may be called multiple times whenever
- * response message is sent
- */
- if (dlg->state == PJSIP_DIALOG_STATE_PROCEEDING &&
- (event->type== PJSIP_EVENT_TSX_STATE_CHANGED) &&
- event->obj.tsx == dlg->invite_tsx)
- {
- /* Sent/received provisional responses. */
- if (cb->on_provisional)
- (*cb->on_provisional)(dlg, event->obj.tsx, event);
- }
-
- if (dlg_event == PJSIP_DIALOG_EVENT_MID_CALL_REQUEST) {
- if (cb->on_mid_call_events)
- (*cb->on_mid_call_events)(dlg, event);
- return;
- }
-
- if (dlg_event != PJSIP_DIALOG_EVENT_STATE_CHANGED)
- return;
-
- if (dlg->state == PJSIP_DIALOG_STATE_INCOMING) {
-
- /* New incoming dialog. */
- pj_assert (event->src_type == PJSIP_EVENT_RX_MSG);
- (*cb->on_incoming)(dlg, event->obj.tsx, event->src.rdata);
-
- } else if (dlg->state == PJSIP_DIALOG_STATE_CALLING) {
-
- /* Dialog has just sent the first INVITE. */
- if (cb->on_calling) {
- (*cb->on_calling)(dlg, event->obj.tsx, event->src.tdata);
- }
-
- } else if (dlg->state == PJSIP_DIALOG_STATE_DISCONNECTED) {
-
- if (cb->on_disconnected)
- (*cb->on_disconnected)(dlg, event);
-
- } else if (dlg->state == PJSIP_DIALOG_STATE_TERMINATED) {
-
- if (cb->on_terminated)
- (*cb->on_terminated)(dlg);
-
- pjsip_ua_destroy_dialog(dlg);
-
- } else if (dlg->state == PJSIP_DIALOG_STATE_CONNECTING) {
-
- if (cb->on_connecting)
- (*cb->on_connecting)(dlg, event);
-
- } else if (dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED) {
-
- if (cb->on_established)
- (*cb->on_established)(dlg, event);
- }
-}
-
-/*
- * This callback receives event from the transaction layer (via User Agent),
- * or from dialog timer (for 200/INVITE or ACK retransmission).
- */
-void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- int status = 0;
- struct dialog_lock_data lck;
-
- PJ_LOG(4, (dlg->obj_name, "state=%s (evt=%s, src=%s)",
- pjsip_dlg_state_str(dlg->state),
- pjsip_event_str(event->type),
- pjsip_event_str(event->src_type)));
-
-
- lock_dialog(dlg, &lck);
-
- status = dlg_on_all_state_pre( dlg, tsx, event);
-
- if (status==0) {
- status = (*dlg->handle_tsx_event)(dlg, tsx, event);
- }
- if (status==0) {
- status = dlg_on_all_state_post( dlg, tsx, event);
- }
-
- unlock_dialog(dlg, &lck);
-}
-
-/*
- * This function contains common processing in all states, and it is called
- * before the FSM is invoked.
- */
-static int dlg_on_all_state_pre( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(event)
-
- if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED)
- return 0;
-
- if (tsx && (tsx->state==PJSIP_TSX_STATE_CALLING ||
- tsx->state==PJSIP_TSX_STATE_TRYING))
- {
- ++dlg->pending_tsx_count;
-
- } else if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED)
- {
- --dlg->pending_tsx_count;
- if (tsx == dlg->invite_tsx)
- dlg->invite_tsx = NULL;
- }
-
- if (tsx->method.id == PJSIP_INVITE_METHOD) {
- tsx->handle_ack = 1;
- }
- return 0;
-}
-
-
-/*
- * This function contains common processing in all states, and it is called
- * after the FSM is invoked.
- */
-static int dlg_on_all_state_post( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(event)
-
- if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED) {
- if (dlg->pending_tsx_count == 0 &&
- dlg->state != PJSIP_DIALOG_STATE_CONNECTING &&
- dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED &&
- dlg->state != PJSIP_DIALOG_STATE_TERMINATED)
- {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- return -1;
- }
- }
-
- return 0;
-}
-
-
-/*
- * Internal function to initialize dialog, contains common initialization
- * for both UAS and UAC dialog.
- */
-static pj_status_t dlg_init( pjsip_dlg *dlg )
-{
- /* Init mutex. */
- dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0);
- if (!dlg->mutex) {
- PJ_PERROR((dlg->obj_name, "pj_mutex_create()"));
- return -1;
- }
-
- /* Init route-set (Initially empty) */
- pj_list_init(&dlg->route_set);
-
- /* Init auth credential list. */
- pj_list_init(&dlg->auth_sess);
-
- return PJ_SUCCESS;
-}
-
-/*
- * This one is called just before dialog is destroyed.
- * It is called while mutex is held.
- */
-PJ_DEF(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg )
-{
- struct dialog_lock_data *lck;
-
- //PJ_TODO(CHECK_THIS);
- pj_assert(dlg->pending_tsx_count == 0);
-
- /* Mark dialog as dead. */
- lck = pj_thread_local_get(pjsip_dlg_lock_tls_id);
- while (lck) {
- if (lck->dlg == dlg)
- lck->is_alive = 0;
- lck = lck->prev;
- }
-}
-
-/*
- * Initialize dialog from the received request.
- * This is an internal function which is called by the User Agent (sip_ua.c),
- * and it will initialize most of dialog's properties with values from the
- * received message.
- */
-pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg, pjsip_rx_data *rdata )
-{
- pjsip_msg *msg = rdata->msg;
- pjsip_to_hdr *to;
- pjsip_contact_hdr *contact;
- pjsip_name_addr *name_addr;
- pjsip_url *url;
- unsigned flag;
- pjsip_event event;
-
- pj_assert(dlg && rdata);
-
- PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p)", rdata));
-
- /* Must be an INVITE request. */
- pj_assert(msg->type == PJSIP_REQUEST_MSG &&
- msg->line.req.method.id == PJSIP_INVITE_METHOD);
-
- /* Init general dialog data. */
- if (dlg_init(dlg) != PJ_SUCCESS) {
- return -1;
- }
-
- /* Get the To header. */
- to = rdata->to;
-
- /* Copy URI in the To header as our local URI. */
- dlg->local.info = pjsip_hdr_clone( dlg->pool, to);
-
- /* Set tag in the local info. */
- dlg->local.info->tag = dlg->local.tag;
-
- /* Create local Contact to be advertised in the response.
- * At the moment, just copy URI from the local URI as our contact.
- */
- dlg->local.contact = pjsip_contact_hdr_create( dlg->pool );
- dlg->local.contact->star = 0;
- name_addr = (pjsip_name_addr *)dlg->local.info->uri;
- dlg->local.contact->uri = (pjsip_uri*) name_addr;
- url = (pjsip_url*) name_addr->uri;
- //url->port = rdata->via->sent_by.port;
- //url->port = pj_sockaddr_get_port( pjsip_transport_get_local_addr(rdata->transport) );
-
- /* Save remote URI. */
- dlg->remote.info = pjsip_hdr_clone( dlg->pool, rdata->from );
- pjsip_fromto_set_to( dlg->remote.info );
- pj_strdup( dlg->pool, &dlg->remote.tag, &rdata->from->tag );
-
- /* Save remote Contact. */
- contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
- if (contact) {
- dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact );
- } else {
- PJ_LOG(3,(dlg->obj_name, "No Contact header in INVITE from %s",
- pj_sockaddr_get_str_addr(&rdata->addr)));
- dlg->remote.contact = pjsip_contact_hdr_create( dlg->pool );
- dlg->remote.contact->uri = dlg->remote.info->uri;
- }
-
- /* Save Call-ID. */
- dlg->call_id = pjsip_cid_hdr_create(dlg->pool);
- pj_strdup( dlg->pool, &dlg->call_id->id, &rdata->call_id );
-
- /* Initialize local CSeq and save remote CSeq.*/
- dlg->local.cseq = rdata->timestamp.sec & 0xFFFF;
- dlg->remote.cseq = rdata->cseq->cseq;
-
- /* Secure? */
- flag = pjsip_transport_get_flag(rdata->transport);
- dlg->secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
-
- /* Initial state is NULL. */
- event.type = event.src_type = PJSIP_EVENT_RX_MSG;
- event.src.rdata = rdata;
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event);
-
- PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p) complete", rdata));
- return PJ_SUCCESS;
-}
-
-/*
- * Set the contact details.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg,
- const pj_str_t *contact )
-{
- pjsip_uri *local_uri;
- pj_str_t tmp;
-
- pj_strdup_with_null(dlg->pool, &tmp, contact);
- local_uri = pjsip_parse_uri( dlg->pool, tmp.ptr, tmp.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (local_uri == NULL) {
- PJ_LOG(2, (dlg->obj_name, "set_contact: invalid URI"));
- return -1;
- }
-
- dlg->local.contact->star = 0;
- dlg->local.contact->uri = local_uri;
- return 0;
-}
-
-/*
- * Set route set.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg,
- const pjsip_route_hdr *route_set )
-{
- pjsip_route_hdr *hdr;
-
- pj_list_init(&dlg->route_set);
- hdr = route_set->next;
- while (hdr != route_set) {
- pjsip_route_hdr *cloned = pjsip_hdr_clone(dlg->pool, hdr);
- pj_list_insert_before( &dlg->route_set, cloned);
- hdr = hdr->next;
- }
- return 0;
-}
-
-/*
- * Set route set without cloning the header.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg,
- pjsip_route_hdr *route_set)
-{
- pjsip_route_hdr *hdr;
-
- pj_list_init(&dlg->route_set);
- hdr = route_set->next;
- while (hdr != route_set) {
- pj_list_insert_before( &dlg->route_set, hdr);
- hdr = hdr->next;
- }
- return 0;
-}
-
-/*
- * Application calls this function when it wants to initiate an outgoing
- * dialog (incoming dialogs are created automatically by UA when it receives
- * INVITE, by calling pjsip_dlg_init_from_rdata()).
- * This function should initialize most of the dialog's properties.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg,
- const pj_str_t *c_local_info,
- const pj_str_t *c_remote_info,
- const pj_str_t *c_target)
-{
- pj_time_val tv;
- pjsip_event event;
- pj_str_t buf;
-
- if (!dlg || !c_local_info || !c_remote_info) {
- pj_assert(dlg && c_local_info && c_remote_info);
- return -1;
- }
-
- PJ_LOG(5, (dlg->obj_name, "initializing"));
-
- /* Init general dialog */
- if (dlg_init(dlg) != PJ_SUCCESS) {
- return -1;
- }
-
- /* Duplicate local info. */
- pj_strdup_with_null( dlg->pool, &buf, c_local_info);
-
- /* Build local URI. */
- dlg->local.target = pjsip_parse_uri(dlg->pool, buf.ptr, buf.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (dlg->local.target == NULL) {
- PJ_LOG(2, (dlg->obj_name,
- "pjsip_dlg_init: invalid local URI %s", buf.ptr));
- return -1;
- }
-
- /* Set local URI. */
- dlg->local.info = pjsip_from_hdr_create(dlg->pool);
- dlg->local.info->uri = dlg->local.target;
- dlg->local.info->tag = dlg->local.tag;
-
- /* Create local Contact to be advertised in the response. */
- dlg->local.contact = pjsip_contact_hdr_create( dlg->pool );
- dlg->local.contact->star = 0;
- dlg->local.contact->uri = dlg->local.target;
-
- /* Set remote URI. */
- dlg->remote.info = pjsip_to_hdr_create(dlg->pool);
-
- /* Duplicate to buffer. */
- pj_strdup_with_null( dlg->pool, &buf, c_remote_info);
-
- /* Build remote info. */
- dlg->remote.info->uri = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (dlg->remote.info->uri == NULL) {
- PJ_LOG(2, (dlg->obj_name,
- "pjsip_dlg_init: invalid remote URI %s", buf.ptr));
- return -1;
- }
-
- /* Set remote Contact initially equal to the remote URI. */
- dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool);
- dlg->remote.contact->star = 0;
- dlg->remote.contact->uri = dlg->remote.info->uri;
-
- /* Set initial remote target. */
- if (c_target != NULL) {
- pj_strdup_with_null( dlg->pool, &buf, c_target);
- dlg->remote.target = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen, 0);
- if (dlg->remote.target == NULL) {
- PJ_LOG(2, (dlg->obj_name,
- "pjsip_dlg_init: invalid remote target %s", buf.ptr));
- return -1;
- }
- } else {
- dlg->remote.target = dlg->remote.info->uri;
- }
-
- /* Create globally unique Call-ID */
- dlg->call_id = pjsip_cid_hdr_create(dlg->pool);
- pj_create_unique_string( dlg->pool, &dlg->call_id->id );
-
- /* Local and remote CSeq */
- pj_gettimeofday(&tv);
- dlg->local.cseq = tv.sec & 0xFFFF;
- dlg->remote.cseq = 0;
-
- /* Initial state is NULL. */
- event.type = event.src_type = PJSIP_EVENT_TX_MSG;
- event.src.data = NULL;
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event);
-
- /* Done. */
- PJ_LOG(4, (dlg->obj_name, "%s dialog initialized, From: %.*s, To: %.*s",
- pjsip_role_name(dlg->role),
- c_local_info->slen, c_local_info->ptr,
- c_remote_info->slen, c_remote_info->ptr));
-
- return PJ_SUCCESS;
-}
-
-/*
- * Set credentials.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_set_credentials( pjsip_dlg *dlg,
- int count,
- const pjsip_cred_info *cred)
-{
- if (count > 0) {
- dlg->cred_info = pj_pool_alloc(dlg->pool, count * sizeof(pjsip_cred_info));
- pj_memcpy(dlg->cred_info, cred, count * sizeof(pjsip_cred_info));
- }
- dlg->cred_count = count;
- return 0;
-}
-
-/*
- * Create a new request within dialog (i.e. after the dialog session has been
- * established). The construction of such requests follows the rule in
- * RFC3261 section 12.2.1.
- */
-static void dlg_create_request_throw( pjsip_tx_data **p_tdata,
- pjsip_dlg *dlg,
- const pjsip_method *method,
- int cseq )
-{
- pjsip_tx_data *tdata;
- pjsip_contact_hdr *contact;
- pjsip_route_hdr *route, *end_list;
-
- /* Contact Header field.
- * Contact can only be present in requests that establish dialog (in the
- * core SIP spec, only INVITE).
- */
- if (method->id == PJSIP_INVITE_METHOD)
- contact = dlg->local.contact;
- else
- contact = NULL;
-
- tdata = pjsip_endpt_create_request_from_hdr( dlg->ua->endpt,
- method,
- dlg->remote.target,
- dlg->local.info,
- dlg->remote.info,
- contact,
- dlg->call_id,
- cseq,
- NULL);
- if (!tdata) {
- PJ_THROW(1);
- return;
- }
-
- /* Just copy dialog route-set to Route header.
- * The transaction will do the processing as specified in Section 12.2.1
- * of RFC 3261 in function tsx_process_route() in sip_transaction.c.
- */
- route = dlg->route_set.next;
- end_list = &dlg->route_set;
- for (; route != end_list; route = route->next ) {
- pjsip_route_hdr *r;
- r = pjsip_hdr_shallow_clone( tdata->pool, route );
- pjsip_routing_hdr_set_route(r);
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)r);
- }
-
- /* Copy authorization headers. */
- pjsip_auth_init_req( dlg->pool, tdata, &dlg->auth_sess,
- dlg->cred_count, dlg->cred_info);
-
- *p_tdata = tdata;
-}
-
-
-/*
- * This function is called by application to create new outgoing request
- * message for this dialog. After the request is created, application can
- * modify the message (such adding headers), and eventually send the request.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg,
- const pjsip_method *method,
- int cseq)
-{
- PJ_USE_EXCEPTION;
- struct dialog_lock_data lck;
- pjsip_tx_data *tdata = NULL;
-
- pj_assert(dlg != NULL && method != NULL);
- if (!dlg || !method) {
- return NULL;
- }
-
- PJ_LOG(5, (dlg->obj_name, "Creating request"));
-
- /* Lock dialog. */
- lock_dialog(dlg, &lck);
-
- /* Use outgoing CSeq and increment it by one. */
- if (cseq < 0)
- cseq = dlg->local.cseq + 1;
-
- PJ_LOG(5, (dlg->obj_name, "creating request %.*s cseq=%d",
- method->name.slen, method->name.ptr, cseq));
-
- /* Create the request. */
- PJ_TRY {
- dlg_create_request_throw(&tdata, dlg, method, cseq);
- PJ_LOG(5, (dlg->obj_name, "request data %s created", tdata->obj_name));
- }
- PJ_DEFAULT {
- /* Failed! Delete transmit data. */
- if (tdata) {
- pjsip_tx_data_dec_ref( tdata );
- tdata = NULL;
- }
- }
- PJ_END;
-
- /* Unlock dialog. */
- unlock_dialog(dlg, &lck);
-
- return tdata;
-}
-
-/*
- * Sends request.
- * Select the transport for the request message
- */
-static pj_status_t dlg_send_request( pjsip_dlg *dlg, pjsip_tx_data *tdata )
-{
- pjsip_transaction *tsx;
- pj_status_t status = PJ_SUCCESS;
- struct dialog_lock_data lck;
-
- pj_assert(dlg != NULL && tdata != NULL);
- if (!dlg || !tdata) {
- return -1;
- }
-
- PJ_LOG(5, (dlg->obj_name, "sending request %s", tdata->obj_name));
-
- /* Lock dialog. */
- lock_dialog(dlg, &lck);
-
- /* Create a new transaction. */
- tsx = pjsip_endpt_create_tsx( dlg->ua->endpt );
- if (!tsx) {
- unlock_dialog(dlg, &lck);
- return -1;
- }
-
- PJ_LOG(4, (dlg->obj_name, "Created new UAC transaction: %s", tsx->obj_name));
-
- /* Initialize transaction */
- tsx->module_data[dlg->ua->mod_id] = dlg;
- status = pjsip_tsx_init_uac( tsx, tdata );
- if (status != PJ_SUCCESS) {
- unlock_dialog(dlg, &lck);
- pjsip_endpt_destroy_tsx( dlg->ua->endpt, tsx );
- return -1;
- }
- pjsip_endpt_register_tsx( dlg->ua->endpt, tsx );
-
- /* Start the transaction. */
- pjsip_tsx_on_tx_msg(tsx, tdata);
-
- /* Unlock dialog. */
- unlock_dialog(dlg, &lck);
-
- return status;
-}
-
-/*
- * This function can be called by application to send ANY outgoing message
- * to remote party.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg, pjsip_tx_data *tdata )
-{
- pj_status_t status;
- int tsx_status;
- struct dialog_lock_data lck;
-
- pj_assert(dlg != NULL && tdata != NULL);
- if (!dlg || !tdata) {
- return -1;
- }
-
- lock_dialog(dlg, &lck);
-
- if (tdata->msg->type == PJSIP_REQUEST_MSG) {
- int request_cseq;
- pjsip_msg *msg = tdata->msg;
- pjsip_cseq_hdr *cseq_hdr;
-
- switch (msg->line.req.method.id) {
- case PJSIP_CANCEL_METHOD:
-
- /* Check the INVITE transaction state. */
- tsx_status = dlg->invite_tsx->status_code;
- if (tsx_status >= 200) {
- /* Already terminated. Can't cancel. */
- status = -1;
- goto on_return;
- }
-
- /* If we've got provisional response, then send CANCEL and wait for
- * the response to INVITE to arrive. Otherwise just send CANCEL and
- * terminate the INVITE.
- */
- if (tsx_status < 100) {
- pjsip_tsx_terminate( dlg->invite_tsx,
- PJSIP_SC_REQUEST_TERMINATED);
- status = 0;
- goto on_return;
- }
-
- status = 0;
- request_cseq = dlg->invite_tsx->cseq;
- break;
-
- case PJSIP_ACK_METHOD:
- /* Sending ACK outside of transaction is not supported at present! */
- pj_assert(0);
- status = 0;
- request_cseq = dlg->local.cseq;
- break;
-
- case PJSIP_INVITE_METHOD:
- /* For an initial INVITE, reset dialog state to NULL so we get
- * 'normal' UAC notifications such as on_provisional(), etc.
- * Initial INVITE is the request that is sent when the dialog has
- * not been established yet. It's not necessarily the first INVITE
- * sent, as when the Authorization fails, subsequent INVITE are also
- * considered as an initial INVITE.
- */
- if (dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
- /* Set state to NULL. */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, NULL);
-
- } else {
- /* This is a re-INVITE */
- }
- status = 0;
- request_cseq = dlg->local.cseq + 1;
- break;
-
- default:
- status = 0;
- request_cseq = dlg->local.cseq + 1;
- break;
- }
-
- if (status != 0)
- goto on_return;
-
- /* Update dialog's local CSeq, if necessary. */
- if (request_cseq != dlg->local.cseq)
- dlg->local.cseq = request_cseq;
-
- /* Update CSeq header in the request. */
- cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg,
- PJSIP_H_CSEQ, NULL);
- pj_assert(cseq_hdr != NULL);
-
- /* Update the CSeq */
- cseq_hdr->cseq = request_cseq;
-
- /* Force the whole message to be re-printed. */
- pjsip_tx_data_invalidate_msg( tdata );
-
- /* Now send the request. */
- status = dlg_send_request(dlg, tdata);
-
- } else {
- /*
- * This is only valid for sending response to INVITE!
- */
- pjsip_cseq_hdr *cseq_hdr;
-
- if (dlg->invite_tsx == NULL || dlg->invite_tsx->status_code >= 200) {
- status = -1;
- goto on_return;
- }
-
- cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg,
- PJSIP_H_CSEQ, NULL);
- pj_assert(cseq_hdr);
-
- if (cseq_hdr->method.id != PJSIP_INVITE_METHOD) {
- status = -1;
- goto on_return;
- }
-
- pj_assert(cseq_hdr->cseq == dlg->invite_tsx->cseq);
-
- pjsip_tsx_on_tx_msg(dlg->invite_tsx, tdata);
- status = 0;
- }
-
-on_return:
- /* Unlock dialog. */
- unlock_dialog(dlg, &lck);
-
- /* Whatever happen delete the message. */
- pjsip_tx_data_dec_ref( tdata );
-
- return status;
-}
-
-/*
- * Sends outgoing invitation.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg )
-{
- pjsip_method method;
- struct dialog_lock_data lck;
- const pjsip_allow_hdr *allow_hdr;
- pjsip_tx_data *tdata;
-
- pj_assert(dlg != NULL);
- if (!dlg) {
- return NULL;
- }
-
- PJ_LOG(4, (dlg->obj_name, "request to send invitation"));
-
- /* Lock dialog. */
- lock_dialog(dlg, &lck);
-
- /* Create request. */
- pjsip_method_set( &method, PJSIP_INVITE_METHOD);
- tdata = pjsip_dlg_create_request( dlg, &method, -1 );
- if (tdata == NULL) {
- unlock_dialog(dlg, &lck);
- return NULL;
- }
-
- /* Invite SHOULD contain "Allow" header. */
- allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt );
- if (allow_hdr) {
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone( tdata->pool, allow_hdr));
- }
-
- /* Unlock dialog. */
- unlock_dialog(dlg, &lck);
-
- return tdata;
-}
-
-/*
- * Cancel pending outgoing dialog invitation.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg )
-{
- pjsip_tx_data *tdata = NULL;
- struct dialog_lock_data lck;
-
- pj_assert(dlg != NULL);
- if (!dlg) {
- return NULL;
- }
-
- PJ_LOG(4, (dlg->obj_name, "request to cancel invitation"));
-
- lock_dialog(dlg, &lck);
-
- /* Check the INVITE transaction. */
- if (dlg->invite_tsx == NULL || dlg->role != PJSIP_ROLE_UAC) {
- PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: "
- "no INVITE transaction found"));
- goto on_return;
- }
-
- /* Construct the CANCEL request. */
- tdata = pjsip_endpt_create_cancel( dlg->ua->endpt,
- dlg->invite_tsx->last_tx );
- if (tdata == NULL) {
- PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: "
- "unable to construct request"));
- goto on_return;
- }
-
- /* Add reference counter to tdata. */
- pjsip_tx_data_add_ref(tdata);
-
-on_return:
- unlock_dialog(dlg, &lck);
- return tdata;
-}
-
-
-/*
- * Answer incoming dialog invitation, with either provisional responses
- * or a final response.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code )
-{
- pjsip_tx_data *tdata = NULL;
- pjsip_msg *msg;
- struct dialog_lock_data lck;
-
- pj_assert(dlg != NULL);
- if (!dlg) {
- return NULL;
- }
-
- PJ_LOG(4, (dlg->obj_name, "pjsip_dlg_answer: code=%d", code));
-
- /* Lock dialog. */
- lock_dialog(dlg, &lck);
-
- /* Must have pending INVITE. */
- if (dlg->invite_tsx == NULL) {
- PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: no INVITE transaction found"));
- goto on_return;
- }
- /* Must be UAS. */
- if (dlg->role != PJSIP_ROLE_UAS) {
- PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: not UAS"));
- goto on_return;
- }
- /* Must have not answered with final response before. */
- if (dlg->invite_tsx->status_code >= 200) {
- PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: transaction already terminated "
- "with status %d", dlg->invite_tsx->status_code));
- goto on_return;
- }
-
- /* Get transmit data and the message.
- * We will rewrite the message with a new status code.
- */
- only if tdata is not pending!!!
- tdata = dlg->invite_tsx->last_tx;
- msg = tdata->msg;
-
- /* Set status code and reason phrase. */
- if (code < 100 || code >= 700) code = 500;
- msg->line.status.code = code;
- msg->line.status.reason = *pjsip_get_status_text(code);
-
- /* For 2xx response, Contact and Record-Route must be added. */
- if (PJSIP_IS_STATUS_IN_CLASS(code,200)) {
- const pjsip_allow_hdr *allow_hdr;
-
- if (pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL) == NULL) {
- pjsip_contact_hdr *contact;
- contact = pjsip_hdr_shallow_clone( tdata->pool, dlg->local.contact);
- pjsip_msg_add_hdr( msg, (pjsip_hdr*)contact );
- }
-
- /* 2xx response MUST contain "Allow" header. */
- allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt );
- if (allow_hdr) {
- pjsip_msg_add_hdr( msg, pjsip_hdr_shallow_clone( tdata->pool, allow_hdr));
- }
- }
-
- /* for all but 100 responses, To-tag must be set. */
- if (code != 100) {
- pjsip_to_hdr *to;
- to = pjsip_msg_find_hdr( msg, PJSIP_H_TO, NULL);
- to->tag = dlg->local.tag;
- }
-
- /* Reset packet buffer. */
- pjsip_tx_data_invalidate_msg(tdata);
-
- /* Add reference counter */
- pjsip_tx_data_add_ref(tdata);
-
-on_return:
-
- /* Unlock dialog. */
- unlock_dialog(dlg, &lck);
-
- return tdata;
-}
-
-
-/*
- * Send BYE request to terminate the dialog's session.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg )
-{
- pjsip_method method;
- struct dialog_lock_data lck;
- pjsip_tx_data *tdata;
-
- if (!dlg) {
- pj_assert(dlg != NULL);
- return NULL;
- }
-
- PJ_LOG(4, (dlg->obj_name, "request to terminate session"));
-
- lock_dialog(dlg, &lck);
-
- pjsip_method_set( &method, PJSIP_BYE_METHOD);
- tdata = pjsip_dlg_create_request( dlg, &method, -1 );
-
- unlock_dialog(dlg, &lck);
-
- return tdata;
-}
-
-/*
- * High level function to disconnect dialog's session. Depending on dialog's
- * state, this function will either send CANCEL, final response, or BYE to
- * trigger the disconnection. A status code must be supplied, which will be
- * sent if dialog will be transmitting a final response to INVITE.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg,
- int status_code )
-{
- pjsip_tx_data *tdata = NULL;
-
- pj_assert(dlg != NULL);
- if (!dlg) {
- return NULL;
- }
-
- switch (dlg->state) {
- case PJSIP_DIALOG_STATE_INCOMING:
- tdata = pjsip_dlg_answer(dlg, status_code);
- break;
-
- case PJSIP_DIALOG_STATE_CALLING:
- tdata = pjsip_dlg_cancel(dlg);
- break;
-
- case PJSIP_DIALOG_STATE_PROCEEDING:
- if (dlg->role == PJSIP_ROLE_UAC) {
- tdata = pjsip_dlg_cancel(dlg);
- } else {
- tdata = pjsip_dlg_answer(dlg, status_code);
- }
- break;
-
- case PJSIP_DIALOG_STATE_ESTABLISHED:
- tdata = pjsip_dlg_bye(dlg);
- break;
-
- default:
- PJ_LOG(4,(dlg->obj_name, "Invalid state %s in pjsip_dlg_disconnect()",
- dlg_state_names[dlg->state]));
- break;
- }
-
- return tdata;
-}
-
-/*
- * Handling of the receipt of 2xx/INVITE response.
- */
-static void dlg_on_recv_2xx_invite( pjsip_dlg *dlg,
- pjsip_event *event )
-{
- pjsip_msg *msg;
- pjsip_contact_hdr *contact;
- pjsip_hdr *hdr, *end_hdr;
- pjsip_method method;
- pjsip_tx_data *ack_tdata;
-
- /* Get the message */
- msg = event->src.rdata->msg;
-
- /* Update remote's tag information. */
- pj_strdup(dlg->pool, &dlg->remote.info->tag, &event->src.rdata->to_tag);
-
- /* Copy Contact information in the 2xx/INVITE response to dialog's.
- * remote contact
- */
- contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
- if (contact) {
- dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact );
- } else {
- /* duplicate contact from "From" header (?) */
- PJ_LOG(4,(dlg->obj_name, "Received 200/OK to INVITE with no Contact!"));
- dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool);
- dlg->remote.contact->uri = dlg->remote.info->uri;
- }
-
- /* Copy Record-Route header (in reverse order) as dialog's route-set,
- * overwriting previous route-set, if any, even if the received route-set
- * is empty.
- */
- pj_list_init(&dlg->route_set);
- end_hdr = &msg->hdr;
- for (hdr = msg->hdr.prev; hdr!=end_hdr; hdr = hdr->prev) {
- if (hdr->type == PJSIP_H_RECORD_ROUTE) {
- pjsip_route_hdr *r;
- r = pjsip_hdr_clone(dlg->pool, hdr);
- pjsip_routing_hdr_set_route(r);
- pj_list_insert_before(&dlg->route_set, r);
- }
- }
-
- /* On receipt of 200/INVITE response, send ACK.
- * This ack must be saved and retransmitted whenever we receive
- * 200/INVITE retransmission, until 64*T1 seconds elapsed.
- */
- pjsip_method_set( &method, PJSIP_ACK_METHOD);
- ack_tdata = pjsip_dlg_create_request( dlg, &method, dlg->invite_tsx->cseq);
- if (ack_tdata == NULL) {
- //PJ_TODO(HANDLE_CREATE_ACK_FAILURE)
- PJ_LOG(2, (dlg->obj_name, "Error sending ACK msg: can't create request"));
- return;
- }
-
- /* Send with the transaction. */
- pjsip_tsx_on_tx_ack( dlg->invite_tsx, ack_tdata);
-
- /* Decrement reference counter because pjsip_dlg_create_request
- * automatically increments the request.
- */
- pjsip_tx_data_dec_ref( ack_tdata );
-}
-
-/*
- * State NULL, before any events have been received.
- */
-static int dlg_on_state_null( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_RX_MSG)
- {
- pjsip_hdr *hdr, *hdr_list;
-
- pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
-
- /* Save the INVITE transaction. */
- dlg->invite_tsx = tsx;
-
- /* Change state to INCOMING */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_INCOMING, event);
-
- /* Create response buffer. */
- tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, 100);
- pjsip_tx_data_add_ref(tsx->last_tx);
-
- /* Copy the Record-Route headers into dialog's route_set, maintaining
- * the order.
- */
- pj_list_init(&dlg->route_set);
- hdr_list = &event->src.rdata->msg->hdr;
- hdr = hdr_list->next;
- while (hdr != hdr_list) {
- if (hdr->type == PJSIP_H_RECORD_ROUTE) {
- pjsip_route_hdr *route;
- route = pjsip_hdr_clone(dlg->pool, hdr);
- pjsip_routing_hdr_set_route(route);
- pj_list_insert_before(&dlg->route_set, route);
- }
- hdr = hdr->next;
- }
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_TX_MSG)
- {
- pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
-
- /* Save the INVITE transaction. */
- dlg->invite_tsx = tsx;
-
- /* Change state to CALLING. */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_CALLING, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
-
- return 0;
-}
-
-/*
- * State INCOMING is after the (callee) dialog has been initialized with
- * the incoming request, but before any responses is sent by the dialog.
- */
-static int dlg_on_state_incoming( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- return dlg_on_state_proceeding_callee( dlg, tsx, event );
-}
-
-/*
- * State CALLING is after the (caller) dialog has sent outgoing invitation
- * but before any responses are received.
- */
-static int dlg_on_state_calling( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- if (tsx == dlg->invite_tsx) {
- return dlg_on_state_proceeding_caller( dlg, tsx, event );
- }
- return 0;
-}
-
-/*
- * State PROCEEDING is after provisional response is received.
- * Since the processing is similar to state CALLING, this function is also
- * called for CALLING state.
- */
-static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- int dlg_is_terminated = 0;
-
- /* We only care about our INVITE transaction.
- * Ignore other transaction progression (such as CANCEL).
- */
- if (tsx != dlg->invite_tsx) {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- return 0;
- }
-
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED) {
- switch (tsx->state) {
- case PJSIP_TSX_STATE_PROCEEDING:
- if (dlg->state != PJSIP_DIALOG_STATE_PROCEEDING) {
- /* Change state to PROCEEDING */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- } else {
- /* Also notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
- break;
-
- case PJSIP_TSX_STATE_COMPLETED:
- /* Change dialog state. */
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
- /* Update remote target, take it from the contact hdr. */
- pjsip_contact_hdr *contact;
- contact = pjsip_msg_find_hdr(event->src.rdata->msg,
- PJSIP_H_CONTACT, NULL);
- if (contact) {
- dlg->remote.target = pjsip_uri_clone(dlg->pool, contact->uri);
- } else {
- PJ_LOG(4,(dlg->obj_name,
- "Warning: found no Contact hdr in 200/OK"));
- }
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event);
- } else if (tsx->status_code==401 || tsx->status_code==407) {
- /* Handle Authentication challenge. */
- pjsip_tx_data *tdata;
- tdata = pjsip_auth_reinit_req( dlg->ua->endpt,
- dlg->pool, &dlg->auth_sess,
- dlg->cred_count, dlg->cred_info,
- tsx->last_tx, event->src.rdata);
- if (tdata) {
- /* Re-use original request, with a new transaction.
- * Need not to worry about CSeq, dialog will take care.
- */
- pjsip_dlg_send_msg(dlg, tdata);
- return 0;
- } else {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
- }
- } else {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
- }
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- /* Send ACK when dialog is connected. */
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
- pj_assert(event->src_type == PJSIP_EVENT_RX_MSG);
- dlg_on_recv_2xx_invite(dlg, event);
- }
- break;
-
- case PJSIP_TSX_STATE_TERMINATED:
- /*
- * Transaction is terminated because of timeout or transport error.
- * To let the application go to normal state progression, call the
- * callback twice. First is to emulate disconnection, and then call
- * again (with state TERMINATED) to destroy the dialog.
- */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- /* The INVITE transaction will be destroyed, so release reference
- * to it.
- */
- dlg->invite_tsx = NULL;
-
- /* We should terminate the dialog now.
- * But it's possible that we have other pending transactions (for
- * example, outgoing CANCEL is in progress).
- * So destroy the dialog only if there's no other transaction.
- */
- if (dlg->pending_tsx_count == 0) {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- dlg_is_terminated = 1;
- }
- break;
-
- default:
- pj_assert(0);
- break;
- }
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
- return dlg_is_terminated ? -1 : 0;
-}
-
-/*
- * State PROCEEDING for UAS is after the callee send provisional response.
- * This function is also called for INCOMING state.
- */
-static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- int dlg_is_terminated = 0;
-
- pj_assert( dlg->invite_tsx != NULL );
-
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_TX_MSG &&
- tsx == dlg->invite_tsx)
- {
- switch (tsx->state) {
- case PJSIP_TSX_STATE_PROCEEDING:
- /* Change state to PROCEEDING */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- break;
-
- case PJSIP_TSX_STATE_COMPLETED:
- case PJSIP_TSX_STATE_TERMINATED:
- /* Change dialog state. */
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event);
- } else {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
- }
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- /* If transaction is terminated in non-2xx situation,
- * terminate dialog as well. This happens when something unexpected
- * occurs, such as transport error.
- */
- if (tsx->state == PJSIP_TSX_STATE_TERMINATED &&
- !PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200))
- {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- dlg_is_terminated = 1;
- }
- break;
-
- default:
- pj_assert(0);
- break;
- }
-
- } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_RX_MSG &&
- tsx->method.id == PJSIP_CANCEL_METHOD)
- {
- pjsip_tx_data *tdata;
-
- /* Check if sequence number matches the pending INVITE. */
- if (dlg->invite_tsx==NULL ||
- pj_strcmp(&tsx->branch, &dlg->invite_tsx->branch) != 0)
- {
- PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no matching INVITE"));
-
- /* No matching INVITE transaction found. */
- tdata = pjsip_endpt_create_response(dlg->ua->endpt,
- event->src.rdata,
- PJSIP_SC_CALL_TSX_DOES_NOT_EXIST );
- pjsip_tsx_on_tx_msg(tsx, tdata);
- return 0;
- }
-
- /* Always respond the CANCEL with 200/CANCEL no matter what. */
- tdata = pjsip_endpt_create_response(dlg->ua->endpt,
- event->src.rdata,
- 200 );
- pjsip_tsx_on_tx_msg( tsx, tdata );
-
- /* Respond the INVITE transaction with 487, only if transaction has
- * not completed.
- */
- if (dlg->invite_tsx->last_tx) {
- if (dlg->invite_tsx->status_code < 200) {
- tdata = dlg->invite_tsx->last_tx;
- tdata->msg->line.status.code = 487;
- tdata->msg->line.status.reason = *pjsip_get_status_text(487);
- /* Reset packet buffer. */
- pjsip_tx_data_invalidate_msg(tdata);
- pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata );
- } else {
- PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no effect, "
- "Transaction already terminated "
- "with status %d",
- dlg->invite_tsx->status_code));
- }
- } else {
- tdata = pjsip_endpt_create_response(dlg->ua->endpt,
- event->src.rdata,
- 487);
- pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata );
- }
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
-
- return dlg_is_terminated ? -1 : 0;
-}
-
-static int dlg_on_state_proceeding( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- if (dlg->role == PJSIP_ROLE_UAC) {
- return dlg_on_state_proceeding_caller( dlg, tsx, event );
- } else {
- return dlg_on_state_proceeding_callee( dlg, tsx, event );
- }
-}
-
-static int dlg_on_state_connecting( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- if (tsx == dlg->invite_tsx) {
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- (tsx->state == PJSIP_TSX_STATE_TERMINATED ||
- tsx->state == PJSIP_TSX_STATE_COMPLETED ||
- tsx->state == PJSIP_TSX_STATE_CONFIRMED))
- {
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_ESTABLISHED, event);
- } else {
- /* Probably because we never get the ACK, or transport error
- * when sending ACK.
- */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
- }
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
- } else {
- /* Handle case when transaction is started when dialog is connecting
- * (e.g. BYE requests cross wire.
- */
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_RX_MSG &&
- tsx->role == PJSIP_ROLE_UAS)
- {
- pjsip_tx_data *response;
-
- if (tsx->status_code >= 200)
- return 0;
-
- if (tsx->method.id == PJSIP_BYE_METHOD) {
- /* Set state to DISCONNECTED. */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- response = pjsip_endpt_create_response( dlg->ua->endpt,
- event->src.rdata, 200);
- } else {
- response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata,
- PJSIP_SC_INTERNAL_SERVER_ERROR);
- }
-
- if (response)
- pjsip_tsx_on_tx_msg(tsx, response);
-
- return 0;
- }
- }
- return 0;
-}
-
-static int dlg_on_state_established( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(tsx)
-
- if (tsx && tsx->method.id == PJSIP_BYE_METHOD) {
- /* Set state to DISCONNECTED. */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- /* Answer with 200/BYE. */
- if (event->src_type == PJSIP_EVENT_RX_MSG) {
- pjsip_tx_data *tdata;
- tdata = pjsip_endpt_create_response(dlg->ua->endpt,
- event->src.rdata,
- 200 );
- if (tdata)
- pjsip_tsx_on_tx_msg( tsx, tdata );
- }
- } else if (tsx && event->src_type == PJSIP_EVENT_RX_MSG) {
- pjsip_method_e method = event->src.rdata->cseq->method.id;
-
- PJ_TODO(PROPERLY_HANDLE_REINVITATION)
-
- /* Reinvitation. The message may be INVITE or an ACK. */
- if (method == PJSIP_INVITE_METHOD) {
- if (dlg->invite_tsx && dlg->invite_tsx->status_code < 200) {
- /* Section 14.2: A UAS that receives a second INVITE before it
- * sends the final response to a first INVITE with a lower
- * CSeq sequence number on the same dialog MUST return a 500
- * (Server Internal Error) response to the second INVITE and
- * MUST include a Retry-After header field with a randomly
- * chosen value of between 0 and 10 seconds.
- */
- pjsip_retry_after_hdr *hdr;
- pjsip_tx_data *tdata =
- pjsip_endpt_create_response(dlg->ua->endpt,
- event->src.rdata, 500);
-
- if (!tdata)
- return 0;
-
- /* Add Retry-After. */
- hdr = pjsip_retry_after_hdr_create(tdata->pool);
- hdr->ivalue = 9;
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
-
- /* Send. */
- pjsip_tsx_on_tx_msg(tsx, tdata);
-
- return 0;
- }
-
- /* Keep this as our current INVITE transaction. */
- dlg->invite_tsx = tsx;
-
- /* Create response buffer. */
- tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt,
- event->src.rdata, 100);
- pjsip_tx_data_add_ref(tsx->last_tx);
-
- }
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_MID_CALL_REQUEST, event);
-
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
-
- return 0;
-}
-
-static int dlg_on_state_disconnected( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(tsx)
-
- /* Handle case when transaction is started when dialog is disconnected
- * (e.g. BYE requests cross wire.
- */
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_RX_MSG &&
- tsx->role == PJSIP_ROLE_UAS)
- {
- pjsip_tx_data *response = NULL;
-
- if (tsx->status_code >= 200)
- return 0;
-
- if (tsx->method.id == PJSIP_BYE_METHOD) {
- response = pjsip_endpt_create_response( dlg->ua->endpt,
- event->src.rdata, 200);
- } else {
- response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata,
- PJSIP_SC_INTERNAL_SERVER_ERROR);
- }
- if (response)
- pjsip_tsx_on_tx_msg(tsx, response);
-
- return 0;
- }
- /* Handle case when outgoing BYE was rejected with 401/407 */
- else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_RX_MSG &&
- tsx->role == PJSIP_ROLE_UAC)
- {
- if (tsx->status_code==401 || tsx->status_code==407) {
- pjsip_tx_data *tdata;
- tdata = pjsip_auth_reinit_req( dlg->ua->endpt, dlg->pool,
- &dlg->auth_sess,
- dlg->cred_count, dlg->cred_info,
- tsx->last_tx, event->src.rdata);
- if (tdata) {
- pjsip_dlg_send_msg(dlg, tdata);
- }
- }
- }
-
-
- if (dlg->pending_tsx_count == 0) {
- /* Set state to TERMINATED. */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- return -1;
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
-
- return 0;
-}
-
-static int dlg_on_state_terminated( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(dlg)
- PJ_UNUSED_ARG(tsx)
- PJ_UNUSED_ARG(event)
-
- return -1;
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip_mod_ua/sip_dialog.h> +#include <pjsip_mod_ua/sip_ua.h> +#include <pjsip_mod_ua/sip_ua_private.h> +#include <pjsip/sip_transport.h> +#include <pjsip/sip_transaction.h> +#include <pjsip/sip_types.h> +#include <pjsip/sip_endpoint.h> +#include <pjsip/sip_uri.h> +#include <pjsip/sip_util.h> +#include <pjsip/sip_module.h> +#include <pjsip/sip_event.h> +#include <pjsip/sip_parser.h> +#include <pj/string.h> +#include <pj/log.h> +#include <pj/os.h> +#include <pj/guid.h> +#include <pj/except.h> +#include <pj/pool.h> + +/* TLS to keep dialog lock record (initialized by UA) */ +int pjsip_dlg_lock_tls_id; + +struct dialog_lock_data +{ + struct dialog_lock_data *prev; + pjsip_dlg *dlg; + int is_alive; +}; + +/* + * Static function prototypes. + */ +static void dlg_create_request_throw( pjsip_tx_data **p_tdata, + pjsip_dlg *dlg, + const pjsip_method *method, + int cseq ); +static int dlg_on_all_state_pre( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_all_state_post( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_null( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_incoming( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_calling( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_proceeding( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_connecting( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_established( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_disconnected( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_terminated( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); + +/* + * Dialog state handlers. + */ +static int (*dlg_state_handlers[])(struct pjsip_dlg *, pjsip_transaction *, + pjsip_event *) = +{ + &dlg_on_state_null, + &dlg_on_state_incoming, + &dlg_on_state_calling, + &dlg_on_state_proceeding, + &dlg_on_state_connecting, + &dlg_on_state_established, + &dlg_on_state_disconnected, + &dlg_on_state_terminated, +}; + +/* + * Dialog state names. + */ +static const char* dlg_state_names[] = +{ + "STATE_NULL", + "STATE_INCOMING", + "STATE_CALLING", + "STATE_PROCEEDING", + "STATE_CONNECTING", + "STATE_ESTABLISHED", + "STATE_DISCONNECTED", + "STATE_TERMINATED", +}; + + +/* + * Get the dialog string state, normally for logging purpose. + */ +const char *pjsip_dlg_state_str(pjsip_dlg_state_e state) +{ + return dlg_state_names[state]; +} + +/* Lock dialog mutex. */ +static void lock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck) +{ + struct dialog_lock_data *prev; + + pj_mutex_lock(dlg->mutex); + prev = pj_thread_local_get(pjsip_dlg_lock_tls_id); + lck->prev = prev; + lck->dlg = dlg; + lck->is_alive = 1; + pj_thread_local_set(pjsip_dlg_lock_tls_id, lck); +} + +/* Carefully unlock dialog mutex, protect against situation when the dialog + * has already been destroyed. + */ +static pj_status_t unlock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck) +{ + pj_assert(pj_thread_local_get(pjsip_dlg_lock_tls_id) == lck); + pj_assert(dlg == lck->dlg); + + pj_thread_local_set(pjsip_dlg_lock_tls_id, lck->prev); + if (lck->is_alive) + pj_mutex_unlock(dlg->mutex); + + return lck->is_alive ? 0 : -1; +} + +/* + * This is called by dialog's FSM to change dialog's state. + */ +static void dlg_set_state( pjsip_dlg *dlg, pjsip_dlg_state_e state, + pjsip_event *event) +{ + PJ_UNUSED_ARG(event); + + PJ_LOG(4, (dlg->obj_name, "State %s-->%s (ev=%s, src=%s, data=%p)", + pjsip_dlg_state_str(dlg->state), pjsip_dlg_state_str(state), + event ? pjsip_event_str(event->type) : "", + event ? pjsip_event_str(event->src_type) : "", + event ? event->src.data : NULL)); + + dlg->state = state; + dlg->handle_tsx_event = dlg_state_handlers[state]; +} + +/* + * Invoke dialog's callback. + * This function is called by dialog's FSM, and interpret the event and call + * the corresponding callback registered by application. + */ +static void dlg_call_dlg_callback( pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event, + pjsip_event *event ) +{ + pjsip_dlg_callback *cb = dlg->ua->dlg_cb; + if (!cb) { + PJ_LOG(4,(dlg->obj_name, "Can not call callback (none registered).")); + return; + } + + /* Low level event: call the all-events callback. */ + if (cb->on_all_events) { + (*cb->on_all_events)(dlg, dlg_event, event); + } + + /* Low level event: call the tx/rx callback if this is a tx/rx event. */ + if (event->type == PJSIP_EVENT_BEFORE_TX && cb->on_before_tx) + { + (*cb->on_before_tx)(dlg, event->obj.tsx, event->src.tdata, event->data.long_data); + } + else if (event->type == PJSIP_EVENT_TX_MSG && + event->src_type == PJSIP_EVENT_TX_MSG && cb->on_tx_msg) + { + (*cb->on_tx_msg)(dlg, event->obj.tsx, event->src.tdata); + } + else if (event->type == PJSIP_EVENT_RX_MSG && + event->src_type == PJSIP_EVENT_RX_MSG && cb->on_rx_msg) { + (*cb->on_rx_msg)(dlg, event->obj.tsx, event->src.rdata); + } + + /* Now trigger high level events. + * High level event should only occurs when dialog's state has changed, + * except for on_provisional, which may be called multiple times whenever + * response message is sent + */ + if (dlg->state == PJSIP_DIALOG_STATE_PROCEEDING && + (event->type== PJSIP_EVENT_TSX_STATE_CHANGED) && + event->obj.tsx == dlg->invite_tsx) + { + /* Sent/received provisional responses. */ + if (cb->on_provisional) + (*cb->on_provisional)(dlg, event->obj.tsx, event); + } + + if (dlg_event == PJSIP_DIALOG_EVENT_MID_CALL_REQUEST) { + if (cb->on_mid_call_events) + (*cb->on_mid_call_events)(dlg, event); + return; + } + + if (dlg_event != PJSIP_DIALOG_EVENT_STATE_CHANGED) + return; + + if (dlg->state == PJSIP_DIALOG_STATE_INCOMING) { + + /* New incoming dialog. */ + pj_assert (event->src_type == PJSIP_EVENT_RX_MSG); + (*cb->on_incoming)(dlg, event->obj.tsx, event->src.rdata); + + } else if (dlg->state == PJSIP_DIALOG_STATE_CALLING) { + + /* Dialog has just sent the first INVITE. */ + if (cb->on_calling) { + (*cb->on_calling)(dlg, event->obj.tsx, event->src.tdata); + } + + } else if (dlg->state == PJSIP_DIALOG_STATE_DISCONNECTED) { + + if (cb->on_disconnected) + (*cb->on_disconnected)(dlg, event); + + } else if (dlg->state == PJSIP_DIALOG_STATE_TERMINATED) { + + if (cb->on_terminated) + (*cb->on_terminated)(dlg); + + pjsip_ua_destroy_dialog(dlg); + + } else if (dlg->state == PJSIP_DIALOG_STATE_CONNECTING) { + + if (cb->on_connecting) + (*cb->on_connecting)(dlg, event); + + } else if (dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED) { + + if (cb->on_established) + (*cb->on_established)(dlg, event); + } +} + +/* + * This callback receives event from the transaction layer (via User Agent), + * or from dialog timer (for 200/INVITE or ACK retransmission). + */ +void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + int status = 0; + struct dialog_lock_data lck; + + PJ_LOG(4, (dlg->obj_name, "state=%s (evt=%s, src=%s)", + pjsip_dlg_state_str(dlg->state), + pjsip_event_str(event->type), + pjsip_event_str(event->src_type))); + + + lock_dialog(dlg, &lck); + + status = dlg_on_all_state_pre( dlg, tsx, event); + + if (status==0) { + status = (*dlg->handle_tsx_event)(dlg, tsx, event); + } + if (status==0) { + status = dlg_on_all_state_post( dlg, tsx, event); + } + + unlock_dialog(dlg, &lck); +} + +/* + * This function contains common processing in all states, and it is called + * before the FSM is invoked. + */ +static int dlg_on_all_state_pre( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + PJ_UNUSED_ARG(event) + + if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED) + return 0; + + if (tsx && (tsx->state==PJSIP_TSX_STATE_CALLING || + tsx->state==PJSIP_TSX_STATE_TRYING)) + { + ++dlg->pending_tsx_count; + + } else if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED) + { + --dlg->pending_tsx_count; + if (tsx == dlg->invite_tsx) + dlg->invite_tsx = NULL; + } + + if (tsx->method.id == PJSIP_INVITE_METHOD) { + tsx->handle_ack = 1; + } + return 0; +} + + +/* + * This function contains common processing in all states, and it is called + * after the FSM is invoked. + */ +static int dlg_on_all_state_post( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + PJ_UNUSED_ARG(event) + + if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED) { + if (dlg->pending_tsx_count == 0 && + dlg->state != PJSIP_DIALOG_STATE_CONNECTING && + dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED && + dlg->state != PJSIP_DIALOG_STATE_TERMINATED) + { + dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event); + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + return -1; + } + } + + return 0; +} + + +/* + * Internal function to initialize dialog, contains common initialization + * for both UAS and UAC dialog. + */ +static pj_status_t dlg_init( pjsip_dlg *dlg ) +{ + /* Init mutex. */ + dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0); + if (!dlg->mutex) { + PJ_PERROR((dlg->obj_name, "pj_mutex_create()")); + return -1; + } + + /* Init route-set (Initially empty) */ + pj_list_init(&dlg->route_set); + + /* Init auth credential list. */ + pj_list_init(&dlg->auth_sess); + + return PJ_SUCCESS; +} + +/* + * This one is called just before dialog is destroyed. + * It is called while mutex is held. + */ +PJ_DEF(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg ) +{ + struct dialog_lock_data *lck; + + //PJ_TODO(CHECK_THIS); + pj_assert(dlg->pending_tsx_count == 0); + + /* Mark dialog as dead. */ + lck = pj_thread_local_get(pjsip_dlg_lock_tls_id); + while (lck) { + if (lck->dlg == dlg) + lck->is_alive = 0; + lck = lck->prev; + } +} + +/* + * Initialize dialog from the received request. + * This is an internal function which is called by the User Agent (sip_ua.c), + * and it will initialize most of dialog's properties with values from the + * received message. + */ +pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg, pjsip_rx_data *rdata ) +{ + pjsip_msg *msg = rdata->msg; + pjsip_to_hdr *to; + pjsip_contact_hdr *contact; + pjsip_name_addr *name_addr; + pjsip_url *url; + unsigned flag; + pjsip_event event; + + pj_assert(dlg && rdata); + + PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p)", rdata)); + + /* Must be an INVITE request. */ + pj_assert(msg->type == PJSIP_REQUEST_MSG && + msg->line.req.method.id == PJSIP_INVITE_METHOD); + + /* Init general dialog data. */ + if (dlg_init(dlg) != PJ_SUCCESS) { + return -1; + } + + /* Get the To header. */ + to = rdata->to; + + /* Copy URI in the To header as our local URI. */ + dlg->local.info = pjsip_hdr_clone( dlg->pool, to); + + /* Set tag in the local info. */ + dlg->local.info->tag = dlg->local.tag; + + /* Create local Contact to be advertised in the response. + * At the moment, just copy URI from the local URI as our contact. + */ + dlg->local.contact = pjsip_contact_hdr_create( dlg->pool ); + dlg->local.contact->star = 0; + name_addr = (pjsip_name_addr *)dlg->local.info->uri; + dlg->local.contact->uri = (pjsip_uri*) name_addr; + url = (pjsip_url*) name_addr->uri; + //url->port = rdata->via->sent_by.port; + //url->port = pj_sockaddr_get_port( pjsip_transport_get_local_addr(rdata->transport) ); + + /* Save remote URI. */ + dlg->remote.info = pjsip_hdr_clone( dlg->pool, rdata->from ); + pjsip_fromto_set_to( dlg->remote.info ); + pj_strdup( dlg->pool, &dlg->remote.tag, &rdata->from->tag ); + + /* Save remote Contact. */ + contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL); + if (contact) { + dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact ); + } else { + PJ_LOG(3,(dlg->obj_name, "No Contact header in INVITE from %s", + pj_sockaddr_get_str_addr(&rdata->addr))); + dlg->remote.contact = pjsip_contact_hdr_create( dlg->pool ); + dlg->remote.contact->uri = dlg->remote.info->uri; + } + + /* Save Call-ID. */ + dlg->call_id = pjsip_cid_hdr_create(dlg->pool); + pj_strdup( dlg->pool, &dlg->call_id->id, &rdata->call_id ); + + /* Initialize local CSeq and save remote CSeq.*/ + dlg->local.cseq = rdata->timestamp.sec & 0xFFFF; + dlg->remote.cseq = rdata->cseq->cseq; + + /* Secure? */ + flag = pjsip_transport_get_flag(rdata->transport); + dlg->secure = (flag & PJSIP_TRANSPORT_SECURE) != 0; + + /* Initial state is NULL. */ + event.type = event.src_type = PJSIP_EVENT_RX_MSG; + event.src.rdata = rdata; + dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event); + + PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p) complete", rdata)); + return PJ_SUCCESS; +} + +/* + * Set the contact details. + */ +PJ_DEF(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg, + const pj_str_t *contact ) +{ + pjsip_uri *local_uri; + pj_str_t tmp; + + pj_strdup_with_null(dlg->pool, &tmp, contact); + local_uri = pjsip_parse_uri( dlg->pool, tmp.ptr, tmp.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (local_uri == NULL) { + PJ_LOG(2, (dlg->obj_name, "set_contact: invalid URI")); + return -1; + } + + dlg->local.contact->star = 0; + dlg->local.contact->uri = local_uri; + return 0; +} + +/* + * Set route set. + */ +PJ_DEF(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg, + const pjsip_route_hdr *route_set ) +{ + pjsip_route_hdr *hdr; + + pj_list_init(&dlg->route_set); + hdr = route_set->next; + while (hdr != route_set) { + pjsip_route_hdr *cloned = pjsip_hdr_clone(dlg->pool, hdr); + pj_list_insert_before( &dlg->route_set, cloned); + hdr = hdr->next; + } + return 0; +} + +/* + * Set route set without cloning the header. + */ +PJ_DEF(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg, + pjsip_route_hdr *route_set) +{ + pjsip_route_hdr *hdr; + + pj_list_init(&dlg->route_set); + hdr = route_set->next; + while (hdr != route_set) { + pj_list_insert_before( &dlg->route_set, hdr); + hdr = hdr->next; + } + return 0; +} + +/* + * Application calls this function when it wants to initiate an outgoing + * dialog (incoming dialogs are created automatically by UA when it receives + * INVITE, by calling pjsip_dlg_init_from_rdata()). + * This function should initialize most of the dialog's properties. + */ +PJ_DEF(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg, + const pj_str_t *c_local_info, + const pj_str_t *c_remote_info, + const pj_str_t *c_target) +{ + pj_time_val tv; + pjsip_event event; + pj_str_t buf; + + if (!dlg || !c_local_info || !c_remote_info) { + pj_assert(dlg && c_local_info && c_remote_info); + return -1; + } + + PJ_LOG(5, (dlg->obj_name, "initializing")); + + /* Init general dialog */ + if (dlg_init(dlg) != PJ_SUCCESS) { + return -1; + } + + /* Duplicate local info. */ + pj_strdup_with_null( dlg->pool, &buf, c_local_info); + + /* Build local URI. */ + dlg->local.target = pjsip_parse_uri(dlg->pool, buf.ptr, buf.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (dlg->local.target == NULL) { + PJ_LOG(2, (dlg->obj_name, + "pjsip_dlg_init: invalid local URI %s", buf.ptr)); + return -1; + } + + /* Set local URI. */ + dlg->local.info = pjsip_from_hdr_create(dlg->pool); + dlg->local.info->uri = dlg->local.target; + dlg->local.info->tag = dlg->local.tag; + + /* Create local Contact to be advertised in the response. */ + dlg->local.contact = pjsip_contact_hdr_create( dlg->pool ); + dlg->local.contact->star = 0; + dlg->local.contact->uri = dlg->local.target; + + /* Set remote URI. */ + dlg->remote.info = pjsip_to_hdr_create(dlg->pool); + + /* Duplicate to buffer. */ + pj_strdup_with_null( dlg->pool, &buf, c_remote_info); + + /* Build remote info. */ + dlg->remote.info->uri = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (dlg->remote.info->uri == NULL) { + PJ_LOG(2, (dlg->obj_name, + "pjsip_dlg_init: invalid remote URI %s", buf.ptr)); + return -1; + } + + /* Set remote Contact initially equal to the remote URI. */ + dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool); + dlg->remote.contact->star = 0; + dlg->remote.contact->uri = dlg->remote.info->uri; + + /* Set initial remote target. */ + if (c_target != NULL) { + pj_strdup_with_null( dlg->pool, &buf, c_target); + dlg->remote.target = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen, 0); + if (dlg->remote.target == NULL) { + PJ_LOG(2, (dlg->obj_name, + "pjsip_dlg_init: invalid remote target %s", buf.ptr)); + return -1; + } + } else { + dlg->remote.target = dlg->remote.info->uri; + } + + /* Create globally unique Call-ID */ + dlg->call_id = pjsip_cid_hdr_create(dlg->pool); + pj_create_unique_string( dlg->pool, &dlg->call_id->id ); + + /* Local and remote CSeq */ + pj_gettimeofday(&tv); + dlg->local.cseq = tv.sec & 0xFFFF; + dlg->remote.cseq = 0; + + /* Initial state is NULL. */ + event.type = event.src_type = PJSIP_EVENT_TX_MSG; + event.src.data = NULL; + dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event); + + /* Done. */ + PJ_LOG(4, (dlg->obj_name, "%s dialog initialized, From: %.*s, To: %.*s", + pjsip_role_name(dlg->role), + c_local_info->slen, c_local_info->ptr, + c_remote_info->slen, c_remote_info->ptr)); + + return PJ_SUCCESS; +} + +/* + * Set credentials. + */ +PJ_DEF(pj_status_t) pjsip_dlg_set_credentials( pjsip_dlg *dlg, + int count, + const pjsip_cred_info *cred) +{ + if (count > 0) { + dlg->cred_info = pj_pool_alloc(dlg->pool, count * sizeof(pjsip_cred_info)); + pj_memcpy(dlg->cred_info, cred, count * sizeof(pjsip_cred_info)); + } + dlg->cred_count = count; + return 0; +} + +/* + * Create a new request within dialog (i.e. after the dialog session has been + * established). The construction of such requests follows the rule in + * RFC3261 section 12.2.1. + */ +static void dlg_create_request_throw( pjsip_tx_data **p_tdata, + pjsip_dlg *dlg, + const pjsip_method *method, + int cseq ) +{ + pjsip_tx_data *tdata; + pjsip_contact_hdr *contact; + pjsip_route_hdr *route, *end_list; + + /* Contact Header field. + * Contact can only be present in requests that establish dialog (in the + * core SIP spec, only INVITE). + */ + if (method->id == PJSIP_INVITE_METHOD) + contact = dlg->local.contact; + else + contact = NULL; + + tdata = pjsip_endpt_create_request_from_hdr( dlg->ua->endpt, + method, + dlg->remote.target, + dlg->local.info, + dlg->remote.info, + contact, + dlg->call_id, + cseq, + NULL); + if (!tdata) { + PJ_THROW(1); + return; + } + + /* Just copy dialog route-set to Route header. + * The transaction will do the processing as specified in Section 12.2.1 + * of RFC 3261 in function tsx_process_route() in sip_transaction.c. + */ + route = dlg->route_set.next; + end_list = &dlg->route_set; + for (; route != end_list; route = route->next ) { + pjsip_route_hdr *r; + r = pjsip_hdr_shallow_clone( tdata->pool, route ); + pjsip_routing_hdr_set_route(r); + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)r); + } + + /* Copy authorization headers. */ + pjsip_auth_init_req( dlg->pool, tdata, &dlg->auth_sess, + dlg->cred_count, dlg->cred_info); + + *p_tdata = tdata; +} + + +/* + * This function is called by application to create new outgoing request + * message for this dialog. After the request is created, application can + * modify the message (such adding headers), and eventually send the request. + */ +PJ_DEF(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg, + const pjsip_method *method, + int cseq) +{ + PJ_USE_EXCEPTION; + struct dialog_lock_data lck; + pjsip_tx_data *tdata = NULL; + + pj_assert(dlg != NULL && method != NULL); + if (!dlg || !method) { + return NULL; + } + + PJ_LOG(5, (dlg->obj_name, "Creating request")); + + /* Lock dialog. */ + lock_dialog(dlg, &lck); + + /* Use outgoing CSeq and increment it by one. */ + if (cseq < 0) + cseq = dlg->local.cseq + 1; + + PJ_LOG(5, (dlg->obj_name, "creating request %.*s cseq=%d", + method->name.slen, method->name.ptr, cseq)); + + /* Create the request. */ + PJ_TRY { + dlg_create_request_throw(&tdata, dlg, method, cseq); + PJ_LOG(5, (dlg->obj_name, "request data %s created", tdata->obj_name)); + } + PJ_DEFAULT { + /* Failed! Delete transmit data. */ + if (tdata) { + pjsip_tx_data_dec_ref( tdata ); + tdata = NULL; + } + } + PJ_END; + + /* Unlock dialog. */ + unlock_dialog(dlg, &lck); + + return tdata; +} + +/* + * Sends request. + * Select the transport for the request message + */ +static pj_status_t dlg_send_request( pjsip_dlg *dlg, pjsip_tx_data *tdata ) +{ + pjsip_transaction *tsx; + pj_status_t status = PJ_SUCCESS; + struct dialog_lock_data lck; + + pj_assert(dlg != NULL && tdata != NULL); + if (!dlg || !tdata) { + return -1; + } + + PJ_LOG(5, (dlg->obj_name, "sending request %s", tdata->obj_name)); + + /* Lock dialog. */ + lock_dialog(dlg, &lck); + + /* Create a new transaction. */ + tsx = pjsip_endpt_create_tsx( dlg->ua->endpt ); + if (!tsx) { + unlock_dialog(dlg, &lck); + return -1; + } + + PJ_LOG(4, (dlg->obj_name, "Created new UAC transaction: %s", tsx->obj_name)); + + /* Initialize transaction */ + tsx->module_data[dlg->ua->mod_id] = dlg; + status = pjsip_tsx_init_uac( tsx, tdata ); + if (status != PJ_SUCCESS) { + unlock_dialog(dlg, &lck); + pjsip_endpt_destroy_tsx( dlg->ua->endpt, tsx ); + return -1; + } + pjsip_endpt_register_tsx( dlg->ua->endpt, tsx ); + + /* Start the transaction. */ + pjsip_tsx_on_tx_msg(tsx, tdata); + + /* Unlock dialog. */ + unlock_dialog(dlg, &lck); + + return status; +} + +/* + * This function can be called by application to send ANY outgoing message + * to remote party. + */ +PJ_DEF(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg, pjsip_tx_data *tdata ) +{ + pj_status_t status; + int tsx_status; + struct dialog_lock_data lck; + + pj_assert(dlg != NULL && tdata != NULL); + if (!dlg || !tdata) { + return -1; + } + + lock_dialog(dlg, &lck); + + if (tdata->msg->type == PJSIP_REQUEST_MSG) { + int request_cseq; + pjsip_msg *msg = tdata->msg; + pjsip_cseq_hdr *cseq_hdr; + + switch (msg->line.req.method.id) { + case PJSIP_CANCEL_METHOD: + + /* Check the INVITE transaction state. */ + tsx_status = dlg->invite_tsx->status_code; + if (tsx_status >= 200) { + /* Already terminated. Can't cancel. */ + status = -1; + goto on_return; + } + + /* If we've got provisional response, then send CANCEL and wait for + * the response to INVITE to arrive. Otherwise just send CANCEL and + * terminate the INVITE. + */ + if (tsx_status < 100) { + pjsip_tsx_terminate( dlg->invite_tsx, + PJSIP_SC_REQUEST_TERMINATED); + status = 0; + goto on_return; + } + + status = 0; + request_cseq = dlg->invite_tsx->cseq; + break; + + case PJSIP_ACK_METHOD: + /* Sending ACK outside of transaction is not supported at present! */ + pj_assert(0); + status = 0; + request_cseq = dlg->local.cseq; + break; + + case PJSIP_INVITE_METHOD: + /* For an initial INVITE, reset dialog state to NULL so we get + * 'normal' UAC notifications such as on_provisional(), etc. + * Initial INVITE is the request that is sent when the dialog has + * not been established yet. It's not necessarily the first INVITE + * sent, as when the Authorization fails, subsequent INVITE are also + * considered as an initial INVITE. + */ + if (dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) { + /* Set state to NULL. */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, NULL); + + } else { + /* This is a re-INVITE */ + } + status = 0; + request_cseq = dlg->local.cseq + 1; + break; + + default: + status = 0; + request_cseq = dlg->local.cseq + 1; + break; + } + + if (status != 0) + goto on_return; + + /* Update dialog's local CSeq, if necessary. */ + if (request_cseq != dlg->local.cseq) + dlg->local.cseq = request_cseq; + + /* Update CSeq header in the request. */ + cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg, + PJSIP_H_CSEQ, NULL); + pj_assert(cseq_hdr != NULL); + + /* Update the CSeq */ + cseq_hdr->cseq = request_cseq; + + /* Force the whole message to be re-printed. */ + pjsip_tx_data_invalidate_msg( tdata ); + + /* Now send the request. */ + status = dlg_send_request(dlg, tdata); + + } else { + /* + * This is only valid for sending response to INVITE! + */ + pjsip_cseq_hdr *cseq_hdr; + + if (dlg->invite_tsx == NULL || dlg->invite_tsx->status_code >= 200) { + status = -1; + goto on_return; + } + + cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg, + PJSIP_H_CSEQ, NULL); + pj_assert(cseq_hdr); + + if (cseq_hdr->method.id != PJSIP_INVITE_METHOD) { + status = -1; + goto on_return; + } + + pj_assert(cseq_hdr->cseq == dlg->invite_tsx->cseq); + + pjsip_tsx_on_tx_msg(dlg->invite_tsx, tdata); + status = 0; + } + +on_return: + /* Unlock dialog. */ + unlock_dialog(dlg, &lck); + + /* Whatever happen delete the message. */ + pjsip_tx_data_dec_ref( tdata ); + + return status; +} + +/* + * Sends outgoing invitation. + */ +PJ_DEF(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg ) +{ + pjsip_method method; + struct dialog_lock_data lck; + const pjsip_allow_hdr *allow_hdr; + pjsip_tx_data *tdata; + + pj_assert(dlg != NULL); + if (!dlg) { + return NULL; + } + + PJ_LOG(4, (dlg->obj_name, "request to send invitation")); + + /* Lock dialog. */ + lock_dialog(dlg, &lck); + + /* Create request. */ + pjsip_method_set( &method, PJSIP_INVITE_METHOD); + tdata = pjsip_dlg_create_request( dlg, &method, -1 ); + if (tdata == NULL) { + unlock_dialog(dlg, &lck); + return NULL; + } + + /* Invite SHOULD contain "Allow" header. */ + allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt ); + if (allow_hdr) { + pjsip_msg_add_hdr( tdata->msg, + pjsip_hdr_shallow_clone( tdata->pool, allow_hdr)); + } + + /* Unlock dialog. */ + unlock_dialog(dlg, &lck); + + return tdata; +} + +/* + * Cancel pending outgoing dialog invitation. + */ +PJ_DEF(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg ) +{ + pjsip_tx_data *tdata = NULL; + struct dialog_lock_data lck; + + pj_assert(dlg != NULL); + if (!dlg) { + return NULL; + } + + PJ_LOG(4, (dlg->obj_name, "request to cancel invitation")); + + lock_dialog(dlg, &lck); + + /* Check the INVITE transaction. */ + if (dlg->invite_tsx == NULL || dlg->role != PJSIP_ROLE_UAC) { + PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: " + "no INVITE transaction found")); + goto on_return; + } + + /* Construct the CANCEL request. */ + tdata = pjsip_endpt_create_cancel( dlg->ua->endpt, + dlg->invite_tsx->last_tx ); + if (tdata == NULL) { + PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: " + "unable to construct request")); + goto on_return; + } + + /* Add reference counter to tdata. */ + pjsip_tx_data_add_ref(tdata); + +on_return: + unlock_dialog(dlg, &lck); + return tdata; +} + + +/* + * Answer incoming dialog invitation, with either provisional responses + * or a final response. + */ +PJ_DEF(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code ) +{ + pjsip_tx_data *tdata = NULL; + pjsip_msg *msg; + struct dialog_lock_data lck; + + pj_assert(dlg != NULL); + if (!dlg) { + return NULL; + } + + PJ_LOG(4, (dlg->obj_name, "pjsip_dlg_answer: code=%d", code)); + + /* Lock dialog. */ + lock_dialog(dlg, &lck); + + /* Must have pending INVITE. */ + if (dlg->invite_tsx == NULL) { + PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: no INVITE transaction found")); + goto on_return; + } + /* Must be UAS. */ + if (dlg->role != PJSIP_ROLE_UAS) { + PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: not UAS")); + goto on_return; + } + /* Must have not answered with final response before. */ + if (dlg->invite_tsx->status_code >= 200) { + PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: transaction already terminated " + "with status %d", dlg->invite_tsx->status_code)); + goto on_return; + } + + /* Get transmit data and the message. + * We will rewrite the message with a new status code. + */ + only if tdata is not pending!!! + tdata = dlg->invite_tsx->last_tx; + msg = tdata->msg; + + /* Set status code and reason phrase. */ + if (code < 100 || code >= 700) code = 500; + msg->line.status.code = code; + msg->line.status.reason = *pjsip_get_status_text(code); + + /* For 2xx response, Contact and Record-Route must be added. */ + if (PJSIP_IS_STATUS_IN_CLASS(code,200)) { + const pjsip_allow_hdr *allow_hdr; + + if (pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL) == NULL) { + pjsip_contact_hdr *contact; + contact = pjsip_hdr_shallow_clone( tdata->pool, dlg->local.contact); + pjsip_msg_add_hdr( msg, (pjsip_hdr*)contact ); + } + + /* 2xx response MUST contain "Allow" header. */ + allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt ); + if (allow_hdr) { + pjsip_msg_add_hdr( msg, pjsip_hdr_shallow_clone( tdata->pool, allow_hdr)); + } + } + + /* for all but 100 responses, To-tag must be set. */ + if (code != 100) { + pjsip_to_hdr *to; + to = pjsip_msg_find_hdr( msg, PJSIP_H_TO, NULL); + to->tag = dlg->local.tag; + } + + /* Reset packet buffer. */ + pjsip_tx_data_invalidate_msg(tdata); + + /* Add reference counter */ + pjsip_tx_data_add_ref(tdata); + +on_return: + + /* Unlock dialog. */ + unlock_dialog(dlg, &lck); + + return tdata; +} + + +/* + * Send BYE request to terminate the dialog's session. + */ +PJ_DEF(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg ) +{ + pjsip_method method; + struct dialog_lock_data lck; + pjsip_tx_data *tdata; + + if (!dlg) { + pj_assert(dlg != NULL); + return NULL; + } + + PJ_LOG(4, (dlg->obj_name, "request to terminate session")); + + lock_dialog(dlg, &lck); + + pjsip_method_set( &method, PJSIP_BYE_METHOD); + tdata = pjsip_dlg_create_request( dlg, &method, -1 ); + + unlock_dialog(dlg, &lck); + + return tdata; +} + +/* + * High level function to disconnect dialog's session. Depending on dialog's + * state, this function will either send CANCEL, final response, or BYE to + * trigger the disconnection. A status code must be supplied, which will be + * sent if dialog will be transmitting a final response to INVITE. + */ +PJ_DEF(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg, + int status_code ) +{ + pjsip_tx_data *tdata = NULL; + + pj_assert(dlg != NULL); + if (!dlg) { + return NULL; + } + + switch (dlg->state) { + case PJSIP_DIALOG_STATE_INCOMING: + tdata = pjsip_dlg_answer(dlg, status_code); + break; + + case PJSIP_DIALOG_STATE_CALLING: + tdata = pjsip_dlg_cancel(dlg); + break; + + case PJSIP_DIALOG_STATE_PROCEEDING: + if (dlg->role == PJSIP_ROLE_UAC) { + tdata = pjsip_dlg_cancel(dlg); + } else { + tdata = pjsip_dlg_answer(dlg, status_code); + } + break; + + case PJSIP_DIALOG_STATE_ESTABLISHED: + tdata = pjsip_dlg_bye(dlg); + break; + + default: + PJ_LOG(4,(dlg->obj_name, "Invalid state %s in pjsip_dlg_disconnect()", + dlg_state_names[dlg->state])); + break; + } + + return tdata; +} + +/* + * Handling of the receipt of 2xx/INVITE response. + */ +static void dlg_on_recv_2xx_invite( pjsip_dlg *dlg, + pjsip_event *event ) +{ + pjsip_msg *msg; + pjsip_contact_hdr *contact; + pjsip_hdr *hdr, *end_hdr; + pjsip_method method; + pjsip_tx_data *ack_tdata; + + /* Get the message */ + msg = event->src.rdata->msg; + + /* Update remote's tag information. */ + pj_strdup(dlg->pool, &dlg->remote.info->tag, &event->src.rdata->to_tag); + + /* Copy Contact information in the 2xx/INVITE response to dialog's. + * remote contact + */ + contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL); + if (contact) { + dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact ); + } else { + /* duplicate contact from "From" header (?) */ + PJ_LOG(4,(dlg->obj_name, "Received 200/OK to INVITE with no Contact!")); + dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool); + dlg->remote.contact->uri = dlg->remote.info->uri; + } + + /* Copy Record-Route header (in reverse order) as dialog's route-set, + * overwriting previous route-set, if any, even if the received route-set + * is empty. + */ + pj_list_init(&dlg->route_set); + end_hdr = &msg->hdr; + for (hdr = msg->hdr.prev; hdr!=end_hdr; hdr = hdr->prev) { + if (hdr->type == PJSIP_H_RECORD_ROUTE) { + pjsip_route_hdr *r; + r = pjsip_hdr_clone(dlg->pool, hdr); + pjsip_routing_hdr_set_route(r); + pj_list_insert_before(&dlg->route_set, r); + } + } + + /* On receipt of 200/INVITE response, send ACK. + * This ack must be saved and retransmitted whenever we receive + * 200/INVITE retransmission, until 64*T1 seconds elapsed. + */ + pjsip_method_set( &method, PJSIP_ACK_METHOD); + ack_tdata = pjsip_dlg_create_request( dlg, &method, dlg->invite_tsx->cseq); + if (ack_tdata == NULL) { + //PJ_TODO(HANDLE_CREATE_ACK_FAILURE) + PJ_LOG(2, (dlg->obj_name, "Error sending ACK msg: can't create request")); + return; + } + + /* Send with the transaction. */ + pjsip_tsx_on_tx_ack( dlg->invite_tsx, ack_tdata); + + /* Decrement reference counter because pjsip_dlg_create_request + * automatically increments the request. + */ + pjsip_tx_data_dec_ref( ack_tdata ); +} + +/* + * State NULL, before any events have been received. + */ +static int dlg_on_state_null( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && + event->src_type == PJSIP_EVENT_RX_MSG) + { + pjsip_hdr *hdr, *hdr_list; + + pj_assert(tsx->method.id == PJSIP_INVITE_METHOD); + + /* Save the INVITE transaction. */ + dlg->invite_tsx = tsx; + + /* Change state to INCOMING */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_INCOMING, event); + + /* Create response buffer. */ + tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, 100); + pjsip_tx_data_add_ref(tsx->last_tx); + + /* Copy the Record-Route headers into dialog's route_set, maintaining + * the order. + */ + pj_list_init(&dlg->route_set); + hdr_list = &event->src.rdata->msg->hdr; + hdr = hdr_list->next; + while (hdr != hdr_list) { + if (hdr->type == PJSIP_H_RECORD_ROUTE) { + pjsip_route_hdr *route; + route = pjsip_hdr_clone(dlg->pool, hdr); + pjsip_routing_hdr_set_route(route); + pj_list_insert_before(&dlg->route_set, route); + } + hdr = hdr->next; + } + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + + } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && + event->src_type == PJSIP_EVENT_TX_MSG) + { + pj_assert(tsx->method.id == PJSIP_INVITE_METHOD); + + /* Save the INVITE transaction. */ + dlg->invite_tsx = tsx; + + /* Change state to CALLING. */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_CALLING, event); + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + + } else { + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); + } + + return 0; +} + +/* + * State INCOMING is after the (callee) dialog has been initialized with + * the incoming request, but before any responses is sent by the dialog. + */ +static int dlg_on_state_incoming( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + return dlg_on_state_proceeding_callee( dlg, tsx, event ); +} + +/* + * State CALLING is after the (caller) dialog has sent outgoing invitation + * but before any responses are received. + */ +static int dlg_on_state_calling( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + if (tsx == dlg->invite_tsx) { + return dlg_on_state_proceeding_caller( dlg, tsx, event ); + } + return 0; +} + +/* + * State PROCEEDING is after provisional response is received. + * Since the processing is similar to state CALLING, this function is also + * called for CALLING state. + */ +static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + int dlg_is_terminated = 0; + + /* We only care about our INVITE transaction. + * Ignore other transaction progression (such as CANCEL). + */ + if (tsx != dlg->invite_tsx) { + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); + return 0; + } + + if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED) { + switch (tsx->state) { + case PJSIP_TSX_STATE_PROCEEDING: + if (dlg->state != PJSIP_DIALOG_STATE_PROCEEDING) { + /* Change state to PROCEEDING */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event); + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + } else { + /* Also notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); + } + break; + + case PJSIP_TSX_STATE_COMPLETED: + /* Change dialog state. */ + if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { + /* Update remote target, take it from the contact hdr. */ + pjsip_contact_hdr *contact; + contact = pjsip_msg_find_hdr(event->src.rdata->msg, + PJSIP_H_CONTACT, NULL); + if (contact) { + dlg->remote.target = pjsip_uri_clone(dlg->pool, contact->uri); + } else { + PJ_LOG(4,(dlg->obj_name, + "Warning: found no Contact hdr in 200/OK")); + } + dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event); + } else if (tsx->status_code==401 || tsx->status_code==407) { + /* Handle Authentication challenge. */ + pjsip_tx_data *tdata; + tdata = pjsip_auth_reinit_req( dlg->ua->endpt, + dlg->pool, &dlg->auth_sess, + dlg->cred_count, dlg->cred_info, + tsx->last_tx, event->src.rdata); + if (tdata) { + /* Re-use original request, with a new transaction. + * Need not to worry about CSeq, dialog will take care. + */ + pjsip_dlg_send_msg(dlg, tdata); + return 0; + } else { + dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); + } + } else { + dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); + } + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + + /* Send ACK when dialog is connected. */ + if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { + pj_assert(event->src_type == PJSIP_EVENT_RX_MSG); + dlg_on_recv_2xx_invite(dlg, event); + } + break; + + case PJSIP_TSX_STATE_TERMINATED: + /* + * Transaction is terminated because of timeout or transport error. + * To let the application go to normal state progression, call the + * callback twice. First is to emulate disconnection, and then call + * again (with state TERMINATED) to destroy the dialog. + */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + + /* The INVITE transaction will be destroyed, so release reference + * to it. + */ + dlg->invite_tsx = NULL; + + /* We should terminate the dialog now. + * But it's possible that we have other pending transactions (for + * example, outgoing CANCEL is in progress). + * So destroy the dialog only if there's no other transaction. + */ + if (dlg->pending_tsx_count == 0) { + dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event); + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + dlg_is_terminated = 1; + } + break; + + default: + pj_assert(0); + break; + } + } else { + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); + } + return dlg_is_terminated ? -1 : 0; +} + +/* + * State PROCEEDING for UAS is after the callee send provisional response. + * This function is also called for INCOMING state. + */ +static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + int dlg_is_terminated = 0; + + pj_assert( dlg->invite_tsx != NULL ); + + if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && + event->src_type == PJSIP_EVENT_TX_MSG && + tsx == dlg->invite_tsx) + { + switch (tsx->state) { + case PJSIP_TSX_STATE_PROCEEDING: + /* Change state to PROCEEDING */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event); + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + break; + + case PJSIP_TSX_STATE_COMPLETED: + case PJSIP_TSX_STATE_TERMINATED: + /* Change dialog state. */ + if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { + dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event); + } else { + dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); + } + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + + /* If transaction is terminated in non-2xx situation, + * terminate dialog as well. This happens when something unexpected + * occurs, such as transport error. + */ + if (tsx->state == PJSIP_TSX_STATE_TERMINATED && + !PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) + { + dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event); + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + dlg_is_terminated = 1; + } + break; + + default: + pj_assert(0); + break; + } + + } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && + event->src_type == PJSIP_EVENT_RX_MSG && + tsx->method.id == PJSIP_CANCEL_METHOD) + { + pjsip_tx_data *tdata; + + /* Check if sequence number matches the pending INVITE. */ + if (dlg->invite_tsx==NULL || + pj_strcmp(&tsx->branch, &dlg->invite_tsx->branch) != 0) + { + PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no matching INVITE")); + + /* No matching INVITE transaction found. */ + tdata = pjsip_endpt_create_response(dlg->ua->endpt, + event->src.rdata, + PJSIP_SC_CALL_TSX_DOES_NOT_EXIST ); + pjsip_tsx_on_tx_msg(tsx, tdata); + return 0; + } + + /* Always respond the CANCEL with 200/CANCEL no matter what. */ + tdata = pjsip_endpt_create_response(dlg->ua->endpt, + event->src.rdata, + 200 ); + pjsip_tsx_on_tx_msg( tsx, tdata ); + + /* Respond the INVITE transaction with 487, only if transaction has + * not completed. + */ + if (dlg->invite_tsx->last_tx) { + if (dlg->invite_tsx->status_code < 200) { + tdata = dlg->invite_tsx->last_tx; + tdata->msg->line.status.code = 487; + tdata->msg->line.status.reason = *pjsip_get_status_text(487); + /* Reset packet buffer. */ + pjsip_tx_data_invalidate_msg(tdata); + pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata ); + } else { + PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no effect, " + "Transaction already terminated " + "with status %d", + dlg->invite_tsx->status_code)); + } + } else { + tdata = pjsip_endpt_create_response(dlg->ua->endpt, + event->src.rdata, + 487); + pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata ); + } + } else { + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); + } + + return dlg_is_terminated ? -1 : 0; +} + +static int dlg_on_state_proceeding( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + if (dlg->role == PJSIP_ROLE_UAC) { + return dlg_on_state_proceeding_caller( dlg, tsx, event ); + } else { + return dlg_on_state_proceeding_callee( dlg, tsx, event ); + } +} + +static int dlg_on_state_connecting( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + if (tsx == dlg->invite_tsx) { + if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && + (tsx->state == PJSIP_TSX_STATE_TERMINATED || + tsx->state == PJSIP_TSX_STATE_COMPLETED || + tsx->state == PJSIP_TSX_STATE_CONFIRMED)) + { + if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { + dlg_set_state(dlg, PJSIP_DIALOG_STATE_ESTABLISHED, event); + } else { + /* Probably because we never get the ACK, or transport error + * when sending ACK. + */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); + } + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + } else { + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); + } + } else { + /* Handle case when transaction is started when dialog is connecting + * (e.g. BYE requests cross wire. + */ + if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && + event->src_type == PJSIP_EVENT_RX_MSG && + tsx->role == PJSIP_ROLE_UAS) + { + pjsip_tx_data *response; + + if (tsx->status_code >= 200) + return 0; + + if (tsx->method.id == PJSIP_BYE_METHOD) { + /* Set state to DISCONNECTED. */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + + response = pjsip_endpt_create_response( dlg->ua->endpt, + event->src.rdata, 200); + } else { + response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, + PJSIP_SC_INTERNAL_SERVER_ERROR); + } + + if (response) + pjsip_tsx_on_tx_msg(tsx, response); + + return 0; + } + } + return 0; +} + +static int dlg_on_state_established( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + PJ_UNUSED_ARG(tsx) + + if (tsx && tsx->method.id == PJSIP_BYE_METHOD) { + /* Set state to DISCONNECTED. */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + + /* Answer with 200/BYE. */ + if (event->src_type == PJSIP_EVENT_RX_MSG) { + pjsip_tx_data *tdata; + tdata = pjsip_endpt_create_response(dlg->ua->endpt, + event->src.rdata, + 200 ); + if (tdata) + pjsip_tsx_on_tx_msg( tsx, tdata ); + } + } else if (tsx && event->src_type == PJSIP_EVENT_RX_MSG) { + pjsip_method_e method = event->src.rdata->cseq->method.id; + + PJ_TODO(PROPERLY_HANDLE_REINVITATION) + + /* Reinvitation. The message may be INVITE or an ACK. */ + if (method == PJSIP_INVITE_METHOD) { + if (dlg->invite_tsx && dlg->invite_tsx->status_code < 200) { + /* Section 14.2: A UAS that receives a second INVITE before it + * sends the final response to a first INVITE with a lower + * CSeq sequence number on the same dialog MUST return a 500 + * (Server Internal Error) response to the second INVITE and + * MUST include a Retry-After header field with a randomly + * chosen value of between 0 and 10 seconds. + */ + pjsip_retry_after_hdr *hdr; + pjsip_tx_data *tdata = + pjsip_endpt_create_response(dlg->ua->endpt, + event->src.rdata, 500); + + if (!tdata) + return 0; + + /* Add Retry-After. */ + hdr = pjsip_retry_after_hdr_create(tdata->pool); + hdr->ivalue = 9; + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); + + /* Send. */ + pjsip_tsx_on_tx_msg(tsx, tdata); + + return 0; + } + + /* Keep this as our current INVITE transaction. */ + dlg->invite_tsx = tsx; + + /* Create response buffer. */ + tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt, + event->src.rdata, 100); + pjsip_tx_data_add_ref(tsx->last_tx); + + } + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_MID_CALL_REQUEST, event); + + } else { + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); + } + + return 0; +} + +static int dlg_on_state_disconnected( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + PJ_UNUSED_ARG(tsx) + + /* Handle case when transaction is started when dialog is disconnected + * (e.g. BYE requests cross wire. + */ + if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && + event->src_type == PJSIP_EVENT_RX_MSG && + tsx->role == PJSIP_ROLE_UAS) + { + pjsip_tx_data *response = NULL; + + if (tsx->status_code >= 200) + return 0; + + if (tsx->method.id == PJSIP_BYE_METHOD) { + response = pjsip_endpt_create_response( dlg->ua->endpt, + event->src.rdata, 200); + } else { + response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, + PJSIP_SC_INTERNAL_SERVER_ERROR); + } + if (response) + pjsip_tsx_on_tx_msg(tsx, response); + + return 0; + } + /* Handle case when outgoing BYE was rejected with 401/407 */ + else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && + event->src_type == PJSIP_EVENT_RX_MSG && + tsx->role == PJSIP_ROLE_UAC) + { + if (tsx->status_code==401 || tsx->status_code==407) { + pjsip_tx_data *tdata; + tdata = pjsip_auth_reinit_req( dlg->ua->endpt, dlg->pool, + &dlg->auth_sess, + dlg->cred_count, dlg->cred_info, + tsx->last_tx, event->src.rdata); + if (tdata) { + pjsip_dlg_send_msg(dlg, tdata); + } + } + } + + + if (dlg->pending_tsx_count == 0) { + /* Set state to TERMINATED. */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event); + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + + return -1; + } else { + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); + } + + return 0; +} + +static int dlg_on_state_terminated( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + PJ_UNUSED_ARG(dlg) + PJ_UNUSED_ARG(tsx) + PJ_UNUSED_ARG(event) + + return -1; +} + diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c index e7bf9bd6..1b22170e 100644 --- a/pjsip/src/pjsip-ua/sip_reg.c +++ b/pjsip/src/pjsip-ua/sip_reg.c @@ -1,511 +1,511 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_mod_ua/sip_reg.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_parser.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_auth_msg.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/guid.h>
-#include <pj/log.h>
-
-#define REFRESH_TIMER 1
-#define DELAY_BEFORE_REFRESH 5
-#define THIS_FILE "sip_regc.c"
-
-/**
- * SIP client registration structure.
- */
-struct pjsip_regc
-{
- pj_pool_t *pool;
- pjsip_endpoint *endpt;
- pj_bool_t _delete_flag;
- int pending_tsx;
-
- void *token;
- pjsip_regc_cb *cb;
-
- pj_str_t str_srv_url;
- pjsip_uri *srv_url;
- pjsip_cid_hdr *cid_hdr;
- pjsip_cseq_hdr *cseq_hdr;
- pjsip_from_hdr *from_hdr;
- pjsip_to_hdr *to_hdr;
- char *contact_buf;
- pjsip_generic_string_hdr *contact_hdr;
- pjsip_expires_hdr *expires_hdr;
- pjsip_contact_hdr *unreg_contact_hdr;
- pjsip_expires_hdr *unreg_expires_hdr;
- pj_uint32_t expires;
-
- /* Credentials. */
- int cred_count;
- pjsip_cred_info *cred_info;
-
- /* Authorization sessions. */
- pjsip_auth_session auth_sess_list;
-
- /* Auto refresh registration. */
- pj_bool_t auto_reg;
- pj_timer_entry timer;
-};
-
-
-
-PJ_DEF(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
- pjsip_regc_cb *cb)
-{
- pj_pool_t *pool;
- pjsip_regc *regc;
-
- if (cb == NULL)
- return NULL;
-
- pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
- regc = pj_pool_calloc(pool, 1, sizeof(struct pjsip_regc));
-
- regc->pool = pool;
- regc->endpt = endpt;
- regc->token = token;
- regc->cb = cb;
- regc->contact_buf = pj_pool_alloc(pool, PJSIP_REGC_CONTACT_BUF_SIZE);
- regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
-
- pj_list_init(®c->auth_sess_list);
-
- return regc;
-}
-
-
-PJ_DEF(void) pjsip_regc_destroy(pjsip_regc *regc)
-{
- if (regc->pending_tsx) {
- regc->_delete_flag = 1;
- regc->cb = NULL;
- } else {
- pjsip_endpt_destroy_pool(regc->endpt, regc->pool);
- }
-}
-
-
-PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
-{
- return regc->pool;
-}
-
-static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
-{
- if (expires != regc->expires) {
- regc->expires_hdr = pjsip_expires_hdr_create(regc->pool);
- regc->expires_hdr->ivalue = expires;
- } else {
- regc->expires_hdr = NULL;
- }
-}
-
-
-static pj_status_t set_contact( pjsip_regc *regc,
- int contact_cnt,
- const pj_str_t contact[] )
-{
- int i;
- char *s;
- const pj_str_t contact_STR = { "Contact", 7};
-
- /* Concatenate contacts. */
- for (i=0, s=regc->contact_buf; i<contact_cnt; ++i) {
- if ((s-regc->contact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) {
- return -1;
- }
- pj_memcpy(s, contact[i].ptr, contact[i].slen);
- s += contact[i].slen;
-
- if (i != contact_cnt - 1) {
- *s++ = ',';
- *s++ = ' ';
- }
- }
-
- /* Set "Contact" header. */
- regc->contact_hdr = pjsip_generic_string_hdr_create( regc->pool, &contact_STR);
- regc->contact_hdr->hvalue.ptr = regc->contact_buf;
- regc->contact_hdr->hvalue.slen = (s - regc->contact_buf);
-
- return 0;
-}
-
-
-PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
- const pj_str_t *srv_url,
- const pj_str_t *from_url,
- const pj_str_t *to_url,
- int contact_cnt,
- const pj_str_t contact[],
- pj_uint32_t expires)
-{
- pj_str_t tmp;
-
- /* Copy server URL. */
- pj_strdup_with_null(regc->pool, ®c->str_srv_url, srv_url);
-
- /* Set server URL. */
- tmp = regc->str_srv_url;
- regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
- if (regc->srv_url == NULL) {
- return -1;
- }
-
- /* Set "From" header. */
- pj_strdup_with_null(regc->pool, &tmp, from_url);
- regc->from_hdr = pjsip_from_hdr_create(regc->pool);
- regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (!regc->from_hdr->uri) {
- PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", from_url->slen, from_url->ptr));
- return -1;
- }
-
- /* Set "To" header. */
- pj_strdup_with_null(regc->pool, &tmp, to_url);
- regc->to_hdr = pjsip_to_hdr_create(regc->pool);
- regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (!regc->to_hdr->uri) {
- PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
- return -1;
- }
-
-
- /* Set "Contact" header. */
- if (set_contact( regc, contact_cnt, contact) != 0)
- return -1;
-
- /* Set "Expires" header, if required. */
- set_expires( regc, expires);
-
- /* Set "Call-ID" header. */
- regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
- pj_create_unique_string(regc->pool, ®c->cid_hdr->id);
-
- /* Set "CSeq" header. */
- regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
- regc->cseq_hdr->cseq = 0;
- pjsip_method_set( ®c->cseq_hdr->method, PJSIP_REGISTER_METHOD);
-
- /* Create "Contact" header used in unregistration. */
- regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool);
- regc->unreg_contact_hdr->star = 1;
-
- /* Create "Expires" header used in unregistration. */
- regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool);
- regc->unreg_expires_hdr->ivalue = 0;
-
- /* Done. */
- return 0;
-}
-
-PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
- int count,
- const pjsip_cred_info cred[] )
-{
- if (count > 0) {
- regc->cred_info = pj_pool_alloc(regc->pool, count * sizeof(pjsip_cred_info));
- pj_memcpy(regc->cred_info, cred, count * sizeof(pjsip_cred_info));
- }
- regc->cred_count = count;
- return 0;
-}
-
-static pjsip_tx_data *create_request(pjsip_regc *regc)
-{
- pjsip_tx_data *tdata;
- pjsip_msg *msg;
-
- /* Create transmit data. */
- tdata = pjsip_endpt_create_tdata(regc->endpt);
- if (!tdata) {
- return NULL;
- }
-
- /* Create request message. */
- msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
- tdata->msg = msg;
-
- /* Initialize request line. */
- pjsip_method_set(&msg->line.req.method, PJSIP_REGISTER_METHOD);
- msg->line.req.uri = regc->srv_url;
-
- /* Add headers. */
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->from_hdr);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->to_hdr);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cid_hdr);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cseq_hdr);
-
- /* Add cached authorization headers. */
- pjsip_auth_init_req( regc->pool, tdata, ®c->auth_sess_list,
- regc->cred_count, regc->cred_info );
-
- /* Add reference counter to transmit data. */
- pjsip_tx_data_add_ref(tdata);
-
- return tdata;
-}
-
-
-PJ_DEF(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg)
-{
- pjsip_msg *msg;
- pjsip_tx_data *tdata;
-
- tdata = create_request(regc);
- if (!tdata)
- return NULL;
-
- msg = tdata->msg;
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->contact_hdr);
- if (regc->expires_hdr)
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->expires_hdr);
-
- if (regc->timer.id != 0) {
- pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
- regc->timer.id = 0;
- }
-
- regc->auto_reg = autoreg;
-
- return tdata;
-}
-
-
-PJ_DEF(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc)
-{
- pjsip_tx_data *tdata;
- pjsip_msg *msg;
-
- if (regc->timer.id != 0) {
- pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
- regc->timer.id = 0;
- }
-
- tdata = create_request(regc);
- if (!tdata)
- return NULL;
-
- msg = tdata->msg;
- pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr);
- pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr);
-
- return tdata;
-}
-
-
-PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
- int contact_cnt,
- const pj_str_t contact[] )
-{
- return set_contact( regc, contact_cnt, contact );
-}
-
-
-PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
- pj_uint32_t expires )
-{
- set_expires( regc, expires );
- return 0;
-}
-
-
-static void call_callback(pjsip_regc *regc, int status, const pj_str_t *reason,
- pjsip_rx_data *rdata, pj_int32_t expiration,
- int contact_cnt, pjsip_contact_hdr *contact[])
-{
- struct pjsip_regc_cbparam cbparam;
-
-
- cbparam.regc = regc;
- cbparam.token = regc->token;
- cbparam.code = status;
- cbparam.reason = *reason;
- cbparam.rdata = rdata;
- cbparam.contact_cnt = contact_cnt;
- cbparam.expiration = expiration;
- if (contact_cnt) {
- pj_memcpy( cbparam.contact, contact,
- contact_cnt*sizeof(pjsip_contact_hdr*));
- }
-
- (*regc->cb)(&cbparam);
-}
-
-static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
- struct pj_timer_entry *entry)
-{
- pjsip_regc *regc = entry->user_data;
- pjsip_tx_data *tdata;
-
- PJ_UNUSED_ARG(timer_heap)
-
- entry->id = 0;
- tdata = pjsip_regc_register(regc, 1);
- if (tdata) {
- pjsip_regc_send(regc, tdata);
- } else {
- pj_str_t reason = pj_str("Unable to create txdata");
- call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
- }
-}
-
-static void tsx_callback(void *token, pjsip_event *event)
-{
- pjsip_regc *regc = token;
- pjsip_transaction *tsx = event->obj.tsx;
-
- /* If registration data has been deleted by user then remove registration
- * data from transaction's callback, and don't call callback.
- */
- if (regc->_delete_flag) {
- --regc->pending_tsx;
-
- } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
- tsx->status_code == PJSIP_SC_UNAUTHORIZED)
- {
- pjsip_rx_data *rdata = event->src.rdata;
- pjsip_tx_data *tdata;
-
- tdata = pjsip_auth_reinit_req( regc->endpt,
- regc->pool, ®c->auth_sess_list,
- regc->cred_count, regc->cred_info,
- tsx->last_tx, event->src.rdata );
-
- if (tdata) {
- --regc->pending_tsx;
- pjsip_regc_send(regc, tdata);
- return;
- } else {
- call_callback(regc, tsx->status_code, &rdata->msg->line.status.reason,
- rdata, -1, 0, NULL);
- --regc->pending_tsx;
- }
- } else {
- int contact_cnt = 0;
- pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
- pjsip_rx_data *rdata;
- pj_int32_t expiration = 0xFFFF;
-
- if (tsx->status_code/100 == 2) {
- int i;
- pjsip_contact_hdr *hdr;
- pjsip_msg *msg;
- pjsip_expires_hdr *expires;
-
- rdata = event->src.rdata;
- msg = rdata->msg;
- hdr = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
- while (hdr) {
- contact[contact_cnt++] = hdr;
- hdr = hdr->next;
- if (hdr == (void*)&msg->hdr)
- break;
- hdr = pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, hdr);
- }
-
- expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
-
- if (expires)
- expiration = expires->ivalue;
-
- for (i=0; i<contact_cnt; ++i) {
- hdr = contact[i];
- if (hdr->expires >= 0 && hdr->expires < expiration)
- expiration = contact[i]->expires;
- }
-
- if (regc->auto_reg && expiration != 0 && expiration != 0xFFFF) {
- pj_time_val delay = { 0, 0};
-
- delay.sec = expiration - DELAY_BEFORE_REFRESH;
- if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
- delay.sec > (pj_int32_t)regc->expires)
- {
- delay.sec = regc->expires;
- }
- if (delay.sec < DELAY_BEFORE_REFRESH)
- delay.sec = DELAY_BEFORE_REFRESH;
- regc->timer.cb = ®c_refresh_timer_cb;
- regc->timer.id = REFRESH_TIMER;
- regc->timer.user_data = regc;
- pjsip_endpt_schedule_timer( regc->endpt, ®c->timer, &delay);
- }
-
- } else {
- rdata = (event->src_type==PJSIP_EVENT_RX_MSG) ? event->src.rdata : NULL;
- }
-
-
- /* Call callback. */
- if (expiration == 0xFFFF) expiration = -1;
- call_callback(regc, tsx->status_code,
- (rdata ? &rdata->msg->line.status.reason
- : pjsip_get_status_text(tsx->status_code)),
- rdata, expiration,
- contact_cnt, contact);
-
- --regc->pending_tsx;
- }
-
- /* Delete the record if user destroy regc during the callback. */
- if (regc->_delete_flag && regc->pending_tsx==0) {
- pjsip_regc_destroy(regc);
- }
-}
-
-PJ_DEF(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
-{
- int status;
-
- /* Make sure we don't have pending transaction. */
- if (regc->pending_tsx) {
- pj_str_t reason = pj_str("Transaction in progress");
- call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
- pjsip_tx_data_dec_ref( tdata );
- return;
- }
-
- /* Invalidate message buffer. */
- pjsip_tx_data_invalidate_msg(tdata);
-
- /* Increment CSeq */
- regc->cseq_hdr->cseq++;
-
- /* Send. */
- status = pjsip_endpt_send_request(regc->endpt, tdata, -1, regc, &tsx_callback);
- if (status==0)
- ++regc->pending_tsx;
- else {
- pj_str_t reason = pj_str("Unable to send request.");
- call_callback(regc, status, &reason, NULL, -1, 0, NULL);
- }
-}
-
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip_mod_ua/sip_reg.h> +#include <pjsip/sip_endpoint.h> +#include <pjsip/sip_parser.h> +#include <pjsip/sip_module.h> +#include <pjsip/sip_transaction.h> +#include <pjsip/sip_event.h> +#include <pjsip/sip_util.h> +#include <pjsip/sip_auth_msg.h> +#include <pj/pool.h> +#include <pj/string.h> +#include <pj/guid.h> +#include <pj/log.h> + +#define REFRESH_TIMER 1 +#define DELAY_BEFORE_REFRESH 5 +#define THIS_FILE "sip_regc.c" + +/** + * SIP client registration structure. + */ +struct pjsip_regc +{ + pj_pool_t *pool; + pjsip_endpoint *endpt; + pj_bool_t _delete_flag; + int pending_tsx; + + void *token; + pjsip_regc_cb *cb; + + pj_str_t str_srv_url; + pjsip_uri *srv_url; + pjsip_cid_hdr *cid_hdr; + pjsip_cseq_hdr *cseq_hdr; + pjsip_from_hdr *from_hdr; + pjsip_to_hdr *to_hdr; + char *contact_buf; + pjsip_generic_string_hdr *contact_hdr; + pjsip_expires_hdr *expires_hdr; + pjsip_contact_hdr *unreg_contact_hdr; + pjsip_expires_hdr *unreg_expires_hdr; + pj_uint32_t expires; + + /* Credentials. */ + int cred_count; + pjsip_cred_info *cred_info; + + /* Authorization sessions. */ + pjsip_auth_session auth_sess_list; + + /* Auto refresh registration. */ + pj_bool_t auto_reg; + pj_timer_entry timer; +}; + + + +PJ_DEF(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token, + pjsip_regc_cb *cb) +{ + pj_pool_t *pool; + pjsip_regc *regc; + + if (cb == NULL) + return NULL; + + pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024); + regc = pj_pool_calloc(pool, 1, sizeof(struct pjsip_regc)); + + regc->pool = pool; + regc->endpt = endpt; + regc->token = token; + regc->cb = cb; + regc->contact_buf = pj_pool_alloc(pool, PJSIP_REGC_CONTACT_BUF_SIZE); + regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED; + + pj_list_init(®c->auth_sess_list); + + return regc; +} + + +PJ_DEF(void) pjsip_regc_destroy(pjsip_regc *regc) +{ + if (regc->pending_tsx) { + regc->_delete_flag = 1; + regc->cb = NULL; + } else { + pjsip_endpt_destroy_pool(regc->endpt, regc->pool); + } +} + + +PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc) +{ + return regc->pool; +} + +static void set_expires( pjsip_regc *regc, pj_uint32_t expires) +{ + if (expires != regc->expires) { + regc->expires_hdr = pjsip_expires_hdr_create(regc->pool); + regc->expires_hdr->ivalue = expires; + } else { + regc->expires_hdr = NULL; + } +} + + +static pj_status_t set_contact( pjsip_regc *regc, + int contact_cnt, + const pj_str_t contact[] ) +{ + int i; + char *s; + const pj_str_t contact_STR = { "Contact", 7}; + + /* Concatenate contacts. */ + for (i=0, s=regc->contact_buf; i<contact_cnt; ++i) { + if ((s-regc->contact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) { + return -1; + } + pj_memcpy(s, contact[i].ptr, contact[i].slen); + s += contact[i].slen; + + if (i != contact_cnt - 1) { + *s++ = ','; + *s++ = ' '; + } + } + + /* Set "Contact" header. */ + regc->contact_hdr = pjsip_generic_string_hdr_create( regc->pool, &contact_STR); + regc->contact_hdr->hvalue.ptr = regc->contact_buf; + regc->contact_hdr->hvalue.slen = (s - regc->contact_buf); + + return 0; +} + + +PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc, + const pj_str_t *srv_url, + const pj_str_t *from_url, + const pj_str_t *to_url, + int contact_cnt, + const pj_str_t contact[], + pj_uint32_t expires) +{ + pj_str_t tmp; + + /* Copy server URL. */ + pj_strdup_with_null(regc->pool, ®c->str_srv_url, srv_url); + + /* Set server URL. */ + tmp = regc->str_srv_url; + regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0); + if (regc->srv_url == NULL) { + return -1; + } + + /* Set "From" header. */ + pj_strdup_with_null(regc->pool, &tmp, from_url); + regc->from_hdr = pjsip_from_hdr_create(regc->pool); + regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (!regc->from_hdr->uri) { + PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", from_url->slen, from_url->ptr)); + return -1; + } + + /* Set "To" header. */ + pj_strdup_with_null(regc->pool, &tmp, to_url); + regc->to_hdr = pjsip_to_hdr_create(regc->pool); + regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (!regc->to_hdr->uri) { + PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr)); + return -1; + } + + + /* Set "Contact" header. */ + if (set_contact( regc, contact_cnt, contact) != 0) + return -1; + + /* Set "Expires" header, if required. */ + set_expires( regc, expires); + + /* Set "Call-ID" header. */ + regc->cid_hdr = pjsip_cid_hdr_create(regc->pool); + pj_create_unique_string(regc->pool, ®c->cid_hdr->id); + + /* Set "CSeq" header. */ + regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool); + regc->cseq_hdr->cseq = 0; + pjsip_method_set( ®c->cseq_hdr->method, PJSIP_REGISTER_METHOD); + + /* Create "Contact" header used in unregistration. */ + regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool); + regc->unreg_contact_hdr->star = 1; + + /* Create "Expires" header used in unregistration. */ + regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool); + regc->unreg_expires_hdr->ivalue = 0; + + /* Done. */ + return 0; +} + +PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc, + int count, + const pjsip_cred_info cred[] ) +{ + if (count > 0) { + regc->cred_info = pj_pool_alloc(regc->pool, count * sizeof(pjsip_cred_info)); + pj_memcpy(regc->cred_info, cred, count * sizeof(pjsip_cred_info)); + } + regc->cred_count = count; + return 0; +} + +static pjsip_tx_data *create_request(pjsip_regc *regc) +{ + pjsip_tx_data *tdata; + pjsip_msg *msg; + + /* Create transmit data. */ + tdata = pjsip_endpt_create_tdata(regc->endpt); + if (!tdata) { + return NULL; + } + + /* Create request message. */ + msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); + tdata->msg = msg; + + /* Initialize request line. */ + pjsip_method_set(&msg->line.req.method, PJSIP_REGISTER_METHOD); + msg->line.req.uri = regc->srv_url; + + /* Add headers. */ + pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->from_hdr); + pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->to_hdr); + pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cid_hdr); + pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cseq_hdr); + + /* Add cached authorization headers. */ + pjsip_auth_init_req( regc->pool, tdata, ®c->auth_sess_list, + regc->cred_count, regc->cred_info ); + + /* Add reference counter to transmit data. */ + pjsip_tx_data_add_ref(tdata); + + return tdata; +} + + +PJ_DEF(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg) +{ + pjsip_msg *msg; + pjsip_tx_data *tdata; + + tdata = create_request(regc); + if (!tdata) + return NULL; + + msg = tdata->msg; + pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->contact_hdr); + if (regc->expires_hdr) + pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->expires_hdr); + + if (regc->timer.id != 0) { + pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); + regc->timer.id = 0; + } + + regc->auto_reg = autoreg; + + return tdata; +} + + +PJ_DEF(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc) +{ + pjsip_tx_data *tdata; + pjsip_msg *msg; + + if (regc->timer.id != 0) { + pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); + regc->timer.id = 0; + } + + tdata = create_request(regc); + if (!tdata) + return NULL; + + msg = tdata->msg; + pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr); + pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr); + + return tdata; +} + + +PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc, + int contact_cnt, + const pj_str_t contact[] ) +{ + return set_contact( regc, contact_cnt, contact ); +} + + +PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc, + pj_uint32_t expires ) +{ + set_expires( regc, expires ); + return 0; +} + + +static void call_callback(pjsip_regc *regc, int status, const pj_str_t *reason, + pjsip_rx_data *rdata, pj_int32_t expiration, + int contact_cnt, pjsip_contact_hdr *contact[]) +{ + struct pjsip_regc_cbparam cbparam; + + + cbparam.regc = regc; + cbparam.token = regc->token; + cbparam.code = status; + cbparam.reason = *reason; + cbparam.rdata = rdata; + cbparam.contact_cnt = contact_cnt; + cbparam.expiration = expiration; + if (contact_cnt) { + pj_memcpy( cbparam.contact, contact, + contact_cnt*sizeof(pjsip_contact_hdr*)); + } + + (*regc->cb)(&cbparam); +} + +static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap, + struct pj_timer_entry *entry) +{ + pjsip_regc *regc = entry->user_data; + pjsip_tx_data *tdata; + + PJ_UNUSED_ARG(timer_heap) + + entry->id = 0; + tdata = pjsip_regc_register(regc, 1); + if (tdata) { + pjsip_regc_send(regc, tdata); + } else { + pj_str_t reason = pj_str("Unable to create txdata"); + call_callback(regc, -1, &reason, NULL, -1, 0, NULL); + } +} + +static void tsx_callback(void *token, pjsip_event *event) +{ + pjsip_regc *regc = token; + pjsip_transaction *tsx = event->obj.tsx; + + /* If registration data has been deleted by user then remove registration + * data from transaction's callback, and don't call callback. + */ + if (regc->_delete_flag) { + --regc->pending_tsx; + + } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED || + tsx->status_code == PJSIP_SC_UNAUTHORIZED) + { + pjsip_rx_data *rdata = event->src.rdata; + pjsip_tx_data *tdata; + + tdata = pjsip_auth_reinit_req( regc->endpt, + regc->pool, ®c->auth_sess_list, + regc->cred_count, regc->cred_info, + tsx->last_tx, event->src.rdata ); + + if (tdata) { + --regc->pending_tsx; + pjsip_regc_send(regc, tdata); + return; + } else { + call_callback(regc, tsx->status_code, &rdata->msg->line.status.reason, + rdata, -1, 0, NULL); + --regc->pending_tsx; + } + } else { + int contact_cnt = 0; + pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT]; + pjsip_rx_data *rdata; + pj_int32_t expiration = 0xFFFF; + + if (tsx->status_code/100 == 2) { + int i; + pjsip_contact_hdr *hdr; + pjsip_msg *msg; + pjsip_expires_hdr *expires; + + rdata = event->src.rdata; + msg = rdata->msg; + hdr = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL); + while (hdr) { + contact[contact_cnt++] = hdr; + hdr = hdr->next; + if (hdr == (void*)&msg->hdr) + break; + hdr = pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, hdr); + } + + expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); + + if (expires) + expiration = expires->ivalue; + + for (i=0; i<contact_cnt; ++i) { + hdr = contact[i]; + if (hdr->expires >= 0 && hdr->expires < expiration) + expiration = contact[i]->expires; + } + + if (regc->auto_reg && expiration != 0 && expiration != 0xFFFF) { + pj_time_val delay = { 0, 0}; + + delay.sec = expiration - DELAY_BEFORE_REFRESH; + if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED && + delay.sec > (pj_int32_t)regc->expires) + { + delay.sec = regc->expires; + } + if (delay.sec < DELAY_BEFORE_REFRESH) + delay.sec = DELAY_BEFORE_REFRESH; + regc->timer.cb = ®c_refresh_timer_cb; + regc->timer.id = REFRESH_TIMER; + regc->timer.user_data = regc; + pjsip_endpt_schedule_timer( regc->endpt, ®c->timer, &delay); + } + + } else { + rdata = (event->src_type==PJSIP_EVENT_RX_MSG) ? event->src.rdata : NULL; + } + + + /* Call callback. */ + if (expiration == 0xFFFF) expiration = -1; + call_callback(regc, tsx->status_code, + (rdata ? &rdata->msg->line.status.reason + : pjsip_get_status_text(tsx->status_code)), + rdata, expiration, + contact_cnt, contact); + + --regc->pending_tsx; + } + + /* Delete the record if user destroy regc during the callback. */ + if (regc->_delete_flag && regc->pending_tsx==0) { + pjsip_regc_destroy(regc); + } +} + +PJ_DEF(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata) +{ + int status; + + /* Make sure we don't have pending transaction. */ + if (regc->pending_tsx) { + pj_str_t reason = pj_str("Transaction in progress"); + call_callback(regc, -1, &reason, NULL, -1, 0, NULL); + pjsip_tx_data_dec_ref( tdata ); + return; + } + + /* Invalidate message buffer. */ + pjsip_tx_data_invalidate_msg(tdata); + + /* Increment CSeq */ + regc->cseq_hdr->cseq++; + + /* Send. */ + status = pjsip_endpt_send_request(regc->endpt, tdata, -1, regc, &tsx_callback); + if (status==0) + ++regc->pending_tsx; + else { + pj_str_t reason = pj_str("Unable to send request."); + call_callback(regc, status, &reason, NULL, -1, 0, NULL); + } +} + + diff --git a/pjsip/src/pjsip-ua/sip_ua.c b/pjsip/src/pjsip-ua/sip_ua.c index 11f7670c..094be07f 100644 --- a/pjsip/src/pjsip-ua/sip_ua.c +++ b/pjsip/src/pjsip-ua/sip_ua.c @@ -1,506 +1,506 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_mod_ua/sip_ua.h>
-#include <pjsip_mod_ua/sip_dialog.h>
-#include <pjsip_mod_ua/sip_ua_private.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_transaction.h>
-#include <pj/list.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/guid.h>
-#include <pj/os.h>
-#include <pj/hash.h>
-#include <pj/pool.h>
-
-#define PJSIP_POOL_LEN_USER_AGENT 1024
-#define PJSIP_POOL_INC_USER_AGENT 0
-
-
-#define LOG_THIS "useragent.."
-
-/*
- * Static prototypes.
- */
-static pj_status_t ua_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id );
-static pj_status_t ua_start( struct pjsip_module *mod );
-static pj_status_t ua_deinit( struct pjsip_module *mod );
-static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *evt );
-static pjsip_dlg *find_dialog( pjsip_user_agent *ua,
- pjsip_rx_data *rdata );
-static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg,
- pj_str_t *key );
-PJ_DECL(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg );
-
-/*
- * Default UA instance.
- */
-static pjsip_user_agent ua_instance;
-
-/*
- * Module interface.
- */
-static struct pjsip_module mod_ua =
-{
- { "User-Agent", 10 }, /* Name. */
- 0, /* Flag */
- 128, /* Priority */
- NULL, /* User agent instance, initialized by APP. */
- 0, /* Number of methods supported (will be initialized later). */
- { 0 }, /* Array of methods (will be initialized later) */
- &ua_init, /* init_module() */
- &ua_start, /* start_module() */
- &ua_deinit, /* deinit_module() */
- &ua_tsx_handler, /* tsx_handler() */
-};
-
-/*
- * Initialize user agent instance.
- */
-static pj_status_t ua_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id )
-{
- static pjsip_method m_invite, m_ack, m_cancel, m_bye;
- pjsip_user_agent *ua = mod->mod_data;
- extern int pjsip_dlg_lock_tls_id; /* defined in sip_dialog.c */
-
- pjsip_method_set( &m_invite, PJSIP_INVITE_METHOD );
- pjsip_method_set( &m_ack, PJSIP_ACK_METHOD );
- pjsip_method_set( &m_cancel, PJSIP_CANCEL_METHOD );
- pjsip_method_set( &m_bye, PJSIP_BYE_METHOD );
-
- mod->method_cnt = 4;
- mod->methods[0] = &m_invite;
- mod->methods[1] = &m_ack;
- mod->methods[2] = &m_cancel;
- mod->methods[3] = &m_bye;
-
- /* Initialize the user agent. */
- ua->endpt = endpt;
- ua->pool = pjsip_endpt_create_pool(endpt, "pua%p", PJSIP_POOL_LEN_UA,
- PJSIP_POOL_INC_UA);
- if (!ua->pool) {
- return -1;
- }
- ua->mod_id = id;
- ua->mutex = pj_mutex_create(ua->pool, " ua%p", 0);
- if (!ua->mutex) {
- return -1;
- }
- ua->dlg_table = pj_hash_create(ua->pool, PJSIP_MAX_DIALOG_COUNT);
- if (ua->dlg_table == NULL) {
- return -1;
- }
- pj_list_init(&ua->dlg_list);
-
- /* Initialize dialog lock. */
- pjsip_dlg_lock_tls_id = pj_thread_local_alloc();
- if (pjsip_dlg_lock_tls_id == -1) {
- return -1;
- }
- pj_thread_local_set(pjsip_dlg_lock_tls_id, NULL);
-
- return 0;
-}
-
-/*
- * Start user agent instance.
- */
-static pj_status_t ua_start( struct pjsip_module *mod )
-{
- PJ_UNUSED_ARG(mod)
- return 0;
-}
-
-/*
- * Destroy user agent.
- */
-static pj_status_t ua_deinit( struct pjsip_module *mod )
-{
- pjsip_user_agent *ua = mod->mod_data;
-
- pj_mutex_unlock(ua->mutex);
-
- /* Release pool */
- if (ua->pool) {
- pjsip_endpt_destroy_pool( ua->endpt, ua->pool );
- }
- return 0;
-}
-
-/*
- * Get the module interface for the UA module.
- */
-PJ_DEF(pjsip_module*) pjsip_ua_get_module(void)
-{
- mod_ua.mod_data = &ua_instance;
- return &mod_ua;
-}
-
-/*
- * Register callback to receive dialog notifications.
- */
-PJ_DEF(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua,
- pjsip_dlg_callback *cb )
-{
- ua->dlg_cb = cb;
-}
-
-/*
- * Find dialog.
- * This function is called for a new transactions, which a dialog hasn't been
- * 'attached' to the transaction.
- */
-static pjsip_dlg *find_dialog( pjsip_user_agent *ua, pjsip_rx_data *rdata )
-{
- pjsip_dlg *dlg;
- pj_str_t *tag;
-
- /* Non-CANCEL requests/response can be found by looking at the tag in the
- * hash table. CANCEL requests don't have tags, so instead we'll try to
- * find the UAS INVITE transaction in endpoint's hash table
- */
- if (rdata->cseq->method.id == PJSIP_CANCEL_METHOD) {
-
- /* Create key for the rdata, but this time, use INVITE as the
- * method.
- */
- pj_str_t key;
- pjsip_role_e role;
- pjsip_method invite_method;
- pjsip_transaction *invite_tsx;
-
- if (rdata->msg->type == PJSIP_REQUEST_MSG) {
- role = PJSIP_ROLE_UAS;
- } else {
- role = PJSIP_ROLE_UAC;
- }
- pjsip_method_set(&invite_method, PJSIP_INVITE_METHOD);
- pjsip_tsx_create_key(rdata->pool, &key, role, &invite_method, rdata);
-
- /* Lookup the INVITE transaction */
- invite_tsx = pjsip_endpt_find_tsx(ua->endpt, &key);
-
- /* We should find the dialog attached to the INVITE transaction */
- return invite_tsx ?
- (pjsip_dlg*) invite_tsx->module_data[ua->mod_id] : NULL;
-
- } else {
- if (rdata->msg->type == PJSIP_REQUEST_MSG) {
- tag = &rdata->to_tag;
- } else {
- tag = &rdata->from_tag;
- }
- /* Find the dialog in UA hash table */
- pj_mutex_lock(ua->mutex);
- dlg = pj_hash_get( ua->dlg_table, tag->ptr, tag->slen );
- pj_mutex_unlock(ua->mutex);
- }
-
- return dlg;
-}
-
-/*
- * This function receives event notification from transactions. It is called by
- * endpoint.
- */
-static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
-{
- pjsip_user_agent *ua = mod->mod_data;
- pjsip_dlg *dlg = NULL;
- pjsip_transaction *tsx = event->obj.tsx;
-
- PJ_LOG(5, (LOG_THIS, "ua_tsx_handler(tsx=%s, evt=%s, src=%s, data=%p)",
- (tsx ? tsx->obj_name : "NULL"), pjsip_event_str(event->type),
- pjsip_event_str(event->src_type), event->src.data));
-
- /* Special case to handle ACK which doesn't match any INVITE transactions. */
- if (event->type == PJSIP_EVENT_RX_ACK_MSG) {
- /* Find the dialog based on the "tag". */
- dlg = find_dialog( ua, event->src.rdata );
-
- /* We should be able to find it. */
- if (!dlg) {
- PJ_LOG(4,(LOG_THIS, "Unable to find dialog for incoming ACK"));
- return;
- }
-
- /* Match CSeq with pending INVITE in dialog. */
- if (dlg->invite_tsx && dlg->invite_tsx->cseq==event->src.rdata->cseq->cseq) {
- /* A match found. */
- tsx = dlg->invite_tsx;
-
- /* Pass the event to transaction if transaction handles ACK. */
- if (tsx->handle_ack) {
- PJ_LOG(4,(LOG_THIS, "Re-routing strandled ACK to transaction"));
- pjsip_tsx_on_rx_msg(tsx, event->src.rdata);
- return;
- }
- } else {
- tsx = NULL;
- PJ_LOG(4,(LOG_THIS, "Unable to find INVITE tsx for incoming ACK"));
- return;
- }
- }
-
- /* For discard event, transaction is NULL. */
- if (tsx == NULL) {
- return;
- }
-
- /* Try to pickup the dlg from the transaction. */
- dlg = (pjsip_dlg*) tsx->module_data[ua->mod_id];
-
- if (dlg != NULL) {
-
- /* Nothing to do now. */
-
- } else if (event->src_type == PJSIP_EVENT_RX_MSG) {
-
- /* This must be a new UAS transaction. */
-
- /* Finds dlg that can handle this transaction. */
- dlg = find_dialog( ua, event->src.rdata);
-
- /* Create a new dlg if there's no existing dlg that can handle
- the request, ONLY if the incoming message is an INVITE request.
- */
- if (dlg==NULL && event->src.rdata->msg->type == PJSIP_REQUEST_MSG) {
-
- if (event->src.rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD) {
- /* Create new dialog. */
- dlg = pjsip_ua_create_dialog( ua, PJSIP_ROLE_UAS );
-
- if (dlg == NULL ||
- pjsip_dlg_init_from_rdata( dlg, event->src.rdata) != 0)
- {
- pjsip_tx_data *tdata;
-
- /* Dialog initialization has failed. Respond request with 500 */
- if (dlg) {
- pjsip_ua_destroy_dialog(dlg);
- }
- tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata,
- PJSIP_SC_INTERNAL_SERVER_ERROR);
- if (tdata) {
- pjsip_tsx_on_tx_msg( event->obj.tsx, tdata );
- }
- return;
- }
-
- } else {
- pjsip_tx_data *tdata;
-
- /* Check the method */
- switch (tsx->method.id) {
- case PJSIP_INVITE_METHOD:
- case PJSIP_ACK_METHOD:
- case PJSIP_BYE_METHOD:
- case PJSIP_CANCEL_METHOD:
- /* Stale non-INVITE request.
- * For now, respond all stale requests with 481 (?).
- */
- tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata,
- PJSIP_SC_CALL_TSX_DOES_NOT_EXIST);
- if (tdata) {
- pjsip_tsx_on_tx_msg( event->obj.tsx, tdata );
- }
- break;
- }
-
- return;
- }
- } else {
- /* Check the method */
- switch (tsx->method.id) {
- case PJSIP_INVITE_METHOD:
- case PJSIP_ACK_METHOD:
- case PJSIP_BYE_METHOD:
- case PJSIP_CANCEL_METHOD:
- /* These methods belongs to dialog.
- * If we receive these methods while no dialog is found,
- * then it must be a stale responses.
- */
- break;
- default:
- return;
- }
-
- }
-
- if (dlg == NULL) {
- PJ_LOG(3, (LOG_THIS, "Receives spurious rdata %p from %s:%d",
- event->src.rdata,
- pj_sockaddr_get_str_addr(&event->src.rdata->addr),
- pj_sockaddr_get_port(&event->src.rdata->addr)));
- }
-
- /* Set the dlg in the transaction (dlg can be NULL). */
- tsx->module_data[ua->mod_id] = dlg;
-
- } else {
- /* This CAN happen with event->src_type == PJSIP_EVENT_TX_MSG
- * if UAS is responding to a transaction which does not exist.
- * Just ignore.
- */
- return;
- }
-
- /* Pass the event to the dlg. */
- if (dlg) {
- pjsip_dlg_on_tsx_event(dlg, tsx, event);
- }
-}
-
-/*
- * Register dialog to UA.
- */
-static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg,
- pj_str_t *key )
-{
- /* Assure that no entry with similar key exists in the hash table. */
- pj_assert( pj_hash_get( ua->dlg_table, key->ptr, key->slen) == 0);
-
- /* Insert entry to hash table. */
- pj_hash_set( dlg->pool, ua->dlg_table,
- key->ptr, key->slen, dlg);
-
- /* Insert to the list. */
- pj_list_insert_before(&ua->dlg_list, dlg);
- return PJ_SUCCESS;
-}
-
-/*
- * Create a new dialog.
- */
-PJ_DEF(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua,
- pjsip_role_e role )
-{
- pj_pool_t *pool;
- pjsip_dlg *dlg;
-
- PJ_UNUSED_ARG(ua)
-
- /* Create pool for the dialog. */
- pool = pjsip_endpt_create_pool( ua->endpt, "pdlg%p",
- PJSIP_POOL_LEN_DIALOG,
- PJSIP_POOL_INC_DIALOG);
-
- /* Create the dialog. */
- dlg = pj_pool_calloc(pool, 1, sizeof(pjsip_dlg));
- dlg->pool = pool;
- dlg->ua = ua;
- dlg->role = role;
- sprintf(dlg->obj_name, "dlg%p", dlg);
-
- /* Create mutex for the dialog. */
- dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0);
- if (!dlg->mutex) {
- pjsip_endpt_destroy_pool(ua->endpt, pool);
- return NULL;
- }
-
- /* Create unique tag for the dialog. */
- pj_create_unique_string( pool, &dlg->local.tag );
-
- /* Register dialog. */
- pj_mutex_lock(ua->mutex);
- if (ua_register_dialog(ua, dlg, &dlg->local.tag) != PJ_SUCCESS) {
- pj_mutex_unlock(ua->mutex);
- pj_mutex_destroy(dlg->mutex);
- pjsip_endpt_destroy_pool( ua->endpt, pool );
- return NULL;
- }
- pj_mutex_unlock(ua->mutex);
-
- PJ_LOG(4, (dlg->obj_name, "new %s dialog created", pjsip_role_name(role)));
- return dlg;
-}
-
-/*
- * Destroy dialog.
- */
-PJ_DEF(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg )
-{
- PJ_LOG(5, (dlg->obj_name, "destroying.."));
-
- /* Lock dialog's mutex.
- * Check the mutex validity first since this function can be called
- * on dialog initialization failure (which might be because mutex could not
- * be allocated in the first place).
- */
- if (dlg->mutex) {
- pj_mutex_lock(dlg->mutex);
- }
-
- /* This must be called while holding dialog's mutex, if any. */
- pjsip_on_dialog_destroyed(dlg);
-
- /* Lock UA. */
- pj_mutex_lock(dlg->ua->mutex);
-
- /* Erase from hash table. */
- pj_hash_set( dlg->pool, dlg->ua->dlg_table,
- dlg->local.tag.ptr, dlg->local.tag.slen, NULL);
-
- /* Erase from the list. */
- pj_list_erase(dlg);
-
- /* Unlock UA. */
- pj_mutex_unlock(dlg->ua->mutex);
-
- /* Unlock mutex. */
- if (dlg->mutex) {
- pj_mutex_unlock(dlg->mutex);
- }
-
- /* Destroy the pool. */
- pjsip_endpt_destroy_pool( dlg->ua->endpt, dlg->pool);
-}
-
-/*
- * Dump user agent state to log file.
- */
-PJ_DEF(void) pjsip_ua_dump(pjsip_user_agent *ua)
-{
-#if PJ_LOG_MAX_LEVEL >= 3
- PJ_LOG(3,(LOG_THIS, "Dumping user agent"));
- PJ_LOG(3,(LOG_THIS, " Pool capacity=%u, used=%u",
- pj_pool_get_capacity(ua->pool),
- pj_pool_get_used_size(ua->pool)));
- PJ_LOG(3,(LOG_THIS, " Number of dialogs=%u", pj_hash_count(ua->dlg_table)));
-
- if (pj_hash_count(ua->dlg_table)) {
- pjsip_dlg *dlg;
-
- PJ_LOG(3,(LOG_THIS, " Dumping dialog list:"));
- dlg = ua->dlg_list.next;
- while (dlg != (pjsip_dlg*) &ua->dlg_list) {
- PJ_LOG(3, (LOG_THIS, " %s %s", dlg->obj_name,
- pjsip_dlg_state_str(dlg->state)));
- dlg = dlg->next;
- }
- }
-#endif
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip_mod_ua/sip_ua.h> +#include <pjsip_mod_ua/sip_dialog.h> +#include <pjsip_mod_ua/sip_ua_private.h> +#include <pjsip/sip_module.h> +#include <pjsip/sip_event.h> +#include <pjsip/sip_util.h> +#include <pjsip/sip_endpoint.h> +#include <pjsip/sip_transaction.h> +#include <pj/list.h> +#include <pj/log.h> +#include <pj/string.h> +#include <pj/guid.h> +#include <pj/os.h> +#include <pj/hash.h> +#include <pj/pool.h> + +#define PJSIP_POOL_LEN_USER_AGENT 1024 +#define PJSIP_POOL_INC_USER_AGENT 0 + + +#define LOG_THIS "useragent.." + +/* + * Static prototypes. + */ +static pj_status_t ua_init( pjsip_endpoint *endpt, + struct pjsip_module *mod, pj_uint32_t id ); +static pj_status_t ua_start( struct pjsip_module *mod ); +static pj_status_t ua_deinit( struct pjsip_module *mod ); +static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *evt ); +static pjsip_dlg *find_dialog( pjsip_user_agent *ua, + pjsip_rx_data *rdata ); +static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg, + pj_str_t *key ); +PJ_DECL(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg ); + +/* + * Default UA instance. + */ +static pjsip_user_agent ua_instance; + +/* + * Module interface. + */ +static struct pjsip_module mod_ua = +{ + { "User-Agent", 10 }, /* Name. */ + 0, /* Flag */ + 128, /* Priority */ + NULL, /* User agent instance, initialized by APP. */ + 0, /* Number of methods supported (will be initialized later). */ + { 0 }, /* Array of methods (will be initialized later) */ + &ua_init, /* init_module() */ + &ua_start, /* start_module() */ + &ua_deinit, /* deinit_module() */ + &ua_tsx_handler, /* tsx_handler() */ +}; + +/* + * Initialize user agent instance. + */ +static pj_status_t ua_init( pjsip_endpoint *endpt, + struct pjsip_module *mod, pj_uint32_t id ) +{ + static pjsip_method m_invite, m_ack, m_cancel, m_bye; + pjsip_user_agent *ua = mod->mod_data; + extern int pjsip_dlg_lock_tls_id; /* defined in sip_dialog.c */ + + pjsip_method_set( &m_invite, PJSIP_INVITE_METHOD ); + pjsip_method_set( &m_ack, PJSIP_ACK_METHOD ); + pjsip_method_set( &m_cancel, PJSIP_CANCEL_METHOD ); + pjsip_method_set( &m_bye, PJSIP_BYE_METHOD ); + + mod->method_cnt = 4; + mod->methods[0] = &m_invite; + mod->methods[1] = &m_ack; + mod->methods[2] = &m_cancel; + mod->methods[3] = &m_bye; + + /* Initialize the user agent. */ + ua->endpt = endpt; + ua->pool = pjsip_endpt_create_pool(endpt, "pua%p", PJSIP_POOL_LEN_UA, + PJSIP_POOL_INC_UA); + if (!ua->pool) { + return -1; + } + ua->mod_id = id; + ua->mutex = pj_mutex_create(ua->pool, " ua%p", 0); + if (!ua->mutex) { + return -1; + } + ua->dlg_table = pj_hash_create(ua->pool, PJSIP_MAX_DIALOG_COUNT); + if (ua->dlg_table == NULL) { + return -1; + } + pj_list_init(&ua->dlg_list); + + /* Initialize dialog lock. */ + pjsip_dlg_lock_tls_id = pj_thread_local_alloc(); + if (pjsip_dlg_lock_tls_id == -1) { + return -1; + } + pj_thread_local_set(pjsip_dlg_lock_tls_id, NULL); + + return 0; +} + +/* + * Start user agent instance. + */ +static pj_status_t ua_start( struct pjsip_module *mod ) +{ + PJ_UNUSED_ARG(mod) + return 0; +} + +/* + * Destroy user agent. + */ +static pj_status_t ua_deinit( struct pjsip_module *mod ) +{ + pjsip_user_agent *ua = mod->mod_data; + + pj_mutex_unlock(ua->mutex); + + /* Release pool */ + if (ua->pool) { + pjsip_endpt_destroy_pool( ua->endpt, ua->pool ); + } + return 0; +} + +/* + * Get the module interface for the UA module. + */ +PJ_DEF(pjsip_module*) pjsip_ua_get_module(void) +{ + mod_ua.mod_data = &ua_instance; + return &mod_ua; +} + +/* + * Register callback to receive dialog notifications. + */ +PJ_DEF(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua, + pjsip_dlg_callback *cb ) +{ + ua->dlg_cb = cb; +} + +/* + * Find dialog. + * This function is called for a new transactions, which a dialog hasn't been + * 'attached' to the transaction. + */ +static pjsip_dlg *find_dialog( pjsip_user_agent *ua, pjsip_rx_data *rdata ) +{ + pjsip_dlg *dlg; + pj_str_t *tag; + + /* Non-CANCEL requests/response can be found by looking at the tag in the + * hash table. CANCEL requests don't have tags, so instead we'll try to + * find the UAS INVITE transaction in endpoint's hash table + */ + if (rdata->cseq->method.id == PJSIP_CANCEL_METHOD) { + + /* Create key for the rdata, but this time, use INVITE as the + * method. + */ + pj_str_t key; + pjsip_role_e role; + pjsip_method invite_method; + pjsip_transaction *invite_tsx; + + if (rdata->msg->type == PJSIP_REQUEST_MSG) { + role = PJSIP_ROLE_UAS; + } else { + role = PJSIP_ROLE_UAC; + } + pjsip_method_set(&invite_method, PJSIP_INVITE_METHOD); + pjsip_tsx_create_key(rdata->pool, &key, role, &invite_method, rdata); + + /* Lookup the INVITE transaction */ + invite_tsx = pjsip_endpt_find_tsx(ua->endpt, &key); + + /* We should find the dialog attached to the INVITE transaction */ + return invite_tsx ? + (pjsip_dlg*) invite_tsx->module_data[ua->mod_id] : NULL; + + } else { + if (rdata->msg->type == PJSIP_REQUEST_MSG) { + tag = &rdata->to_tag; + } else { + tag = &rdata->from_tag; + } + /* Find the dialog in UA hash table */ + pj_mutex_lock(ua->mutex); + dlg = pj_hash_get( ua->dlg_table, tag->ptr, tag->slen ); + pj_mutex_unlock(ua->mutex); + } + + return dlg; +} + +/* + * This function receives event notification from transactions. It is called by + * endpoint. + */ +static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *event ) +{ + pjsip_user_agent *ua = mod->mod_data; + pjsip_dlg *dlg = NULL; + pjsip_transaction *tsx = event->obj.tsx; + + PJ_LOG(5, (LOG_THIS, "ua_tsx_handler(tsx=%s, evt=%s, src=%s, data=%p)", + (tsx ? tsx->obj_name : "NULL"), pjsip_event_str(event->type), + pjsip_event_str(event->src_type), event->src.data)); + + /* Special case to handle ACK which doesn't match any INVITE transactions. */ + if (event->type == PJSIP_EVENT_RX_ACK_MSG) { + /* Find the dialog based on the "tag". */ + dlg = find_dialog( ua, event->src.rdata ); + + /* We should be able to find it. */ + if (!dlg) { + PJ_LOG(4,(LOG_THIS, "Unable to find dialog for incoming ACK")); + return; + } + + /* Match CSeq with pending INVITE in dialog. */ + if (dlg->invite_tsx && dlg->invite_tsx->cseq==event->src.rdata->cseq->cseq) { + /* A match found. */ + tsx = dlg->invite_tsx; + + /* Pass the event to transaction if transaction handles ACK. */ + if (tsx->handle_ack) { + PJ_LOG(4,(LOG_THIS, "Re-routing strandled ACK to transaction")); + pjsip_tsx_on_rx_msg(tsx, event->src.rdata); + return; + } + } else { + tsx = NULL; + PJ_LOG(4,(LOG_THIS, "Unable to find INVITE tsx for incoming ACK")); + return; + } + } + + /* For discard event, transaction is NULL. */ + if (tsx == NULL) { + return; + } + + /* Try to pickup the dlg from the transaction. */ + dlg = (pjsip_dlg*) tsx->module_data[ua->mod_id]; + + if (dlg != NULL) { + + /* Nothing to do now. */ + + } else if (event->src_type == PJSIP_EVENT_RX_MSG) { + + /* This must be a new UAS transaction. */ + + /* Finds dlg that can handle this transaction. */ + dlg = find_dialog( ua, event->src.rdata); + + /* Create a new dlg if there's no existing dlg that can handle + the request, ONLY if the incoming message is an INVITE request. + */ + if (dlg==NULL && event->src.rdata->msg->type == PJSIP_REQUEST_MSG) { + + if (event->src.rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD) { + /* Create new dialog. */ + dlg = pjsip_ua_create_dialog( ua, PJSIP_ROLE_UAS ); + + if (dlg == NULL || + pjsip_dlg_init_from_rdata( dlg, event->src.rdata) != 0) + { + pjsip_tx_data *tdata; + + /* Dialog initialization has failed. Respond request with 500 */ + if (dlg) { + pjsip_ua_destroy_dialog(dlg); + } + tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata, + PJSIP_SC_INTERNAL_SERVER_ERROR); + if (tdata) { + pjsip_tsx_on_tx_msg( event->obj.tsx, tdata ); + } + return; + } + + } else { + pjsip_tx_data *tdata; + + /* Check the method */ + switch (tsx->method.id) { + case PJSIP_INVITE_METHOD: + case PJSIP_ACK_METHOD: + case PJSIP_BYE_METHOD: + case PJSIP_CANCEL_METHOD: + /* Stale non-INVITE request. + * For now, respond all stale requests with 481 (?). + */ + tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata, + PJSIP_SC_CALL_TSX_DOES_NOT_EXIST); + if (tdata) { + pjsip_tsx_on_tx_msg( event->obj.tsx, tdata ); + } + break; + } + + return; + } + } else { + /* Check the method */ + switch (tsx->method.id) { + case PJSIP_INVITE_METHOD: + case PJSIP_ACK_METHOD: + case PJSIP_BYE_METHOD: + case PJSIP_CANCEL_METHOD: + /* These methods belongs to dialog. + * If we receive these methods while no dialog is found, + * then it must be a stale responses. + */ + break; + default: + return; + } + + } + + if (dlg == NULL) { + PJ_LOG(3, (LOG_THIS, "Receives spurious rdata %p from %s:%d", + event->src.rdata, + pj_sockaddr_get_str_addr(&event->src.rdata->addr), + pj_sockaddr_get_port(&event->src.rdata->addr))); + } + + /* Set the dlg in the transaction (dlg can be NULL). */ + tsx->module_data[ua->mod_id] = dlg; + + } else { + /* This CAN happen with event->src_type == PJSIP_EVENT_TX_MSG + * if UAS is responding to a transaction which does not exist. + * Just ignore. + */ + return; + } + + /* Pass the event to the dlg. */ + if (dlg) { + pjsip_dlg_on_tsx_event(dlg, tsx, event); + } +} + +/* + * Register dialog to UA. + */ +static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg, + pj_str_t *key ) +{ + /* Assure that no entry with similar key exists in the hash table. */ + pj_assert( pj_hash_get( ua->dlg_table, key->ptr, key->slen) == 0); + + /* Insert entry to hash table. */ + pj_hash_set( dlg->pool, ua->dlg_table, + key->ptr, key->slen, dlg); + + /* Insert to the list. */ + pj_list_insert_before(&ua->dlg_list, dlg); + return PJ_SUCCESS; +} + +/* + * Create a new dialog. + */ +PJ_DEF(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua, + pjsip_role_e role ) +{ + pj_pool_t *pool; + pjsip_dlg *dlg; + + PJ_UNUSED_ARG(ua) + + /* Create pool for the dialog. */ + pool = pjsip_endpt_create_pool( ua->endpt, "pdlg%p", + PJSIP_POOL_LEN_DIALOG, + PJSIP_POOL_INC_DIALOG); + + /* Create the dialog. */ + dlg = pj_pool_calloc(pool, 1, sizeof(pjsip_dlg)); + dlg->pool = pool; + dlg->ua = ua; + dlg->role = role; + sprintf(dlg->obj_name, "dlg%p", dlg); + + /* Create mutex for the dialog. */ + dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0); + if (!dlg->mutex) { + pjsip_endpt_destroy_pool(ua->endpt, pool); + return NULL; + } + + /* Create unique tag for the dialog. */ + pj_create_unique_string( pool, &dlg->local.tag ); + + /* Register dialog. */ + pj_mutex_lock(ua->mutex); + if (ua_register_dialog(ua, dlg, &dlg->local.tag) != PJ_SUCCESS) { + pj_mutex_unlock(ua->mutex); + pj_mutex_destroy(dlg->mutex); + pjsip_endpt_destroy_pool( ua->endpt, pool ); + return NULL; + } + pj_mutex_unlock(ua->mutex); + + PJ_LOG(4, (dlg->obj_name, "new %s dialog created", pjsip_role_name(role))); + return dlg; +} + +/* + * Destroy dialog. + */ +PJ_DEF(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg ) +{ + PJ_LOG(5, (dlg->obj_name, "destroying..")); + + /* Lock dialog's mutex. + * Check the mutex validity first since this function can be called + * on dialog initialization failure (which might be because mutex could not + * be allocated in the first place). + */ + if (dlg->mutex) { + pj_mutex_lock(dlg->mutex); + } + + /* This must be called while holding dialog's mutex, if any. */ + pjsip_on_dialog_destroyed(dlg); + + /* Lock UA. */ + pj_mutex_lock(dlg->ua->mutex); + + /* Erase from hash table. */ + pj_hash_set( dlg->pool, dlg->ua->dlg_table, + dlg->local.tag.ptr, dlg->local.tag.slen, NULL); + + /* Erase from the list. */ + pj_list_erase(dlg); + + /* Unlock UA. */ + pj_mutex_unlock(dlg->ua->mutex); + + /* Unlock mutex. */ + if (dlg->mutex) { + pj_mutex_unlock(dlg->mutex); + } + + /* Destroy the pool. */ + pjsip_endpt_destroy_pool( dlg->ua->endpt, dlg->pool); +} + +/* + * Dump user agent state to log file. + */ +PJ_DEF(void) pjsip_ua_dump(pjsip_user_agent *ua) +{ +#if PJ_LOG_MAX_LEVEL >= 3 + PJ_LOG(3,(LOG_THIS, "Dumping user agent")); + PJ_LOG(3,(LOG_THIS, " Pool capacity=%u, used=%u", + pj_pool_get_capacity(ua->pool), + pj_pool_get_used_size(ua->pool))); + PJ_LOG(3,(LOG_THIS, " Number of dialogs=%u", pj_hash_count(ua->dlg_table))); + + if (pj_hash_count(ua->dlg_table)) { + pjsip_dlg *dlg; + + PJ_LOG(3,(LOG_THIS, " Dumping dialog list:")); + dlg = ua->dlg_list.next; + while (dlg != (pjsip_dlg*) &ua->dlg_list) { + PJ_LOG(3, (LOG_THIS, " %s %s", dlg->obj_name, + pjsip_dlg_state_str(dlg->state))); + dlg = dlg->next; + } + } +#endif +} + diff --git a/pjsip/src/pjsip-ua/sip_ua_private.h b/pjsip/src/pjsip-ua/sip_ua_private.h index 93657ddf..8a174afe 100644 --- a/pjsip/src/pjsip-ua/sip_ua_private.h +++ b/pjsip/src/pjsip-ua/sip_ua_private.h @@ -1,36 +1,36 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_UA_PRIVATE_H__
-#define __PJSIP_UA_PRIVATE_H__
-
-
-/*
- * Internal dialog functions.
- */
-pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg,
- pjsip_rx_data *rdata );
-
-
-void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-
-
-#endif /* __PJSIP_UA_PRIVATE_H__ */
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSIP_UA_PRIVATE_H__ +#define __PJSIP_UA_PRIVATE_H__ + + +/* + * Internal dialog functions. + */ +pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg, + pjsip_rx_data *rdata ); + + +void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); + + +#endif /* __PJSIP_UA_PRIVATE_H__ */ + diff --git a/pjsip/src/pjsip/sip_auth.c b/pjsip/src/pjsip/sip_auth.c index b3375867..9b7c52bb 100644 --- a/pjsip/src/pjsip/sip_auth.c +++ b/pjsip/src/pjsip/sip_auth.c @@ -1,804 +1,804 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_auth.h>
-#include <pjsip/sip_auth_parser.h> /* just to get pjsip_DIGEST_STR */
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjlib-util/md5.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-#include <pj/guid.h>
-#include <pj/assert.h>
-#include <pj/ctype.h>
-
-/* Length of digest string. */
-#define MD5STRLEN 32
-
-/* Maximum stack size we use for storing username+realm+password etc. */
-#define MAX_TEMP 128
-
-/* A macro just to get rid of type mismatch between char and unsigned char */
-#define MD5_APPEND(pms,buf,len) pj_md5_update(pms, (const pj_uint8_t*)buf, len)
-
-/* Logging. */
-#define THIS_FILE "sip_auth.c"
-#if 0
-# define AUTH_TRACE_(expr) PJ_LOG(3, expr)
-#else
-# define AUTH_TRACE_(expr)
-#endif
-
-static const char hex[] = "0123456789abcdef";
-
-/* Transform digest to string.
- * output must be at least MD5STRLEN+1 bytes.
- *
- * NOTE: THE OUTPUT STRING IS NOT NULL TERMINATED!
- */
-static void digest2str(const unsigned char digest[], char *output)
-{
- char *p = output;
- int i;
-
- for (i = 0; i<16; ++i) {
- int val = digest[i];
- *p++ = hex[val >> 4];
- *p++ = hex[val & 0x0F];
- }
-}
-
-/*
- * Create response digest based on the parameters and store the
- * digest ASCII in 'result'.
- */
-static void create_digest( pj_str_t *result,
- const pj_str_t *nonce,
- const pj_str_t *nc,
- const pj_str_t *cnonce,
- const pj_str_t *qop,
- const pj_str_t *uri,
- const pjsip_cred_info *cred_info,
- const pj_str_t *method)
-{
- char ha1[MD5STRLEN];
- char ha2[MD5STRLEN];
- unsigned char digest[16];
- pj_md5_context pms;
-
- pj_assert(result->slen >= MD5STRLEN);
-
- AUTH_TRACE_((THIS_FILE, "Begin creating digest"));
-
- if (cred_info->data_type == PJSIP_CRED_DATA_PLAIN_PASSWD) {
- /***
- *** ha1 = MD5(username ":" realm ":" password)
- ***/
- pj_md5_init(&pms);
- MD5_APPEND( &pms, cred_info->username.ptr, cred_info->username.slen);
- MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, cred_info->realm.ptr, cred_info->realm.slen);
- MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, cred_info->data.ptr, cred_info->data.slen);
- pj_md5_final(&pms, digest);
-
- digest2str(digest, ha1);
-
- } else if (cred_info->data_type == PJSIP_CRED_DATA_DIGEST) {
- pj_assert(cred_info->data.slen == 32);
- pj_memcpy( ha1, cred_info->data.ptr, cred_info->data.slen );
- }
-
- AUTH_TRACE_((THIS_FILE, " ha1=%.32s", ha1));
-
- /***
- *** ha2 = MD5(method ":" req_uri)
- ***/
- pj_md5_init(&pms);
- MD5_APPEND( &pms, method->ptr, method->slen);
- MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, uri->ptr, uri->slen);
- pj_md5_final(&pms, digest);
- digest2str(digest, ha2);
-
- AUTH_TRACE_((THIS_FILE, " ha2=%.32s", ha2));
-
- /***
- *** When qop is not used:
- *** response = MD5(ha1 ":" nonce ":" ha2)
- ***
- *** When qop=auth is used:
- *** response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2)
- ***/
- pj_md5_init(&pms);
- MD5_APPEND( &pms, ha1, MD5STRLEN);
- MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, nonce->ptr, nonce->slen);
- if (qop && qop->slen != 0) {
- MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, nc->ptr, nc->slen);
- MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, cnonce->ptr, cnonce->slen);
- MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, qop->ptr, qop->slen);
- }
- MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, ha2, MD5STRLEN);
-
- /* This is the final response digest. */
- pj_md5_final(&pms, digest);
-
- /* Convert digest to string and store in chal->response. */
- result->slen = MD5STRLEN;
- digest2str(digest, result->ptr);
-
- AUTH_TRACE_((THIS_FILE, " digest=%.32s", result->ptr));
- AUTH_TRACE_((THIS_FILE, "Digest created"));
-}
-
-/*
- * Finds out if qop offer contains "auth" token.
- */
-static pj_bool_t has_auth_qop( pj_pool_t *pool, const pj_str_t *qop_offer)
-{
- pj_str_t qop;
- char *p;
-
- pj_strdup_with_null( pool, &qop, qop_offer);
- p = qop.ptr;
- while (*p) {
- *p = (char)pj_tolower(*p);
- ++p;
- }
-
- p = qop.ptr;
- while (*p) {
- if (*p=='a' && *(p+1)=='u' && *(p+2)=='t' && *(p+3)=='h') {
- int e = *(p+4);
- if (e=='"' || e==',' || e==0)
- return PJ_TRUE;
- else
- p += 4;
- } else {
- ++p;
- }
- }
-
- return PJ_FALSE;
-}
-
-/*
- * Generate response digest.
- * Most of the parameters to generate the digest (i.e. username, realm, uri,
- * and nonce) are expected to be in the credential. Additional parameters (i.e.
- * password and method param) should be supplied in the argument.
- *
- * The resulting digest will be stored in cred->response.
- * The pool is used to allocate 32 bytes to store the digest in cred->response.
- */
-static pj_status_t respond_digest( pj_pool_t *pool,
- pjsip_digest_credential *cred,
- const pjsip_digest_challenge *chal,
- const pj_str_t *uri,
- const pjsip_cred_info *cred_info,
- const pj_str_t *cnonce,
- pj_uint32_t nc,
- const pj_str_t *method)
-{
- /* Check algorithm is supported. We only support MD5. */
- if (chal->algorithm.slen && pj_stricmp(&chal->algorithm, &pjsip_MD5_STR))
- {
- PJ_LOG(4,(THIS_FILE, "Unsupported digest algorithm \"%.*s\"",
- chal->algorithm.slen, chal->algorithm.ptr));
- return -1;
- }
-
- /* Build digest credential from arguments. */
- pj_strdup(pool, &cred->username, &cred_info->username);
- pj_strdup(pool, &cred->realm, &chal->realm);
- pj_strdup(pool, &cred->nonce, &chal->nonce);
- pj_strdup(pool, &cred->uri, uri);
- cred->algorithm = pjsip_MD5_STR;
- pj_strdup(pool, &cred->opaque, &chal->opaque);
-
- /* Allocate memory. */
- cred->response.ptr = pj_pool_alloc(pool, MD5STRLEN);
- cred->response.slen = MD5STRLEN;
-
- if (chal->qop.slen == 0) {
- /* Server doesn't require quality of protection. */
-
- /* Convert digest to string and store in chal->response. */
- create_digest( &cred->response, &cred->nonce, NULL, NULL, NULL,
- uri, cred_info, method);
-
- } else if (has_auth_qop(pool, &chal->qop)) {
- /* Server requires quality of protection.
- * We respond with selecting "qop=auth" protection.
- */
- cred->qop = pjsip_AUTH_STR;
- cred->nc.ptr = pj_pool_alloc(pool, 16);
- pj_snprintf(cred->nc.ptr, 16, "%06u", nc);
-
- if (cnonce && cnonce->slen) {
- pj_strdup(pool, &cred->cnonce, cnonce);
- } else {
- pj_str_t dummy_cnonce = { "b39971", 6};
- pj_strdup(pool, &cred->cnonce, &dummy_cnonce);
- }
-
- create_digest( &cred->response, &cred->nonce, &cred->nc, cnonce,
- &pjsip_AUTH_STR, uri, cred_info, method );
-
- } else {
- /* Server requires quality protection that we don't support. */
- PJ_LOG(4,(THIS_FILE, "Unsupported qop offer %.*s",
- chal->qop.slen, chal->qop.ptr));
- return -1;
- }
-
- return 0;
-}
-
-#if PJSIP_AUTH_QOP_SUPPORT
-/*
- * Update authentication session with a challenge.
- */
-static void update_digest_session( pj_pool_t *ses_pool,
- pjsip_auth_session *auth_sess,
- const pjsip_www_authenticate_hdr *hdr )
-{
- if (hdr->challenge.digest.qop.slen == 0)
- return;
-
- /* Initialize cnonce and qop if not present. */
- if (auth_sess->cnonce.slen == 0) {
- /* Save the whole challenge */
- auth_sess->last_chal = pjsip_hdr_clone(ses_pool, hdr);
-
- /* Create cnonce */
- pj_create_unique_string( ses_pool, &auth_sess->cnonce );
-
- /* Initialize nonce-count */
- auth_sess->nc = 1;
-
- /* Save realm. */
- pj_assert(auth_sess->realm.slen != 0);
- if (auth_sess->realm.slen == 0) {
- pj_strdup(ses_pool, &auth_sess->realm,
- &hdr->challenge.digest.realm);
- }
-
- } else {
- /* Update last_nonce and nonce-count */
- if (!pj_strcmp(&hdr->challenge.digest.nonce,
- &auth_sess->last_chal->challenge.digest.nonce))
- {
- /* Same nonce, increment nonce-count */
- ++auth_sess->nc;
- } else {
- /* Server gives new nonce. */
- pj_strdup(ses_pool, &auth_sess->last_chal->challenge.digest.nonce,
- &hdr->challenge.digest.nonce);
- /* Has the opaque changed? */
- if (pj_strcmp(&auth_sess->last_chal->challenge.digest.opaque,
- &hdr->challenge.digest.opaque))
- {
- pj_strdup(ses_pool,
- &auth_sess->last_chal->challenge.digest.opaque,
- &hdr->challenge.digest.opaque);
- }
- auth_sess->nc = 1;
- }
- }
-}
-#endif /* PJSIP_AUTH_QOP_SUPPORT */
-
-
-/* Find authentication session in the list. */
-static pjsip_auth_session *find_session( pjsip_auth_session *sess_list,
- const pj_str_t *realm )
-{
- pjsip_auth_session *sess = sess_list->next;
- while (sess != sess_list) {
- if (pj_stricmp(&sess->realm, realm) == 0)
- return sess;
- sess = sess->next;
- }
-
- return NULL;
-}
-
-/*
- * Create Authorization/Proxy-Authorization response header based on the challege
- * in WWW-Authenticate/Proxy-Authenticate header.
- */
-PJ_DEF(pjsip_authorization_hdr*)
-pjsip_auth_respond( pj_pool_t *req_pool,
- const pjsip_www_authenticate_hdr *hdr,
- const pjsip_uri *uri,
- const pjsip_cred_info *cred_info,
- const pjsip_method *method,
- pj_pool_t *sess_pool,
- pjsip_auth_session *auth_sess)
-{
- pjsip_authorization_hdr *auth;
- char tmp[PJSIP_MAX_URL_SIZE];
- pj_str_t uri_str;
- pj_pool_t *pool;
-
- pj_assert(hdr != NULL);
- pj_assert(uri != NULL);
- pj_assert(cred_info != NULL);
- pj_assert(method != NULL);
-
- /* Print URL in the original request. */
- uri_str.ptr = tmp;
- uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp, sizeof(tmp));
- if (uri_str.slen < 1) {
- pj_assert(!"URL is too long!");
- PJ_LOG(4,(THIS_FILE, "Unable to authorize: URI is too long!"));
- return NULL;
- }
-
-# if (PJSIP_AUTH_HEADER_CACHING)
- {
- pool = sess_pool;
- PJ_UNUSED_ARG(req_pool);
- }
-# else
- {
- pool = req_pool;
- PJ_UNUSED_ARG(sess_pool);
- }
-# endif
-
- if (hdr->type == PJSIP_H_WWW_AUTHENTICATE)
- auth = pjsip_authorization_hdr_create(pool);
- else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE)
- auth = pjsip_proxy_authorization_hdr_create(pool);
- else {
- pj_assert(0);
- return NULL;
- }
-
- /* Only support digest scheme at the moment. */
- if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
- pj_status_t rc;
- pj_str_t *cnonce = NULL;
- pj_uint32_t nc = 1;
-
- /* Update the session (nonce-count etc) if required. */
-# if PJSIP_AUTH_QOP_SUPPORT
- {
- if (auth_sess) {
- update_digest_session( sess_pool, auth_sess, hdr );
-
- cnonce = &auth_sess->cnonce;
- nc = auth_sess->nc;
- }
- }
-# endif /* PJSIP_AUTH_QOP_SUPPORT */
-
- auth->scheme = pjsip_DIGEST_STR;
- rc = respond_digest( pool, &auth->credential.digest,
- &hdr->challenge.digest, &uri_str, cred_info,
- cnonce, nc, &method->name);
- if (rc != 0)
- return NULL;
-
- /* Set qop type in auth session the first time only. */
- if (hdr->challenge.digest.qop.slen != 0 && auth_sess) {
- if (auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) {
- pj_str_t *qop_val = &auth->credential.digest.qop;
- if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) {
- auth_sess->qop_value = PJSIP_AUTH_QOP_AUTH;
- } else {
- auth_sess->qop_value = PJSIP_AUTH_QOP_UNKNOWN;
- }
- }
- }
- } else {
- auth = NULL;
- }
-
- /* Keep the new authorization header in the cache, only
- * if no qop is not present.
- */
-# if PJSIP_AUTH_HEADER_CACHING
- {
- if (auth && auth_sess && auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) {
- pjsip_cached_auth_hdr *cached_hdr;
-
- /* Delete old header with the same method. */
- cached_hdr = auth_sess->cached_hdr.next;
- while (cached_hdr != &auth_sess->cached_hdr) {
- if (pjsip_method_cmp(method, &cached_hdr->method)==0)
- break;
- cached_hdr = cached_hdr->next;
- }
-
- /* Save the header to the list. */
- if (cached_hdr != &auth_sess->cached_hdr) {
- cached_hdr->hdr = auth;
- } else {
- cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr));
- pjsip_method_copy( pool, &cached_hdr->method, method);
- cached_hdr->hdr = auth;
- pj_list_insert_before( &auth_sess->cached_hdr, cached_hdr );
- }
- }
- }
-# endif
-
- return auth;
-
-}
-
-/* Verify incoming Authorization/Proxy-Authorization header against existing
- * credentials. Will return TRUE if the authorization request matches any of
- * the credential.
- */
-PJ_DEF(pj_bool_t) pjsip_auth_verify(const pjsip_authorization_hdr *hdr,
- const pj_str_t *method,
- const pjsip_cred_info *cred_info )
-{
- if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) {
- char digest_buf[MD5STRLEN];
- pj_str_t digest;
- const pjsip_digest_credential *dig = &hdr->credential.digest;
-
- /* Check that username match. */
- if (pj_strcmp(&dig->username, &cred_info->username) != 0)
- return PJ_FALSE;
-
- /* Check that realm match. */
- if (pj_strcmp(&dig->realm, &cred_info->realm) != 0)
- return PJ_FALSE;
-
- /* Prepare for our digest calculation. */
- digest.ptr = digest_buf;
- digest.slen = MD5STRLEN;
-
- /* Create digest for comparison. */
- create_digest( &digest,
- &hdr->credential.digest.nonce,
- &hdr->credential.digest.nc,
- &hdr->credential.digest.cnonce,
- &hdr->credential.digest.qop,
- &hdr->credential.digest.uri,
- cred_info,
- method );
-
- return pj_stricmp(&digest, &hdr->credential.digest.response) == 0;
-
- } else {
- pj_assert(0);
- return PJ_FALSE;
- }
-}
-
-/* Find credential to use for the specified realm and scheme. */
-PJ_DEF(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count,
- const pjsip_cred_info cred[],
- const pj_str_t *realm,
- const pj_str_t *scheme)
-{
- unsigned i;
- PJ_UNUSED_ARG(scheme);
- for (i=0; i<count; ++i) {
- if (pj_stricmp(&cred[i].realm, realm) == 0)
- return &cred[i];
- }
- return NULL;
-}
-
-#if PJSIP_AUTH_AUTO_SEND_NEXT
-static void new_auth_for_req( pjsip_tx_data *tdata,
- pj_pool_t *sess_pool,
- pjsip_auth_session *sess,
- int cred_count,
- const pjsip_cred_info cred_info[])
-{
- const pjsip_cred_info *cred;
- pjsip_authorization_hdr *hauth;
-
- pj_assert(sess->last_chal != NULL);
-
- cred = pjsip_auth_find_cred( cred_count, cred_info, &sess->realm,
- &sess->last_chal->scheme );
- if (!cred)
- return;
-
-
- hauth = pjsip_auth_respond( tdata->pool, sess->last_chal,
- tdata->msg->line.req.uri,
- cred, &tdata->msg->line.req.method,
- sess_pool, sess);
- if (hauth) {
- pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hauth);
- }
-}
-#endif
-
-/*
- * Initialize new request message with authorization headers.
- * This function will put Authorization/Proxy-Authorization headers to the
- * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING)
- * and the session has previously sent Authorization/Proxy-Authorization header
- * with the same method, then the same Authorization/Proxy-Authorization header
- * will be resent from the cache only if qop is not present. If the stack is
- * configured to automatically generate next Authorization/Proxy-Authorization
- * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy-
- * Authorization headers are calculated and generated when they are not present
- * in the case or if authorization session has qop.
- *
- * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag
- * are not set, this function will do nothing. The stack then will only send
- * Authorization/Proxy-Authorization to respond 401/407 response.
- */
-PJ_DEF(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool,
- pjsip_tx_data *tdata,
- pjsip_auth_session *sess_list,
- int cred_count,
- const pjsip_cred_info cred_info[])
-{
- pjsip_auth_session *sess;
- pjsip_method *method = &tdata->msg->line.req.method;
-
- pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
-
- if (!sess_list)
- return 0;
-
- sess = sess_list->next;
- while (sess != sess_list) {
- if (sess->qop_value == PJSIP_AUTH_QOP_NONE) {
-# if (PJSIP_AUTH_HEADER_CACHING)
- {
- pjsip_cached_auth_hdr *entry = sess->cached_hdr.next;
- while (entry != &sess->cached_hdr) {
- if (pjsip_method_cmp(&entry->method, method)==0) {
- pjsip_authorization_hdr *hauth;
- hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr);
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
- } else {
-# if (PJSIP_AUTH_AUTO_SEND_NEXT)
- {
- new_auth_for_req( tdata, sess_pool, sess,
- cred_count, cred_info);
- }
-# else
- {
- PJ_UNUSED_ARG(sess_pool);
- PJ_UNUSED_ARG(cred_count);
- PJ_UNUSED_ARG(cred_info);
- }
-# endif /* PJSIP_AUTH_AUTO_SEND_NEXT */
- }
- entry = entry->next;
- }
- }
-# elif (PJSIP_AUTH_AUTO_SEND_NEXT)
- {
- new_auth_for_req( tdata, sess_pool, sess,
- cred_count, cred_info);
- }
-# else
- {
- PJ_UNUSED_ARG(sess_pool);
- PJ_UNUSED_ARG(cred_count);
- PJ_UNUSED_ARG(cred_info);
- }
-# endif /* PJSIP_AUTH_HEADER_CACHING */
-
- }
-# if (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT)
- else if (sess->qop_value == PJSIP_AUTH_QOP_AUTH) {
- /* For qop="auth", we have to re-create the authorization header.
- */
- const pjsip_cred_info *cred;
- pjsip_authorization_hdr *hauth;
-
- cred = pjsip_auth_find_cred( cred_count, cred_info,
- &sess->realm,
- &sess->last_chal->scheme);
- if (!cred) {
- sess = sess->next;
- continue;
- }
-
- hauth = pjsip_auth_respond( tdata->pool, sess->last_chal,
- tdata->msg->line.req.uri,
- cred,
- &tdata->msg->line.req.method,
- sess_pool, sess );
- if (hauth) {
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
- }
- }
-# endif /* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */
-
- sess = sess->next;
- }
- return 0;
-}
-
-/* Process authorization challenge */
-static pjsip_authorization_hdr *process_auth( pj_pool_t *req_pool,
- const pjsip_www_authenticate_hdr *hchal,
- const pjsip_uri *uri,
- pjsip_tx_data *tdata,
- int cred_count,
- const pjsip_cred_info cred_info[],
- pj_pool_t *ses_pool,
- pjsip_auth_session *auth_sess)
-{
- const pjsip_cred_info *cred;
- pjsip_authorization_hdr *sent_auth = NULL, *hauth;
- pjsip_hdr *hdr;
-
- /* See if we have sent authorization header for this realm */
- hdr = tdata->msg->hdr.next;
- while (hdr != &tdata->msg->hdr) {
- if ((hchal->type == PJSIP_H_WWW_AUTHENTICATE &&
- hdr->type == PJSIP_H_AUTHORIZATION) ||
- (hchal->type == PJSIP_H_PROXY_AUTHENTICATE &&
- hdr->type == PJSIP_H_PROXY_AUTHORIZATION))
- {
- sent_auth = (pjsip_authorization_hdr*) hdr;
- if (pj_stricmp(&hchal->challenge.common.realm,
- &sent_auth->credential.common.realm )==0)
- {
- break;
- }
- }
- hdr = hdr->next;
- }
-
- /* If we have sent, see if server rejected because of stale nonce or
- * other causes.
- */
- if (hdr != &tdata->msg->hdr) {
- if (hchal->challenge.digest.stale == 0) {
- /* Our credential is rejected. No point in trying to re-supply
- * the same credential.
- */
- PJ_LOG(4, (THIS_FILE, "Authorization failed for %.*s@%.*s",
- sent_auth->credential.digest.username.slen,
- sent_auth->credential.digest.username.ptr,
- sent_auth->credential.digest.realm.slen,
- sent_auth->credential.digest.realm.ptr));
- return NULL;
- }
-
- /* Otherwise remove old, stale authorization header from the mesasge.
- * We will supply a new one.
- */
- pj_list_erase(sent_auth);
- }
-
- /* Find credential to be used for the challenge. */
- cred = pjsip_auth_find_cred( cred_count, cred_info,
- &hchal->challenge.common.realm, &hchal->scheme);
- if (!cred) {
- const pj_str_t *realm = &hchal->challenge.common.realm;
- PJ_LOG(4,(THIS_FILE,
- "Unable to set auth for %s: can not find credential for %.*s/%.*s",
- tdata->obj_name,
- realm->slen, realm->ptr,
- hchal->scheme.slen, hchal->scheme.ptr));
- return NULL;
- }
-
- /* Respond to authorization challenge. */
- hauth = pjsip_auth_respond( req_pool, hchal, uri, cred,
- &tdata->msg->line.req.method,
- ses_pool, auth_sess);
- return hauth;
-}
-
-
-/* Reinitialize outgoing request after 401/407 response is received.
- * The purpose of this function is:
- * - to add a Authorization/Proxy-Authorization header.
- * - to put the newly created Authorization/Proxy-Authorization header
- * in cached_list.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_auth_reinit_req( pjsip_endpoint *endpt,
- pj_pool_t *ses_pool,
- pjsip_auth_session *sess_list,
- int cred_count,
- const pjsip_cred_info cred_info[],
- pjsip_tx_data *tdata,
- const pjsip_rx_data *rdata)
-{
- const pjsip_hdr *hdr;
- pjsip_via_hdr *via;
-
- PJ_UNUSED_ARG(endpt);
-
- pj_assert(rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG);
- pj_assert(rdata->msg_info.msg->line.status.code == 401 ||
- rdata->msg_info.msg->line.status.code == 407 );
-
- /*
- * Respond to each authentication challenge.
- */
- hdr = rdata->msg_info.msg->hdr.next;
- while (hdr != &rdata->msg_info.msg->hdr) {
- pjsip_auth_session *sess;
- const pjsip_www_authenticate_hdr *hchal;
- pjsip_authorization_hdr *hauth;
-
- /* Find WWW-Authenticate or Proxy-Authenticate header. */
- while (hdr->type != PJSIP_H_WWW_AUTHENTICATE &&
- hdr->type != PJSIP_H_PROXY_AUTHENTICATE &&
- hdr != &rdata->msg_info.msg->hdr)
- {
- hdr = hdr->next;
- }
- if (hdr == &rdata->msg_info.msg->hdr)
- break;
-
- hchal = (const pjsip_www_authenticate_hdr*) hdr;
-
- /* Find authentication session for this realm, create a new one
- * if not present.
- */
- sess = find_session(sess_list, &hchal->challenge.common.realm );
- if (!sess) {
- sess = pj_pool_calloc( ses_pool, 1, sizeof(*sess));
- pj_strdup( ses_pool, &sess->realm, &hchal->challenge.common.realm);
- sess->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE);
-# if (PJSIP_AUTH_HEADER_CACHING)
- {
- pj_list_init(&sess->cached_hdr);
- }
-# endif
- pj_list_insert_before( sess_list, sess );
- }
-
- /* Create authorization header for this challenge, and update
- * authorization session.
- */
- hauth = process_auth( tdata->pool, hchal, tdata->msg->line.req.uri,
- tdata, cred_count, cred_info, ses_pool, sess );
- if (!hauth)
- return NULL;
-
- /* Add to the message. */
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
-
- /* Process next header. */
- hdr = hdr->next;
- }
-
-
- /* Remove branch param in Via header. */
- via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
- via->branch_param.slen = 0;
-
- /* Increment reference counter. */
- pjsip_tx_data_add_ref(tdata);
-
- /* Done. */
- return tdata;
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip/sip_auth.h> +#include <pjsip/sip_auth_parser.h> /* just to get pjsip_DIGEST_STR */ +#include <pjsip/sip_transport.h> +#include <pjsip/sip_endpoint.h> +#include <pjlib-util/md5.h> +#include <pj/log.h> +#include <pj/string.h> +#include <pj/pool.h> +#include <pj/guid.h> +#include <pj/assert.h> +#include <pj/ctype.h> + +/* Length of digest string. */ +#define MD5STRLEN 32 + +/* Maximum stack size we use for storing username+realm+password etc. */ +#define MAX_TEMP 128 + +/* A macro just to get rid of type mismatch between char and unsigned char */ +#define MD5_APPEND(pms,buf,len) pj_md5_update(pms, (const pj_uint8_t*)buf, len) + +/* Logging. */ +#define THIS_FILE "sip_auth.c" +#if 0 +# define AUTH_TRACE_(expr) PJ_LOG(3, expr) +#else +# define AUTH_TRACE_(expr) +#endif + +static const char hex[] = "0123456789abcdef"; + +/* Transform digest to string. + * output must be at least MD5STRLEN+1 bytes. + * + * NOTE: THE OUTPUT STRING IS NOT NULL TERMINATED! + */ +static void digest2str(const unsigned char digest[], char *output) +{ + char *p = output; + int i; + + for (i = 0; i<16; ++i) { + int val = digest[i]; + *p++ = hex[val >> 4]; + *p++ = hex[val & 0x0F]; + } +} + +/* + * Create response digest based on the parameters and store the + * digest ASCII in 'result'. + */ +static void create_digest( pj_str_t *result, + const pj_str_t *nonce, + const pj_str_t *nc, + const pj_str_t *cnonce, + const pj_str_t *qop, + const pj_str_t *uri, + const pjsip_cred_info *cred_info, + const pj_str_t *method) +{ + char ha1[MD5STRLEN]; + char ha2[MD5STRLEN]; + unsigned char digest[16]; + pj_md5_context pms; + + pj_assert(result->slen >= MD5STRLEN); + + AUTH_TRACE_((THIS_FILE, "Begin creating digest")); + + if (cred_info->data_type == PJSIP_CRED_DATA_PLAIN_PASSWD) { + /*** + *** ha1 = MD5(username ":" realm ":" password) + ***/ + pj_md5_init(&pms); + MD5_APPEND( &pms, cred_info->username.ptr, cred_info->username.slen); + MD5_APPEND( &pms, ":", 1); + MD5_APPEND( &pms, cred_info->realm.ptr, cred_info->realm.slen); + MD5_APPEND( &pms, ":", 1); + MD5_APPEND( &pms, cred_info->data.ptr, cred_info->data.slen); + pj_md5_final(&pms, digest); + + digest2str(digest, ha1); + + } else if (cred_info->data_type == PJSIP_CRED_DATA_DIGEST) { + pj_assert(cred_info->data.slen == 32); + pj_memcpy( ha1, cred_info->data.ptr, cred_info->data.slen ); + } + + AUTH_TRACE_((THIS_FILE, " ha1=%.32s", ha1)); + + /*** + *** ha2 = MD5(method ":" req_uri) + ***/ + pj_md5_init(&pms); + MD5_APPEND( &pms, method->ptr, method->slen); + MD5_APPEND( &pms, ":", 1); + MD5_APPEND( &pms, uri->ptr, uri->slen); + pj_md5_final(&pms, digest); + digest2str(digest, ha2); + + AUTH_TRACE_((THIS_FILE, " ha2=%.32s", ha2)); + + /*** + *** When qop is not used: + *** response = MD5(ha1 ":" nonce ":" ha2) + *** + *** When qop=auth is used: + *** response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2) + ***/ + pj_md5_init(&pms); + MD5_APPEND( &pms, ha1, MD5STRLEN); + MD5_APPEND( &pms, ":", 1); + MD5_APPEND( &pms, nonce->ptr, nonce->slen); + if (qop && qop->slen != 0) { + MD5_APPEND( &pms, ":", 1); + MD5_APPEND( &pms, nc->ptr, nc->slen); + MD5_APPEND( &pms, ":", 1); + MD5_APPEND( &pms, cnonce->ptr, cnonce->slen); + MD5_APPEND( &pms, ":", 1); + MD5_APPEND( &pms, qop->ptr, qop->slen); + } + MD5_APPEND( &pms, ":", 1); + MD5_APPEND( &pms, ha2, MD5STRLEN); + + /* This is the final response digest. */ + pj_md5_final(&pms, digest); + + /* Convert digest to string and store in chal->response. */ + result->slen = MD5STRLEN; + digest2str(digest, result->ptr); + + AUTH_TRACE_((THIS_FILE, " digest=%.32s", result->ptr)); + AUTH_TRACE_((THIS_FILE, "Digest created")); +} + +/* + * Finds out if qop offer contains "auth" token. + */ +static pj_bool_t has_auth_qop( pj_pool_t *pool, const pj_str_t *qop_offer) +{ + pj_str_t qop; + char *p; + + pj_strdup_with_null( pool, &qop, qop_offer); + p = qop.ptr; + while (*p) { + *p = (char)pj_tolower(*p); + ++p; + } + + p = qop.ptr; + while (*p) { + if (*p=='a' && *(p+1)=='u' && *(p+2)=='t' && *(p+3)=='h') { + int e = *(p+4); + if (e=='"' || e==',' || e==0) + return PJ_TRUE; + else + p += 4; + } else { + ++p; + } + } + + return PJ_FALSE; +} + +/* + * Generate response digest. + * Most of the parameters to generate the digest (i.e. username, realm, uri, + * and nonce) are expected to be in the credential. Additional parameters (i.e. + * password and method param) should be supplied in the argument. + * + * The resulting digest will be stored in cred->response. + * The pool is used to allocate 32 bytes to store the digest in cred->response. + */ +static pj_status_t respond_digest( pj_pool_t *pool, + pjsip_digest_credential *cred, + const pjsip_digest_challenge *chal, + const pj_str_t *uri, + const pjsip_cred_info *cred_info, + const pj_str_t *cnonce, + pj_uint32_t nc, + const pj_str_t *method) +{ + /* Check algorithm is supported. We only support MD5. */ + if (chal->algorithm.slen && pj_stricmp(&chal->algorithm, &pjsip_MD5_STR)) + { + PJ_LOG(4,(THIS_FILE, "Unsupported digest algorithm \"%.*s\"", + chal->algorithm.slen, chal->algorithm.ptr)); + return -1; + } + + /* Build digest credential from arguments. */ + pj_strdup(pool, &cred->username, &cred_info->username); + pj_strdup(pool, &cred->realm, &chal->realm); + pj_strdup(pool, &cred->nonce, &chal->nonce); + pj_strdup(pool, &cred->uri, uri); + cred->algorithm = pjsip_MD5_STR; + pj_strdup(pool, &cred->opaque, &chal->opaque); + + /* Allocate memory. */ + cred->response.ptr = pj_pool_alloc(pool, MD5STRLEN); + cred->response.slen = MD5STRLEN; + + if (chal->qop.slen == 0) { + /* Server doesn't require quality of protection. */ + + /* Convert digest to string and store in chal->response. */ + create_digest( &cred->response, &cred->nonce, NULL, NULL, NULL, + uri, cred_info, method); + + } else if (has_auth_qop(pool, &chal->qop)) { + /* Server requires quality of protection. + * We respond with selecting "qop=auth" protection. + */ + cred->qop = pjsip_AUTH_STR; + cred->nc.ptr = pj_pool_alloc(pool, 16); + pj_snprintf(cred->nc.ptr, 16, "%06u", nc); + + if (cnonce && cnonce->slen) { + pj_strdup(pool, &cred->cnonce, cnonce); + } else { + pj_str_t dummy_cnonce = { "b39971", 6}; + pj_strdup(pool, &cred->cnonce, &dummy_cnonce); + } + + create_digest( &cred->response, &cred->nonce, &cred->nc, cnonce, + &pjsip_AUTH_STR, uri, cred_info, method ); + + } else { + /* Server requires quality protection that we don't support. */ + PJ_LOG(4,(THIS_FILE, "Unsupported qop offer %.*s", + chal->qop.slen, chal->qop.ptr)); + return -1; + } + + return 0; +} + +#if PJSIP_AUTH_QOP_SUPPORT +/* + * Update authentication session with a challenge. + */ +static void update_digest_session( pj_pool_t *ses_pool, + pjsip_auth_session *auth_sess, + const pjsip_www_authenticate_hdr *hdr ) +{ + if (hdr->challenge.digest.qop.slen == 0) + return; + + /* Initialize cnonce and qop if not present. */ + if (auth_sess->cnonce.slen == 0) { + /* Save the whole challenge */ + auth_sess->last_chal = pjsip_hdr_clone(ses_pool, hdr); + + /* Create cnonce */ + pj_create_unique_string( ses_pool, &auth_sess->cnonce ); + + /* Initialize nonce-count */ + auth_sess->nc = 1; + + /* Save realm. */ + pj_assert(auth_sess->realm.slen != 0); + if (auth_sess->realm.slen == 0) { + pj_strdup(ses_pool, &auth_sess->realm, + &hdr->challenge.digest.realm); + } + + } else { + /* Update last_nonce and nonce-count */ + if (!pj_strcmp(&hdr->challenge.digest.nonce, + &auth_sess->last_chal->challenge.digest.nonce)) + { + /* Same nonce, increment nonce-count */ + ++auth_sess->nc; + } else { + /* Server gives new nonce. */ + pj_strdup(ses_pool, &auth_sess->last_chal->challenge.digest.nonce, + &hdr->challenge.digest.nonce); + /* Has the opaque changed? */ + if (pj_strcmp(&auth_sess->last_chal->challenge.digest.opaque, + &hdr->challenge.digest.opaque)) + { + pj_strdup(ses_pool, + &auth_sess->last_chal->challenge.digest.opaque, + &hdr->challenge.digest.opaque); + } + auth_sess->nc = 1; + } + } +} +#endif /* PJSIP_AUTH_QOP_SUPPORT */ + + +/* Find authentication session in the list. */ +static pjsip_auth_session *find_session( pjsip_auth_session *sess_list, + const pj_str_t *realm ) +{ + pjsip_auth_session *sess = sess_list->next; + while (sess != sess_list) { + if (pj_stricmp(&sess->realm, realm) == 0) + return sess; + sess = sess->next; + } + + return NULL; +} + +/* + * Create Authorization/Proxy-Authorization response header based on the challege + * in WWW-Authenticate/Proxy-Authenticate header. + */ +PJ_DEF(pjsip_authorization_hdr*) +pjsip_auth_respond( pj_pool_t *req_pool, + const pjsip_www_authenticate_hdr *hdr, + const pjsip_uri *uri, + const pjsip_cred_info *cred_info, + const pjsip_method *method, + pj_pool_t *sess_pool, + pjsip_auth_session *auth_sess) +{ + pjsip_authorization_hdr *auth; + char tmp[PJSIP_MAX_URL_SIZE]; + pj_str_t uri_str; + pj_pool_t *pool; + + pj_assert(hdr != NULL); + pj_assert(uri != NULL); + pj_assert(cred_info != NULL); + pj_assert(method != NULL); + + /* Print URL in the original request. */ + uri_str.ptr = tmp; + uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp, sizeof(tmp)); + if (uri_str.slen < 1) { + pj_assert(!"URL is too long!"); + PJ_LOG(4,(THIS_FILE, "Unable to authorize: URI is too long!")); + return NULL; + } + +# if (PJSIP_AUTH_HEADER_CACHING) + { + pool = sess_pool; + PJ_UNUSED_ARG(req_pool); + } +# else + { + pool = req_pool; + PJ_UNUSED_ARG(sess_pool); + } +# endif + + if (hdr->type == PJSIP_H_WWW_AUTHENTICATE) + auth = pjsip_authorization_hdr_create(pool); + else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE) + auth = pjsip_proxy_authorization_hdr_create(pool); + else { + pj_assert(0); + return NULL; + } + + /* Only support digest scheme at the moment. */ + if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { + pj_status_t rc; + pj_str_t *cnonce = NULL; + pj_uint32_t nc = 1; + + /* Update the session (nonce-count etc) if required. */ +# if PJSIP_AUTH_QOP_SUPPORT + { + if (auth_sess) { + update_digest_session( sess_pool, auth_sess, hdr ); + + cnonce = &auth_sess->cnonce; + nc = auth_sess->nc; + } + } +# endif /* PJSIP_AUTH_QOP_SUPPORT */ + + auth->scheme = pjsip_DIGEST_STR; + rc = respond_digest( pool, &auth->credential.digest, + &hdr->challenge.digest, &uri_str, cred_info, + cnonce, nc, &method->name); + if (rc != 0) + return NULL; + + /* Set qop type in auth session the first time only. */ + if (hdr->challenge.digest.qop.slen != 0 && auth_sess) { + if (auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) { + pj_str_t *qop_val = &auth->credential.digest.qop; + if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) { + auth_sess->qop_value = PJSIP_AUTH_QOP_AUTH; + } else { + auth_sess->qop_value = PJSIP_AUTH_QOP_UNKNOWN; + } + } + } + } else { + auth = NULL; + } + + /* Keep the new authorization header in the cache, only + * if no qop is not present. + */ +# if PJSIP_AUTH_HEADER_CACHING + { + if (auth && auth_sess && auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) { + pjsip_cached_auth_hdr *cached_hdr; + + /* Delete old header with the same method. */ + cached_hdr = auth_sess->cached_hdr.next; + while (cached_hdr != &auth_sess->cached_hdr) { + if (pjsip_method_cmp(method, &cached_hdr->method)==0) + break; + cached_hdr = cached_hdr->next; + } + + /* Save the header to the list. */ + if (cached_hdr != &auth_sess->cached_hdr) { + cached_hdr->hdr = auth; + } else { + cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr)); + pjsip_method_copy( pool, &cached_hdr->method, method); + cached_hdr->hdr = auth; + pj_list_insert_before( &auth_sess->cached_hdr, cached_hdr ); + } + } + } +# endif + + return auth; + +} + +/* Verify incoming Authorization/Proxy-Authorization header against existing + * credentials. Will return TRUE if the authorization request matches any of + * the credential. + */ +PJ_DEF(pj_bool_t) pjsip_auth_verify(const pjsip_authorization_hdr *hdr, + const pj_str_t *method, + const pjsip_cred_info *cred_info ) +{ + if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) { + char digest_buf[MD5STRLEN]; + pj_str_t digest; + const pjsip_digest_credential *dig = &hdr->credential.digest; + + /* Check that username match. */ + if (pj_strcmp(&dig->username, &cred_info->username) != 0) + return PJ_FALSE; + + /* Check that realm match. */ + if (pj_strcmp(&dig->realm, &cred_info->realm) != 0) + return PJ_FALSE; + + /* Prepare for our digest calculation. */ + digest.ptr = digest_buf; + digest.slen = MD5STRLEN; + + /* Create digest for comparison. */ + create_digest( &digest, + &hdr->credential.digest.nonce, + &hdr->credential.digest.nc, + &hdr->credential.digest.cnonce, + &hdr->credential.digest.qop, + &hdr->credential.digest.uri, + cred_info, + method ); + + return pj_stricmp(&digest, &hdr->credential.digest.response) == 0; + + } else { + pj_assert(0); + return PJ_FALSE; + } +} + +/* Find credential to use for the specified realm and scheme. */ +PJ_DEF(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count, + const pjsip_cred_info cred[], + const pj_str_t *realm, + const pj_str_t *scheme) +{ + unsigned i; + PJ_UNUSED_ARG(scheme); + for (i=0; i<count; ++i) { + if (pj_stricmp(&cred[i].realm, realm) == 0) + return &cred[i]; + } + return NULL; +} + +#if PJSIP_AUTH_AUTO_SEND_NEXT +static void new_auth_for_req( pjsip_tx_data *tdata, + pj_pool_t *sess_pool, + pjsip_auth_session *sess, + int cred_count, + const pjsip_cred_info cred_info[]) +{ + const pjsip_cred_info *cred; + pjsip_authorization_hdr *hauth; + + pj_assert(sess->last_chal != NULL); + + cred = pjsip_auth_find_cred( cred_count, cred_info, &sess->realm, + &sess->last_chal->scheme ); + if (!cred) + return; + + + hauth = pjsip_auth_respond( tdata->pool, sess->last_chal, + tdata->msg->line.req.uri, + cred, &tdata->msg->line.req.method, + sess_pool, sess); + if (hauth) { + pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hauth); + } +} +#endif + +/* + * Initialize new request message with authorization headers. + * This function will put Authorization/Proxy-Authorization headers to the + * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING) + * and the session has previously sent Authorization/Proxy-Authorization header + * with the same method, then the same Authorization/Proxy-Authorization header + * will be resent from the cache only if qop is not present. If the stack is + * configured to automatically generate next Authorization/Proxy-Authorization + * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy- + * Authorization headers are calculated and generated when they are not present + * in the case or if authorization session has qop. + * + * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag + * are not set, this function will do nothing. The stack then will only send + * Authorization/Proxy-Authorization to respond 401/407 response. + */ +PJ_DEF(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool, + pjsip_tx_data *tdata, + pjsip_auth_session *sess_list, + int cred_count, + const pjsip_cred_info cred_info[]) +{ + pjsip_auth_session *sess; + pjsip_method *method = &tdata->msg->line.req.method; + + pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG); + + if (!sess_list) + return 0; + + sess = sess_list->next; + while (sess != sess_list) { + if (sess->qop_value == PJSIP_AUTH_QOP_NONE) { +# if (PJSIP_AUTH_HEADER_CACHING) + { + pjsip_cached_auth_hdr *entry = sess->cached_hdr.next; + while (entry != &sess->cached_hdr) { + if (pjsip_method_cmp(&entry->method, method)==0) { + pjsip_authorization_hdr *hauth; + hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr); + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); + } else { +# if (PJSIP_AUTH_AUTO_SEND_NEXT) + { + new_auth_for_req( tdata, sess_pool, sess, + cred_count, cred_info); + } +# else + { + PJ_UNUSED_ARG(sess_pool); + PJ_UNUSED_ARG(cred_count); + PJ_UNUSED_ARG(cred_info); + } +# endif /* PJSIP_AUTH_AUTO_SEND_NEXT */ + } + entry = entry->next; + } + } +# elif (PJSIP_AUTH_AUTO_SEND_NEXT) + { + new_auth_for_req( tdata, sess_pool, sess, + cred_count, cred_info); + } +# else + { + PJ_UNUSED_ARG(sess_pool); + PJ_UNUSED_ARG(cred_count); + PJ_UNUSED_ARG(cred_info); + } +# endif /* PJSIP_AUTH_HEADER_CACHING */ + + } +# if (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT) + else if (sess->qop_value == PJSIP_AUTH_QOP_AUTH) { + /* For qop="auth", we have to re-create the authorization header. + */ + const pjsip_cred_info *cred; + pjsip_authorization_hdr *hauth; + + cred = pjsip_auth_find_cred( cred_count, cred_info, + &sess->realm, + &sess->last_chal->scheme); + if (!cred) { + sess = sess->next; + continue; + } + + hauth = pjsip_auth_respond( tdata->pool, sess->last_chal, + tdata->msg->line.req.uri, + cred, + &tdata->msg->line.req.method, + sess_pool, sess ); + if (hauth) { + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); + } + } +# endif /* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */ + + sess = sess->next; + } + return 0; +} + +/* Process authorization challenge */ +static pjsip_authorization_hdr *process_auth( pj_pool_t *req_pool, + const pjsip_www_authenticate_hdr *hchal, + const pjsip_uri *uri, + pjsip_tx_data *tdata, + int cred_count, + const pjsip_cred_info cred_info[], + pj_pool_t *ses_pool, + pjsip_auth_session *auth_sess) +{ + const pjsip_cred_info *cred; + pjsip_authorization_hdr *sent_auth = NULL, *hauth; + pjsip_hdr *hdr; + + /* See if we have sent authorization header for this realm */ + hdr = tdata->msg->hdr.next; + while (hdr != &tdata->msg->hdr) { + if ((hchal->type == PJSIP_H_WWW_AUTHENTICATE && + hdr->type == PJSIP_H_AUTHORIZATION) || + (hchal->type == PJSIP_H_PROXY_AUTHENTICATE && + hdr->type == PJSIP_H_PROXY_AUTHORIZATION)) + { + sent_auth = (pjsip_authorization_hdr*) hdr; + if (pj_stricmp(&hchal->challenge.common.realm, + &sent_auth->credential.common.realm )==0) + { + break; + } + } + hdr = hdr->next; + } + + /* If we have sent, see if server rejected because of stale nonce or + * other causes. + */ + if (hdr != &tdata->msg->hdr) { + if (hchal->challenge.digest.stale == 0) { + /* Our credential is rejected. No point in trying to re-supply + * the same credential. + */ + PJ_LOG(4, (THIS_FILE, "Authorization failed for %.*s@%.*s", + sent_auth->credential.digest.username.slen, + sent_auth->credential.digest.username.ptr, + sent_auth->credential.digest.realm.slen, + sent_auth->credential.digest.realm.ptr)); + return NULL; + } + + /* Otherwise remove old, stale authorization header from the mesasge. + * We will supply a new one. + */ + pj_list_erase(sent_auth); + } + + /* Find credential to be used for the challenge. */ + cred = pjsip_auth_find_cred( cred_count, cred_info, + &hchal->challenge.common.realm, &hchal->scheme); + if (!cred) { + const pj_str_t *realm = &hchal->challenge.common.realm; + PJ_LOG(4,(THIS_FILE, + "Unable to set auth for %s: can not find credential for %.*s/%.*s", + tdata->obj_name, + realm->slen, realm->ptr, + hchal->scheme.slen, hchal->scheme.ptr)); + return NULL; + } + + /* Respond to authorization challenge. */ + hauth = pjsip_auth_respond( req_pool, hchal, uri, cred, + &tdata->msg->line.req.method, + ses_pool, auth_sess); + return hauth; +} + + +/* Reinitialize outgoing request after 401/407 response is received. + * The purpose of this function is: + * - to add a Authorization/Proxy-Authorization header. + * - to put the newly created Authorization/Proxy-Authorization header + * in cached_list. + */ +PJ_DEF(pjsip_tx_data*) pjsip_auth_reinit_req( pjsip_endpoint *endpt, + pj_pool_t *ses_pool, + pjsip_auth_session *sess_list, + int cred_count, + const pjsip_cred_info cred_info[], + pjsip_tx_data *tdata, + const pjsip_rx_data *rdata) +{ + const pjsip_hdr *hdr; + pjsip_via_hdr *via; + + PJ_UNUSED_ARG(endpt); + + pj_assert(rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG); + pj_assert(rdata->msg_info.msg->line.status.code == 401 || + rdata->msg_info.msg->line.status.code == 407 ); + + /* + * Respond to each authentication challenge. + */ + hdr = rdata->msg_info.msg->hdr.next; + while (hdr != &rdata->msg_info.msg->hdr) { + pjsip_auth_session *sess; + const pjsip_www_authenticate_hdr *hchal; + pjsip_authorization_hdr *hauth; + + /* Find WWW-Authenticate or Proxy-Authenticate header. */ + while (hdr->type != PJSIP_H_WWW_AUTHENTICATE && + hdr->type != PJSIP_H_PROXY_AUTHENTICATE && + hdr != &rdata->msg_info.msg->hdr) + { + hdr = hdr->next; + } + if (hdr == &rdata->msg_info.msg->hdr) + break; + + hchal = (const pjsip_www_authenticate_hdr*) hdr; + + /* Find authentication session for this realm, create a new one + * if not present. + */ + sess = find_session(sess_list, &hchal->challenge.common.realm ); + if (!sess) { + sess = pj_pool_calloc( ses_pool, 1, sizeof(*sess)); + pj_strdup( ses_pool, &sess->realm, &hchal->challenge.common.realm); + sess->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE); +# if (PJSIP_AUTH_HEADER_CACHING) + { + pj_list_init(&sess->cached_hdr); + } +# endif + pj_list_insert_before( sess_list, sess ); + } + + /* Create authorization header for this challenge, and update + * authorization session. + */ + hauth = process_auth( tdata->pool, hchal, tdata->msg->line.req.uri, + tdata, cred_count, cred_info, ses_pool, sess ); + if (!hauth) + return NULL; + + /* Add to the message. */ + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); + + /* Process next header. */ + hdr = hdr->next; + } + + + /* Remove branch param in Via header. */ + via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); + via->branch_param.slen = 0; + + /* Increment reference counter. */ + pjsip_tx_data_add_ref(tdata); + + /* Done. */ + return tdata; +} + diff --git a/pjsip/src/pjsip/sip_auth_msg.c b/pjsip/src/pjsip/sip_auth_msg.c index 3eca0cb2..366bff0b 100644 --- a/pjsip/src/pjsip/sip_auth_msg.c +++ b/pjsip/src/pjsip/sip_auth_msg.c @@ -1,310 +1,327 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_auth_msg.h>
-#include <pjsip/sip_auth_parser.h>
-#include <pj/pool.h>
-#include <pj/list.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-#include <pjsip/print_util.h>
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Authorization and Proxy-Authorization header.
- */
-static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool,
- const pjsip_authorization_hdr *hdr);
-static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_authorization_hdr *hdr);
-static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr,
- char *buf, pj_size_t size);
-
-static pjsip_hdr_vptr authorization_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_authorization_hdr_print,
-};
-
-
-PJ_DEF(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool)
-{
- pjsip_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_AUTHORIZATION, &authorization_hdr_vptr);
- return hdr;
-}
-
-PJ_DEF(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool)
-{
- pjsip_proxy_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_PROXY_AUTHORIZATION, &authorization_hdr_vptr);
- return hdr;
-}
-
-static int print_digest_credential(pjsip_digest_credential *cred, char *buf, pj_size_t size)
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
-
- copy_advance_pair_quote_cond(buf, "username=", 9, cred->username, '"', '"');
- copy_advance_pair_quote_cond(buf, ", realm=", 8, cred->realm, '"', '"');
- copy_advance_pair_quote_cond(buf, ", nonce=", 8, cred->nonce, '"', '"');
- copy_advance_pair_quote_cond(buf, ", uri=", 6, cred->uri, '"', '"');
- copy_advance_pair_quote_cond(buf, ", response=", 11, cred->response, '"', '"');
- copy_advance_pair(buf, ", algorithm=", 12, cred->algorithm);
- copy_advance_pair_quote_cond(buf, ", cnonce=", 9, cred->cnonce, '"', '"');
- copy_advance_pair_quote_cond(buf, ", opaque=", 9, cred->opaque, '"', '"');
- //Note: there's no dbl-quote in qop in Authorization header
- // (unlike WWW-Authenticate)
- //copy_advance_pair_quote_cond(buf, ", qop=", 6, cred->qop, '"', '"');
- copy_advance_pair(buf, ", qop=", 6, cred->qop);
- copy_advance_pair(buf, ", nc=", 5, cred->nc);
- copy_advance(buf, cred->other_param);
-
- return (int) (buf-startbuf);
-}
-
-static int print_pgp_credential(pjsip_pgp_credential *cred, char *buf, pj_size_t size)
-{
- PJ_UNUSED_ARG(cred);
- PJ_UNUSED_ARG(buf);
- PJ_UNUSED_ARG(size);
- return -1;
-}
-
-static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr,
- char *buf, pj_size_t size)
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
-
- copy_advance(buf, hdr->name);
- *buf++ = ':';
- *buf++ = ' ';
-
- copy_advance(buf, hdr->scheme);
- *buf++ = ' ';
-
- if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0)
- {
- printed = print_digest_credential(&hdr->credential.digest, buf, endbuf - buf);
- }
- else if (pj_stricmp(&hdr->scheme, &pjsip_PGP_STR) == 0)
- {
- printed = print_pgp_credential(&hdr->credential.pgp, buf, endbuf - buf);
- }
- else {
- pj_assert(0);
- return -1;
- }
-
- if (printed == -1)
- return -1;
-
- buf += printed;
- *buf = '\0';
- return (int)(buf-startbuf);
-}
-
-static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool,
- const pjsip_authorization_hdr *rhs)
-{
- /* This function also serves Proxy-Authorization header. */
- pjsip_authorization_hdr *hdr;
- if (rhs->type == PJSIP_H_AUTHORIZATION)
- hdr = pjsip_authorization_hdr_create(pool);
- else
- hdr = pjsip_proxy_authorization_hdr_create(pool);
-
- pj_strdup(pool, &hdr->scheme, &rhs->scheme);
-
- if (pj_stricmp2(&hdr->scheme, "digest") == 0) {
- pj_strdup(pool, &hdr->credential.digest.username, &rhs->credential.digest.username);
- pj_strdup(pool, &hdr->credential.digest.realm, &rhs->credential.digest.realm);
- pj_strdup(pool, &hdr->credential.digest.nonce, &rhs->credential.digest.nonce);
- pj_strdup(pool, &hdr->credential.digest.uri, &rhs->credential.digest.uri);
- pj_strdup(pool, &hdr->credential.digest.response, &rhs->credential.digest.response);
- pj_strdup(pool, &hdr->credential.digest.algorithm, &rhs->credential.digest.algorithm);
- pj_strdup(pool, &hdr->credential.digest.cnonce, &rhs->credential.digest.cnonce);
- pj_strdup(pool, &hdr->credential.digest.opaque, &rhs->credential.digest.opaque);
- pj_strdup(pool, &hdr->credential.digest.qop, &rhs->credential.digest.qop);
- pj_strdup(pool, &hdr->credential.digest.nc, &rhs->credential.digest.nc);
- pj_strdup(pool, &hdr->credential.digest.other_param, &rhs->credential.digest.other_param);
- } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) {
- pj_assert(0);
- return NULL;
- } else {
- pj_assert(0);
- return NULL;
- }
-
- return hdr;
-}
-
-static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_authorization_hdr *rhs)
-{
- /* This function also serves Proxy-Authorization header. */
- pjsip_authorization_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Proxy-Authenticate and WWW-Authenticate header.
- */
-static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr,
- char *buf, pj_size_t size);
-static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool,
- const pjsip_www_authenticate_hdr *hdr);
-static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_www_authenticate_hdr *hdr);
-
-static pjsip_hdr_vptr www_authenticate_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_www_authenticate_hdr_print,
-};
-
-
-PJ_DEF(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool)
-{
- pjsip_www_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_WWW_AUTHENTICATE, &www_authenticate_hdr_vptr);
- return hdr;
-}
-
-
-PJ_DEF(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool)
-{
- pjsip_proxy_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_PROXY_AUTHENTICATE, &www_authenticate_hdr_vptr);
- return hdr;
-}
-
-static int print_digest_challenge( pjsip_digest_challenge *chal,
- char *buf, pj_size_t size)
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
-
- copy_advance_pair_quote_cond(buf, " realm=", 7, chal->realm, '"', '"');
- copy_advance_pair_quote_cond(buf, ",domain=", 8, chal->domain, '"', '"');
- copy_advance_pair_quote_cond(buf, ",nonce=", 7, chal->nonce, '"', '"');
- copy_advance_pair_quote_cond(buf, ",opaque=", 8, chal->opaque, '"', '"');
- if (chal->stale) {
- pj_str_t true_str = { "true", 4 };
- copy_advance_pair(buf, ",stale=", 7, true_str);
- }
- copy_advance_pair(buf, ",algorithm=", 11, chal->algorithm);
- copy_advance_pair_quote_cond(buf, ",qop=", 5, chal->qop, '"', '"');
- copy_advance(buf, chal->other_param);
-
- return (int)(buf-startbuf);
-}
-
-static int print_pgp_challenge( pjsip_pgp_challenge *chal,
- char *buf, pj_size_t size)
-{
- PJ_UNUSED_ARG(chal);
- PJ_UNUSED_ARG(buf);
- PJ_UNUSED_ARG(size);
- return -1;
-}
-
-static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr,
- char *buf, pj_size_t size)
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
-
- copy_advance(buf, hdr->name);
- *buf++ = ':';
- *buf++ = ' ';
-
- copy_advance(buf, hdr->scheme);
- *buf++ = ' ';
-
- if (pj_stricmp2(&hdr->scheme, "digest") == 0)
- printed = print_digest_challenge(&hdr->challenge.digest, buf, endbuf - buf);
- else if (pj_stricmp2(&hdr->scheme, "pgp") == 0)
- printed = print_pgp_challenge(&hdr->challenge.pgp, buf, endbuf - buf);
- else {
- pj_assert(0);
- return -1;
- }
-
- if (printed == -1)
- return -1;
-
- buf += printed;
- *buf = '\0';
- return (int)(buf-startbuf);
-}
-
-static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool,
- const pjsip_www_authenticate_hdr *rhs)
-{
- /* This function also serves Proxy-Authenticate header. */
- pjsip_www_authenticate_hdr *hdr;
- if (rhs->type == PJSIP_H_WWW_AUTHENTICATE)
- hdr = pjsip_www_authenticate_hdr_create(pool);
- else
- hdr = pjsip_proxy_authenticate_hdr_create(pool);
-
- pj_strdup(pool, &hdr->scheme, &rhs->scheme);
-
- if (pj_stricmp2(&hdr->scheme, "digest") == 0) {
- pj_strdup(pool, &hdr->challenge.digest.realm, &rhs->challenge.digest.realm);
- pj_strdup(pool, &hdr->challenge.digest.domain, &rhs->challenge.digest.domain);
- pj_strdup(pool, &hdr->challenge.digest.nonce, &rhs->challenge.digest.nonce);
- pj_strdup(pool, &hdr->challenge.digest.opaque, &rhs->challenge.digest.opaque);
- hdr->challenge.digest.stale = rhs->challenge.digest.stale;
- pj_strdup(pool, &hdr->challenge.digest.algorithm, &rhs->challenge.digest.algorithm);
- pj_strdup(pool, &hdr->challenge.digest.qop, &rhs->challenge.digest.qop);
- pj_strdup(pool, &hdr->challenge.digest.other_param, &rhs->challenge.digest.other_param);
- } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) {
- pj_assert(0);
- return NULL;
- } else {
- pj_assert(0);
- return NULL;
- }
-
- return hdr;
-
-}
-
-static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_www_authenticate_hdr *rhs)
-{
- /* This function also serves Proxy-Authenticate header. */
- pjsip_www_authenticate_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip/sip_auth_msg.h> +#include <pjsip/sip_auth_parser.h> +#include <pj/pool.h> +#include <pj/list.h> +#include <pj/string.h> +#include <pj/assert.h> +#include <pjsip/print_util.h> + +/////////////////////////////////////////////////////////////////////////////// +/* + * Authorization and Proxy-Authorization header. + */ +static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool, + const pjsip_authorization_hdr *hdr); +static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_authorization_hdr *hdr); +static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr, + char *buf, pj_size_t size); + +static pjsip_hdr_vptr authorization_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_authorization_hdr_print, +}; + + +PJ_DEF(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool) +{ + pjsip_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_AUTHORIZATION, &authorization_hdr_vptr); + pj_list_init(&hdr->credential.common.other_param); + return hdr; +} + +PJ_DEF(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool) +{ + pjsip_proxy_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_PROXY_AUTHORIZATION, &authorization_hdr_vptr); + pj_list_init(&hdr->credential.common.other_param); + return hdr; +} + +static int print_digest_credential(pjsip_digest_credential *cred, char *buf, pj_size_t size) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + + copy_advance_pair_quote_cond(buf, "username=", 9, cred->username, '"', '"'); + copy_advance_pair_quote_cond(buf, ", realm=", 8, cred->realm, '"', '"'); + copy_advance_pair_quote_cond(buf, ", nonce=", 8, cred->nonce, '"', '"'); + copy_advance_pair_quote_cond(buf, ", uri=", 6, cred->uri, '"', '"'); + copy_advance_pair_quote_cond(buf, ", response=", 11, cred->response, '"', '"'); + copy_advance_pair(buf, ", algorithm=", 12, cred->algorithm); + copy_advance_pair_quote_cond(buf, ", cnonce=", 9, cred->cnonce, '"', '"'); + copy_advance_pair_quote_cond(buf, ", opaque=", 9, cred->opaque, '"', '"'); + //Note: there's no dbl-quote in qop in Authorization header + // (unlike WWW-Authenticate) + //copy_advance_pair_quote_cond(buf, ", qop=", 6, cred->qop, '"', '"'); + copy_advance_pair(buf, ", qop=", 6, cred->qop); + copy_advance_pair(buf, ", nc=", 5, cred->nc); + + printed = pjsip_param_print_on(&cred->other_param, buf, endbuf-buf, ','); + if (printed < 0) + return -1; + buf += printed; + + return (int) (buf-startbuf); +} + +static int print_pgp_credential(pjsip_pgp_credential *cred, char *buf, pj_size_t size) +{ + PJ_UNUSED_ARG(cred); + PJ_UNUSED_ARG(buf); + PJ_UNUSED_ARG(size); + return -1; +} + +static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr, + char *buf, pj_size_t size) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + + copy_advance(buf, hdr->name); + *buf++ = ':'; + *buf++ = ' '; + + copy_advance(buf, hdr->scheme); + *buf++ = ' '; + + if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) + { + printed = print_digest_credential(&hdr->credential.digest, buf, endbuf - buf); + } + else if (pj_stricmp(&hdr->scheme, &pjsip_PGP_STR) == 0) + { + printed = print_pgp_credential(&hdr->credential.pgp, buf, endbuf - buf); + } + else { + pj_assert(0); + return -1; + } + + if (printed == -1) + return -1; + + buf += printed; + *buf = '\0'; + return (int)(buf-startbuf); +} + +static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool, + const pjsip_authorization_hdr *rhs) +{ + /* This function also serves Proxy-Authorization header. */ + pjsip_authorization_hdr *hdr; + if (rhs->type == PJSIP_H_AUTHORIZATION) + hdr = pjsip_authorization_hdr_create(pool); + else + hdr = pjsip_proxy_authorization_hdr_create(pool); + + pj_strdup(pool, &hdr->scheme, &rhs->scheme); + + if (pj_stricmp2(&hdr->scheme, "digest") == 0) { + pj_strdup(pool, &hdr->credential.digest.username, &rhs->credential.digest.username); + pj_strdup(pool, &hdr->credential.digest.realm, &rhs->credential.digest.realm); + pj_strdup(pool, &hdr->credential.digest.nonce, &rhs->credential.digest.nonce); + pj_strdup(pool, &hdr->credential.digest.uri, &rhs->credential.digest.uri); + pj_strdup(pool, &hdr->credential.digest.response, &rhs->credential.digest.response); + pj_strdup(pool, &hdr->credential.digest.algorithm, &rhs->credential.digest.algorithm); + pj_strdup(pool, &hdr->credential.digest.cnonce, &rhs->credential.digest.cnonce); + pj_strdup(pool, &hdr->credential.digest.opaque, &rhs->credential.digest.opaque); + pj_strdup(pool, &hdr->credential.digest.qop, &rhs->credential.digest.qop); + pj_strdup(pool, &hdr->credential.digest.nc, &rhs->credential.digest.nc); + pjsip_param_clone(pool, &hdr->credential.digest.other_param, &rhs->credential.digest.other_param); + } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) { + pj_assert(0); + return NULL; + } else { + pj_assert(0); + return NULL; + } + + return hdr; +} + +static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_authorization_hdr *rhs) +{ + /* This function also serves Proxy-Authorization header. */ + pjsip_authorization_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + pjsip_param_shallow_clone(pool, &hdr->credential.common.other_param, + &rhs->credential.common.other_param); + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Proxy-Authenticate and WWW-Authenticate header. + */ +static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr, + char *buf, pj_size_t size); +static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool, + const pjsip_www_authenticate_hdr *hdr); +static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_www_authenticate_hdr *hdr); + +static pjsip_hdr_vptr www_authenticate_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_www_authenticate_hdr_print, +}; + + +PJ_DEF(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool) +{ + pjsip_www_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_WWW_AUTHENTICATE, &www_authenticate_hdr_vptr); + pj_list_init(&hdr->challenge.common.other_param); + return hdr; +} + + +PJ_DEF(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool) +{ + pjsip_proxy_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_PROXY_AUTHENTICATE, &www_authenticate_hdr_vptr); + pj_list_init(&hdr->challenge.common.other_param); + return hdr; +} + +static int print_digest_challenge( pjsip_digest_challenge *chal, + char *buf, pj_size_t size) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + + copy_advance_pair_quote_cond(buf, " realm=", 7, chal->realm, '"', '"'); + copy_advance_pair_quote_cond(buf, ",domain=", 8, chal->domain, '"', '"'); + copy_advance_pair_quote_cond(buf, ",nonce=", 7, chal->nonce, '"', '"'); + copy_advance_pair_quote_cond(buf, ",opaque=", 8, chal->opaque, '"', '"'); + if (chal->stale) { + pj_str_t true_str = { "true", 4 }; + copy_advance_pair(buf, ",stale=", 7, true_str); + } + copy_advance_pair(buf, ",algorithm=", 11, chal->algorithm); + copy_advance_pair_quote_cond(buf, ",qop=", 5, chal->qop, '"', '"'); + + printed = pjsip_param_print_on(&chal->other_param, buf, endbuf-buf, ','); + if (printed < 0) + return -1; + buf += printed; + + return (int)(buf-startbuf); +} + +static int print_pgp_challenge( pjsip_pgp_challenge *chal, + char *buf, pj_size_t size) +{ + PJ_UNUSED_ARG(chal); + PJ_UNUSED_ARG(buf); + PJ_UNUSED_ARG(size); + return -1; +} + +static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr, + char *buf, pj_size_t size) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + + copy_advance(buf, hdr->name); + *buf++ = ':'; + *buf++ = ' '; + + copy_advance(buf, hdr->scheme); + *buf++ = ' '; + + if (pj_stricmp2(&hdr->scheme, "digest") == 0) + printed = print_digest_challenge(&hdr->challenge.digest, buf, endbuf - buf); + else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) + printed = print_pgp_challenge(&hdr->challenge.pgp, buf, endbuf - buf); + else { + pj_assert(0); + return -1; + } + + if (printed == -1) + return -1; + + buf += printed; + *buf = '\0'; + return (int)(buf-startbuf); +} + +static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool, + const pjsip_www_authenticate_hdr *rhs) +{ + /* This function also serves Proxy-Authenticate header. */ + pjsip_www_authenticate_hdr *hdr; + if (rhs->type == PJSIP_H_WWW_AUTHENTICATE) + hdr = pjsip_www_authenticate_hdr_create(pool); + else + hdr = pjsip_proxy_authenticate_hdr_create(pool); + + pj_strdup(pool, &hdr->scheme, &rhs->scheme); + + if (pj_stricmp2(&hdr->scheme, "digest") == 0) { + pj_strdup(pool, &hdr->challenge.digest.realm, &rhs->challenge.digest.realm); + pj_strdup(pool, &hdr->challenge.digest.domain, &rhs->challenge.digest.domain); + pj_strdup(pool, &hdr->challenge.digest.nonce, &rhs->challenge.digest.nonce); + pj_strdup(pool, &hdr->challenge.digest.opaque, &rhs->challenge.digest.opaque); + hdr->challenge.digest.stale = rhs->challenge.digest.stale; + pj_strdup(pool, &hdr->challenge.digest.algorithm, &rhs->challenge.digest.algorithm); + pj_strdup(pool, &hdr->challenge.digest.qop, &rhs->challenge.digest.qop); + pjsip_param_clone(pool, &hdr->challenge.digest.other_param, + &rhs->challenge.digest.other_param); + } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) { + pj_assert(0); + return NULL; + } else { + pj_assert(0); + return NULL; + } + + return hdr; + +} + +static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_www_authenticate_hdr *rhs) +{ + /* This function also serves Proxy-Authenticate header. */ + pjsip_www_authenticate_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + pjsip_param_shallow_clone(pool, &hdr->challenge.common.other_param, + &rhs->challenge.common.other_param); + return hdr; +} + + diff --git a/pjsip/src/pjsip/sip_auth_parser.c b/pjsip/src/pjsip/sip_auth_parser.c index 4a1ffbb7..d050ffb2 100644 --- a/pjsip/src/pjsip/sip_auth_parser.c +++ b/pjsip/src/pjsip/sip_auth_parser.c @@ -1,291 +1,303 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_auth_parser.h>
-#include <pjsip/sip_auth_msg.h>
-#include <pjsip/sip_parser.h>
-#include <pj/assert.h>
-#include <pj/string.h>
-#include <pj/except.h>
-
-static pjsip_hdr* parse_hdr_authorization ( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_proxy_authorization ( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_www_authenticate ( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_proxy_authenticate ( pjsip_parse_ctx *ctx );
-
-static void parse_digest_credential ( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_digest_credential *cred);
-static void parse_pgp_credential ( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_pgp_credential *cred);
-static void parse_digest_challenge ( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_digest_challenge *chal);
-static void parse_pgp_challenge ( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_pgp_challenge *chal);
-
-const pj_str_t pjsip_USERNAME_STR = { "username", 8 },
- pjsip_REALM_STR = { "realm", 5},
- pjsip_NONCE_STR = { "nonce", 5},
- pjsip_URI_STR = { "uri", 3 },
- pjsip_RESPONSE_STR = { "response", 8 },
- pjsip_ALGORITHM_STR = { "algorithm", 9 },
- pjsip_DOMAIN_STR = { "domain", 6 },
- pjsip_STALE_STR = { "stale", 5},
- pjsip_QOP_STR = { "qop", 3},
- pjsip_CNONCE_STR = { "cnonce", 6},
- pjsip_OPAQUE_STR = { "opaque", 6},
- pjsip_NC_STR = { "nc", 2},
- pjsip_TRUE_STR = { "true", 4},
- pjsip_QUOTED_TRUE_STR = { "\"true\"", 6},
- pjsip_FALSE_STR = { "false", 5},
- pjsip_QUOTED_FALSE_STR = { "\"false\"", 7},
- pjsip_DIGEST_STR = { "Digest", 6},
- pjsip_QUOTED_DIGEST_STR = { "\"Digest\"", 8},
- pjsip_PGP_STR = { "PGP", 3 },
- pjsip_QUOTED_PGP_STR = { "\"PGP\"", 5 },
- pjsip_MD5_STR = { "md5", 3 },
- pjsip_QUOTED_MD5_STR = { "\"md5\"", 5},
- pjsip_AUTH_STR = { "auth", 4},
- pjsip_QUOTED_AUTH_STR = { "\"auth\"", 6 };
-
-
-static void parse_digest_credential( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_digest_credential *cred)
-{
- for (;;) {
- pj_str_t name, value;
-
- pjsip_parse_param_imp(scanner, &name, &value,PJSIP_PARSE_REMOVE_QUOTE);
-
- if (!pj_stricmp(&name, &pjsip_USERNAME_STR)) {
- cred->username = value;
-
- } else if (!pj_stricmp(&name, &pjsip_REALM_STR)) {
- cred->realm = value;
-
- } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) {
- cred->nonce = value;
-
- } else if (!pj_stricmp(&name, &pjsip_URI_STR)) {
- cred->uri = value;
-
- } else if (!pj_stricmp(&name, &pjsip_RESPONSE_STR)) {
- cred->response = value;
-
- } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) {
- cred->algorithm = value;
-
- } else if (!pj_stricmp(&name, &pjsip_CNONCE_STR)) {
- cred->cnonce = value;
-
- } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) {
- cred->opaque = value;
-
- } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) {
- cred->qop = value;
-
- } else if (!pj_stricmp(&name, &pjsip_NC_STR)) {
- cred->nc = value;
-
- } else {
- pjsip_concat_param_imp(&cred->other_param,pool,&name,&value, ',');
- }
-
- /* Eat comma */
- if (!pj_scan_is_eof(scanner) && *scanner->curptr == ',')
- pj_scan_get_char(scanner);
- else
- break;
- }
-}
-
-static void parse_pgp_credential( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_pgp_credential *cred)
-{
- PJ_UNUSED_ARG(scanner);
- PJ_UNUSED_ARG(pool);
- PJ_UNUSED_ARG(cred);
-
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
-}
-
-static void parse_digest_challenge( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_digest_challenge *chal)
-{
- for (;;) {
- pj_str_t name, value;
-
- pjsip_parse_param_imp(scanner, &name, &value,PJSIP_PARSE_REMOVE_QUOTE);
-
- if (!pj_stricmp(&name, &pjsip_REALM_STR)) {
- chal->realm = value;
-
- } else if (!pj_stricmp(&name, &pjsip_DOMAIN_STR)) {
- chal->domain = value;
-
- } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) {
- chal->nonce = value;
-
- } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) {
- chal->opaque = value;
-
- } else if (!pj_stricmp(&name, &pjsip_STALE_STR)) {
- if (!pj_stricmp(&value, &pjsip_TRUE_STR) ||
- !pj_stricmp(&value, &pjsip_QUOTED_TRUE_STR))
- {
- chal->stale = 1;
- }
-
- } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) {
- chal->algorithm = value;
-
-
- } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) {
- chal->qop = value;
-
- } else {
- pjsip_concat_param_imp(&chal->other_param, pool,
- &name, &value, ',');
- }
-
- /* Eat comma */
- if (!pj_scan_is_eof(scanner) && *scanner->curptr == ',')
- pj_scan_get_char(scanner);
- else
- break;
- }
-}
-
-static void parse_pgp_challenge( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_pgp_challenge *chal)
-{
- PJ_UNUSED_ARG(scanner);
- PJ_UNUSED_ARG(pool);
- PJ_UNUSED_ARG(chal);
-
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
-}
-
-static void int_parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_authorization_hdr *hdr)
-{
- if (*scanner->curptr == '"') {
- pj_scan_get_quote(scanner, '"', '"', &hdr->scheme);
- hdr->scheme.ptr++;
- hdr->scheme.slen -= 2;
- } else {
- pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->scheme);
- }
-
- if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
-
- parse_digest_credential(scanner, pool, &hdr->credential.digest);
-
- } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) {
-
- parse_pgp_credential( scanner, pool, &hdr->credential.pgp);
-
- } else {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- }
-
- pjsip_parse_end_hdr_imp( scanner );
-}
-
-static void int_parse_hdr_authenticate( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_www_authenticate_hdr *hdr)
-{
- if (*scanner->curptr == '"') {
- pj_scan_get_quote(scanner, '"', '"', &hdr->scheme);
- hdr->scheme.ptr++;
- hdr->scheme.slen -= 2;
- } else {
- pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->scheme);
- }
-
- if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
-
- parse_digest_challenge(scanner, pool, &hdr->challenge.digest);
-
- } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) {
-
- parse_pgp_challenge(scanner, pool, &hdr->challenge.pgp);
-
- } else {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- }
-
- pjsip_parse_end_hdr_imp( scanner );
-}
-
-
-static pjsip_hdr* parse_hdr_authorization( pjsip_parse_ctx *ctx )
-{
- pjsip_authorization_hdr *hdr = pjsip_authorization_hdr_create(ctx->pool);
- int_parse_hdr_authorization(ctx->scanner, ctx->pool, hdr);
- return (pjsip_hdr*)hdr;
-}
-
-static pjsip_hdr* parse_hdr_proxy_authorization( pjsip_parse_ctx *ctx )
-{
- pjsip_proxy_authorization_hdr *hdr =
- pjsip_proxy_authorization_hdr_create(ctx->pool);
- int_parse_hdr_authorization(ctx->scanner, ctx->pool, hdr);
- return (pjsip_hdr*)hdr;
-}
-
-static pjsip_hdr* parse_hdr_www_authenticate( pjsip_parse_ctx *ctx )
-{
- pjsip_www_authenticate_hdr *hdr =
- pjsip_www_authenticate_hdr_create(ctx->pool);
- int_parse_hdr_authenticate(ctx->scanner, ctx->pool, hdr);
- return (pjsip_hdr*)hdr;
-}
-
-static pjsip_hdr* parse_hdr_proxy_authenticate( pjsip_parse_ctx *ctx )
-{
- pjsip_proxy_authenticate_hdr *hdr =
- pjsip_proxy_authenticate_hdr_create(ctx->pool);
- int_parse_hdr_authenticate(ctx->scanner, ctx->pool, hdr);
- return (pjsip_hdr*)hdr;
-}
-
-
-PJ_DEF(pj_status_t) pjsip_auth_init_parser()
-{
- pj_status_t status;
-
- status = pjsip_register_hdr_parser( "Authorization", NULL,
- &parse_hdr_authorization);
- PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
- status = pjsip_register_hdr_parser( "Proxy-Authorization", NULL,
- &parse_hdr_proxy_authorization);
- PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
- status = pjsip_register_hdr_parser( "WWW-Authenticate", NULL,
- &parse_hdr_www_authenticate);
- PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
- status = pjsip_register_hdr_parser( "Proxy-Authenticate", NULL,
- &parse_hdr_proxy_authenticate);
- PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(void) pjsip_auth_deinit_parser()
-{
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip/sip_auth_parser.h> +#include <pjsip/sip_auth_msg.h> +#include <pjsip/sip_parser.h> +#include <pj/assert.h> +#include <pj/string.h> +#include <pj/except.h> +#include <pj/pool.h> + +static pjsip_hdr* parse_hdr_authorization ( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_proxy_authorization ( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_www_authenticate ( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_proxy_authenticate ( pjsip_parse_ctx *ctx ); + +static void parse_digest_credential ( pj_scanner *scanner, pj_pool_t *pool, + pjsip_digest_credential *cred); +static void parse_pgp_credential ( pj_scanner *scanner, pj_pool_t *pool, + pjsip_pgp_credential *cred); +static void parse_digest_challenge ( pj_scanner *scanner, pj_pool_t *pool, + pjsip_digest_challenge *chal); +static void parse_pgp_challenge ( pj_scanner *scanner, pj_pool_t *pool, + pjsip_pgp_challenge *chal); + +const pj_str_t pjsip_USERNAME_STR = { "username", 8 }, + pjsip_REALM_STR = { "realm", 5}, + pjsip_NONCE_STR = { "nonce", 5}, + pjsip_URI_STR = { "uri", 3 }, + pjsip_RESPONSE_STR = { "response", 8 }, + pjsip_ALGORITHM_STR = { "algorithm", 9 }, + pjsip_DOMAIN_STR = { "domain", 6 }, + pjsip_STALE_STR = { "stale", 5}, + pjsip_QOP_STR = { "qop", 3}, + pjsip_CNONCE_STR = { "cnonce", 6}, + pjsip_OPAQUE_STR = { "opaque", 6}, + pjsip_NC_STR = { "nc", 2}, + pjsip_TRUE_STR = { "true", 4}, + pjsip_QUOTED_TRUE_STR = { "\"true\"", 6}, + pjsip_FALSE_STR = { "false", 5}, + pjsip_QUOTED_FALSE_STR = { "\"false\"", 7}, + pjsip_DIGEST_STR = { "Digest", 6}, + pjsip_QUOTED_DIGEST_STR = { "\"Digest\"", 8}, + pjsip_PGP_STR = { "PGP", 3 }, + pjsip_QUOTED_PGP_STR = { "\"PGP\"", 5 }, + pjsip_MD5_STR = { "md5", 3 }, + pjsip_QUOTED_MD5_STR = { "\"md5\"", 5}, + pjsip_AUTH_STR = { "auth", 4}, + pjsip_QUOTED_AUTH_STR = { "\"auth\"", 6 }; + + +static void parse_digest_credential( pj_scanner *scanner, pj_pool_t *pool, + pjsip_digest_credential *cred) +{ + pj_list_init(&cred->other_param); + + for (;;) { + pj_str_t name, value; + + pjsip_parse_param_imp(scanner, pool, &name, &value, + PJSIP_PARSE_REMOVE_QUOTE); + + if (!pj_stricmp(&name, &pjsip_USERNAME_STR)) { + cred->username = value; + + } else if (!pj_stricmp(&name, &pjsip_REALM_STR)) { + cred->realm = value; + + } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) { + cred->nonce = value; + + } else if (!pj_stricmp(&name, &pjsip_URI_STR)) { + cred->uri = value; + + } else if (!pj_stricmp(&name, &pjsip_RESPONSE_STR)) { + cred->response = value; + + } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) { + cred->algorithm = value; + + } else if (!pj_stricmp(&name, &pjsip_CNONCE_STR)) { + cred->cnonce = value; + + } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) { + cred->opaque = value; + + } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) { + cred->qop = value; + + } else if (!pj_stricmp(&name, &pjsip_NC_STR)) { + cred->nc = value; + + } else { + pjsip_param *p = pj_pool_alloc(pool, sizeof(pjsip_param)); + p->name = name; + p->value = value; + pj_list_insert_before(&cred->other_param, p); + } + + /* Eat comma */ + if (!pj_scan_is_eof(scanner) && *scanner->curptr == ',') + pj_scan_get_char(scanner); + else + break; + } +} + +static void parse_pgp_credential( pj_scanner *scanner, pj_pool_t *pool, + pjsip_pgp_credential *cred) +{ + PJ_UNUSED_ARG(scanner); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(cred); + + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); +} + +static void parse_digest_challenge( pj_scanner *scanner, pj_pool_t *pool, + pjsip_digest_challenge *chal) +{ + pj_list_init(&chal->other_param); + + for (;;) { + pj_str_t name, value; + + pjsip_parse_param_imp(scanner, pool, &name, &value, + PJSIP_PARSE_REMOVE_QUOTE); + + if (!pj_stricmp(&name, &pjsip_REALM_STR)) { + chal->realm = value; + + } else if (!pj_stricmp(&name, &pjsip_DOMAIN_STR)) { + chal->domain = value; + + } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) { + chal->nonce = value; + + } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) { + chal->opaque = value; + + } else if (!pj_stricmp(&name, &pjsip_STALE_STR)) { + if (!pj_stricmp(&value, &pjsip_TRUE_STR) || + !pj_stricmp(&value, &pjsip_QUOTED_TRUE_STR)) + { + chal->stale = 1; + } + + } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) { + chal->algorithm = value; + + + } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) { + chal->qop = value; + + } else { + pjsip_param *p = pj_pool_alloc(pool, sizeof(pjsip_param)); + p->name = name; + p->value = value; + pj_list_insert_before(&chal->other_param, p); + } + + /* Eat comma */ + if (!pj_scan_is_eof(scanner) && *scanner->curptr == ',') + pj_scan_get_char(scanner); + else + break; + } +} + +static void parse_pgp_challenge( pj_scanner *scanner, pj_pool_t *pool, + pjsip_pgp_challenge *chal) +{ + PJ_UNUSED_ARG(scanner); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(chal); + + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); +} + +static void int_parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool, + pjsip_authorization_hdr *hdr) +{ + if (*scanner->curptr == '"') { + pj_scan_get_quote(scanner, '"', '"', &hdr->scheme); + hdr->scheme.ptr++; + hdr->scheme.slen -= 2; + } else { + pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->scheme); + } + + if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { + + parse_digest_credential(scanner, pool, &hdr->credential.digest); + + } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) { + + parse_pgp_credential( scanner, pool, &hdr->credential.pgp); + + } else { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + } + + pjsip_parse_end_hdr_imp( scanner ); +} + +static void int_parse_hdr_authenticate( pj_scanner *scanner, pj_pool_t *pool, + pjsip_www_authenticate_hdr *hdr) +{ + if (*scanner->curptr == '"') { + pj_scan_get_quote(scanner, '"', '"', &hdr->scheme); + hdr->scheme.ptr++; + hdr->scheme.slen -= 2; + } else { + pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->scheme); + } + + if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { + + parse_digest_challenge(scanner, pool, &hdr->challenge.digest); + + } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) { + + parse_pgp_challenge(scanner, pool, &hdr->challenge.pgp); + + } else { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + } + + pjsip_parse_end_hdr_imp( scanner ); +} + + +static pjsip_hdr* parse_hdr_authorization( pjsip_parse_ctx *ctx ) +{ + pjsip_authorization_hdr *hdr = pjsip_authorization_hdr_create(ctx->pool); + int_parse_hdr_authorization(ctx->scanner, ctx->pool, hdr); + return (pjsip_hdr*)hdr; +} + +static pjsip_hdr* parse_hdr_proxy_authorization( pjsip_parse_ctx *ctx ) +{ + pjsip_proxy_authorization_hdr *hdr = + pjsip_proxy_authorization_hdr_create(ctx->pool); + int_parse_hdr_authorization(ctx->scanner, ctx->pool, hdr); + return (pjsip_hdr*)hdr; +} + +static pjsip_hdr* parse_hdr_www_authenticate( pjsip_parse_ctx *ctx ) +{ + pjsip_www_authenticate_hdr *hdr = + pjsip_www_authenticate_hdr_create(ctx->pool); + int_parse_hdr_authenticate(ctx->scanner, ctx->pool, hdr); + return (pjsip_hdr*)hdr; +} + +static pjsip_hdr* parse_hdr_proxy_authenticate( pjsip_parse_ctx *ctx ) +{ + pjsip_proxy_authenticate_hdr *hdr = + pjsip_proxy_authenticate_hdr_create(ctx->pool); + int_parse_hdr_authenticate(ctx->scanner, ctx->pool, hdr); + return (pjsip_hdr*)hdr; +} + + +PJ_DEF(pj_status_t) pjsip_auth_init_parser() +{ + pj_status_t status; + + status = pjsip_register_hdr_parser( "Authorization", NULL, + &parse_hdr_authorization); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + status = pjsip_register_hdr_parser( "Proxy-Authorization", NULL, + &parse_hdr_proxy_authorization); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + status = pjsip_register_hdr_parser( "WWW-Authenticate", NULL, + &parse_hdr_www_authenticate); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + status = pjsip_register_hdr_parser( "Proxy-Authenticate", NULL, + &parse_hdr_proxy_authenticate); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + + return PJ_SUCCESS; +} + +PJ_DEF(void) pjsip_auth_deinit_parser() +{ +} + diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c index d68d35d0..53311f36 100644 --- a/pjsip/src/pjsip/sip_endpoint.c +++ b/pjsip/src/pjsip/sip_endpoint.c @@ -1,1112 +1,1112 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_private.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_resolve.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_errno.h>
-#include <pj/except.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <pj/hash.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
-#include <pj/lock.h>
-
-#define PJSIP_EX_NO_MEMORY PJ_NO_MEMORY_EXCEPTION
-#define THIS_FILE "endpoint"
-
-#define MAX_METHODS 32
-
-/**
- * The SIP endpoint.
- */
-struct pjsip_endpoint
-{
- /** Pool to allocate memory for the endpoint. */
- pj_pool_t *pool;
-
- /** Mutex for the pool, hash table, and event list/queue. */
- pj_mutex_t *mutex;
-
- /** Pool factory. */
- pj_pool_factory *pf;
-
- /** Name. */
- pj_str_t name;
-
- /** Transaction table. */
- pj_hash_table_t *tsx_table;
-
- /** Mutex for transaction table. */
- pj_mutex_t *tsx_table_mutex;
-
- /** Timer heap. */
- pj_timer_heap_t *timer_heap;
-
- /** Transport manager. */
- pjsip_tpmgr *transport_mgr;
-
- /** Ioqueue. */
- pj_ioqueue_t *ioqueue;
-
- /** DNS Resolver. */
- pjsip_resolver_t *resolver;
-
- /** Number of modules registered. */
- pj_uint32_t mod_count;
-
- /** Modules. */
- pjsip_module *modules[PJSIP_MAX_MODULE];
-
- /** Number of supported methods. */
- unsigned method_cnt;
-
- /** Array of supported methods. */
- const pjsip_method *methods[MAX_METHODS];
-
- /** Allow header. */
- pjsip_allow_hdr *allow_hdr;
-
- /** Route header list. */
- pjsip_route_hdr route_hdr_list;
-
- /** Additional request headers. */
- pjsip_hdr req_hdr;
-};
-
-
-
-/*
- * Prototypes.
- */
-static void endpt_transport_callback(pjsip_endpoint*,
- pj_status_t, pjsip_rx_data*);
-
-
-/*
- * This is the global handler for memory allocation failure, for pools that
- * are created by the endpoint (by default, all pools ARE allocated by
- * endpoint). The error is handled by throwing exception, and hopefully,
- * the exception will be handled by the application (or this library).
- */
-static void pool_callback( pj_pool_t *pool, pj_size_t size )
-{
- PJ_UNUSED_ARG(pool);
- PJ_UNUSED_ARG(size);
-
- PJ_THROW(PJSIP_EX_NO_MEMORY);
-}
-
-
-/*
- * Initialize modules.
- */
-static pj_status_t init_modules( pjsip_endpoint *endpt )
-{
- pj_status_t status;
- unsigned i;
- //pj_str_t str_COMMA = { ", ", 2 };
- extern pjsip_module aux_tsx_module;
-
- PJ_LOG(5, (THIS_FILE, "init_modules()"));
-
- /* Load static modules. */
- endpt->mod_count = PJSIP_MAX_MODULE;
- status = register_static_modules( &endpt->mod_count, endpt->modules );
- if (status != 0) {
- return status;
- }
-
- /* Add mini aux module. */
- endpt->modules[endpt->mod_count++] = &aux_tsx_module;
-
- /* Load dynamic modules. */
- // Not supported yet!
-
- /* Sort modules on the priority. */
- for (i=endpt->mod_count-1; i>0; --i) {
- pj_uint32_t max = 0;
- unsigned j;
- for (j=1; j<=i; ++j) {
- if (endpt->modules[j]->priority > endpt->modules[max]->priority)
- max = j;
- }
- if (max != i) {
- pjsip_module *temp = endpt->modules[max];
- endpt->modules[max] = endpt->modules[i];
- endpt->modules[i] = temp;
- }
- }
-
- /* Initialize each module. */
- for (i=0; i < endpt->mod_count; ++i) {
- int j;
-
- pjsip_module *mod = endpt->modules[i];
- if (mod->init_module) {
- status = mod->init_module(endpt, mod, i);
- if (status != 0) {
- return status;
- }
- }
-
- /* Collect all supported methods from modules. */
- for (j=0; j<mod->method_cnt; ++j) {
- unsigned k;
- for (k=0; k<endpt->method_cnt; ++k) {
- if (pjsip_method_cmp(mod->methods[j], endpt->methods[k]) == 0)
- break;
- }
- if (k == endpt->method_cnt) {
- if (endpt->method_cnt < MAX_METHODS) {
- endpt->methods[endpt->method_cnt++] = mod->methods[j];
- } else {
- PJ_LOG(1,(THIS_FILE, "Too many methods"));
- return -1;
- }
- }
- }
- }
-
- /* Create Allow header. */
- endpt->allow_hdr = pjsip_allow_hdr_create( endpt->pool );
- endpt->allow_hdr->count = endpt->method_cnt;
- for (i=0; i<endpt->method_cnt; ++i) {
- endpt->allow_hdr->values[i] = endpt->methods[i]->name;
- }
-
- /* Start each module. */
- for (i=0; i < endpt->mod_count; ++i) {
- pjsip_module *mod = endpt->modules[i];
- if (mod->start_module) {
- status = mod->start_module(mod);
- if (status != 0) {
- return status;
- }
- }
- }
-
- /* Done. */
- return 0;
-}
-
-/*
- * Unregister the transaction from the hash table, and destroy the resources
- * from the transaction.
- */
-PJ_DEF(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt,
- pjsip_transaction *tsx)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_tsx(%s)", tsx->obj_name));
-
- pj_assert(tsx->state == PJSIP_TSX_STATE_DESTROYED);
-
- /* No need to lock transaction.
- * This function typically is called from the transaction callback, which
- * means that transaction mutex is being held.
- */
- pj_assert( pj_mutex_is_locked(tsx->mutex) );
-
- /* Lock endpoint. */
- pj_mutex_lock( endpt->tsx_table_mutex );
-
- /* Unregister from the hash table. */
- pj_hash_set( NULL, endpt->tsx_table, tsx->transaction_key.ptr,
- tsx->transaction_key.slen, NULL);
-
- /* Unlock endpoint mutex. */
- pj_mutex_unlock( endpt->tsx_table_mutex );
-
- /* Destroy transaction mutex. */
- pj_mutex_destroy( tsx->mutex );
-
- /* Release the pool for the transaction. */
- pj_pool_release(tsx->pool);
-
- PJ_LOG(4, (THIS_FILE, "tsx%p destroyed", tsx));
-}
-
-
-/*
- * Receive transaction events from transactions and dispatch them to the
- * modules.
- */
-static void endpt_do_event( pjsip_endpoint *endpt, pjsip_event *evt)
-{
- unsigned i;
-
- /* Dispatch event to modules. */
- for (i=0; i<endpt->mod_count; ++i) {
- pjsip_module *mod = endpt->modules[i];
- if (mod && mod->tsx_handler) {
- mod->tsx_handler( mod, evt );
- }
- }
-
- /* Destroy transaction if it is terminated. */
- if (evt->type == PJSIP_EVENT_TSX_STATE &&
- evt->body.tsx_state.tsx->state == PJSIP_TSX_STATE_DESTROYED)
- {
- /* No need to lock mutex. Mutex is locked inside the destroy function */
- pjsip_endpt_destroy_tsx( endpt, evt->body.tsx_state.tsx );
- }
-}
-
-/*
- * Receive transaction events from transactions and put in the event queue
- * to be processed later.
- */
-void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt )
-{
- // Need to protect this with try/catch?
- endpt_do_event(endpt, evt);
-}
-
-/*
- * Get "Allow" header.
- */
-PJ_DEF(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt )
-{
- return endpt->allow_hdr;
-}
-
-/*
- * Get additional headers to be put in outgoing request message.
- */
-PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt)
-{
- return &endpt->req_hdr;
-}
-
-PJ_DEF(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt,
- int url_cnt, const pj_str_t url[])
-{
- int i;
- pjsip_route_hdr *hdr;
- pj_str_t str_ROUTE = { "Route", 5 };
-
- /* Lock endpoint mutex. */
- pj_mutex_lock(endpt->mutex);
-
- pj_list_init(&endpt->route_hdr_list);
-
- for (i=0; i<url_cnt; ++i) {
- int len = url[i].slen;
- char *dup = pj_pool_alloc(endpt->pool, len + 1);
- pj_memcpy(dup, url[i].ptr, len);
- dup[len] = '\0';
-
- hdr = pjsip_parse_hdr(endpt->pool, &str_ROUTE, dup, len, NULL);
- if (!hdr) {
- pj_mutex_unlock(endpt->mutex);
- PJ_LOG(4,(THIS_FILE, "Invalid URL %s in proxy URL", dup));
- return -1;
- }
-
- pj_assert(hdr->type == PJSIP_H_ROUTE);
- pj_list_insert_before(&endpt->route_hdr_list, hdr);
- }
-
- /* Unlock endpoint mutex. */
- pj_mutex_unlock(endpt->mutex);
-
- return 0;
-}
-
-/*
- * Get "Route" header list.
- */
-PJ_DEF(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt )
-{
- return &endpt->route_hdr_list;
-}
-
-
-/*
- * Initialize endpoint.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf,
- const char *name,
- pjsip_endpoint **p_endpt)
-{
- pj_status_t status;
- pj_pool_t *pool;
- pjsip_endpoint *endpt;
- pjsip_max_forwards_hdr *mf_hdr;
- pj_lock_t *lock = NULL;
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create()"));
-
- *p_endpt = NULL;
-
- /* Create pool */
- pool = pj_pool_create(pf, "pept%p",
- PJSIP_POOL_LEN_ENDPT, PJSIP_POOL_INC_ENDPT,
- &pool_callback);
- if (!pool)
- return PJ_ENOMEM;
-
- /* Create endpoint. */
- endpt = pj_pool_calloc(pool, 1, sizeof(*endpt));
- endpt->pool = pool;
- endpt->pf = pf;
-
- /* Get name. */
- if (name != NULL) {
- pj_str_t temp;
- pj_strdup_with_null(endpt->pool, &endpt->name, pj_cstr(&temp, name));
- } else {
- pj_strdup_with_null(endpt->pool, &endpt->name, pj_gethostname());
- }
-
- /* Create mutex for the events, etc. */
- status = pj_mutex_create_recursive( endpt->pool, "ept%p", &endpt->mutex );
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
- /* Create mutex for the transaction table. */
- status = pj_mutex_create_recursive( endpt->pool, "mtbl%p",
- &endpt->tsx_table_mutex);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
- /* Create hash table for transaction. */
- endpt->tsx_table = pj_hash_create( endpt->pool, PJSIP_MAX_TSX_COUNT );
- if (!endpt->tsx_table) {
- status = PJ_ENOMEM;
- goto on_error;
- }
-
- /* Create timer heap to manage all timers within this endpoint. */
- status = pj_timer_heap_create( endpt->pool, PJSIP_MAX_TIMER_COUNT,
- &endpt->timer_heap);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
- /* Set recursive lock for the timer heap. */
- status = pj_lock_create_recursive_mutex( endpt->pool, "edpt%p", &lock);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
- pj_timer_heap_set_lock(endpt->timer_heap, lock, PJ_TRUE);
-
- /* Set maximum timed out entries to process in a single poll. */
- pj_timer_heap_set_max_timed_out_per_poll(endpt->timer_heap,
- PJSIP_MAX_TIMED_OUT_ENTRIES);
-
- /* Create ioqueue. */
- status = pj_ioqueue_create( endpt->pool, PJSIP_MAX_TRANSPORTS, &endpt->ioqueue);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
- /* Create transport manager. */
- status = pjsip_tpmgr_create( endpt->pool, endpt,
- &endpt_transport_callback,
- &endpt->transport_mgr);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
- /* Create asynchronous DNS resolver. */
- endpt->resolver = pjsip_resolver_create(endpt->pool);
- if (!endpt->resolver) {
- PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error creating resolver"));
- goto on_error;
- }
-
- /* Initialize TLS ID for transaction lock. */
- status = pj_thread_local_alloc(&pjsip_tsx_lock_tls_id);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
- pj_thread_local_set(pjsip_tsx_lock_tls_id, NULL);
-
- /* Initialize request headers. */
- pj_list_init(&endpt->req_hdr);
-
- /* Initialist "Route" header list. */
- pj_list_init(&endpt->route_hdr_list);
-
- /* Add "Max-Forwards" for request header. */
- mf_hdr = pjsip_max_forwards_hdr_create(endpt->pool);
- mf_hdr->ivalue = PJSIP_MAX_FORWARDS_VALUE;
- pj_list_insert_before( &endpt->req_hdr, mf_hdr);
-
- /* Load and init modules. */
- status = init_modules(endpt);
- if (status != PJ_SUCCESS) {
- PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error in init_modules()"));
- return status;
- }
-
- /* Done. */
- *p_endpt = endpt;
- return status;
-
-on_error:
- if (endpt->transport_mgr) {
- pjsip_tpmgr_destroy(endpt->transport_mgr);
- endpt->transport_mgr = NULL;
- }
- if (endpt->mutex) {
- pj_mutex_destroy(endpt->mutex);
- endpt->mutex = NULL;
- }
- if (endpt->tsx_table_mutex) {
- pj_mutex_destroy(endpt->tsx_table_mutex);
- endpt->tsx_table_mutex = NULL;
- }
- pj_pool_release( endpt->pool );
-
- PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init() failed"));
- return status;
-}
-
-/*
- * Destroy endpoint.
- */
-PJ_DEF(void) pjsip_endpt_destroy(pjsip_endpoint *endpt)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy()"));
-
- /* Shutdown and destroy all transports. */
- pjsip_tpmgr_destroy(endpt->transport_mgr);
-
- /* Delete endpoint mutex. */
- pj_mutex_destroy(endpt->mutex);
-
- /* Delete transaction table mutex. */
- pj_mutex_destroy(endpt->tsx_table_mutex);
-
- /* Finally destroy pool. */
- pj_pool_release(endpt->pool);
-}
-
-/*
- * Get endpoint name.
- */
-PJ_DEF(const pj_str_t*) pjsip_endpt_name(const pjsip_endpoint *endpt)
-{
- return &endpt->name;
-}
-
-
-/*
- * Create new pool.
- */
-PJ_DEF(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt,
- const char *pool_name,
- pj_size_t initial,
- pj_size_t increment )
-{
- pj_pool_t *pool;
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_pool()"));
-
- /* Lock endpoint mutex. */
- pj_mutex_lock(endpt->mutex);
-
- /* Create pool */
- pool = pj_pool_create( endpt->pf, pool_name,
- initial, increment, &pool_callback);
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->mutex);
-
- if (pool) {
- PJ_LOG(5, (THIS_FILE, " pool %s created", pj_pool_getobjname(pool)));
- } else {
- PJ_LOG(4, (THIS_FILE, "Unable to create pool %s!", pool_name));
- }
-
- return pool;
-}
-
-/*
- * Return back pool to endpoint's pool manager to be either destroyed or
- * recycled.
- */
-PJ_DEF(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt, pj_pool_t *pool )
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_pool(%s)", pj_pool_getobjname(pool)));
-
- pj_mutex_lock(endpt->mutex);
- pj_pool_release( pool );
- pj_mutex_unlock(endpt->mutex);
-}
-
-/*
- * Handle events.
- */
-PJ_DEF(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt,
- const pj_time_val *max_timeout)
-{
- /* timeout is 'out' var. This just to make compiler happy. */
- pj_time_val timeout = { 0, 0};
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_handle_events()"));
-
- /* Poll the timer. The timer heap has its own mutex for better
- * granularity, so we don't need to lock end endpoint.
- */
- timeout.sec = timeout.msec = 0;
- pj_timer_heap_poll( endpt->timer_heap, &timeout );
-
- /* If caller specifies maximum time to wait, then compare the value with
- * the timeout to wait from timer, and use the minimum value.
- */
- if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) {
- timeout = *max_timeout;
- }
-
- /* Poll ioqueue. */
- pj_ioqueue_poll( endpt->ioqueue, &timeout);
-}
-
-/*
- * Schedule timer.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt,
- pj_timer_entry *entry,
- const pj_time_val *delay )
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)",
- entry, delay->sec, delay->msec));
- return pj_timer_heap_schedule( endpt->timer_heap, entry, delay );
-}
-
-/*
- * Cancel the previously registered timer.
- */
-PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt,
- pj_timer_entry *entry )
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_cancel_timer(entry=%p)", entry));
- pj_timer_heap_cancel( endpt->timer_heap, entry );
-}
-
-/*
- * Create a new transaction.
- * Endpoint must then initialize the new transaction as either UAS or UAC, and
- * register it to the hash table.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_create_tsx(pjsip_endpoint *endpt,
- pjsip_transaction **p_tsx)
-{
- pj_pool_t *pool;
-
- PJ_ASSERT_RETURN(endpt && p_tsx, PJ_EINVAL);
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tsx()"));
-
- /* Request one pool for the transaction. Mutex is locked there. */
- pool = pjsip_endpt_create_pool(endpt, "ptsx%p",
- PJSIP_POOL_LEN_TSX, PJSIP_POOL_INC_TSX);
- if (pool == NULL) {
- return PJ_ENOMEM;
- }
-
- /* Create the transaction. */
- return pjsip_tsx_create(pool, endpt, p_tsx);
-}
-
-/*
- * Register the transaction to the endpoint.
- * This will put the transaction to the transaction hash table. Before calling
- * this function, the transaction must be INITIALIZED as either UAS or UAC, so
- * that the transaction key is built.
- */
-PJ_DEF(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt,
- pjsip_transaction *tsx)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_register_tsx(%s)", tsx->obj_name));
-
- pj_assert(tsx->transaction_key.slen != 0);
- //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL);
-
- /* Lock hash table mutex. */
- pj_mutex_lock(endpt->tsx_table_mutex);
-
- /* Register the transaction to the hash table. */
- pj_hash_set( tsx->pool, endpt->tsx_table, tsx->transaction_key.ptr,
- tsx->transaction_key.slen, tsx);
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->tsx_table_mutex);
-}
-
-/*
- * Find transaction by the key.
- */
-PJ_DEF(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt,
- const pj_str_t *key )
-{
- pjsip_transaction *tsx;
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_find_tsx()"));
-
- /* Start lock mutex in the endpoint. */
- pj_mutex_lock(endpt->tsx_table_mutex);
-
- /* Find the transaction in the hash table. */
- tsx = pj_hash_get( endpt->tsx_table, key->ptr, key->slen );
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->tsx_table_mutex);
-
- return tsx;
-}
-
-/*
- * Create key.
- */
-static void rdata_create_key( pjsip_rx_data *rdata)
-{
- pjsip_role_e role;
- if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
- role = PJSIP_ROLE_UAS;
- } else {
- role = PJSIP_ROLE_UAC;
- }
- pjsip_tsx_create_key(rdata->tp_info.pool, &rdata->endpt_info.key, role,
- &rdata->msg_info.cseq->method, rdata);
-}
-
-/*
- * This is the callback that is called by the transport manager when it
- * receives a message from the network.
- */
-static void endpt_transport_callback( pjsip_endpoint *endpt,
- pj_status_t status,
- pjsip_rx_data *rdata )
-{
- pjsip_msg *msg = rdata->msg_info.msg;
- pjsip_transaction *tsx;
- pj_bool_t a_new_transaction_just_been_created = PJ_FALSE;
-
- PJ_LOG(5, (THIS_FILE, "endpt_transport_callback(rdata=%p)", rdata));
-
- if (status != PJ_SUCCESS) {
- const char *src_addr = pj_inet_ntoa(rdata->pkt_info.addr.sin_addr);
- int port = pj_ntohs(rdata->pkt_info.addr.sin_port);
- PJSIP_ENDPT_LOG_ERROR((endpt, "transport", status,
- "Src.addr=%s:%d, packet:--\n"
- "%s\n"
- "-- end of packet. Error",
- src_addr, port, rdata->msg_info.msg_buf));
- return;
- }
-
- /* For response, check that the value in Via sent-by match the transport.
- * If not matched, silently drop the response.
- * Ref: RFC3261 Section 18.1.2 Receiving Response
- */
- if (msg->type == PJSIP_RESPONSE_MSG) {
- const pj_sockaddr_in *addr;
- const char *addr_addr;
- int port = rdata->msg_info.via->sent_by.port;
- pj_bool_t mismatch = PJ_FALSE;
- if (port == 0) {
- int type;
- type = rdata->tp_info.transport->type;
- port = pjsip_transport_get_default_port_for_type(type);
- }
- addr = &rdata->tp_info.transport->public_addr;
- addr_addr = pj_inet_ntoa(addr->sin_addr);
- if (pj_strcmp2(&rdata->msg_info.via->sent_by.host, addr_addr) != 0)
- mismatch = PJ_TRUE;
- else if (port != pj_ntohs(addr->sin_port)) {
- /* Port or address mismatch, we should discard response */
- /* But we saw one implementation (we don't want to name it to
- * protect the innocence) which put wrong sent-by port although
- * the "rport" parameter is correct.
- * So we discard the response only if the port doesn't match
- * both the port in sent-by and rport. We try to be lenient here!
- */
- if (rdata->msg_info.via->rport_param != pj_sockaddr_in_get_port(addr))
- mismatch = PJ_TRUE;
- else {
- PJ_LOG(4,(THIS_FILE, "Response %p has mismatch port in sent-by"
- " but the rport parameter is correct",
- rdata));
- }
- }
-
- if (mismatch) {
- pjsip_event e;
-
- PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata, PJSIP_EINVALIDVIA);
- endpt_do_event( endpt, &e );
- return;
- }
- }
-
- /* Create key for transaction lookup. */
- rdata_create_key( rdata);
-
- /* Find the transaction for the received message. */
- PJ_LOG(5, (THIS_FILE, "finding tsx with key=%.*s",
- rdata->endpt_info.key.slen, rdata->endpt_info.key.ptr));
-
- /* Start lock mutex in the endpoint. */
- pj_mutex_lock(endpt->tsx_table_mutex);
-
- /* Find the transaction in the hash table. */
- tsx = pj_hash_get( endpt->tsx_table, rdata->endpt_info.key.ptr, rdata->endpt_info.key.slen );
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->tsx_table_mutex);
-
- /* If the transaction is not found... */
- if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) {
-
- /*
- * For response message, discard the message, except if the response is
- * an 2xx class response to INVITE, which in this case it must be
- * passed to TU to be acked.
- */
- if (msg->type == PJSIP_RESPONSE_MSG) {
-
- /* Inform TU about the 200 message, only if it's INVITE. */
- if (PJSIP_IS_STATUS_IN_CLASS(msg->line.status.code, 200) &&
- rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD)
- {
- pjsip_event e;
-
- /* Should not happen for UA. Tsx theoritically lives until
- * all responses are absorbed.
- */
- pj_assert(0);
-
- PJSIP_EVENT_INIT_RX_200_MSG(e, rdata);
- endpt_do_event( endpt, &e );
-
- } else {
- /* Just discard the response, inform TU. */
- pjsip_event e;
-
- PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata,
- PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_CALL_TSX_DOES_NOT_EXIST));
- endpt_do_event( endpt, &e );
- }
-
- /*
- * For non-ACK request message, create a new transaction.
- */
- } else if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
-
- pj_status_t status;
-
- /* Create transaction, mutex is locked there. */
- status = pjsip_endpt_create_tsx(endpt, &tsx);
- if (status != PJ_SUCCESS) {
- PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
- "Unable to create transaction"));
- return;
- }
-
- /* Initialize transaction as UAS. */
- pjsip_tsx_init_uas( tsx, rdata );
-
- /* Register transaction, mutex is locked there. */
- pjsip_endpt_register_tsx( endpt, tsx );
-
- a_new_transaction_just_been_created = PJ_TRUE;
- }
- }
-
- /* If transaction is found (or newly created), pass the message.
- * Otherwise if it's an ACK request, pass directly to TU.
- */
- if (tsx && tsx->state != PJSIP_TSX_STATE_TERMINATED) {
- /* Dispatch message to transaction. */
- pjsip_tsx_on_rx_msg( tsx, rdata );
-
- } else if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD) {
- /*
- * This is an ACK message, but the INVITE transaction could not
- * be found (possibly because the branch parameter in Via in ACK msg
- * is different than the branch in original INVITE). This happens with
- * SER!
- */
- pjsip_event event;
-
- PJSIP_EVENT_INIT_RX_ACK_MSG(event,rdata);
- endpt_do_event( endpt, &event );
- }
-
- /*
- * If a new request message has just been receieved, but no modules
- * seem to be able to handle the request message, then terminate the
- * transaction.
- *
- * Ideally for cases like "unsupported method", we should be able to
- * answer the request statelessly. But we can not do that since the
- * endpoint shoule be able to be used as both user agent and proxy stack,
- * and a proxy stack should be able to handle arbitrary methods.
- */
- if (a_new_transaction_just_been_created && tsx->status_code < 100) {
- /* Certainly no modules has sent any response message.
- * Check that any modules has attached a module data.
- */
- int i;
- for (i=0; i<PJSIP_MAX_MODULE; ++i) {
- if (tsx->module_data[i] != NULL) {
- break;
- }
- }
- if (i == PJSIP_MAX_MODULE) {
- /* No modules have attached itself to the transaction.
- * Terminate the transaction with 501/Not Implemented.
- */
- pjsip_tx_data *tdata;
- pj_status_t status;
-
- if (tsx->method.id == PJSIP_OPTIONS_METHOD) {
- status = pjsip_endpt_create_response(endpt, rdata, 200,
- &tdata);
- } else {
- status = pjsip_endpt_create_response(endpt, rdata,
- PJSIP_SC_METHOD_NOT_ALLOWED,
- &tdata);
- }
-
- if (status != PJ_SUCCESS) {
- PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
- "Unable to create response"));
- return;
- }
-
- if (endpt->allow_hdr) {
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, endpt->allow_hdr));
- }
- pjsip_tsx_on_tx_msg( tsx, tdata );
-
- } else {
- /*
- * If a module has registered itself in the transaction but it
- * hasn't responded the request, chances are the module wouldn't
- * respond to the request at all. We terminate the request here
- * with 500/Internal Server Error, to be safe.
- */
- pjsip_tx_data *tdata;
- pj_status_t status;
-
- status = pjsip_endpt_create_response(endpt, rdata, 500, &tdata);
- if (status != PJ_SUCCESS) {
- PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
- "Unable to create response"));
- return;
- }
-
- pjsip_tsx_on_tx_msg(tsx, tdata);
- }
- }
-}
-
-/*
- * Create transmit data buffer.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_create_tdata( pjsip_endpoint *endpt,
- pjsip_tx_data **p_tdata)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tdata()"));
- return pjsip_tx_data_create(endpt->transport_mgr, p_tdata);
-}
-
-/*
- * Resolve
- */
-PJ_DEF(void) pjsip_endpt_resolve( pjsip_endpoint *endpt,
- pj_pool_t *pool,
- pjsip_host_port *target,
- void *token,
- pjsip_resolver_callback *cb)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_resolve()"));
- pjsip_resolve( endpt->resolver, pool, target, token, cb);
-}
-
-/*
- * Get transport manager.
- */
-PJ_DEF(pjsip_tpmgr*) pjsip_endpt_get_tpmgr(pjsip_endpoint *endpt)
-{
- return endpt->transport_mgr;
-}
-
-/*
- * Get ioqueue instance.
- */
-PJ_DEF(pj_ioqueue_t*) pjsip_endpt_get_ioqueue(pjsip_endpoint *endpt)
-{
- return endpt->ioqueue;
-}
-
-/*
- * Find/create transport.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_alloc_transport( pjsip_endpoint *endpt,
- pjsip_transport_type_e type,
- const pj_sockaddr_in *remote,
- pjsip_transport **p_transport)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_alloc_transport()"));
- return pjsip_tpmgr_alloc_transport( endpt->transport_mgr, type, remote,
- p_transport);
-}
-
-
-/*
- * Report error.
- */
-PJ_DEF(void) pjsip_endpt_log_error( pjsip_endpoint *endpt,
- const char *sender,
- pj_status_t error_code,
- const char *format,
- ... )
-{
- char newformat[256];
- int len;
- va_list marker;
-
- va_start(marker, format);
-
- PJ_UNUSED_ARG(endpt);
-
- len = pj_native_strlen(format);
- if (len < sizeof(newformat)-30) {
- pj_str_t errstr;
-
- pj_native_strcpy(newformat, format);
- pj_snprintf(newformat+len, sizeof(newformat)-len-1,
- ": [err %d] ", error_code);
- len += pj_native_strlen(newformat+len);
-
- errstr = pjsip_strerror(error_code, newformat+len,
- sizeof(newformat)-len-1);
-
- len += errstr.slen;
- newformat[len] = '\0';
-
- pj_log(sender, 1, newformat, marker);
- } else {
- pj_log(sender, 1, format, marker);
- }
-
- va_end(marker);
-}
-
-
-/*
- * Dump endpoint.
- */
-PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail )
-{
-#if PJ_LOG_MAX_LEVEL >= 3
- unsigned count;
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_dump()"));
-
- /* Lock mutex. */
- pj_mutex_lock(endpt->mutex);
-
- PJ_LOG(3, (THIS_FILE, "Dumping endpoint %p:", endpt));
-
- /* Dumping pool factory. */
- (*endpt->pf->dump_status)(endpt->pf, detail);
-
- /* Pool health. */
- PJ_LOG(3, (THIS_FILE," Endpoint pool capacity=%u, used_size=%u",
- pj_pool_get_capacity(endpt->pool),
- pj_pool_get_used_size(endpt->pool)));
-
- /* Transaction tables. */
- count = pj_hash_count(endpt->tsx_table);
- PJ_LOG(3, (THIS_FILE, " Number of transactions: %u", count));
-
- if (count && detail) {
- pj_hash_iterator_t it_val;
- pj_hash_iterator_t *it;
- pj_time_val now;
-
- PJ_LOG(3, (THIS_FILE, " Dumping transaction tables:"));
-
- pj_gettimeofday(&now);
- it = pj_hash_first(endpt->tsx_table, &it_val);
-
- while (it != NULL) {
- int timeout_diff;
-
- /* Get the transaction. No need to lock transaction's mutex
- * since we already hold endpoint mutex, so that no transactions
- * will be deleted.
- */
- pjsip_transaction *tsx = pj_hash_this(endpt->tsx_table, it);
-
- const char *role = (tsx->role == PJSIP_ROLE_UAS ? "UAS" : "UAC");
-
- if (tsx->timeout_timer._timer_id != -1) {
- if (tsx->timeout_timer._timer_value.sec > now.sec) {
- timeout_diff = tsx->timeout_timer._timer_value.sec - now.sec;
- } else {
- timeout_diff = now.sec - tsx->timeout_timer._timer_value.sec;
- timeout_diff = 0 - timeout_diff;
- }
- } else {
- timeout_diff = -1;
- }
-
- PJ_LOG(3, (THIS_FILE, " %s %s %10.*s %.9u %s t=%ds",
- tsx->obj_name, role,
- tsx->method.name.slen, tsx->method.name.ptr,
- tsx->cseq,
- pjsip_tsx_state_str(tsx->state),
- timeout_diff));
-
- it = pj_hash_next(endpt->tsx_table, it);
- }
- }
-
- /* Transports.
- */
- pjsip_tpmgr_dump_transports( endpt->transport_mgr );
-
- /* Timer. */
- PJ_LOG(3,(THIS_FILE, " Timer heap has %u entries",
- pj_timer_heap_count(endpt->timer_heap)));
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->mutex);
-#else
- PJ_LOG(3,(THIS_FILE, "pjsip_end_dump: can't dump because it's disabled."));
-#endif
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip/sip_endpoint.h> +#include <pjsip/sip_transaction.h> +#include <pjsip/sip_private.h> +#include <pjsip/sip_event.h> +#include <pjsip/sip_resolve.h> +#include <pjsip/sip_module.h> +#include <pjsip/sip_util.h> +#include <pjsip/sip_errno.h> +#include <pj/except.h> +#include <pj/log.h> +#include <pj/string.h> +#include <pj/os.h> +#include <pj/pool.h> +#include <pj/hash.h> +#include <pj/assert.h> +#include <pj/errno.h> +#include <pj/lock.h> + +#define PJSIP_EX_NO_MEMORY PJ_NO_MEMORY_EXCEPTION +#define THIS_FILE "endpoint" + +#define MAX_METHODS 32 + +/** + * The SIP endpoint. + */ +struct pjsip_endpoint +{ + /** Pool to allocate memory for the endpoint. */ + pj_pool_t *pool; + + /** Mutex for the pool, hash table, and event list/queue. */ + pj_mutex_t *mutex; + + /** Pool factory. */ + pj_pool_factory *pf; + + /** Name. */ + pj_str_t name; + + /** Transaction table. */ + pj_hash_table_t *tsx_table; + + /** Mutex for transaction table. */ + pj_mutex_t *tsx_table_mutex; + + /** Timer heap. */ + pj_timer_heap_t *timer_heap; + + /** Transport manager. */ + pjsip_tpmgr *transport_mgr; + + /** Ioqueue. */ + pj_ioqueue_t *ioqueue; + + /** DNS Resolver. */ + pjsip_resolver_t *resolver; + + /** Number of modules registered. */ + pj_uint32_t mod_count; + + /** Modules. */ + pjsip_module *modules[PJSIP_MAX_MODULE]; + + /** Number of supported methods. */ + unsigned method_cnt; + + /** Array of supported methods. */ + const pjsip_method *methods[MAX_METHODS]; + + /** Allow header. */ + pjsip_allow_hdr *allow_hdr; + + /** Route header list. */ + pjsip_route_hdr route_hdr_list; + + /** Additional request headers. */ + pjsip_hdr req_hdr; +}; + + + +/* + * Prototypes. + */ +static void endpt_transport_callback(pjsip_endpoint*, + pj_status_t, pjsip_rx_data*); + + +/* + * This is the global handler for memory allocation failure, for pools that + * are created by the endpoint (by default, all pools ARE allocated by + * endpoint). The error is handled by throwing exception, and hopefully, + * the exception will be handled by the application (or this library). + */ +static void pool_callback( pj_pool_t *pool, pj_size_t size ) +{ + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(size); + + PJ_THROW(PJSIP_EX_NO_MEMORY); +} + + +/* + * Initialize modules. + */ +static pj_status_t init_modules( pjsip_endpoint *endpt ) +{ + pj_status_t status; + unsigned i; + //pj_str_t str_COMMA = { ", ", 2 }; + extern pjsip_module aux_tsx_module; + + PJ_LOG(5, (THIS_FILE, "init_modules()")); + + /* Load static modules. */ + endpt->mod_count = PJSIP_MAX_MODULE; + status = register_static_modules( &endpt->mod_count, endpt->modules ); + if (status != 0) { + return status; + } + + /* Add mini aux module. */ + endpt->modules[endpt->mod_count++] = &aux_tsx_module; + + /* Load dynamic modules. */ + // Not supported yet! + + /* Sort modules on the priority. */ + for (i=endpt->mod_count-1; i>0; --i) { + pj_uint32_t max = 0; + unsigned j; + for (j=1; j<=i; ++j) { + if (endpt->modules[j]->priority > endpt->modules[max]->priority) + max = j; + } + if (max != i) { + pjsip_module *temp = endpt->modules[max]; + endpt->modules[max] = endpt->modules[i]; + endpt->modules[i] = temp; + } + } + + /* Initialize each module. */ + for (i=0; i < endpt->mod_count; ++i) { + int j; + + pjsip_module *mod = endpt->modules[i]; + if (mod->init_module) { + status = mod->init_module(endpt, mod, i); + if (status != 0) { + return status; + } + } + + /* Collect all supported methods from modules. */ + for (j=0; j<mod->method_cnt; ++j) { + unsigned k; + for (k=0; k<endpt->method_cnt; ++k) { + if (pjsip_method_cmp(mod->methods[j], endpt->methods[k]) == 0) + break; + } + if (k == endpt->method_cnt) { + if (endpt->method_cnt < MAX_METHODS) { + endpt->methods[endpt->method_cnt++] = mod->methods[j]; + } else { + PJ_LOG(1,(THIS_FILE, "Too many methods")); + return -1; + } + } + } + } + + /* Create Allow header. */ + endpt->allow_hdr = pjsip_allow_hdr_create( endpt->pool ); + endpt->allow_hdr->count = endpt->method_cnt; + for (i=0; i<endpt->method_cnt; ++i) { + endpt->allow_hdr->values[i] = endpt->methods[i]->name; + } + + /* Start each module. */ + for (i=0; i < endpt->mod_count; ++i) { + pjsip_module *mod = endpt->modules[i]; + if (mod->start_module) { + status = mod->start_module(mod); + if (status != 0) { + return status; + } + } + } + + /* Done. */ + return 0; +} + +/* + * Unregister the transaction from the hash table, and destroy the resources + * from the transaction. + */ +PJ_DEF(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt, + pjsip_transaction *tsx) +{ + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_tsx(%s)", tsx->obj_name)); + + pj_assert(tsx->state == PJSIP_TSX_STATE_DESTROYED); + + /* No need to lock transaction. + * This function typically is called from the transaction callback, which + * means that transaction mutex is being held. + */ + pj_assert( pj_mutex_is_locked(tsx->mutex) ); + + /* Lock endpoint. */ + pj_mutex_lock( endpt->tsx_table_mutex ); + + /* Unregister from the hash table. */ + pj_hash_set( NULL, endpt->tsx_table, tsx->transaction_key.ptr, + tsx->transaction_key.slen, NULL); + + /* Unlock endpoint mutex. */ + pj_mutex_unlock( endpt->tsx_table_mutex ); + + /* Destroy transaction mutex. */ + pj_mutex_destroy( tsx->mutex ); + + /* Release the pool for the transaction. */ + pj_pool_release(tsx->pool); + + PJ_LOG(4, (THIS_FILE, "tsx%p destroyed", tsx)); +} + + +/* + * Receive transaction events from transactions and dispatch them to the + * modules. + */ +static void endpt_do_event( pjsip_endpoint *endpt, pjsip_event *evt) +{ + unsigned i; + + /* Dispatch event to modules. */ + for (i=0; i<endpt->mod_count; ++i) { + pjsip_module *mod = endpt->modules[i]; + if (mod && mod->tsx_handler) { + mod->tsx_handler( mod, evt ); + } + } + + /* Destroy transaction if it is terminated. */ + if (evt->type == PJSIP_EVENT_TSX_STATE && + evt->body.tsx_state.tsx->state == PJSIP_TSX_STATE_DESTROYED) + { + /* No need to lock mutex. Mutex is locked inside the destroy function */ + pjsip_endpt_destroy_tsx( endpt, evt->body.tsx_state.tsx ); + } +} + +/* + * Receive transaction events from transactions and put in the event queue + * to be processed later. + */ +void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt ) +{ + // Need to protect this with try/catch? + endpt_do_event(endpt, evt); +} + +/* + * Get "Allow" header. + */ +PJ_DEF(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt ) +{ + return endpt->allow_hdr; +} + +/* + * Get additional headers to be put in outgoing request message. + */ +PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt) +{ + return &endpt->req_hdr; +} + +PJ_DEF(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt, + int url_cnt, const pj_str_t url[]) +{ + int i; + pjsip_route_hdr *hdr; + pj_str_t str_ROUTE = { "Route", 5 }; + + /* Lock endpoint mutex. */ + pj_mutex_lock(endpt->mutex); + + pj_list_init(&endpt->route_hdr_list); + + for (i=0; i<url_cnt; ++i) { + int len = url[i].slen; + char *dup = pj_pool_alloc(endpt->pool, len + 1); + pj_memcpy(dup, url[i].ptr, len); + dup[len] = '\0'; + + hdr = pjsip_parse_hdr(endpt->pool, &str_ROUTE, dup, len, NULL); + if (!hdr) { + pj_mutex_unlock(endpt->mutex); + PJ_LOG(4,(THIS_FILE, "Invalid URL %s in proxy URL", dup)); + return -1; + } + + pj_assert(hdr->type == PJSIP_H_ROUTE); + pj_list_insert_before(&endpt->route_hdr_list, hdr); + } + + /* Unlock endpoint mutex. */ + pj_mutex_unlock(endpt->mutex); + + return 0; +} + +/* + * Get "Route" header list. + */ +PJ_DEF(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt ) +{ + return &endpt->route_hdr_list; +} + + +/* + * Initialize endpoint. + */ +PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf, + const char *name, + pjsip_endpoint **p_endpt) +{ + pj_status_t status; + pj_pool_t *pool; + pjsip_endpoint *endpt; + pjsip_max_forwards_hdr *mf_hdr; + pj_lock_t *lock = NULL; + + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create()")); + + *p_endpt = NULL; + + /* Create pool */ + pool = pj_pool_create(pf, "pept%p", + PJSIP_POOL_LEN_ENDPT, PJSIP_POOL_INC_ENDPT, + &pool_callback); + if (!pool) + return PJ_ENOMEM; + + /* Create endpoint. */ + endpt = pj_pool_calloc(pool, 1, sizeof(*endpt)); + endpt->pool = pool; + endpt->pf = pf; + + /* Get name. */ + if (name != NULL) { + pj_str_t temp; + pj_strdup_with_null(endpt->pool, &endpt->name, pj_cstr(&temp, name)); + } else { + pj_strdup_with_null(endpt->pool, &endpt->name, pj_gethostname()); + } + + /* Create mutex for the events, etc. */ + status = pj_mutex_create_recursive( endpt->pool, "ept%p", &endpt->mutex ); + if (status != PJ_SUCCESS) { + goto on_error; + } + + /* Create mutex for the transaction table. */ + status = pj_mutex_create_recursive( endpt->pool, "mtbl%p", + &endpt->tsx_table_mutex); + if (status != PJ_SUCCESS) { + goto on_error; + } + + /* Create hash table for transaction. */ + endpt->tsx_table = pj_hash_create( endpt->pool, PJSIP_MAX_TSX_COUNT ); + if (!endpt->tsx_table) { + status = PJ_ENOMEM; + goto on_error; + } + + /* Create timer heap to manage all timers within this endpoint. */ + status = pj_timer_heap_create( endpt->pool, PJSIP_MAX_TIMER_COUNT, + &endpt->timer_heap); + if (status != PJ_SUCCESS) { + goto on_error; + } + + /* Set recursive lock for the timer heap. */ + status = pj_lock_create_recursive_mutex( endpt->pool, "edpt%p", &lock); + if (status != PJ_SUCCESS) { + goto on_error; + } + pj_timer_heap_set_lock(endpt->timer_heap, lock, PJ_TRUE); + + /* Set maximum timed out entries to process in a single poll. */ + pj_timer_heap_set_max_timed_out_per_poll(endpt->timer_heap, + PJSIP_MAX_TIMED_OUT_ENTRIES); + + /* Create ioqueue. */ + status = pj_ioqueue_create( endpt->pool, PJSIP_MAX_TRANSPORTS, &endpt->ioqueue); + if (status != PJ_SUCCESS) { + goto on_error; + } + + /* Create transport manager. */ + status = pjsip_tpmgr_create( endpt->pool, endpt, + &endpt_transport_callback, + &endpt->transport_mgr); + if (status != PJ_SUCCESS) { + goto on_error; + } + + /* Create asynchronous DNS resolver. */ + endpt->resolver = pjsip_resolver_create(endpt->pool); + if (!endpt->resolver) { + PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error creating resolver")); + goto on_error; + } + + /* Initialize TLS ID for transaction lock. */ + status = pj_thread_local_alloc(&pjsip_tsx_lock_tls_id); + if (status != PJ_SUCCESS) { + goto on_error; + } + pj_thread_local_set(pjsip_tsx_lock_tls_id, NULL); + + /* Initialize request headers. */ + pj_list_init(&endpt->req_hdr); + + /* Initialist "Route" header list. */ + pj_list_init(&endpt->route_hdr_list); + + /* Add "Max-Forwards" for request header. */ + mf_hdr = pjsip_max_forwards_hdr_create(endpt->pool); + mf_hdr->ivalue = PJSIP_MAX_FORWARDS_VALUE; + pj_list_insert_before( &endpt->req_hdr, mf_hdr); + + /* Load and init modules. */ + status = init_modules(endpt); + if (status != PJ_SUCCESS) { + PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error in init_modules()")); + return status; + } + + /* Done. */ + *p_endpt = endpt; + return status; + +on_error: + if (endpt->transport_mgr) { + pjsip_tpmgr_destroy(endpt->transport_mgr); + endpt->transport_mgr = NULL; + } + if (endpt->mutex) { + pj_mutex_destroy(endpt->mutex); + endpt->mutex = NULL; + } + if (endpt->tsx_table_mutex) { + pj_mutex_destroy(endpt->tsx_table_mutex); + endpt->tsx_table_mutex = NULL; + } + pj_pool_release( endpt->pool ); + + PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init() failed")); + return status; +} + +/* + * Destroy endpoint. + */ +PJ_DEF(void) pjsip_endpt_destroy(pjsip_endpoint *endpt) +{ + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy()")); + + /* Shutdown and destroy all transports. */ + pjsip_tpmgr_destroy(endpt->transport_mgr); + + /* Delete endpoint mutex. */ + pj_mutex_destroy(endpt->mutex); + + /* Delete transaction table mutex. */ + pj_mutex_destroy(endpt->tsx_table_mutex); + + /* Finally destroy pool. */ + pj_pool_release(endpt->pool); +} + +/* + * Get endpoint name. + */ +PJ_DEF(const pj_str_t*) pjsip_endpt_name(const pjsip_endpoint *endpt) +{ + return &endpt->name; +} + + +/* + * Create new pool. + */ +PJ_DEF(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt, + const char *pool_name, + pj_size_t initial, + pj_size_t increment ) +{ + pj_pool_t *pool; + + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_pool()")); + + /* Lock endpoint mutex. */ + pj_mutex_lock(endpt->mutex); + + /* Create pool */ + pool = pj_pool_create( endpt->pf, pool_name, + initial, increment, &pool_callback); + + /* Unlock mutex. */ + pj_mutex_unlock(endpt->mutex); + + if (pool) { + PJ_LOG(5, (THIS_FILE, " pool %s created", pj_pool_getobjname(pool))); + } else { + PJ_LOG(4, (THIS_FILE, "Unable to create pool %s!", pool_name)); + } + + return pool; +} + +/* + * Return back pool to endpoint's pool manager to be either destroyed or + * recycled. + */ +PJ_DEF(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt, pj_pool_t *pool ) +{ + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_pool(%s)", pj_pool_getobjname(pool))); + + pj_mutex_lock(endpt->mutex); + pj_pool_release( pool ); + pj_mutex_unlock(endpt->mutex); +} + +/* + * Handle events. + */ +PJ_DEF(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt, + const pj_time_val *max_timeout) +{ + /* timeout is 'out' var. This just to make compiler happy. */ + pj_time_val timeout = { 0, 0}; + + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_handle_events()")); + + /* Poll the timer. The timer heap has its own mutex for better + * granularity, so we don't need to lock end endpoint. + */ + timeout.sec = timeout.msec = 0; + pj_timer_heap_poll( endpt->timer_heap, &timeout ); + + /* If caller specifies maximum time to wait, then compare the value with + * the timeout to wait from timer, and use the minimum value. + */ + if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) { + timeout = *max_timeout; + } + + /* Poll ioqueue. */ + pj_ioqueue_poll( endpt->ioqueue, &timeout); +} + +/* + * Schedule timer. + */ +PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt, + pj_timer_entry *entry, + const pj_time_val *delay ) +{ + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)", + entry, delay->sec, delay->msec)); + return pj_timer_heap_schedule( endpt->timer_heap, entry, delay ); +} + +/* + * Cancel the previously registered timer. + */ +PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, + pj_timer_entry *entry ) +{ + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_cancel_timer(entry=%p)", entry)); + pj_timer_heap_cancel( endpt->timer_heap, entry ); +} + +/* + * Create a new transaction. + * Endpoint must then initialize the new transaction as either UAS or UAC, and + * register it to the hash table. + */ +PJ_DEF(pj_status_t) pjsip_endpt_create_tsx(pjsip_endpoint *endpt, + pjsip_transaction **p_tsx) +{ + pj_pool_t *pool; + + PJ_ASSERT_RETURN(endpt && p_tsx, PJ_EINVAL); + + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tsx()")); + + /* Request one pool for the transaction. Mutex is locked there. */ + pool = pjsip_endpt_create_pool(endpt, "ptsx%p", + PJSIP_POOL_LEN_TSX, PJSIP_POOL_INC_TSX); + if (pool == NULL) { + return PJ_ENOMEM; + } + + /* Create the transaction. */ + return pjsip_tsx_create(pool, endpt, p_tsx); +} + +/* + * Register the transaction to the endpoint. + * This will put the transaction to the transaction hash table. Before calling + * this function, the transaction must be INITIALIZED as either UAS or UAC, so + * that the transaction key is built. + */ +PJ_DEF(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt, + pjsip_transaction *tsx) +{ + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_register_tsx(%s)", tsx->obj_name)); + + pj_assert(tsx->transaction_key.slen != 0); + //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL); + + /* Lock hash table mutex. */ + pj_mutex_lock(endpt->tsx_table_mutex); + + /* Register the transaction to the hash table. */ + pj_hash_set( tsx->pool, endpt->tsx_table, tsx->transaction_key.ptr, + tsx->transaction_key.slen, tsx); + + /* Unlock mutex. */ + pj_mutex_unlock(endpt->tsx_table_mutex); +} + +/* + * Find transaction by the key. + */ +PJ_DEF(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt, + const pj_str_t *key ) +{ + pjsip_transaction *tsx; + + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_find_tsx()")); + + /* Start lock mutex in the endpoint. */ + pj_mutex_lock(endpt->tsx_table_mutex); + + /* Find the transaction in the hash table. */ + tsx = pj_hash_get( endpt->tsx_table, key->ptr, key->slen ); + + /* Unlock mutex. */ + pj_mutex_unlock(endpt->tsx_table_mutex); + + return tsx; +} + +/* + * Create key. + */ +static void rdata_create_key( pjsip_rx_data *rdata) +{ + pjsip_role_e role; + if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) { + role = PJSIP_ROLE_UAS; + } else { + role = PJSIP_ROLE_UAC; + } + pjsip_tsx_create_key(rdata->tp_info.pool, &rdata->endpt_info.key, role, + &rdata->msg_info.cseq->method, rdata); +} + +/* + * This is the callback that is called by the transport manager when it + * receives a message from the network. + */ +static void endpt_transport_callback( pjsip_endpoint *endpt, + pj_status_t status, + pjsip_rx_data *rdata ) +{ + pjsip_msg *msg = rdata->msg_info.msg; + pjsip_transaction *tsx; + pj_bool_t a_new_transaction_just_been_created = PJ_FALSE; + + PJ_LOG(5, (THIS_FILE, "endpt_transport_callback(rdata=%p)", rdata)); + + if (status != PJ_SUCCESS) { + const char *src_addr = pj_inet_ntoa(rdata->pkt_info.addr.sin_addr); + int port = pj_ntohs(rdata->pkt_info.addr.sin_port); + PJSIP_ENDPT_LOG_ERROR((endpt, "transport", status, + "Src.addr=%s:%d, packet:--\n" + "%s\n" + "-- end of packet. Error", + src_addr, port, rdata->msg_info.msg_buf)); + return; + } + + /* For response, check that the value in Via sent-by match the transport. + * If not matched, silently drop the response. + * Ref: RFC3261 Section 18.1.2 Receiving Response + */ + if (msg->type == PJSIP_RESPONSE_MSG) { + const pj_sockaddr_in *addr; + const char *addr_addr; + int port = rdata->msg_info.via->sent_by.port; + pj_bool_t mismatch = PJ_FALSE; + if (port == 0) { + int type; + type = rdata->tp_info.transport->type; + port = pjsip_transport_get_default_port_for_type(type); + } + addr = &rdata->tp_info.transport->public_addr; + addr_addr = pj_inet_ntoa(addr->sin_addr); + if (pj_strcmp2(&rdata->msg_info.via->sent_by.host, addr_addr) != 0) + mismatch = PJ_TRUE; + else if (port != pj_ntohs(addr->sin_port)) { + /* Port or address mismatch, we should discard response */ + /* But we saw one implementation (we don't want to name it to + * protect the innocence) which put wrong sent-by port although + * the "rport" parameter is correct. + * So we discard the response only if the port doesn't match + * both the port in sent-by and rport. We try to be lenient here! + */ + if (rdata->msg_info.via->rport_param != pj_sockaddr_in_get_port(addr)) + mismatch = PJ_TRUE; + else { + PJ_LOG(4,(THIS_FILE, "Response %p has mismatch port in sent-by" + " but the rport parameter is correct", + rdata)); + } + } + + if (mismatch) { + pjsip_event e; + + PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata, PJSIP_EINVALIDVIA); + endpt_do_event( endpt, &e ); + return; + } + } + + /* Create key for transaction lookup. */ + rdata_create_key( rdata); + + /* Find the transaction for the received message. */ + PJ_LOG(5, (THIS_FILE, "finding tsx with key=%.*s", + rdata->endpt_info.key.slen, rdata->endpt_info.key.ptr)); + + /* Start lock mutex in the endpoint. */ + pj_mutex_lock(endpt->tsx_table_mutex); + + /* Find the transaction in the hash table. */ + tsx = pj_hash_get( endpt->tsx_table, rdata->endpt_info.key.ptr, rdata->endpt_info.key.slen ); + + /* Unlock mutex. */ + pj_mutex_unlock(endpt->tsx_table_mutex); + + /* If the transaction is not found... */ + if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) { + + /* + * For response message, discard the message, except if the response is + * an 2xx class response to INVITE, which in this case it must be + * passed to TU to be acked. + */ + if (msg->type == PJSIP_RESPONSE_MSG) { + + /* Inform TU about the 200 message, only if it's INVITE. */ + if (PJSIP_IS_STATUS_IN_CLASS(msg->line.status.code, 200) && + rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD) + { + pjsip_event e; + + /* Should not happen for UA. Tsx theoritically lives until + * all responses are absorbed. + */ + pj_assert(0); + + PJSIP_EVENT_INIT_RX_200_MSG(e, rdata); + endpt_do_event( endpt, &e ); + + } else { + /* Just discard the response, inform TU. */ + pjsip_event e; + + PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata, + PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_CALL_TSX_DOES_NOT_EXIST)); + endpt_do_event( endpt, &e ); + } + + /* + * For non-ACK request message, create a new transaction. + */ + } else if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { + + pj_status_t status; + + /* Create transaction, mutex is locked there. */ + status = pjsip_endpt_create_tsx(endpt, &tsx); + if (status != PJ_SUCCESS) { + PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status, + "Unable to create transaction")); + return; + } + + /* Initialize transaction as UAS. */ + pjsip_tsx_init_uas( tsx, rdata ); + + /* Register transaction, mutex is locked there. */ + pjsip_endpt_register_tsx( endpt, tsx ); + + a_new_transaction_just_been_created = PJ_TRUE; + } + } + + /* If transaction is found (or newly created), pass the message. + * Otherwise if it's an ACK request, pass directly to TU. + */ + if (tsx && tsx->state != PJSIP_TSX_STATE_TERMINATED) { + /* Dispatch message to transaction. */ + pjsip_tsx_on_rx_msg( tsx, rdata ); + + } else if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD) { + /* + * This is an ACK message, but the INVITE transaction could not + * be found (possibly because the branch parameter in Via in ACK msg + * is different than the branch in original INVITE). This happens with + * SER! + */ + pjsip_event event; + + PJSIP_EVENT_INIT_RX_ACK_MSG(event,rdata); + endpt_do_event( endpt, &event ); + } + + /* + * If a new request message has just been receieved, but no modules + * seem to be able to handle the request message, then terminate the + * transaction. + * + * Ideally for cases like "unsupported method", we should be able to + * answer the request statelessly. But we can not do that since the + * endpoint shoule be able to be used as both user agent and proxy stack, + * and a proxy stack should be able to handle arbitrary methods. + */ + if (a_new_transaction_just_been_created && tsx->status_code < 100) { + /* Certainly no modules has sent any response message. + * Check that any modules has attached a module data. + */ + int i; + for (i=0; i<PJSIP_MAX_MODULE; ++i) { + if (tsx->module_data[i] != NULL) { + break; + } + } + if (i == PJSIP_MAX_MODULE) { + /* No modules have attached itself to the transaction. + * Terminate the transaction with 501/Not Implemented. + */ + pjsip_tx_data *tdata; + pj_status_t status; + + if (tsx->method.id == PJSIP_OPTIONS_METHOD) { + status = pjsip_endpt_create_response(endpt, rdata, 200, + &tdata); + } else { + status = pjsip_endpt_create_response(endpt, rdata, + PJSIP_SC_METHOD_NOT_ALLOWED, + &tdata); + } + + if (status != PJ_SUCCESS) { + PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status, + "Unable to create response")); + return; + } + + if (endpt->allow_hdr) { + pjsip_msg_add_hdr( tdata->msg, + pjsip_hdr_shallow_clone(tdata->pool, endpt->allow_hdr)); + } + pjsip_tsx_on_tx_msg( tsx, tdata ); + + } else { + /* + * If a module has registered itself in the transaction but it + * hasn't responded the request, chances are the module wouldn't + * respond to the request at all. We terminate the request here + * with 500/Internal Server Error, to be safe. + */ + pjsip_tx_data *tdata; + pj_status_t status; + + status = pjsip_endpt_create_response(endpt, rdata, 500, &tdata); + if (status != PJ_SUCCESS) { + PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status, + "Unable to create response")); + return; + } + + pjsip_tsx_on_tx_msg(tsx, tdata); + } + } +} + +/* + * Create transmit data buffer. + */ +PJ_DEF(pj_status_t) pjsip_endpt_create_tdata( pjsip_endpoint *endpt, + pjsip_tx_data **p_tdata) +{ + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tdata()")); + return pjsip_tx_data_create(endpt->transport_mgr, p_tdata); +} + +/* + * Resolve + */ +PJ_DEF(void) pjsip_endpt_resolve( pjsip_endpoint *endpt, + pj_pool_t *pool, + pjsip_host_port *target, + void *token, + pjsip_resolver_callback *cb) +{ + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_resolve()")); + pjsip_resolve( endpt->resolver, pool, target, token, cb); +} + +/* + * Get transport manager. + */ +PJ_DEF(pjsip_tpmgr*) pjsip_endpt_get_tpmgr(pjsip_endpoint *endpt) +{ + return endpt->transport_mgr; +} + +/* + * Get ioqueue instance. + */ +PJ_DEF(pj_ioqueue_t*) pjsip_endpt_get_ioqueue(pjsip_endpoint *endpt) +{ + return endpt->ioqueue; +} + +/* + * Find/create transport. + */ +PJ_DEF(pj_status_t) pjsip_endpt_alloc_transport( pjsip_endpoint *endpt, + pjsip_transport_type_e type, + const pj_sockaddr_in *remote, + pjsip_transport **p_transport) +{ + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_alloc_transport()")); + return pjsip_tpmgr_alloc_transport( endpt->transport_mgr, type, remote, + p_transport); +} + + +/* + * Report error. + */ +PJ_DEF(void) pjsip_endpt_log_error( pjsip_endpoint *endpt, + const char *sender, + pj_status_t error_code, + const char *format, + ... ) +{ + char newformat[256]; + int len; + va_list marker; + + va_start(marker, format); + + PJ_UNUSED_ARG(endpt); + + len = pj_native_strlen(format); + if (len < sizeof(newformat)-30) { + pj_str_t errstr; + + pj_native_strcpy(newformat, format); + pj_snprintf(newformat+len, sizeof(newformat)-len-1, + ": [err %d] ", error_code); + len += pj_native_strlen(newformat+len); + + errstr = pjsip_strerror(error_code, newformat+len, + sizeof(newformat)-len-1); + + len += errstr.slen; + newformat[len] = '\0'; + + pj_log(sender, 1, newformat, marker); + } else { + pj_log(sender, 1, format, marker); + } + + va_end(marker); +} + + +/* + * Dump endpoint. + */ +PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail ) +{ +#if PJ_LOG_MAX_LEVEL >= 3 + unsigned count; + + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_dump()")); + + /* Lock mutex. */ + pj_mutex_lock(endpt->mutex); + + PJ_LOG(3, (THIS_FILE, "Dumping endpoint %p:", endpt)); + + /* Dumping pool factory. */ + (*endpt->pf->dump_status)(endpt->pf, detail); + + /* Pool health. */ + PJ_LOG(3, (THIS_FILE," Endpoint pool capacity=%u, used_size=%u", + pj_pool_get_capacity(endpt->pool), + pj_pool_get_used_size(endpt->pool))); + + /* Transaction tables. */ + count = pj_hash_count(endpt->tsx_table); + PJ_LOG(3, (THIS_FILE, " Number of transactions: %u", count)); + + if (count && detail) { + pj_hash_iterator_t it_val; + pj_hash_iterator_t *it; + pj_time_val now; + + PJ_LOG(3, (THIS_FILE, " Dumping transaction tables:")); + + pj_gettimeofday(&now); + it = pj_hash_first(endpt->tsx_table, &it_val); + + while (it != NULL) { + int timeout_diff; + + /* Get the transaction. No need to lock transaction's mutex + * since we already hold endpoint mutex, so that no transactions + * will be deleted. + */ + pjsip_transaction *tsx = pj_hash_this(endpt->tsx_table, it); + + const char *role = (tsx->role == PJSIP_ROLE_UAS ? "UAS" : "UAC"); + + if (tsx->timeout_timer._timer_id != -1) { + if (tsx->timeout_timer._timer_value.sec > now.sec) { + timeout_diff = tsx->timeout_timer._timer_value.sec - now.sec; + } else { + timeout_diff = now.sec - tsx->timeout_timer._timer_value.sec; + timeout_diff = 0 - timeout_diff; + } + } else { + timeout_diff = -1; + } + + PJ_LOG(3, (THIS_FILE, " %s %s %10.*s %.9u %s t=%ds", + tsx->obj_name, role, + tsx->method.name.slen, tsx->method.name.ptr, + tsx->cseq, + pjsip_tsx_state_str(tsx->state), + timeout_diff)); + + it = pj_hash_next(endpt->tsx_table, it); + } + } + + /* Transports. + */ + pjsip_tpmgr_dump_transports( endpt->transport_mgr ); + + /* Timer. */ + PJ_LOG(3,(THIS_FILE, " Timer heap has %u entries", + pj_timer_heap_count(endpt->timer_heap))); + + /* Unlock mutex. */ + pj_mutex_unlock(endpt->mutex); +#else + PJ_LOG(3,(THIS_FILE, "pjsip_end_dump: can't dump because it's disabled.")); +#endif +} + diff --git a/pjsip/src/pjsip/sip_errno.c b/pjsip/src/pjsip/sip_errno.c index 7e24f3ee..999156c6 100644 --- a/pjsip/src/pjsip/sip_errno.c +++ b/pjsip/src/pjsip/sip_errno.c @@ -1,125 +1,125 @@ -/* $Id: $ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_errno.h>
-#include <pjsip/sip_msg.h>
-#include <pj/string.h>
-#include <pj/errno.h>
-
-/* PJSIP's own error codes/messages
- * MUST KEEP THIS ARRAY SORTED!!
- */
-static const struct
-{
- int code;
- const char *msg;
-} err_str[] =
-{
- /* Generic SIP errors */
- { PJSIP_EBUSY, "Object is busy" },
- { PJSIP_ETYPEEXISTS , "Object with the same type exists" },
-
- /* Messaging errors */
- { PJSIP_EINVALIDMSG, "Invalid message/syntax error" },
- { PJSIP_EINVALIDSCHEME, "Invalid URI scheme" },
- { PJSIP_EMSGTOOLONG, "Message too long" },
- { PJSIP_EPARTIALMSG, "Partial message" },
- { PJSIP_EMISSINGHDR, "Missing required header(s)" },
- { PJSIP_EINVALIDVIA, "Invalid Via header" },
- { PJSIP_EMULTIPLEVIA, "Multiple Via headers in response" },
-
- /* Transport errors */
- { PJSIP_EUNSUPTRANSPORT, "Unsupported transport"},
- { PJSIP_EPENDINGTX, "Transmit buffer already pending"},
- { PJSIP_ERXOVERFLOW, "Rx buffer overflow"},
-
- /* Transaction errors */
- { PJSIP_ETSXDESTROYED, "Transaction has been destroyed"},
-};
-
-
-/*
- * pjsip_strerror()
- */
-PJ_DEF(pj_str_t) pjsip_strerror( pj_status_t statcode,
- char *buf, pj_size_t bufsize )
-{
- pj_str_t errstr;
-
- if (statcode >= PJSIP_ERRNO_START && statcode < PJSIP_ERRNO_START+800)
- {
- /* Status code. */
- const pj_str_t *status_text =
- pjsip_get_status_text(PJSIP_ERRNO_TO_SIP_STATUS(statcode));
-
- errstr.ptr = buf;
- pj_strncpy_with_null(&errstr, status_text, bufsize);
- return errstr;
- }
- else if (statcode >= PJSIP_ERRNO_START_PJSIP &&
- statcode < PJSIP_ERRNO_START_PJSIP + 1000)
- {
- /* Find the error in the table.
- * Use binary search!
- */
- int first = 0;
- int n = PJ_ARRAY_SIZE(err_str);
-
- while (n > 0) {
- int half = n/2;
- int mid = first + half;
-
- if (err_str[mid].code < statcode) {
- first = mid+1;
- n -= (half+1);
- } else if (err_str[mid].code > statcode) {
- n = half;
- } else {
- first = mid;
- break;
- }
- }
-
-
- if (PJ_ARRAY_SIZE(err_str) && err_str[first].code == statcode) {
- pj_str_t msg;
-
- msg.ptr = (char*)err_str[first].msg;
- msg.slen = pj_native_strlen(err_str[first].msg);
-
- errstr.ptr = buf;
- pj_strncpy_with_null(&errstr, &msg, bufsize);
- return errstr;
-
- } else {
- /* Error not found. */
- errstr.ptr = buf;
- errstr.slen = pj_snprintf(buf, bufsize,
- "Unknown error %d",
- statcode);
-
- return errstr;
- }
- }
- else {
- /* Not our code. Give it to PJLIB. */
- return pj_strerror(statcode, buf, bufsize);
- }
-
-}
-
+/* $Id: $ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip/sip_errno.h> +#include <pjsip/sip_msg.h> +#include <pj/string.h> +#include <pj/errno.h> + +/* PJSIP's own error codes/messages + * MUST KEEP THIS ARRAY SORTED!! + */ +static const struct +{ + int code; + const char *msg; +} err_str[] = +{ + /* Generic SIP errors */ + { PJSIP_EBUSY, "Object is busy" }, + { PJSIP_ETYPEEXISTS , "Object with the same type exists" }, + + /* Messaging errors */ + { PJSIP_EINVALIDMSG, "Invalid message/syntax error" }, + { PJSIP_EINVALIDSCHEME, "Invalid URI scheme" }, + { PJSIP_EMSGTOOLONG, "Message too long" }, + { PJSIP_EPARTIALMSG, "Partial message" }, + { PJSIP_EMISSINGHDR, "Missing required header(s)" }, + { PJSIP_EINVALIDVIA, "Invalid Via header" }, + { PJSIP_EMULTIPLEVIA, "Multiple Via headers in response" }, + + /* Transport errors */ + { PJSIP_EUNSUPTRANSPORT, "Unsupported transport"}, + { PJSIP_EPENDINGTX, "Transmit buffer already pending"}, + { PJSIP_ERXOVERFLOW, "Rx buffer overflow"}, + + /* Transaction errors */ + { PJSIP_ETSXDESTROYED, "Transaction has been destroyed"}, +}; + + +/* + * pjsip_strerror() + */ +PJ_DEF(pj_str_t) pjsip_strerror( pj_status_t statcode, + char *buf, pj_size_t bufsize ) +{ + pj_str_t errstr; + + if (statcode >= PJSIP_ERRNO_START && statcode < PJSIP_ERRNO_START+800) + { + /* Status code. */ + const pj_str_t *status_text = + pjsip_get_status_text(PJSIP_ERRNO_TO_SIP_STATUS(statcode)); + + errstr.ptr = buf; + pj_strncpy_with_null(&errstr, status_text, bufsize); + return errstr; + } + else if (statcode >= PJSIP_ERRNO_START_PJSIP && + statcode < PJSIP_ERRNO_START_PJSIP + 1000) + { + /* Find the error in the table. + * Use binary search! + */ + int first = 0; + int n = PJ_ARRAY_SIZE(err_str); + + while (n > 0) { + int half = n/2; + int mid = first + half; + + if (err_str[mid].code < statcode) { + first = mid+1; + n -= (half+1); + } else if (err_str[mid].code > statcode) { + n = half; + } else { + first = mid; + break; + } + } + + + if (PJ_ARRAY_SIZE(err_str) && err_str[first].code == statcode) { + pj_str_t msg; + + msg.ptr = (char*)err_str[first].msg; + msg.slen = pj_native_strlen(err_str[first].msg); + + errstr.ptr = buf; + pj_strncpy_with_null(&errstr, &msg, bufsize); + return errstr; + + } else { + /* Error not found. */ + errstr.ptr = buf; + errstr.slen = pj_snprintf(buf, bufsize, + "Unknown error %d", + statcode); + + return errstr; + } + } + else { + /* Not our code. Give it to PJLIB. */ + return pj_strerror(statcode, buf, bufsize); + } + +} + diff --git a/pjsip/src/pjsip/sip_msg.c b/pjsip/src/pjsip/sip_msg.c index 23e184fa..6f963abb 100644 --- a/pjsip/src/pjsip/sip_msg.c +++ b/pjsip/src/pjsip/sip_msg.c @@ -1,1432 +1,1452 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_msg.h>
-#include <pjsip/print_util.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-
-/*
- * Include inline definitions here if functions are NOT inlined.
- */
-#if PJ_FUNCTIONS_ARE_INLINED==0
-# include <pjsip/sip_msg_i.h>
-#endif
-
-static const pj_str_t method_names[] =
-{
- { "INVITE", 6 },
- { "CANCEL", 6 },
- { "ACK", 3 },
- { "BYE", 3 },
- { "REGISTER", 8 },
- { "OPTIONS", 7 },
-};
-
-const pj_str_t pjsip_hdr_names[] =
-{
- { "Accept", 6 }, // PJSIP_H_ACCEPT,
- { "Accept-Encoding", 15 }, // PJSIP_H_ACCEPT_ENCODING,
- { "Accept-Language", 15 }, // PJSIP_H_ACCEPT_LANGUAGE,
- { "Alert-Info", 10 }, // PJSIP_H_ALERT_INFO,
- { "Allow", 5 }, // PJSIP_H_ALLOW,
- { "Authentication-Info",19 }, // PJSIP_H_AUTHENTICATION_INFO,
- { "Authorization", 13 }, // PJSIP_H_AUTHORIZATION,
- { "Call-ID", 7 }, // PJSIP_H_CALL_ID,
- { "Call-Info", 9 }, // PJSIP_H_CALL_INFO,
- { "Contact", 7 }, // PJSIP_H_CONTACT,
- { "Content-Disposition",19 }, // PJSIP_H_CONTENT_DISPOSITION,
- { "Content-Encoding", 16 }, // PJSIP_H_CONTENT_ENCODING,
- { "Content-Language", 16 }, // PJSIP_H_CONTENT_LANGUAGE,
- { "Content-Length", 14 }, // PJSIP_H_CONTENT_LENGTH,
- { "Content-Type", 12 }, // PJSIP_H_CONTENT_TYPE,
- { "CSeq", 4 }, // PJSIP_H_CSEQ,
- { "Date", 4 }, // PJSIP_H_DATE,
- { "Error-Info", 10 }, // PJSIP_H_ERROR_INFO,
- { "Expires", 7 }, // PJSIP_H_EXPIRES,
- { "From", 4 }, // PJSIP_H_FROM,
- { "In-Reply-To", 11 }, // PJSIP_H_IN_REPLY_TO,
- { "Max-Forwards", 12 }, // PJSIP_H_MAX_FORWARDS,
- { "MIME-Version", 12 }, // PJSIP_H_MIME_VERSION,
- { "Min-Expires", 11 }, // PJSIP_H_MIN_EXPIRES,
- { "Organization", 12 }, // PJSIP_H_ORGANIZATION,
- { "Priority", 8 }, // PJSIP_H_PRIORITY,
- { "Proxy-Authenticate", 18 }, // PJSIP_H_PROXY_AUTHENTICATE,
- { "Proxy-Authorization",19 }, // PJSIP_H_PROXY_AUTHORIZATION,
- { "Proxy-Require", 13 }, // PJSIP_H_PROXY_REQUIRE,
- { "Record-Route", 12 }, // PJSIP_H_RECORD_ROUTE,
- { "Reply-To", 8 }, // PJSIP_H_REPLY_TO,
- { "Require", 7 }, // PJSIP_H_REQUIRE,
- { "Retry-After", 11 }, // PJSIP_H_RETRY_AFTER,
- { "Route", 5 }, // PJSIP_H_ROUTE,
- { "Server", 6 }, // PJSIP_H_SERVER,
- { "Subject", 7 }, // PJSIP_H_SUBJECT,
- { "Supported", 9 }, // PJSIP_H_SUPPORTED,
- { "Timestamp", 9 }, // PJSIP_H_TIMESTAMP,
- { "To", 2 }, // PJSIP_H_TO,
- { "Unsupported", 11 }, // PJSIP_H_UNSUPPORTED,
- { "User-Agent", 10 }, // PJSIP_H_USER_AGENT,
- { "Via", 3 }, // PJSIP_H_VIA,
- { "Warning", 7 }, // PJSIP_H_WARNING,
- { "WWW-Authenticate", 16 }, // PJSIP_H_WWW_AUTHENTICATE,
-
- { "_Unknown-Header", 15 }, // PJSIP_H_OTHER,
-};
-
-static pj_str_t status_phrase[710];
-static int print_media_type(char *buf, const pjsip_media_type *media);
-
-static int init_status_phrase()
-{
- int i;
- pj_str_t default_reason_phrase = { "Default status message", 22};
-
- for (i=0; i<PJ_ARRAY_SIZE(status_phrase); ++i)
- status_phrase[i] = default_reason_phrase;
-
- pj_strset2( &status_phrase[100], "Trying");
- pj_strset2( &status_phrase[180], "Ringing");
- pj_strset2( &status_phrase[181], "Call Is Being Forwarded");
- pj_strset2( &status_phrase[182], "Queued");
- pj_strset2( &status_phrase[183], "Session Progress");
-
- pj_strset2( &status_phrase[200], "OK");
-
- pj_strset2( &status_phrase[300], "Multiple Choices");
- pj_strset2( &status_phrase[301], "Moved Permanently");
- pj_strset2( &status_phrase[302], "Moved Temporarily");
- pj_strset2( &status_phrase[305], "Use Proxy");
- pj_strset2( &status_phrase[380], "Alternative Service");
-
- pj_strset2( &status_phrase[400], "Bad Request");
- pj_strset2( &status_phrase[401], "Unauthorized");
- pj_strset2( &status_phrase[402], "Payment Required");
- pj_strset2( &status_phrase[403], "Forbidden");
- pj_strset2( &status_phrase[404], "Not Found");
- pj_strset2( &status_phrase[405], "Method Not Allowed");
- pj_strset2( &status_phrase[406], "Not Acceptable");
- pj_strset2( &status_phrase[407], "Proxy Authentication Required");
- pj_strset2( &status_phrase[408], "Request Timeout");
- pj_strset2( &status_phrase[410], "Gone");
- pj_strset2( &status_phrase[413], "Request Entity Too Large");
- pj_strset2( &status_phrase[414], "Request URI Too Long");
- pj_strset2( &status_phrase[415], "Unsupported Media Type");
- pj_strset2( &status_phrase[416], "Unsupported URI Scheme");
- pj_strset2( &status_phrase[420], "Bad Extension");
- pj_strset2( &status_phrase[421], "Extension Required");
- pj_strset2( &status_phrase[423], "Interval Too Brief");
- pj_strset2( &status_phrase[480], "Temporarily Unavailable");
- pj_strset2( &status_phrase[481], "Call/Transaction Does Not Exist");
- pj_strset2( &status_phrase[482], "Loop Detected");
- pj_strset2( &status_phrase[483], "Too Many Hops");
- pj_strset2( &status_phrase[484], "Address Incompleted");
- pj_strset2( &status_phrase[485], "Ambiguous");
- pj_strset2( &status_phrase[486], "Busy Here");
- pj_strset2( &status_phrase[487], "Request Terminated");
- pj_strset2( &status_phrase[488], "Not Acceptable Here");
- pj_strset2( &status_phrase[491], "Request Pending");
- pj_strset2( &status_phrase[493], "Undecipherable");
-
- pj_strset2( &status_phrase[500], "Internal Server Error");
- pj_strset2( &status_phrase[501], "Not Implemented");
- pj_strset2( &status_phrase[502], "Bad Gateway");
- pj_strset2( &status_phrase[503], "Service Unavailable");
- pj_strset2( &status_phrase[504], "Server Timeout");
- pj_strset2( &status_phrase[505], "Version Not Supported");
- pj_strset2( &status_phrase[513], "Message Too Large");
-
- pj_strset2( &status_phrase[600], "Busy Everywhere");
- pj_strset2( &status_phrase[603], "Decline");
- pj_strset2( &status_phrase[604], "Does Not Exist Anywhere");
- pj_strset2( &status_phrase[606], "Not Acceptable");
-
- pj_strset2( &status_phrase[701], "No response from destination server");
- pj_strset2( &status_phrase[702], "Unable to resolve destination server");
- pj_strset2( &status_phrase[703], "Error sending message to destination server");
-
- return 1;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Method.
- */
-
-PJ_DEF(void) pjsip_method_init( pjsip_method *m,
- pj_pool_t *pool,
- const pj_str_t *str)
-{
- pj_str_t dup;
- pjsip_method_init_np(m, pj_strdup(pool, &dup, str));
-}
-
-PJ_DEF(void) pjsip_method_set( pjsip_method *m, pjsip_method_e me )
-{
- m->id = me;
- m->name = method_names[me];
-}
-
-PJ_DEF(void) pjsip_method_init_np(pjsip_method *m,
- pj_str_t *str)
-{
- int i;
- for (i=0; i<PJ_ARRAY_SIZE(method_names); ++i) {
- if (pj_stricmp(str, &method_names[i])==0) {
- m->id = (pjsip_method_e)i;
- m->name = method_names[i];
- return;
- }
- }
- m->id = PJSIP_OTHER_METHOD;
- m->name = *str;
-}
-
-PJ_DEF(void) pjsip_method_copy( pj_pool_t *pool,
- pjsip_method *method,
- const pjsip_method *rhs )
-{
- method->id = rhs->id;
- if (rhs->id != PJSIP_OTHER_METHOD) {
- method->name = rhs->name;
- } else {
- pj_strdup(pool, &method->name, &rhs->name);
- }
-}
-
-
-PJ_DEF(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2)
-{
- if (m1->id == m2->id) {
- if (m1->id != PJSIP_OTHER_METHOD)
- return 0;
- return pj_stricmp(&m1->name, &m2->name);
- }
-
- return ( m1->id < m2->id ) ? -1 : 1;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Message.
- */
-
-PJ_DEF(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type)
-{
- pjsip_msg *msg = pj_pool_alloc(pool, sizeof(pjsip_msg));
- pj_list_init(&msg->hdr);
- msg->type = type;
- msg->body = NULL;
- return msg;
-}
-
-PJ_DEF(void*) pjsip_msg_find_hdr( pjsip_msg *msg,
- pjsip_hdr_e hdr_type, void *start)
-{
- pjsip_hdr *hdr=start, *end=&msg->hdr;
-
- if (hdr == NULL) {
- hdr = msg->hdr.next;
- }
- for (; hdr!=end; hdr = hdr->next) {
- if (hdr->type == hdr_type)
- return hdr;
- }
- return NULL;
-}
-
-PJ_DEF(void*) pjsip_msg_find_hdr_by_name( pjsip_msg *msg,
- const pj_str_t *name, void *start)
-{
- pjsip_hdr *hdr=start, *end=&msg->hdr;
-
- if (hdr == NULL) {
- hdr = msg->hdr.next;
- }
- for (; hdr!=end; hdr = hdr->next) {
- if (hdr->type < PJSIP_H_OTHER) {
- if (pj_stricmp(&pjsip_hdr_names[hdr->type], name) == 0)
- return hdr;
- } else {
- if (pj_stricmp(&hdr->name, name) == 0)
- return hdr;
- }
- }
- return NULL;
-}
-
-PJ_DEF(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg,
- pjsip_hdr_e hdr_type, void *start)
-{
- pjsip_hdr *hdr = pjsip_msg_find_hdr(msg, hdr_type, start);
- if (hdr) {
- pj_list_erase(hdr);
- }
- return hdr;
-}
-
-PJ_DEF(pj_ssize_t) pjsip_msg_print( pjsip_msg *msg, char *buf, pj_size_t size)
-{
- char *p=buf, *end=buf+size;
- int len;
- pjsip_hdr *hdr;
- pj_str_t clen_hdr = { "Content-Length: ", 16};
-
- /* Get a wild guess on how many bytes are typically needed.
- * We'll check this later in detail, but this serves as a quick check.
- */
- if (size < 256)
- return -1;
-
- /* Print request line or status line depending on message type */
- if (msg->type == PJSIP_REQUEST_MSG) {
- pjsip_uri *uri;
-
- /* Add method. */
- len = msg->line.req.method.name.slen;
- pj_memcpy(p, msg->line.req.method.name.ptr, len);
- p += len;
- *p++ = ' ';
-
- /* Add URI */
- uri = pjsip_uri_get_uri(msg->line.req.uri);
- len = pjsip_uri_print( PJSIP_URI_IN_REQ_URI, uri, p, end-p);
- if (len < 1)
- return -1;
- p += len;
-
- /* Add ' SIP/2.0' */
- if (end-p < 16)
- return -1;
- pj_memcpy(p, " SIP/2.0\r\n", 10);
- p += 10;
-
- } else {
-
- /* Add 'SIP/2.0 ' */
- pj_memcpy(p, "SIP/2.0 ", 8);
- p += 8;
-
- /* Add status code. */
- len = pj_utoa(msg->line.status.code, p);
- p += len;
- *p++ = ' ';
-
- /* Add reason text. */
- len = msg->line.status.reason.slen;
- pj_memcpy(p, msg->line.status.reason.ptr, len );
- p += len;
-
- /* Add newline. */
- *p++ = '\r';
- *p++ = '\n';
- }
-
- /* Print each of the headers. */
- for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
- len = (*hdr->vptr->print_on)(hdr, p, end-p);
- if (len < 1)
- return -1;
- p += len;
-
- if (p+3 >= end)
- return -1;
-
- *p++ = '\r';
- *p++ = '\n';
- }
-
- /* Process message body. */
- if (msg->body) {
- pj_str_t ctype_hdr = { "Content-Type: ", 14};
- int len;
- const pjsip_media_type *media = &msg->body->content_type;
- char *clen_pos;
-
- /* Add Content-Type header. */
- if ( (end-p) < 24+media->type.slen+media->subtype.slen+media->param.slen) {
- return -1;
- }
- pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen);
- p += ctype_hdr.slen;
- p += print_media_type(p, media);
- *p++ = '\r';
- *p++ = '\n';
-
- /* Add Content-Length header. */
- if ((end-p) < clen_hdr.slen+12+2) {
- return -1;
- }
- pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen);
- p += clen_hdr.slen;
-
- /* Print blanks after "Content-Type:", this is where we'll put
- * the content length value after we know the length of the
- * body.
- */
- pj_memset(p, ' ', 12);
- clen_pos = p;
- p += 12;
- *p++ = '\r';
- *p++ = '\n';
-
- /* Add blank newline. */
- *p++ = '\r';
- *p++ = '\n';
-
- /* Print the message body itself. */
- len = (*msg->body->print_body)(msg->body, p, end-p);
- if (len < 0) {
- return -1;
- }
- p += len;
-
- /* Now that we have the length of the body, print this to the
- * Content-Length header.
- */
- len = pj_utoa(len, clen_pos);
- clen_pos[len] = ' ';
-
- } else {
- /* There's no message body.
- * Add Content-Length with zero value.
- */
- if ((end-p) < clen_hdr.slen+8) {
- return -1;
- }
- pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen);
- p += clen_hdr.slen;
- *p++ = '0';
- *p++ = '\r';
- *p++ = '\n';
- *p++ = '\r';
- *p++ = '\n';
- }
-
- *p = '\0';
- return p-buf;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-PJ_DEF(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr_ptr )
-{
- const pjsip_hdr *hdr = hdr_ptr;
- return (*hdr->vptr->clone)(pool, hdr_ptr);
-}
-
-
-PJ_DEF(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr_ptr )
-{
- const pjsip_hdr *hdr = hdr_ptr;
- return (*hdr->vptr->shallow_clone)(pool, hdr_ptr);
-}
-
-PJ_DEF(int) pjsip_hdr_print_on( void *hdr_ptr, char *buf, pj_size_t len)
-{
- pjsip_hdr *hdr = hdr_ptr;
- return (*hdr->vptr->print_on)(hdr_ptr, buf, len);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Status/Reason Phrase
- */
-
-PJ_DEF(const pj_str_t*) pjsip_get_status_text(int code)
-{
- static int is_initialized;
- if (is_initialized == 0) {
- is_initialized = 1;
- init_status_phrase();
- }
-
- return (code>=100 && code<(sizeof(status_phrase)/sizeof(status_phrase[0]))) ?
- &status_phrase[code] : &status_phrase[0];
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Generic pjsip_hdr_names/hvalue header.
- */
-
-static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr,
- char *buf, pj_size_t size);
-static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool,
- const pjsip_generic_string_hdr *hdr);
-static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_generic_string_hdr *hdr );
-
-static pjsip_hdr_vptr generic_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_generic_string_hdr_print,
-};
-
-PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create( pj_pool_t *pool,
- const pj_str_t *hnames )
-{
- pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_string_hdr));
- init_hdr(hdr, PJSIP_H_OTHER, &generic_hdr_vptr);
- if (hnames) {
- pj_strdup(pool, &hdr->name, hnames);
- hdr->sname = hdr->name;
- }
- hdr->hvalue.ptr = NULL;
- hdr->hvalue.slen = 0;
- return hdr;
-}
-
-PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create_with_text( pj_pool_t *pool,
- const pj_str_t *hname,
- const pj_str_t *hvalue)
-{
- pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, hname);
- pj_strdup(pool, &hdr->hvalue, hvalue);
- return hdr;
-}
-
-static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr,
- char *buf, pj_size_t size)
-{
- char *p = buf;
-
- if ((pj_ssize_t)size < hdr->name.slen + hdr->hvalue.slen + 5)
- return -1;
-
- pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
- p += hdr->name.slen;
- *p++ = ':';
- *p++ = ' ';
- pj_memcpy(p, hdr->hvalue.ptr, hdr->hvalue.slen);
- p += hdr->hvalue.slen;
- *p = '\0';
-
- return p - buf;
-}
-
-static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool,
- const pjsip_generic_string_hdr *rhs)
-{
- pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, &rhs->name);
-
- hdr->type = rhs->type;
- hdr->sname = hdr->name;
- pj_strdup( pool, &hdr->hvalue, &rhs->hvalue);
- return hdr;
-}
-
-static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_generic_string_hdr *rhs )
-{
- pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Generic pjsip_hdr_names/integer value header.
- */
-
-static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr,
- char *buf, pj_size_t size);
-static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool,
- const pjsip_generic_int_hdr *hdr);
-static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_generic_int_hdr *hdr );
-
-static pjsip_hdr_vptr generic_int_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_generic_int_hdr_print,
-};
-
-PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool,
- const pj_str_t *hnames )
-{
- pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_int_hdr));
- init_hdr(hdr, PJSIP_H_OTHER, &generic_int_hdr_vptr);
- if (hnames) {
- pj_strdup(pool, &hdr->name, hnames);
- hdr->sname = hdr->name;
- }
- hdr->ivalue = 0;
- return hdr;
-}
-
-PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create_with_value( pj_pool_t *pool,
- const pj_str_t *hname,
- int value)
-{
- pjsip_generic_int_hdr *hdr = pjsip_generic_int_hdr_create(pool, hname);
- hdr->ivalue = value;
- return hdr;
-}
-
-static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr,
- char *buf, pj_size_t size)
-{
- char *p = buf;
-
- if ((pj_ssize_t)size < hdr->name.slen + 15)
- return -1;
-
- pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
- p += hdr->name.slen;
- *p++ = ':';
- *p++ = ' ';
-
- p += pj_utoa(hdr->ivalue, p);
-
- return p - buf;
-}
-
-static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool,
- const pjsip_generic_int_hdr *rhs)
-{
- pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_generic_int_hdr *rhs )
-{
- pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Generic array header.
- */
-static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr, char *buf, pj_size_t size);
-static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool,
- const pjsip_generic_array_hdr *hdr);
-static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_generic_array_hdr *hdr);
-
-static pjsip_hdr_vptr generic_array_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_generic_array_hdr_print,
-};
-
-PJ_DEF(pjsip_generic_array_hdr*) pjsip_generic_array_create( pj_pool_t *pool,
- const pj_str_t *hnames)
-{
- pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_array_hdr));
- init_hdr(hdr, PJSIP_H_OTHER, &generic_array_hdr_vptr);
- if (hnames) {
- pj_strdup(pool, &hdr->name, hnames);
- hdr->sname = hdr->name;
- }
- hdr->count = 0;
- return hdr;
-
-}
-
-static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr,
- char *buf, pj_size_t size)
-{
- char *p = buf, *endbuf = buf+size;
-
- copy_advance(p, hdr->name);
- *p++ = ':';
- *p++ = ' ';
-
- if (hdr->count > 0) {
- unsigned i;
- int printed;
- copy_advance(p, hdr->values[0]);
- for (i=1; i<hdr->count; ++i) {
- copy_advance_pair(p, ", ", 2, hdr->values[i]);
- }
- }
-
- return p - buf;
-}
-
-static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool,
- const pjsip_generic_array_hdr *rhs)
-{
- unsigned i;
- pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
-
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- for (i=0; i<rhs->count; ++i) {
- pj_strdup(pool, &hdr->values[i], &rhs->values[i]);
- }
-
- return hdr;
-}
-
-
-static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_generic_array_hdr *rhs)
-{
- pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Accept header.
- */
-PJ_DEF(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool)
-{
- pjsip_accept_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_ACCEPT, &generic_array_hdr_vptr);
- hdr->count = 0;
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Allow header.
- */
-
-PJ_DEF(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool)
-{
- pjsip_allow_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_ALLOW, &generic_array_hdr_vptr);
- hdr->count = 0;
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Call-ID header.
- */
-
-PJ_DEF(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool )
-{
- pjsip_cid_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_CALL_ID, &generic_hdr_vptr);
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Content-Length header.
- */
-static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr, char *buf, pj_size_t size);
-static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *hdr);
-#define pjsip_clen_hdr_shallow_clone pjsip_clen_hdr_clone
-
-static pjsip_hdr_vptr clen_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_clen_hdr_print,
-};
-
-PJ_DEF(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool )
-{
- pjsip_clen_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_clen_hdr));
- init_hdr(hdr, PJSIP_H_CONTENT_LENGTH, &clen_hdr_vptr);
- hdr->len = 0;
- return hdr;
-}
-
-static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr,
- char *buf, pj_size_t size)
-{
- char *p = buf;
- int len;
-
- if ((pj_ssize_t)size < hdr->name.slen + 14)
- return -1;
-
- pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
- p += hdr->name.slen;
- *p++ = ':';
- *p++ = ' ';
-
- len = pj_utoa(hdr->len, p);
- p += len;
- *p = '\0';
-
- return p-buf;
-}
-
-static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *rhs)
-{
- pjsip_clen_hdr *hdr = pjsip_clen_hdr_create(pool);
- hdr->len = rhs->len;
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * CSeq header.
- */
-static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size);
-static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr);
-static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr );
-
-static pjsip_hdr_vptr cseq_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_cseq_hdr_print,
-};
-
-PJ_DEF(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool )
-{
- pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_cseq_hdr));
- init_hdr(hdr, PJSIP_H_CSEQ, &cseq_hdr_vptr);
- hdr->cseq = 0;
- hdr->method.id = PJSIP_OTHER_METHOD;
- hdr->method.name.ptr = NULL;
- hdr->method.name.slen = 0;
- return hdr;
-}
-
-static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size)
-{
- char *p = buf;
- int len;
-
- if ((pj_ssize_t)size < hdr->name.slen + hdr->method.name.slen + 15)
- return -1;
-
- pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
- p += hdr->name.slen;
- *p++ = ':';
- *p++ = ' ';
-
- len = pj_utoa(hdr->cseq, p);
- p += len;
- *p++ = ' ';
-
- pj_memcpy(p, hdr->method.name.ptr, hdr->method.name.slen);
- p += hdr->method.name.slen;
-
- *p = '\0';
-
- return p-buf;
-}
-
-static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool,
- const pjsip_cseq_hdr *rhs)
-{
- pjsip_cseq_hdr *hdr = pjsip_cseq_hdr_create(pool);
- hdr->cseq = rhs->cseq;
- pjsip_method_copy(pool, &hdr->method, &rhs->method);
- return hdr;
-}
-
-static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_cseq_hdr *rhs )
-{
- pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Contact header.
- */
-static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf, pj_size_t size);
-static pjsip_contact_hdr* pjsip_contact_hdr_clone( pj_pool_t *pool, const pjsip_contact_hdr *hdr);
-static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool, const pjsip_contact_hdr *);
-
-static pjsip_hdr_vptr contact_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_contact_hdr_print,
-};
-
-PJ_DEF(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool )
-{
- pjsip_contact_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_CONTACT, &contact_hdr_vptr);
- hdr->expires = -1;
- return hdr;
-}
-
-static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf,
- pj_size_t size)
-{
- if (hdr->star) {
- char *p = buf;
- if ((pj_ssize_t)size < hdr->name.slen + 6)
- return -1;
- pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
- p += hdr->name.slen;
- *p++ = ':';
- *p++ = ' ';
- *p++ = '*';
- *p = '\0';
- return p - buf;
-
- } else {
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
-
- copy_advance(buf, hdr->name);
- *buf++ = ':';
- *buf++ = ' ';
-
- printed = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, hdr->uri,
- buf, endbuf-buf);
- if (printed < 1)
- return -1;
-
- buf += printed;
-
- if (hdr->q1000) {
- if (buf+19 >= endbuf)
- return -1;
-
- /*
- printed = sprintf(buf, ";q=%u.%03u",
- hdr->q1000/1000, hdr->q1000 % 1000);
- */
- pj_memcpy(buf, ";q=", 3);
- printed = pj_utoa(hdr->q1000/1000, buf+3);
- buf += printed + 3;
- *buf++ = '.';
- printed = pj_utoa(hdr->q1000 % 1000, buf);
- buf += printed;
- }
-
- if (hdr->expires >= 0) {
- if (buf+23 >= endbuf)
- return -1;
-
- pj_memcpy(buf, ";expires=", 9);
- printed = pj_utoa(hdr->expires, buf+9);
- buf += printed + 9;
- }
-
- if (hdr->other_param.slen) {
- copy_advance(buf, hdr->other_param);
- }
-
- *buf = '\0';
- return buf-startbuf;
- }
-}
-
-static pjsip_contact_hdr* pjsip_contact_hdr_clone( pj_pool_t *pool,
- const pjsip_contact_hdr *rhs)
-{
- pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(pool);
-
- hdr->star = rhs->star;
- if (hdr->star)
- return hdr;
-
- hdr->uri = pjsip_uri_clone(pool, rhs->uri);
- hdr->q1000 = rhs->q1000;
- hdr->expires = rhs->expires;
- pj_strdup(pool, &hdr->other_param, &rhs->other_param);
- return hdr;
-}
-
-static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_contact_hdr *rhs)
-{
- pjsip_contact_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Content-Type header..
- */
-static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr, char *buf, pj_size_t size);
-static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool, const pjsip_ctype_hdr *hdr);
-#define pjsip_ctype_hdr_shallow_clone pjsip_ctype_hdr_clone
-
-static pjsip_hdr_vptr ctype_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_ctype_hdr_print,
-};
-
-PJ_DEF(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool )
-{
- pjsip_ctype_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_CONTENT_TYPE, &ctype_hdr_vptr);
- return hdr;
-}
-
-static int print_media_type(char *buf, const pjsip_media_type *media)
-{
- char *p = buf;
-
- pj_memcpy(p, media->type.ptr, media->type.slen);
- p += media->type.slen;
- *p++ = '/';
- pj_memcpy(p, media->subtype.ptr, media->subtype.slen);
- p += media->subtype.slen;
-
- if (media->param.slen) {
- pj_memcpy(p, media->param.ptr, media->param.slen);
- p += media->param.slen;
- }
-
- return p-buf;
-}
-
-static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr,
- char *buf, pj_size_t size)
-{
- char *p = buf;
- int len;
-
- if ((pj_ssize_t)size < hdr->name.slen +
- hdr->media.type.slen + hdr->media.subtype.slen +
- hdr->media.param.slen + 8)
- {
- return -1;
- }
-
- pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
- p += hdr->name.slen;
- *p++ = ':';
- *p++ = ' ';
-
- len = print_media_type(p, &hdr->media);
- p += len;
-
- *p = '\0';
- return p-buf;
-}
-
-static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool,
- const pjsip_ctype_hdr *rhs)
-{
- pjsip_ctype_hdr *hdr = pjsip_ctype_hdr_create(pool);
- pj_strdup(pool, &hdr->media.type, &rhs->media.type);
- pj_strdup(pool, &hdr->media.param, &rhs->media.param);
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Expires header.
- */
-PJ_DEF(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool )
-{
- pjsip_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_EXPIRES, &generic_int_hdr_vptr);
- hdr->ivalue = 0;
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * To or From header.
- */
-static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr,
- char *buf, pj_size_t size);
-static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool,
- const pjsip_fromto_hdr *hdr);
-static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_fromto_hdr *hdr);
-
-
-static pjsip_hdr_vptr fromto_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_fromto_hdr_print,
-};
-
-PJ_DEF(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool )
-{
- pjsip_from_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_FROM, &fromto_hdr_vptr);
- return hdr;
-}
-
-PJ_DEF(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool )
-{
- pjsip_to_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_TO, &fromto_hdr_vptr);
- return hdr;
-}
-
-PJ_DEF(pjsip_from_hdr*) pjsip_fromto_set_from( pjsip_fromto_hdr *hdr )
-{
- hdr->type = PJSIP_H_FROM;
- hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_FROM];
- return hdr;
-}
-
-PJ_DEF(pjsip_to_hdr*) pjsip_fromto_set_to( pjsip_fromto_hdr *hdr )
-{
- hdr->type = PJSIP_H_TO;
- hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_TO];
- return hdr;
-}
-
-static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr,
- char *buf, pj_size_t size)
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
-
- copy_advance(buf, hdr->name);
- *buf++ = ':';
- *buf++ = ' ';
-
- printed = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, hdr->uri, buf, endbuf-buf);
- if (printed < 1)
- return -1;
-
- buf += printed;
-
- copy_advance_pair(buf, ";tag=", 5, hdr->tag);
- if (hdr->other_param.slen)
- copy_advance(buf, hdr->other_param);
-
- return buf-startbuf;
-}
-
-static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool,
- const pjsip_fromto_hdr *rhs)
-{
- pjsip_fromto_hdr *hdr = pjsip_from_hdr_create(pool);
-
- hdr->type = rhs->type;
- hdr->name = rhs->name;
- hdr->sname = rhs->sname;
- hdr->uri = pjsip_uri_clone(pool, rhs->uri);
- pj_strdup( pool, &hdr->tag, &rhs->tag);
- pj_strdup( pool, &hdr->other_param, &rhs->other_param);
-
- return hdr;
-}
-
-static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_fromto_hdr *rhs)
-{
- pjsip_fromto_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Max-Forwards header.
- */
-PJ_DEF(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool)
-{
- pjsip_max_forwards_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_MAX_FORWARDS, &generic_int_hdr_vptr);
- hdr->ivalue = 0;
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Min-Expires header.
- */
-PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool)
-{
- pjsip_min_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_MIN_EXPIRES, &generic_int_hdr_vptr);
- hdr->ivalue = 0;
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Record-Route and Route header.
- */
-static int pjsip_routing_hdr_print( pjsip_routing_hdr *r, char *buf, pj_size_t size );
-static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool, const pjsip_routing_hdr *r );
-static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool, const pjsip_routing_hdr *r );
-
-static pjsip_hdr_vptr routing_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_routing_hdr_print,
-};
-
-PJ_DEF(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool )
-{
- pjsip_rr_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_RECORD_ROUTE, &routing_hdr_vptr);
- pjsip_name_addr_init(&hdr->name_addr);
- pj_memset(&hdr->other_param, 0, sizeof(hdr->other_param));
- return hdr;
-}
-
-PJ_DEF(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool )
-{
- pjsip_route_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_ROUTE, &routing_hdr_vptr);
- pjsip_name_addr_init(&hdr->name_addr);
- pj_memset(&hdr->other_param, 0, sizeof(hdr->other_param));
- return hdr;
-}
-
-PJ_DEF(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *hdr )
-{
- hdr->type = PJSIP_H_RECORD_ROUTE;
- hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_RECORD_ROUTE];
- return hdr;
-}
-
-PJ_DEF(pjsip_route_hdr*) pjsip_routing_hdr_set_route( pjsip_routing_hdr *hdr )
-{
- hdr->type = PJSIP_H_ROUTE;
- hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_ROUTE];
- return hdr;
-}
-
-static int pjsip_routing_hdr_print( pjsip_routing_hdr *hdr,
- char *buf, pj_size_t size )
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
-
- copy_advance(buf, hdr->name);
- *buf++ = ':';
- *buf++ = ' ';
-
- printed = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, &hdr->name_addr, buf, endbuf-buf);
- if (printed < 1)
- return -1;
- buf += printed;
-
- if (hdr->other_param.slen) {
- copy_advance(buf, hdr->other_param);
- }
-
- return buf-startbuf;
-}
-
-static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool,
- const pjsip_routing_hdr *rhs )
-{
- pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
-
- init_hdr(hdr, rhs->type, rhs->vptr);
- pjsip_name_addr_init(&hdr->name_addr);
- pjsip_name_addr_assign(pool, &hdr->name_addr, &rhs->name_addr);
- pj_strdup( pool, &hdr->other_param, &rhs->other_param);
- return hdr;
-}
-
-static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_routing_hdr *rhs )
-{
- pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Require header.
- */
-PJ_DEF(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool)
-{
- pjsip_require_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_REQUIRE, &generic_array_hdr_vptr);
- hdr->count = 0;
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Retry-After header.
- */
-PJ_DEF(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool)
-{
- pjsip_retry_after_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_RETRY_AFTER, &generic_int_hdr_vptr);
- hdr->ivalue = 0;
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Supported header.
- */
-PJ_DEF(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool)
-{
- pjsip_supported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_SUPPORTED, &generic_array_hdr_vptr);
- hdr->count = 0;
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Unsupported header.
- */
-PJ_DEF(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool)
-{
- pjsip_unsupported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_UNSUPPORTED, &generic_array_hdr_vptr);
- hdr->count = 0;
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Via header.
- */
-static int pjsip_via_hdr_print( pjsip_via_hdr *hdr, char *buf, pj_size_t size);
-static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr);
-static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr );
-
-static pjsip_hdr_vptr via_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_via_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_via_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_via_hdr_print,
-};
-
-PJ_DEF(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool )
-{
- pjsip_via_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_VIA, &via_hdr_vptr);
- hdr->sent_by.port = 5060;
- hdr->ttl_param = -1;
- hdr->rport_param = -1;
- return hdr;
-}
-
-static int pjsip_via_hdr_print( pjsip_via_hdr *hdr,
- char *buf, pj_size_t size)
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
- pj_str_t sip_ver = { "SIP/2.0/", 8 };
-
- if ((pj_ssize_t)size < hdr->name.slen + sip_ver.slen +
- hdr->transport.slen + hdr->sent_by.host.slen + 12)
- {
- return -1;
- }
-
- /* pjsip_hdr_names */
- copy_advance(buf, hdr->name);
- *buf++ = ':';
- *buf++ = ' ';
-
- /* SIP/2.0/transport host:port */
- pj_memcpy(buf, sip_ver.ptr, sip_ver.slen);
- buf += sip_ver.slen;
- pj_memcpy(buf, hdr->transport.ptr, hdr->transport.slen);
- buf += hdr->transport.slen;
- *buf++ = ' ';
- pj_memcpy(buf, hdr->sent_by.host.ptr, hdr->sent_by.host.slen);
- buf += hdr->sent_by.host.slen;
- *buf++ = ':';
- printed = pj_utoa(hdr->sent_by.port, buf);
- buf += printed;
-
- if (hdr->ttl_param >= 0) {
- size = endbuf-buf;
- if (size < 14)
- return -1;
- pj_memcpy(buf, ";ttl=", 5);
- printed = pj_utoa(hdr->ttl_param, buf+5);
- buf += printed + 5;
- }
-
- if (hdr->rport_param >= 0) {
- size = endbuf-buf;
- if (size < 14)
- return -1;
- pj_memcpy(buf, ";rport", 6);
- buf += 6;
- if (hdr->rport_param > 0) {
- *buf++ = '=';
- buf += pj_utoa(hdr->rport_param, buf);
- }
- }
-
-
- copy_advance_pair(buf, ";maddr=", 7, hdr->maddr_param);
- copy_advance_pair(buf, ";received=", 10, hdr->recvd_param);
- copy_advance_pair(buf, ";branch=", 8, hdr->branch_param);
- copy_advance(buf, hdr->other_param);
-
- *buf = '\0';
- return buf-startbuf;
-}
-
-static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool,
- const pjsip_via_hdr *rhs)
-{
- pjsip_via_hdr *hdr = pjsip_via_hdr_create(pool);
- pj_strdup(pool, &hdr->transport, &rhs->transport);
- pj_strdup(pool, &hdr->sent_by.host, &rhs->sent_by.host);
- hdr->sent_by.port = rhs->sent_by.port;
- hdr->ttl_param = rhs->ttl_param;
- hdr->rport_param = rhs->rport_param;
- pj_strdup(pool, &hdr->maddr_param, &rhs->maddr_param);
- pj_strdup(pool, &hdr->recvd_param, &rhs->recvd_param);
- pj_strdup(pool, &hdr->branch_param, &rhs->branch_param);
- pj_strdup(pool, &hdr->other_param, &rhs->other_param);
- return hdr;
-}
-
-static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_via_hdr *rhs )
-{
- pjsip_via_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * General purpose function to textual data in a SIP body.
- */
-PJ_DEF(int) pjsip_print_text_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size)
-{
- if (size < msg_body->len)
- return -1;
- pj_memcpy(buf, msg_body->data, msg_body->len);
- return msg_body->len;
-}
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip/sip_msg.h> +#include <pjsip/print_util.h> +#include <pj/string.h> +#include <pj/pool.h> + +/* + * Include inline definitions here if functions are NOT inlined. + */ +#if PJ_FUNCTIONS_ARE_INLINED==0 +# include <pjsip/sip_msg_i.h> +#endif + +static const pj_str_t method_names[] = +{ + { "INVITE", 6 }, + { "CANCEL", 6 }, + { "ACK", 3 }, + { "BYE", 3 }, + { "REGISTER", 8 }, + { "OPTIONS", 7 }, +}; + +const pj_str_t pjsip_hdr_names[] = +{ + { "Accept", 6 }, // PJSIP_H_ACCEPT, + { "Accept-Encoding", 15 }, // PJSIP_H_ACCEPT_ENCODING, + { "Accept-Language", 15 }, // PJSIP_H_ACCEPT_LANGUAGE, + { "Alert-Info", 10 }, // PJSIP_H_ALERT_INFO, + { "Allow", 5 }, // PJSIP_H_ALLOW, + { "Authentication-Info",19 }, // PJSIP_H_AUTHENTICATION_INFO, + { "Authorization", 13 }, // PJSIP_H_AUTHORIZATION, + { "Call-ID", 7 }, // PJSIP_H_CALL_ID, + { "Call-Info", 9 }, // PJSIP_H_CALL_INFO, + { "Contact", 7 }, // PJSIP_H_CONTACT, + { "Content-Disposition",19 }, // PJSIP_H_CONTENT_DISPOSITION, + { "Content-Encoding", 16 }, // PJSIP_H_CONTENT_ENCODING, + { "Content-Language", 16 }, // PJSIP_H_CONTENT_LANGUAGE, + { "Content-Length", 14 }, // PJSIP_H_CONTENT_LENGTH, + { "Content-Type", 12 }, // PJSIP_H_CONTENT_TYPE, + { "CSeq", 4 }, // PJSIP_H_CSEQ, + { "Date", 4 }, // PJSIP_H_DATE, + { "Error-Info", 10 }, // PJSIP_H_ERROR_INFO, + { "Expires", 7 }, // PJSIP_H_EXPIRES, + { "From", 4 }, // PJSIP_H_FROM, + { "In-Reply-To", 11 }, // PJSIP_H_IN_REPLY_TO, + { "Max-Forwards", 12 }, // PJSIP_H_MAX_FORWARDS, + { "MIME-Version", 12 }, // PJSIP_H_MIME_VERSION, + { "Min-Expires", 11 }, // PJSIP_H_MIN_EXPIRES, + { "Organization", 12 }, // PJSIP_H_ORGANIZATION, + { "Priority", 8 }, // PJSIP_H_PRIORITY, + { "Proxy-Authenticate", 18 }, // PJSIP_H_PROXY_AUTHENTICATE, + { "Proxy-Authorization",19 }, // PJSIP_H_PROXY_AUTHORIZATION, + { "Proxy-Require", 13 }, // PJSIP_H_PROXY_REQUIRE, + { "Record-Route", 12 }, // PJSIP_H_RECORD_ROUTE, + { "Reply-To", 8 }, // PJSIP_H_REPLY_TO, + { "Require", 7 }, // PJSIP_H_REQUIRE, + { "Retry-After", 11 }, // PJSIP_H_RETRY_AFTER, + { "Route", 5 }, // PJSIP_H_ROUTE, + { "Server", 6 }, // PJSIP_H_SERVER, + { "Subject", 7 }, // PJSIP_H_SUBJECT, + { "Supported", 9 }, // PJSIP_H_SUPPORTED, + { "Timestamp", 9 }, // PJSIP_H_TIMESTAMP, + { "To", 2 }, // PJSIP_H_TO, + { "Unsupported", 11 }, // PJSIP_H_UNSUPPORTED, + { "User-Agent", 10 }, // PJSIP_H_USER_AGENT, + { "Via", 3 }, // PJSIP_H_VIA, + { "Warning", 7 }, // PJSIP_H_WARNING, + { "WWW-Authenticate", 16 }, // PJSIP_H_WWW_AUTHENTICATE, + + { "_Unknown-Header", 15 }, // PJSIP_H_OTHER, +}; + +static pj_str_t status_phrase[710]; +static int print_media_type(char *buf, const pjsip_media_type *media); + +static int init_status_phrase() +{ + int i; + pj_str_t default_reason_phrase = { "Default status message", 22}; + + for (i=0; i<PJ_ARRAY_SIZE(status_phrase); ++i) + status_phrase[i] = default_reason_phrase; + + pj_strset2( &status_phrase[100], "Trying"); + pj_strset2( &status_phrase[180], "Ringing"); + pj_strset2( &status_phrase[181], "Call Is Being Forwarded"); + pj_strset2( &status_phrase[182], "Queued"); + pj_strset2( &status_phrase[183], "Session Progress"); + + pj_strset2( &status_phrase[200], "OK"); + + pj_strset2( &status_phrase[300], "Multiple Choices"); + pj_strset2( &status_phrase[301], "Moved Permanently"); + pj_strset2( &status_phrase[302], "Moved Temporarily"); + pj_strset2( &status_phrase[305], "Use Proxy"); + pj_strset2( &status_phrase[380], "Alternative Service"); + + pj_strset2( &status_phrase[400], "Bad Request"); + pj_strset2( &status_phrase[401], "Unauthorized"); + pj_strset2( &status_phrase[402], "Payment Required"); + pj_strset2( &status_phrase[403], "Forbidden"); + pj_strset2( &status_phrase[404], "Not Found"); + pj_strset2( &status_phrase[405], "Method Not Allowed"); + pj_strset2( &status_phrase[406], "Not Acceptable"); + pj_strset2( &status_phrase[407], "Proxy Authentication Required"); + pj_strset2( &status_phrase[408], "Request Timeout"); + pj_strset2( &status_phrase[410], "Gone"); + pj_strset2( &status_phrase[413], "Request Entity Too Large"); + pj_strset2( &status_phrase[414], "Request URI Too Long"); + pj_strset2( &status_phrase[415], "Unsupported Media Type"); + pj_strset2( &status_phrase[416], "Unsupported URI Scheme"); + pj_strset2( &status_phrase[420], "Bad Extension"); + pj_strset2( &status_phrase[421], "Extension Required"); + pj_strset2( &status_phrase[423], "Interval Too Brief"); + pj_strset2( &status_phrase[480], "Temporarily Unavailable"); + pj_strset2( &status_phrase[481], "Call/Transaction Does Not Exist"); + pj_strset2( &status_phrase[482], "Loop Detected"); + pj_strset2( &status_phrase[483], "Too Many Hops"); + pj_strset2( &status_phrase[484], "Address Incompleted"); + pj_strset2( &status_phrase[485], "Ambiguous"); + pj_strset2( &status_phrase[486], "Busy Here"); + pj_strset2( &status_phrase[487], "Request Terminated"); + pj_strset2( &status_phrase[488], "Not Acceptable Here"); + pj_strset2( &status_phrase[491], "Request Pending"); + pj_strset2( &status_phrase[493], "Undecipherable"); + + pj_strset2( &status_phrase[500], "Internal Server Error"); + pj_strset2( &status_phrase[501], "Not Implemented"); + pj_strset2( &status_phrase[502], "Bad Gateway"); + pj_strset2( &status_phrase[503], "Service Unavailable"); + pj_strset2( &status_phrase[504], "Server Timeout"); + pj_strset2( &status_phrase[505], "Version Not Supported"); + pj_strset2( &status_phrase[513], "Message Too Large"); + + pj_strset2( &status_phrase[600], "Busy Everywhere"); + pj_strset2( &status_phrase[603], "Decline"); + pj_strset2( &status_phrase[604], "Does Not Exist Anywhere"); + pj_strset2( &status_phrase[606], "Not Acceptable"); + + pj_strset2( &status_phrase[701], "No response from destination server"); + pj_strset2( &status_phrase[702], "Unable to resolve destination server"); + pj_strset2( &status_phrase[703], "Error sending message to destination server"); + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Method. + */ + +PJ_DEF(void) pjsip_method_init( pjsip_method *m, + pj_pool_t *pool, + const pj_str_t *str) +{ + pj_str_t dup; + pjsip_method_init_np(m, pj_strdup(pool, &dup, str)); +} + +PJ_DEF(void) pjsip_method_set( pjsip_method *m, pjsip_method_e me ) +{ + m->id = me; + m->name = method_names[me]; +} + +PJ_DEF(void) pjsip_method_init_np(pjsip_method *m, + pj_str_t *str) +{ + int i; + for (i=0; i<PJ_ARRAY_SIZE(method_names); ++i) { + if (pj_stricmp(str, &method_names[i])==0) { + m->id = (pjsip_method_e)i; + m->name = method_names[i]; + return; + } + } + m->id = PJSIP_OTHER_METHOD; + m->name = *str; +} + +PJ_DEF(void) pjsip_method_copy( pj_pool_t *pool, + pjsip_method *method, + const pjsip_method *rhs ) +{ + method->id = rhs->id; + if (rhs->id != PJSIP_OTHER_METHOD) { + method->name = rhs->name; + } else { + pj_strdup(pool, &method->name, &rhs->name); + } +} + + +PJ_DEF(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2) +{ + if (m1->id == m2->id) { + if (m1->id != PJSIP_OTHER_METHOD) + return 0; + return pj_stricmp(&m1->name, &m2->name); + } + + return ( m1->id < m2->id ) ? -1 : 1; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Message. + */ + +PJ_DEF(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type) +{ + pjsip_msg *msg = pj_pool_alloc(pool, sizeof(pjsip_msg)); + pj_list_init(&msg->hdr); + msg->type = type; + msg->body = NULL; + return msg; +} + +PJ_DEF(void*) pjsip_msg_find_hdr( pjsip_msg *msg, + pjsip_hdr_e hdr_type, void *start) +{ + pjsip_hdr *hdr=start, *end=&msg->hdr; + + if (hdr == NULL) { + hdr = msg->hdr.next; + } + for (; hdr!=end; hdr = hdr->next) { + if (hdr->type == hdr_type) + return hdr; + } + return NULL; +} + +PJ_DEF(void*) pjsip_msg_find_hdr_by_name( pjsip_msg *msg, + const pj_str_t *name, void *start) +{ + pjsip_hdr *hdr=start, *end=&msg->hdr; + + if (hdr == NULL) { + hdr = msg->hdr.next; + } + for (; hdr!=end; hdr = hdr->next) { + if (hdr->type < PJSIP_H_OTHER) { + if (pj_stricmp(&pjsip_hdr_names[hdr->type], name) == 0) + return hdr; + } else { + if (pj_stricmp(&hdr->name, name) == 0) + return hdr; + } + } + return NULL; +} + +PJ_DEF(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg, + pjsip_hdr_e hdr_type, void *start) +{ + pjsip_hdr *hdr = pjsip_msg_find_hdr(msg, hdr_type, start); + if (hdr) { + pj_list_erase(hdr); + } + return hdr; +} + +PJ_DEF(pj_ssize_t) pjsip_msg_print( pjsip_msg *msg, char *buf, pj_size_t size) +{ + char *p=buf, *end=buf+size; + int len; + pjsip_hdr *hdr; + pj_str_t clen_hdr = { "Content-Length: ", 16}; + + /* Get a wild guess on how many bytes are typically needed. + * We'll check this later in detail, but this serves as a quick check. + */ + if (size < 256) + return -1; + + /* Print request line or status line depending on message type */ + if (msg->type == PJSIP_REQUEST_MSG) { + pjsip_uri *uri; + + /* Add method. */ + len = msg->line.req.method.name.slen; + pj_memcpy(p, msg->line.req.method.name.ptr, len); + p += len; + *p++ = ' '; + + /* Add URI */ + uri = pjsip_uri_get_uri(msg->line.req.uri); + len = pjsip_uri_print( PJSIP_URI_IN_REQ_URI, uri, p, end-p); + if (len < 1) + return -1; + p += len; + + /* Add ' SIP/2.0' */ + if (end-p < 16) + return -1; + pj_memcpy(p, " SIP/2.0\r\n", 10); + p += 10; + + } else { + + /* Add 'SIP/2.0 ' */ + pj_memcpy(p, "SIP/2.0 ", 8); + p += 8; + + /* Add status code. */ + len = pj_utoa(msg->line.status.code, p); + p += len; + *p++ = ' '; + + /* Add reason text. */ + len = msg->line.status.reason.slen; + pj_memcpy(p, msg->line.status.reason.ptr, len ); + p += len; + + /* Add newline. */ + *p++ = '\r'; + *p++ = '\n'; + } + + /* Print each of the headers. */ + for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) { + len = (*hdr->vptr->print_on)(hdr, p, end-p); + if (len < 1) + return -1; + p += len; + + if (p+3 >= end) + return -1; + + *p++ = '\r'; + *p++ = '\n'; + } + + /* Process message body. */ + if (msg->body) { + pj_str_t ctype_hdr = { "Content-Type: ", 14}; + int len; + const pjsip_media_type *media = &msg->body->content_type; + char *clen_pos; + + /* Add Content-Type header. */ + if ( (end-p) < 24+media->type.slen+media->subtype.slen+media->param.slen) { + return -1; + } + pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen); + p += ctype_hdr.slen; + p += print_media_type(p, media); + *p++ = '\r'; + *p++ = '\n'; + + /* Add Content-Length header. */ + if ((end-p) < clen_hdr.slen+12+2) { + return -1; + } + pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen); + p += clen_hdr.slen; + + /* Print blanks after "Content-Type:", this is where we'll put + * the content length value after we know the length of the + * body. + */ + pj_memset(p, ' ', 12); + clen_pos = p; + p += 12; + *p++ = '\r'; + *p++ = '\n'; + + /* Add blank newline. */ + *p++ = '\r'; + *p++ = '\n'; + + /* Print the message body itself. */ + len = (*msg->body->print_body)(msg->body, p, end-p); + if (len < 0) { + return -1; + } + p += len; + + /* Now that we have the length of the body, print this to the + * Content-Length header. + */ + len = pj_utoa(len, clen_pos); + clen_pos[len] = ' '; + + } else { + /* There's no message body. + * Add Content-Length with zero value. + */ + if ((end-p) < clen_hdr.slen+8) { + return -1; + } + pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen); + p += clen_hdr.slen; + *p++ = '0'; + *p++ = '\r'; + *p++ = '\n'; + *p++ = '\r'; + *p++ = '\n'; + } + + *p = '\0'; + return p-buf; +} + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr_ptr ) +{ + const pjsip_hdr *hdr = hdr_ptr; + return (*hdr->vptr->clone)(pool, hdr_ptr); +} + + +PJ_DEF(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr_ptr ) +{ + const pjsip_hdr *hdr = hdr_ptr; + return (*hdr->vptr->shallow_clone)(pool, hdr_ptr); +} + +PJ_DEF(int) pjsip_hdr_print_on( void *hdr_ptr, char *buf, pj_size_t len) +{ + pjsip_hdr *hdr = hdr_ptr; + return (*hdr->vptr->print_on)(hdr_ptr, buf, len); +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Status/Reason Phrase + */ + +PJ_DEF(const pj_str_t*) pjsip_get_status_text(int code) +{ + static int is_initialized; + if (is_initialized == 0) { + is_initialized = 1; + init_status_phrase(); + } + + return (code>=100 && code<(sizeof(status_phrase)/sizeof(status_phrase[0]))) ? + &status_phrase[code] : &status_phrase[0]; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Generic pjsip_hdr_names/hvalue header. + */ + +static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr, + char *buf, pj_size_t size); +static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool, + const pjsip_generic_string_hdr *hdr); +static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_generic_string_hdr *hdr ); + +static pjsip_hdr_vptr generic_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_generic_string_hdr_print, +}; + +PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create( pj_pool_t *pool, + const pj_str_t *hnames ) +{ + pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_string_hdr)); + init_hdr(hdr, PJSIP_H_OTHER, &generic_hdr_vptr); + if (hnames) { + pj_strdup(pool, &hdr->name, hnames); + hdr->sname = hdr->name; + } + hdr->hvalue.ptr = NULL; + hdr->hvalue.slen = 0; + return hdr; +} + +PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create_with_text( pj_pool_t *pool, + const pj_str_t *hname, + const pj_str_t *hvalue) +{ + pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, hname); + pj_strdup(pool, &hdr->hvalue, hvalue); + return hdr; +} + +static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr, + char *buf, pj_size_t size) +{ + char *p = buf; + + if ((pj_ssize_t)size < hdr->name.slen + hdr->hvalue.slen + 5) + return -1; + + pj_memcpy(p, hdr->name.ptr, hdr->name.slen); + p += hdr->name.slen; + *p++ = ':'; + *p++ = ' '; + pj_memcpy(p, hdr->hvalue.ptr, hdr->hvalue.slen); + p += hdr->hvalue.slen; + *p = '\0'; + + return p - buf; +} + +static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool, + const pjsip_generic_string_hdr *rhs) +{ + pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, &rhs->name); + + hdr->type = rhs->type; + hdr->sname = hdr->name; + pj_strdup( pool, &hdr->hvalue, &rhs->hvalue); + return hdr; +} + +static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_generic_string_hdr *rhs ) +{ + pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Generic pjsip_hdr_names/integer value header. + */ + +static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr, + char *buf, pj_size_t size); +static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool, + const pjsip_generic_int_hdr *hdr); +static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_generic_int_hdr *hdr ); + +static pjsip_hdr_vptr generic_int_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_generic_int_hdr_print, +}; + +PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool, + const pj_str_t *hnames ) +{ + pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_int_hdr)); + init_hdr(hdr, PJSIP_H_OTHER, &generic_int_hdr_vptr); + if (hnames) { + pj_strdup(pool, &hdr->name, hnames); + hdr->sname = hdr->name; + } + hdr->ivalue = 0; + return hdr; +} + +PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create_with_value( pj_pool_t *pool, + const pj_str_t *hname, + int value) +{ + pjsip_generic_int_hdr *hdr = pjsip_generic_int_hdr_create(pool, hname); + hdr->ivalue = value; + return hdr; +} + +static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr, + char *buf, pj_size_t size) +{ + char *p = buf; + + if ((pj_ssize_t)size < hdr->name.slen + 15) + return -1; + + pj_memcpy(p, hdr->name.ptr, hdr->name.slen); + p += hdr->name.slen; + *p++ = ':'; + *p++ = ' '; + + p += pj_utoa(hdr->ivalue, p); + + return p - buf; +} + +static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool, + const pjsip_generic_int_hdr *rhs) +{ + pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + +static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_generic_int_hdr *rhs ) +{ + pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Generic array header. + */ +static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr, char *buf, pj_size_t size); +static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool, + const pjsip_generic_array_hdr *hdr); +static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_generic_array_hdr *hdr); + +static pjsip_hdr_vptr generic_array_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_generic_array_hdr_print, +}; + +PJ_DEF(pjsip_generic_array_hdr*) pjsip_generic_array_create( pj_pool_t *pool, + const pj_str_t *hnames) +{ + pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_array_hdr)); + init_hdr(hdr, PJSIP_H_OTHER, &generic_array_hdr_vptr); + if (hnames) { + pj_strdup(pool, &hdr->name, hnames); + hdr->sname = hdr->name; + } + hdr->count = 0; + return hdr; + +} + +static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr, + char *buf, pj_size_t size) +{ + char *p = buf, *endbuf = buf+size; + + copy_advance(p, hdr->name); + *p++ = ':'; + *p++ = ' '; + + if (hdr->count > 0) { + unsigned i; + int printed; + copy_advance(p, hdr->values[0]); + for (i=1; i<hdr->count; ++i) { + copy_advance_pair(p, ", ", 2, hdr->values[i]); + } + } + + return p - buf; +} + +static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool, + const pjsip_generic_array_hdr *rhs) +{ + unsigned i; + pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + + pj_memcpy(hdr, rhs, sizeof(*hdr)); + for (i=0; i<rhs->count; ++i) { + pj_strdup(pool, &hdr->values[i], &rhs->values[i]); + } + + return hdr; +} + + +static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_generic_array_hdr *rhs) +{ + pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Accept header. + */ +PJ_DEF(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool) +{ + pjsip_accept_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_ACCEPT, &generic_array_hdr_vptr); + hdr->count = 0; + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Allow header. + */ + +PJ_DEF(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool) +{ + pjsip_allow_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_ALLOW, &generic_array_hdr_vptr); + hdr->count = 0; + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Call-ID header. + */ + +PJ_DEF(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool ) +{ + pjsip_cid_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_CALL_ID, &generic_hdr_vptr); + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Content-Length header. + */ +static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr, char *buf, pj_size_t size); +static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *hdr); +#define pjsip_clen_hdr_shallow_clone pjsip_clen_hdr_clone + +static pjsip_hdr_vptr clen_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_clen_hdr_print, +}; + +PJ_DEF(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool ) +{ + pjsip_clen_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_clen_hdr)); + init_hdr(hdr, PJSIP_H_CONTENT_LENGTH, &clen_hdr_vptr); + hdr->len = 0; + return hdr; +} + +static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr, + char *buf, pj_size_t size) +{ + char *p = buf; + int len; + + if ((pj_ssize_t)size < hdr->name.slen + 14) + return -1; + + pj_memcpy(p, hdr->name.ptr, hdr->name.slen); + p += hdr->name.slen; + *p++ = ':'; + *p++ = ' '; + + len = pj_utoa(hdr->len, p); + p += len; + *p = '\0'; + + return p-buf; +} + +static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *rhs) +{ + pjsip_clen_hdr *hdr = pjsip_clen_hdr_create(pool); + hdr->len = rhs->len; + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * CSeq header. + */ +static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size); +static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr); +static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr ); + +static pjsip_hdr_vptr cseq_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_cseq_hdr_print, +}; + +PJ_DEF(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool ) +{ + pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_cseq_hdr)); + init_hdr(hdr, PJSIP_H_CSEQ, &cseq_hdr_vptr); + hdr->cseq = 0; + hdr->method.id = PJSIP_OTHER_METHOD; + hdr->method.name.ptr = NULL; + hdr->method.name.slen = 0; + return hdr; +} + +static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size) +{ + char *p = buf; + int len; + + if ((pj_ssize_t)size < hdr->name.slen + hdr->method.name.slen + 15) + return -1; + + pj_memcpy(p, hdr->name.ptr, hdr->name.slen); + p += hdr->name.slen; + *p++ = ':'; + *p++ = ' '; + + len = pj_utoa(hdr->cseq, p); + p += len; + *p++ = ' '; + + pj_memcpy(p, hdr->method.name.ptr, hdr->method.name.slen); + p += hdr->method.name.slen; + + *p = '\0'; + + return p-buf; +} + +static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool, + const pjsip_cseq_hdr *rhs) +{ + pjsip_cseq_hdr *hdr = pjsip_cseq_hdr_create(pool); + hdr->cseq = rhs->cseq; + pjsip_method_copy(pool, &hdr->method, &rhs->method); + return hdr; +} + +static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_cseq_hdr *rhs ) +{ + pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Contact header. + */ +static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf, pj_size_t size); +static pjsip_contact_hdr* pjsip_contact_hdr_clone( pj_pool_t *pool, const pjsip_contact_hdr *hdr); +static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool, const pjsip_contact_hdr *); + +static pjsip_hdr_vptr contact_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_contact_hdr_print, +}; + +PJ_DEF(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool ) +{ + pjsip_contact_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_CONTACT, &contact_hdr_vptr); + hdr->expires = -1; + pj_list_init(&hdr->other_param); + return hdr; +} + +static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf, + pj_size_t size) +{ + if (hdr->star) { + char *p = buf; + if ((pj_ssize_t)size < hdr->name.slen + 6) + return -1; + pj_memcpy(p, hdr->name.ptr, hdr->name.slen); + p += hdr->name.slen; + *p++ = ':'; + *p++ = ' '; + *p++ = '*'; + return p - buf; + + } else { + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + + copy_advance(buf, hdr->name); + *buf++ = ':'; + *buf++ = ' '; + + printed = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, hdr->uri, + buf, endbuf-buf); + if (printed < 1) + return -1; + + buf += printed; + + if (hdr->q1000) { + if (buf+19 >= endbuf) + return -1; + + /* + printed = sprintf(buf, ";q=%u.%03u", + hdr->q1000/1000, hdr->q1000 % 1000); + */ + pj_memcpy(buf, ";q=", 3); + printed = pj_utoa(hdr->q1000/1000, buf+3); + buf += printed + 3; + *buf++ = '.'; + printed = pj_utoa(hdr->q1000 % 1000, buf); + buf += printed; + } + + if (hdr->expires >= 0) { + if (buf+23 >= endbuf) + return -1; + + pj_memcpy(buf, ";expires=", 9); + printed = pj_utoa(hdr->expires, buf+9); + buf += printed + 9; + } + + printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf,';'); + if (printed < 0) + return printed; + buf += printed; + + return buf-startbuf; + } +} + +static pjsip_contact_hdr* pjsip_contact_hdr_clone(pj_pool_t *pool, + const pjsip_contact_hdr *rhs) +{ + pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(pool); + + hdr->star = rhs->star; + if (hdr->star) + return hdr; + + hdr->uri = pjsip_uri_clone(pool, rhs->uri); + hdr->q1000 = rhs->q1000; + hdr->expires = rhs->expires; + pjsip_param_clone(pool, &hdr->other_param, &rhs->other_param); + return hdr; +} + +static pjsip_contact_hdr* +pjsip_contact_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_contact_hdr *rhs) +{ + pjsip_contact_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + pjsip_param_shallow_clone(pool, &hdr->other_param, &rhs->other_param); + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Content-Type header.. + */ +static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr, char *buf, + pj_size_t size); +static pjsip_ctype_hdr* pjsip_ctype_hdr_clone(pj_pool_t *pool, + const pjsip_ctype_hdr *hdr); +#define pjsip_ctype_hdr_shallow_clone pjsip_ctype_hdr_clone + +static pjsip_hdr_vptr ctype_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_ctype_hdr_print, +}; + +PJ_DEF(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool ) +{ + pjsip_ctype_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_CONTENT_TYPE, &ctype_hdr_vptr); + return hdr; +} + +static int print_media_type(char *buf, const pjsip_media_type *media) +{ + char *p = buf; + + pj_memcpy(p, media->type.ptr, media->type.slen); + p += media->type.slen; + *p++ = '/'; + pj_memcpy(p, media->subtype.ptr, media->subtype.slen); + p += media->subtype.slen; + + if (media->param.slen) { + pj_memcpy(p, media->param.ptr, media->param.slen); + p += media->param.slen; + } + + return p-buf; +} + +static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr, + char *buf, pj_size_t size) +{ + char *p = buf; + int len; + + if ((pj_ssize_t)size < hdr->name.slen + + hdr->media.type.slen + hdr->media.subtype.slen + + hdr->media.param.slen + 8) + { + return -1; + } + + pj_memcpy(p, hdr->name.ptr, hdr->name.slen); + p += hdr->name.slen; + *p++ = ':'; + *p++ = ' '; + + len = print_media_type(p, &hdr->media); + p += len; + + *p = '\0'; + return p-buf; +} + +static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool, + const pjsip_ctype_hdr *rhs) +{ + pjsip_ctype_hdr *hdr = pjsip_ctype_hdr_create(pool); + pj_strdup(pool, &hdr->media.type, &rhs->media.type); + pj_strdup(pool, &hdr->media.param, &rhs->media.param); + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Expires header. + */ +PJ_DEF(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool ) +{ + pjsip_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_EXPIRES, &generic_int_hdr_vptr); + hdr->ivalue = 0; + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * To or From header. + */ +static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr, + char *buf, pj_size_t size); +static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool, + const pjsip_fromto_hdr *hdr); +static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_fromto_hdr *hdr); + + +static pjsip_hdr_vptr fromto_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_fromto_hdr_print, +}; + +PJ_DEF(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool ) +{ + pjsip_from_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_FROM, &fromto_hdr_vptr); + pj_list_init(&hdr->other_param); + return hdr; +} + +PJ_DEF(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool ) +{ + pjsip_to_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_TO, &fromto_hdr_vptr); + pj_list_init(&hdr->other_param); + return hdr; +} + +PJ_DEF(pjsip_from_hdr*) pjsip_fromto_set_from( pjsip_fromto_hdr *hdr ) +{ + hdr->type = PJSIP_H_FROM; + hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_FROM]; + return hdr; +} + +PJ_DEF(pjsip_to_hdr*) pjsip_fromto_set_to( pjsip_fromto_hdr *hdr ) +{ + hdr->type = PJSIP_H_TO; + hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_TO]; + return hdr; +} + +static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr, + char *buf, pj_size_t size) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + + copy_advance(buf, hdr->name); + *buf++ = ':'; + *buf++ = ' '; + + printed = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, hdr->uri, + buf, endbuf-buf); + if (printed < 1) + return -1; + + buf += printed; + + copy_advance_pair(buf, ";tag=", 5, hdr->tag); + + printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf, ';'); + if (printed < 0) + return -1; + buf += printed; + + return buf-startbuf; +} + +static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool, + const pjsip_fromto_hdr *rhs) +{ + pjsip_fromto_hdr *hdr = pjsip_from_hdr_create(pool); + + hdr->type = rhs->type; + hdr->name = rhs->name; + hdr->sname = rhs->sname; + hdr->uri = pjsip_uri_clone(pool, rhs->uri); + pj_strdup( pool, &hdr->tag, &rhs->tag); + pjsip_param_clone( pool, &hdr->other_param, &rhs->other_param); + + return hdr; +} + +static pjsip_fromto_hdr* +pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_fromto_hdr *rhs) +{ + pjsip_fromto_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + pjsip_param_shallow_clone( pool, &hdr->other_param, &rhs->other_param); + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Max-Forwards header. + */ +PJ_DEF(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool) +{ + pjsip_max_forwards_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_MAX_FORWARDS, &generic_int_hdr_vptr); + hdr->ivalue = 0; + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Min-Expires header. + */ +PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool) +{ + pjsip_min_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_MIN_EXPIRES, &generic_int_hdr_vptr); + hdr->ivalue = 0; + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Record-Route and Route header. + */ +static int pjsip_routing_hdr_print( pjsip_routing_hdr *r, char *buf, pj_size_t size ); +static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool, const pjsip_routing_hdr *r ); +static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool, const pjsip_routing_hdr *r ); + +static pjsip_hdr_vptr routing_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_routing_hdr_print, +}; + +PJ_DEF(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool ) +{ + pjsip_rr_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_RECORD_ROUTE, &routing_hdr_vptr); + pjsip_name_addr_init(&hdr->name_addr); + pj_list_init(&hdr->other_param); + return hdr; +} + +PJ_DEF(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool ) +{ + pjsip_route_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_ROUTE, &routing_hdr_vptr); + pjsip_name_addr_init(&hdr->name_addr); + pj_list_init(&hdr->other_param); + return hdr; +} + +PJ_DEF(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *hdr ) +{ + hdr->type = PJSIP_H_RECORD_ROUTE; + hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_RECORD_ROUTE]; + return hdr; +} + +PJ_DEF(pjsip_route_hdr*) pjsip_routing_hdr_set_route( pjsip_routing_hdr *hdr ) +{ + hdr->type = PJSIP_H_ROUTE; + hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_ROUTE]; + return hdr; +} + +static int pjsip_routing_hdr_print( pjsip_routing_hdr *hdr, + char *buf, pj_size_t size ) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + + copy_advance(buf, hdr->name); + *buf++ = ':'; + *buf++ = ' '; + + printed = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, &hdr->name_addr, buf, + endbuf-buf); + if (printed < 1) + return -1; + buf += printed; + + printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf, ';'); + if (printed < 0) + return -1; + buf += printed; + + return buf-startbuf; +} + +static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool, + const pjsip_routing_hdr *rhs ) +{ + pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + + init_hdr(hdr, rhs->type, rhs->vptr); + pjsip_name_addr_init(&hdr->name_addr); + pjsip_name_addr_assign(pool, &hdr->name_addr, &rhs->name_addr); + pjsip_param_clone( pool, &hdr->other_param, &rhs->other_param); + return hdr; +} + +static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_routing_hdr *rhs ) +{ + pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + pjsip_param_shallow_clone( pool, &hdr->other_param, &rhs->other_param); + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Require header. + */ +PJ_DEF(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool) +{ + pjsip_require_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_REQUIRE, &generic_array_hdr_vptr); + hdr->count = 0; + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Retry-After header. + */ +PJ_DEF(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool) +{ + pjsip_retry_after_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_RETRY_AFTER, &generic_int_hdr_vptr); + hdr->ivalue = 0; + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Supported header. + */ +PJ_DEF(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool) +{ + pjsip_supported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_SUPPORTED, &generic_array_hdr_vptr); + hdr->count = 0; + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Unsupported header. + */ +PJ_DEF(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool) +{ + pjsip_unsupported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_UNSUPPORTED, &generic_array_hdr_vptr); + hdr->count = 0; + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Via header. + */ +static int pjsip_via_hdr_print( pjsip_via_hdr *hdr, char *buf, pj_size_t size); +static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr); +static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr ); + +static pjsip_hdr_vptr via_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_via_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_via_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_via_hdr_print, +}; + +PJ_DEF(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool ) +{ + pjsip_via_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_VIA, &via_hdr_vptr); + hdr->sent_by.port = 5060; + hdr->ttl_param = -1; + hdr->rport_param = -1; + pj_list_init(&hdr->other_param); + return hdr; +} + +static int pjsip_via_hdr_print( pjsip_via_hdr *hdr, + char *buf, pj_size_t size) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + pj_str_t sip_ver = { "SIP/2.0/", 8 }; + + if ((pj_ssize_t)size < hdr->name.slen + sip_ver.slen + + hdr->transport.slen + hdr->sent_by.host.slen + 12) + { + return -1; + } + + /* pjsip_hdr_names */ + copy_advance(buf, hdr->name); + *buf++ = ':'; + *buf++ = ' '; + + /* SIP/2.0/transport host:port */ + pj_memcpy(buf, sip_ver.ptr, sip_ver.slen); + buf += sip_ver.slen; + pj_memcpy(buf, hdr->transport.ptr, hdr->transport.slen); + buf += hdr->transport.slen; + *buf++ = ' '; + pj_memcpy(buf, hdr->sent_by.host.ptr, hdr->sent_by.host.slen); + buf += hdr->sent_by.host.slen; + *buf++ = ':'; + printed = pj_utoa(hdr->sent_by.port, buf); + buf += printed; + + if (hdr->ttl_param >= 0) { + size = endbuf-buf; + if (size < 14) + return -1; + pj_memcpy(buf, ";ttl=", 5); + printed = pj_utoa(hdr->ttl_param, buf+5); + buf += printed + 5; + } + + if (hdr->rport_param >= 0) { + size = endbuf-buf; + if (size < 14) + return -1; + pj_memcpy(buf, ";rport", 6); + buf += 6; + if (hdr->rport_param > 0) { + *buf++ = '='; + buf += pj_utoa(hdr->rport_param, buf); + } + } + + + copy_advance_pair(buf, ";maddr=", 7, hdr->maddr_param); + copy_advance_pair(buf, ";received=", 10, hdr->recvd_param); + copy_advance_pair(buf, ";branch=", 8, hdr->branch_param); + + printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf, ';'); + if (printed < 0) + return -1; + buf += printed; + + return buf-startbuf; +} + +static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool, + const pjsip_via_hdr *rhs) +{ + pjsip_via_hdr *hdr = pjsip_via_hdr_create(pool); + pj_strdup(pool, &hdr->transport, &rhs->transport); + pj_strdup(pool, &hdr->sent_by.host, &rhs->sent_by.host); + hdr->sent_by.port = rhs->sent_by.port; + hdr->ttl_param = rhs->ttl_param; + hdr->rport_param = rhs->rport_param; + pj_strdup(pool, &hdr->maddr_param, &rhs->maddr_param); + pj_strdup(pool, &hdr->recvd_param, &rhs->recvd_param); + pj_strdup(pool, &hdr->branch_param, &rhs->branch_param); + pjsip_param_clone(pool, &hdr->other_param, &rhs->other_param); + return hdr; +} + +static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_via_hdr *rhs ) +{ + pjsip_via_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + pjsip_param_shallow_clone(pool, &hdr->other_param, &rhs->other_param); + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * General purpose function to textual data in a SIP body. + */ +PJ_DEF(int) pjsip_print_text_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size) +{ + if (size < msg_body->len) + return -1; + pj_memcpy(buf, msg_body->data, msg_body->len); + return msg_body->len; +} diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c index 7b869492..4af7a630 100644 --- a/pjsip/src/pjsip/sip_parser.c +++ b/pjsip/src/pjsip/sip_parser.c @@ -1,1774 +1,1790 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_parser.h>
-#include <pjsip/sip_uri.h>
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_auth_parser.h>
-#include <pjsip/sip_errno.h>
-#include <pjsip/sip_transport.h> /* rdata structure */
-#include <pjlib-util/scanner.h>
-#include <pjlib-util/string.h>
-#include <pj/except.h>
-#include <pj/log.h>
-#include <pj/hash.h>
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/ctype.h>
-#include <pj/assert.h>
-
-#define ALNUM
-#define RESERVED ";/?:@&=+$,"
-#define MARK "-_.!~*'()"
-#define UNRESERVED ALNUM MARK
-#define ESCAPED "%"
-#define USER_UNRESERVED "&=+$,;?/"
-#define PASS "&=+$,"
-#define TOKEN "-.!%*_=`'~+" /* '+' is because of app/pidf+xml
- * in Content-Type! */
-#define HOST "_-."
-#define HEX_DIGIT "abcdefABCDEF"
-#define PARAM_CHAR "[]/:&+$" UNRESERVED ESCAPED
-#define HNV_UNRESERVED "[]/?:+$"
-#define HDR_CHAR HNV_UNRESERVED UNRESERVED ESCAPED
-
-#define PJSIP_VERSION "SIP/2.0"
-#define PJSIP_SYN_ERR_EXCEPTION 1
-
-#define UNREACHED(expr)
-
-typedef struct handler_rec
-{
- char hname[PJSIP_MAX_HNAME_LEN+1];
- pj_size_t hname_len;
- pj_uint32_t hname_hash;
- pjsip_parse_hdr_func *handler;
-} handler_rec;
-
-static handler_rec handler[127];
-static unsigned handler_count;
-static int parser_is_initialized;
-
-
-/*
- * Global vars (also extern).
- */
-const pj_str_t pjsip_USER_STR = { "user", 4};
-const pj_str_t pjsip_METHOD_STR = { "method", 6};
-const pj_str_t pjsip_TRANSPORT_STR = { "transport", 9};
-const pj_str_t pjsip_MADDR_STR = { "maddr", 5 };
-const pj_str_t pjsip_LR_STR = { "lr", 2 };
-const pj_str_t pjsip_SIP_STR = { "sip", 3 };
-const pj_str_t pjsip_SIPS_STR = { "sips", 4 };
-const pj_str_t pjsip_TEL_STR = { "tel", 3 };
-const pj_str_t pjsip_BRANCH_STR = { "branch", 6 };
-const pj_str_t pjsip_TTL_STR = { "ttl", 3 };
-const pj_str_t pjsip_PNAME_STR = { "received", 8 };
-const pj_str_t pjsip_Q_STR = { "q", 1 };
-const pj_str_t pjsip_EXPIRES_STR = { "expires", 7 };
-const pj_str_t pjsip_TAG_STR = { "tag", 3 };
-const pj_str_t pjsip_RPORT_STR = { "rport", 5};
-
-/* Character Input Specification buffer. */
-static pj_cis_buf_t cis_buf;
-
-/* Character Input Specifications. */
-pj_cis_t pjsip_HOST_SPEC, /* For scanning host part. */
- pjsip_DIGIT_SPEC, /* Decimal digits */
- pjsip_ALPHA_SPEC, /* Alpha (A-Z, a-z) */
- pjsip_ALNUM_SPEC, /* Decimal + Alpha. */
- pjsip_TOKEN_SPEC, /* Token. */
- pjsip_HEX_SPEC, /* Hexadecimal digits. */
- pjsip_PARAM_CHAR_SPEC, /* For scanning pname (or pvalue when
- * it's not quoted.) */
- pjsip_HDR_CHAR_SPEC, /* Chars in hname or hvalue */
- pjsip_PROBE_USER_HOST_SPEC, /* Hostname characters. */
- pjsip_PASSWD_SPEC, /* Password. */
- pjsip_USER_SPEC, /* User */
- pjsip_ARRAY_ELEMENTS, /* Array separator. */
- pjsip_NEWLINE_OR_EOF_SPEC, /* For eating up header.*/
- pjsip_DISPLAY_SCAN_SPEC; /* Used when searching for display name
- * in URL. */
-
-
-/*
- * Forward decl.
- */
-static pjsip_msg * int_parse_msg( pjsip_parse_ctx *ctx,
- pjsip_parser_err_report *err_list);
-static void int_parse_param( pj_scanner *scanner,
- pj_str_t *pname,
- pj_str_t *pvalue);
-static void int_parse_hparam( pj_scanner *scanner,
- pj_str_t *hname,
- pj_str_t *hvalue );
-static void int_parse_req_line( pj_scanner *scanner,
- pj_pool_t *pool,
- pjsip_request_line *req_line);
-static int int_is_next_user( pj_scanner *scanner);
-static void int_parse_status_line( pj_scanner *scanner,
- pjsip_status_line *line);
-static void int_parse_user_pass( pj_scanner *scanner,
- pj_str_t *user,
- pj_str_t *pass);
-static void int_parse_uri_host_port( pj_scanner *scanner,
- pj_str_t *p_host,
- int *p_port);
-static pjsip_uri * int_parse_uri_or_name_addr( pj_scanner *scanner,
- pj_pool_t *pool,
- unsigned option);
-static pjsip_url * int_parse_sip_url( pj_scanner *scanner,
- pj_pool_t *pool,
- pj_bool_t parse_params);
-static pjsip_name_addr *
- int_parse_name_addr( pj_scanner *scanner,
- pj_pool_t *pool );
-static void parse_hdr_end( pj_scanner *scanner );
-
-static pjsip_hdr* parse_hdr_accept( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_allow( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_call_id( pjsip_parse_ctx *ctx);
-static pjsip_hdr* parse_hdr_contact( pjsip_parse_ctx *ctx);
-static pjsip_hdr* parse_hdr_content_len( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_content_type( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_expires( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_from( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_max_forwards( pjsip_parse_ctx *ctx);
-static pjsip_hdr* parse_hdr_min_expires( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_rr( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_route( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_require( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_retry_after( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_supported( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_to( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_unsupported( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_generic_string( pjsip_parse_ctx *ctx);
-
-/* Convert non NULL terminated string to integer. */
-static unsigned long pj_strtoul_mindigit(const pj_str_t *str,
- unsigned mindig)
-{
- unsigned long value;
- unsigned i;
-
- value = 0;
- for (i=0; i<(unsigned)str->slen; ++i) {
- value = value * 10 + (str->ptr[i] - '0');
- }
- for (; i<mindig; ++i) {
- value = value * 10;
- }
- return value;
-}
-
-/* Case insensitive comparison */
-#define parser_stricmp(str1, str2) pj_stricmp(&str1, &str2)
-
-
-/* Syntax error handler for parser. */
-static void on_syntax_error(pj_scanner *scanner)
-{
- PJ_UNUSED_ARG(scanner);
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
-}
-
-/* Concatenate unrecognized params into single string. */
-void pjsip_concat_param_imp( pj_str_t *param, pj_pool_t *pool,
- const pj_str_t *pname, const pj_str_t *pvalue,
- int sepchar)
-{
- char *new_param, *p;
- int len;
-
- len = param->slen + pname->slen + pvalue->slen + 3;
- p = new_param = pj_pool_alloc(pool, len);
-
- if (param->slen) {
- int old_len = param->slen;
- pj_memcpy(p, param->ptr, old_len);
- p += old_len;
- }
- *p++ = (char)sepchar;
- pj_memcpy(p, pname->ptr, pname->slen);
- p += pname->slen;
-
- if (pvalue->slen) {
- *p++ = '=';
- pj_memcpy(p, pvalue->ptr, pvalue->slen);
- p += pvalue->slen;
- }
-
- *p = '\0';
-
- param->ptr = new_param;
- param->slen = p - new_param;
-}
-
-/* Concatenate unrecognized params into single string. */
-static void concat_param( pj_str_t *param, pj_pool_t *pool,
- const pj_str_t *pname, const pj_str_t *pvalue )
-{
- pjsip_concat_param_imp(param, pool, pname, pvalue, ';');
-}
-
-/* Initialize static properties of the parser. */
-static pj_status_t init_parser()
-{
- static int initialized;
- pj_status_t status;
-
- if (initialized)
- return PJ_SUCCESS;
-
- initialized = 1;
-
- pj_cis_buf_init(&cis_buf);
-
- status = pj_cis_init(&cis_buf, &pjsip_DIGIT_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_num(&pjsip_DIGIT_SPEC);
-
- status = pj_cis_init(&cis_buf, &pjsip_ALPHA_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_alpha( &pjsip_ALPHA_SPEC );
-
- status = pj_cis_init(&cis_buf, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_alpha( &pjsip_ALNUM_SPEC );
- pj_cis_add_num( &pjsip_ALNUM_SPEC );
-
- status = pj_cis_init(&cis_buf, &pjsip_NEWLINE_OR_EOF_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str(&pjsip_NEWLINE_OR_EOF_SPEC, "\r\n");
- //pj_cs_set(pjsip_NEWLINE_OR_EOF_SPEC, 0);
-
- status = pj_cis_init(&cis_buf, &pjsip_ARRAY_ELEMENTS);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str( &pjsip_ARRAY_ELEMENTS, ",\r\n");
-
- status = pj_cis_dup(&pjsip_TOKEN_SPEC, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str( &pjsip_TOKEN_SPEC, TOKEN);
-
- status = pj_cis_dup(&pjsip_HOST_SPEC, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str( &pjsip_HOST_SPEC, HOST);
-
- status = pj_cis_dup(&pjsip_HEX_SPEC, &pjsip_DIGIT_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str( &pjsip_HEX_SPEC, HEX_DIGIT);
-
- status = pj_cis_dup(&pjsip_PARAM_CHAR_SPEC, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str(&pjsip_PARAM_CHAR_SPEC, PARAM_CHAR);
-
- status = pj_cis_dup(&pjsip_HDR_CHAR_SPEC, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str(&pjsip_HDR_CHAR_SPEC, HDR_CHAR);
-
- status = pj_cis_dup(&pjsip_USER_SPEC, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str( &pjsip_USER_SPEC, ESCAPED USER_UNRESERVED );
-
- status = pj_cis_dup(&pjsip_PASSWD_SPEC, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str( &pjsip_PASSWD_SPEC, MARK ESCAPED PASS);
-
- status = pj_cis_init(&cis_buf, &pjsip_PROBE_USER_HOST_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str( &pjsip_PROBE_USER_HOST_SPEC, "@ \n>");
- pj_cis_invert( &pjsip_PROBE_USER_HOST_SPEC );
-
- status = pj_cis_init(&cis_buf, &pjsip_DISPLAY_SCAN_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str( &pjsip_DISPLAY_SCAN_SPEC, ":\r\n<");
-
- status = pjsip_register_hdr_parser( "Accept", NULL, &parse_hdr_accept);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Allow", NULL, &parse_hdr_allow);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Call-ID", NULL, &parse_hdr_call_id);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Contact", "m", &parse_hdr_contact);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Content-Length", NULL,
- &parse_hdr_content_len);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Content-Type", NULL,
- &parse_hdr_content_type);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "CSeq", NULL, &parse_hdr_cseq);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Expires", NULL, &parse_hdr_expires);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "From", "f", &parse_hdr_from);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Max-Forwards", NULL,
- &parse_hdr_max_forwards);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Min-Expires", NULL,
- &parse_hdr_min_expires);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Record-Route", NULL, &parse_hdr_rr);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Route", NULL, &parse_hdr_route);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Require", NULL, &parse_hdr_require);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Retry-After", NULL,
- &parse_hdr_retry_after);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Supported", "k",
- &parse_hdr_supported);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "To", "t", &parse_hdr_to);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Unsupported", NULL,
- &parse_hdr_unsupported);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Via", NULL, &parse_hdr_via);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- /* Register auth parser. */
- status = pjsip_auth_init_parser();
-
- return status;
-}
-
-static void init_sip_parser(void)
-{
- if (!parser_is_initialized) {
- /* Prevent race cond. */
- pj_enter_critical_section();
- if (!parser_is_initialized) {
- init_parser();
- parser_is_initialized = 1;
- }
- pj_leave_critical_section();
- }
-}
-
-/* Compare the handler record with header name, and return:
- * - 0 if handler match.
- * - <0 if handler is 'less' than the header name.
- * - >0 if handler is 'greater' than header name.
- */
-static int compare_handler( const handler_rec *r1,
- const char *name,
- pj_size_t name_len,
- pj_uint32_t hash )
-{
- /* Compare length. */
- if (r1->hname_len < name_len)
- return -1;
- if (r1->hname_len > name_len)
- return 1;
-
- /* Length is equal, compare hashed value. */
- if (r1->hname_hash < hash)
- return -1;
- if (r1->hname_hash > hash)
- return 1;
-
- /* Equal length and equal hash. compare the strings. */
- return pj_native_strcmp(r1->hname, name);
-}
-
-/* Register one handler for one header name. */
-static pj_status_t int_register_parser( const char *name,
- pjsip_parse_hdr_func *fptr )
-{
- unsigned pos;
- handler_rec rec;
- unsigned i;
-
- if (handler_count >= PJ_ARRAY_SIZE(handler)) {
- return PJ_ETOOMANY;
- }
-
- /* Initialize temporary handler. */
- rec.handler = fptr;
- rec.hname_len = strlen(name);
- if (rec.hname_len >= sizeof(rec.hname)) {
- return PJ_ENAMETOOLONG;
- }
- /* Name is copied in lowercase. */
- for (i=0; i<rec.hname_len; ++i) {
- rec.hname[i] = (char)pj_tolower(name[i]);
- }
- rec.hname[i] = '\0';
- /* Hash value is calculated from the lowercase name. */
- rec.hname_hash = pj_hash_calc(0, rec.hname, PJ_HASH_KEY_STRING);
-
- /* Get the pos to insert the new handler. */
- for (pos=0; pos < handler_count; ++pos) {
- int d;
- d = compare_handler(&handler[pos], rec.hname, rec.hname_len,
- rec.hname_hash);
- if (d == 0) {
- pj_assert(0);
- return PJ_EEXISTS;
- }
- if (d > 0) {
- break;
- }
- }
-
- /* Shift handlers. */
- if (pos != handler_count) {
- pj_memmove( &handler[pos+1], &handler[pos],
- (handler_count-pos)*sizeof(handler_rec));
- }
- /* Add new handler. */
- pj_memcpy( &handler[pos], &rec, sizeof(handler_rec));
- ++handler_count;
-
- return PJ_SUCCESS;
-}
-
-/* Register parser handler. If both header name and short name are valid,
- * then two instances of handler will be registered.
- */
-PJ_DEF(pj_status_t) pjsip_register_hdr_parser( const char *hname,
- const char *hshortname,
- pjsip_parse_hdr_func *fptr)
-{
- pj_status_t status;
-
- status = int_register_parser(hname, fptr);
- if (status != PJ_SUCCESS) {
- return status;
- }
- if (hshortname) {
- status = int_register_parser(hshortname, fptr);
- if (status != PJ_SUCCESS)
- return status;
- }
- return PJ_SUCCESS;
-}
-
-/* Find handler to parse the header name. */
-static pjsip_parse_hdr_func * find_handler(const pj_str_t *hname)
-{
- handler_rec *first;
- char hname_copy[PJSIP_MAX_HNAME_LEN];
- pj_uint32_t hash;
- int comp;
- unsigned n;
-
- if (hname->slen >= PJSIP_MAX_HNAME_LEN) {
- pj_assert(!"Header name is too long!");
- return NULL;
- }
-
- /* Calculate hash value while converting the header to lowercase.
- * Don't assume that 'hname' is NULL terminated.
- */
- hash = pj_hash_calc_tolower(0, hname_copy, hname);
- hname_copy[hname->slen] = '\0';
-
- /* Binary search for the handler. */
- comp = -1;
- first = &handler[0];
- n = handler_count;
- for (; n > 0; ) {
- unsigned half = n / 2;
- handler_rec *mid = first + half;
-
- comp = compare_handler(mid, hname_copy, hname->slen, hash);
- if (comp < 0) {
- first = ++mid;
- n -= half + 1;
- } else if (comp==0) {
- first = mid;
- break;
- } else {
- n = half;
- }
- }
-
- return comp==0 ? first->handler : NULL;
-}
-
-/* Public function to parse SIP message. */
-PJ_DEF(pjsip_msg*) pjsip_parse_msg( pj_pool_t *pool,
- char *buf, pj_size_t size,
- pjsip_parser_err_report *err_list)
-{
- pjsip_msg *msg = NULL;
- pj_scanner scanner;
- pjsip_parse_ctx context;
- PJ_USE_EXCEPTION;
-
- init_sip_parser();
-
- pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER,
- &on_syntax_error);
-
- context.scanner = &scanner;
- context.pool = pool;
- context.rdata = NULL;
-
- PJ_TRY {
- msg = int_parse_msg(&context, err_list);
- }
- PJ_DEFAULT {
- msg = NULL;
- }
- PJ_END
-
- pj_scan_fini(&scanner);
- return msg;
-}
-
-/* Public function to parse as rdata.*/
-PJ_DEF(pjsip_msg *) pjsip_parse_rdata( char *buf, pj_size_t size,
- pjsip_rx_data *rdata )
-{
- pj_scanner scanner;
- pjsip_parse_ctx context;
- PJ_USE_EXCEPTION;
-
- init_sip_parser();
-
- pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER,
- &on_syntax_error);
-
- context.scanner = &scanner;
- context.pool = rdata->tp_info.pool;
- context.rdata = rdata;
-
- PJ_TRY {
- rdata->msg_info.msg = int_parse_msg(&context, &rdata->msg_info.parse_err);
- }
- PJ_DEFAULT {
- rdata->msg_info.msg = NULL;
- }
- PJ_END
-
- pj_scan_fini(&scanner);
- return rdata->msg_info.msg;
-}
-
-/* Determine if a message has been received. */
-PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size,
- pj_bool_t is_datagram, pj_size_t *msg_size)
-{
-#if PJ_HAS_TCP
- const char *hdr_end;
- const char *body_start;
- const char *pos;
- const char *line;
- int content_length = -1;
-
- *msg_size = size;
-
- /* For datagram, the whole datagram IS the message. */
- if (is_datagram) {
- return PJ_SUCCESS;
- }
-
-
- /* Find the end of header area by finding an empty line. */
- if ((pos = pj_native_strstr(buf, "\n\r\n")) == NULL) {
- return PJSIP_EPARTIALMSG;
- }
-
- hdr_end = pos+1;
- body_start = pos+3;
-
- /* Find "Content-Length" header the hard way. */
- line = pj_native_strchr(buf, '\n');
- while (line && line < hdr_end-14) {
- ++line;
- if ( ((*line=='C' || *line=='c') &&
- pj_native_strncasecmp(line, "Content-Length", 14) == 0) ||
- ((*line=='l' || *line=='L') &&
- (*(line+1)==' ' || *(line+1)=='\t' || *(line+1)==':')))
- {
- /* Try to parse the header. */
- pj_scanner scanner;
- PJ_USE_EXCEPTION;
-
- init_sip_parser();
-
- pj_scan_init(&scanner, (char*)line, hdr_end-line,
- PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error);
-
- PJ_TRY {
- pj_str_t str_clen;
-
- /* Get "Content-Length" or "L" name */
- if (*line=='C' || *line=='c')
- pj_scan_advance_n(&scanner, 14, PJ_TRUE);
- else if (*line=='l' || *line=='L')
- pj_scan_advance_n(&scanner, 1, PJ_TRUE);
-
- /* Get colon */
- if (pj_scan_get_char(&scanner) != ':') {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- }
-
- /* Get number */
- pj_scan_get(&scanner, &pjsip_DIGIT_SPEC, &str_clen);
-
- /* Get newline. */
- pj_scan_get_newline(&scanner);
-
- /* Found a valid Content-Length header. */
- content_length = pj_strtoul(&str_clen);
- }
- PJ_END
-
- pj_scan_fini(&scanner);
- }
-
- /* Found valid Content-Length? */
- if (content_length != -1)
- break;
-
- /* Go to next line. */
- line = pj_native_strchr(line, '\n');
- }
-
- /* Found Content-Length? */
- if (content_length == -1) {
- return PJSIP_EMISSINGHDR;
- }
-
- /* Enough packet received? */
- *msg_size = (body_start - buf) + content_length;
- return (*msg_size) <= size ? PJ_SUCCESS : PJSIP_EPARTIALMSG;
-#else
- PJ_UNUSED_ARG(buf);
- PJ_UNUSED_ARG(is_datagram);
- *msg_size = size;
- return PJ_SUCCESS;
-#endif
-}
-
-/* Public function to parse URI */
-PJ_DEF(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool,
- char *buf, pj_size_t size,
- unsigned option)
-{
- PJ_USE_EXCEPTION;
- pj_scanner scanner;
- pjsip_uri *uri = NULL;
-
- init_sip_parser();
-
- pj_scan_init(&scanner, buf, size, 0, &on_syntax_error);
-
-
- PJ_TRY {
- uri = int_parse_uri_or_name_addr(&scanner, pool, option);
- }
- PJ_END;
-
- /* Must have exhausted all inputs. */
- if (pj_scan_is_eof(&scanner) || *scanner.curptr=='\r' ||
- *scanner.curptr=='\n')
- {
- /* Success. */
- pj_scan_fini(&scanner);
- return uri;
- }
-
- /* Still have some characters unparsed. */
- pj_scan_fini(&scanner);
- return NULL;
-}
-
-/* Generic function to print message body.
- * This assumes that the 'data' member points to a contigous memory where the
- * actual body is laid.
- */
-static int generic_print_body (pjsip_msg_body *msg_body,
- char *buf, pj_size_t size)
-{
- pjsip_msg_body *body = msg_body;
- if (size < body->len)
- return 0;
-
- pj_memcpy (buf, body->data, body->len);
- return body->len;
-}
-
-/* Internal function to parse SIP message */
-static pjsip_msg *int_parse_msg( pjsip_parse_ctx *ctx,
- pjsip_parser_err_report *err_list)
-{
- PJ_USE_EXCEPTION;
- int ch;
- pjsip_msg *msg;
- pjsip_ctype_hdr *ctype_hdr = NULL;
- pj_scanner *scanner = ctx->scanner;
- pj_pool_t *pool = ctx->pool;
-
- /* Skip leading newlines. */
- ch = *scanner->curptr;
- while (ch=='\r' || ch=='\n') {
- pj_scan_get_char(scanner);
- ch = *scanner->curptr;
- }
-
- msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG);
-
- /* Parse request or status line */
- if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) == 0) {
- msg->type = PJSIP_RESPONSE_MSG;
- int_parse_status_line( scanner, &msg->line.status );
- } else {
- msg->type = PJSIP_REQUEST_MSG;
- int_parse_req_line(scanner, pool, &msg->line.req );
- }
-
- /* Parse headers. */
- do {
- pj_str_t hname;
- pjsip_parse_hdr_func * handler;
- pjsip_hdr *hdr = NULL;
-
- /* Get hname. */
- pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &hname);
- ch = pj_scan_get_char( scanner );
- if (ch != ':') {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- }
-
- /* Find handler. */
- handler = find_handler(&hname);
-
- PJ_TRY {
- /* Call the handler if found.
- * If no handler is found, then treat the header as generic
- * hname/hvalue pair.
- */
- if (handler) {
- hdr = (*handler)(ctx);
- } else {
- hdr = parse_hdr_generic_string(ctx);
- hdr->type = PJSIP_H_OTHER;
- hdr->name = hdr->sname = hname;
- }
-
- /* Check if we've just parsed a Content-Type header.
- * We will check for a message body if we've got Content-Type header.
- */
- if (hdr->type == PJSIP_H_CONTENT_TYPE) {
- ctype_hdr = (pjsip_ctype_hdr*)hdr;
- }
-
- }
- PJ_DEFAULT {
- /* Exception was thrown during parsing.
- * Skip until newline, and parse next header.
- */
- pj_str_t token;
- hdr = NULL;
-
- //PJ_LOG(4,("sipparser",
- // "Syntax error in line %d col %d (hname=%.*s)",
- // scanner->line, scanner->col, hname.slen, hname.ptr));
-
- if (err_list) {
- pjsip_parser_err_report *err_info;
-
- err_info = pj_pool_alloc(pool, sizeof(*err_info));
- err_info->exception_code = PJ_GET_EXCEPTION();
- err_info->line = scanner->line;
- err_info->col = scanner->col;
- err_info->hname = hname;
-
- pj_list_insert_before(err_list, err_info);
- }
-
- if (!pj_scan_is_eof(scanner)) {
- pj_scan_get_until(scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &token);
- parse_hdr_end(scanner);
- }
- }
- PJ_END;
-
- if (hdr) {
- /* Single parse of header line can produce multiple headers.
- * For example, if one Contact: header contains Contact list
- * separated by comma, then these Contacts will be split into
- * different Contact headers.
- * So here we must insert list instead of just insert one header.
- */
- pj_list_insert_nodes_before(&msg->hdr, hdr);
- }
-
- /* Parse until EOF or an empty line is found. */
- } while (!pj_scan_is_eof(scanner) &&
- *scanner->curptr != '\r' && *scanner->curptr != '\n');
-
- /* If empty line is found, eat it. */
- if (!pj_scan_is_eof(scanner)) {
- if (*scanner->curptr=='\r' || *scanner->curptr=='\n') {
- pj_scan_get_newline(scanner);
- }
- }
-
- /* If we have Content-Type header, treat the rest of the message as body.*/
- if (ctype_hdr) {
- pjsip_msg_body *body = pj_pool_alloc(pool, sizeof(pjsip_msg_body));
- pj_strdup(pool, &body->content_type.type, &ctype_hdr->media.type);
- pj_strdup(pool, &body->content_type.subtype, &ctype_hdr->media.subtype);
- pj_strdup(pool, &body->content_type.param, &ctype_hdr->media.param);
- body->data = scanner->curptr;
- body->len = scanner->end - scanner->curptr;
- body->print_body = &generic_print_body;
-
- msg->body = body;
- }
-
- return msg;
-}
-
-/* Parse parameter (pname ["=" pvalue]). */
-void pjsip_parse_param_imp( pj_scanner *scanner,
- pj_str_t *pname, pj_str_t *pvalue,
- unsigned option)
-{
- /* pname */
- pj_scan_get(scanner, &pjsip_PARAM_CHAR_SPEC, pname);
- pj_str_unescape(pname);
-
- /* pvalue, if any */
- if (*scanner->curptr == '=') {
- pj_scan_get_char(scanner);
- /* pvalue can be a quoted string. */
- if (*scanner->curptr == '"') {
- pj_scan_get_quote( scanner, '"', '"', pvalue);
- if (option & PJSIP_PARSE_REMOVE_QUOTE) {
- pvalue->ptr++;
- pvalue->slen -= 2;
- }
- } else {
- pj_scan_get(scanner, &pjsip_PARAM_CHAR_SPEC, pvalue);
- pj_str_unescape(pvalue);
- }
- } else {
- pvalue->ptr = NULL;
- pvalue->slen = 0;
- }
-}
-
-/* Parse parameter (";" pname ["=" pvalue]). */
-static void int_parse_param( pj_scanner *scanner,
- pj_str_t *pname, pj_str_t *pvalue)
-{
- /* Get ';' character */
- pj_scan_get_char(scanner);
-
- /* Get pname and optionally pvalue */
- pjsip_parse_param_imp(scanner, pname, pvalue, 0);
-}
-
-/* Parse header parameter. */
-static void int_parse_hparam( pj_scanner *scanner,
- pj_str_t *hname, pj_str_t *hvalue )
-{
- /* Get '?' or '&' character. */
- pj_scan_get_char(scanner);
-
- /* hname */
- pj_scan_get(scanner, &pjsip_HDR_CHAR_SPEC, hname);
- pj_str_unescape(hname);
-
- /* pvalue, if any */
- if (*scanner->curptr == '=') {
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, &pjsip_HDR_CHAR_SPEC, hvalue);
- pj_str_unescape(hvalue);
- } else {
- hvalue->ptr = NULL;
- hvalue->slen = 0;
- }
-}
-
-/* Parse host:port in URI. */
-static void int_parse_uri_host_port( pj_scanner *scanner,
- pj_str_t *host, int *p_port)
-{
- pj_scan_get( scanner, &pjsip_HOST_SPEC, host);
- /* RFC3261 section 19.1.2: host don't need to be unescaped */
- if (*scanner->curptr == ':') {
- pj_str_t port;
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, &pjsip_DIGIT_SPEC, &port);
- pj_str_unescape(&port);
- *p_port = pj_strtoul(&port);
- } else {
- *p_port = 0;
- }
-}
-
-/* Determine if the next token in an URI is a user specification. */
-static int int_is_next_user(pj_scanner *scanner)
-{
- pj_str_t dummy;
- int is_user;
-
- /* Find character '@'. If this character exist, then the token
- * must be a username.
- */
- if (pj_scan_peek( scanner, &pjsip_PROBE_USER_HOST_SPEC, &dummy) == '@')
- is_user = 1;
- else
- is_user = 0;
-
- return is_user;
-}
-
-/* Parse user:pass tokens in an URI. */
-static void int_parse_user_pass( pj_scanner *scanner,
- pj_str_t *user, pj_str_t *pass)
-{
- pj_scan_get( scanner, &pjsip_USER_SPEC, user);
- pj_str_unescape(user);
-
- if ( *scanner->curptr == ':') {
- pj_scan_get_char( scanner );
- pj_scan_get( scanner, &pjsip_PASSWD_SPEC, pass);
- pj_str_unescape(pass);
- } else {
- pass->ptr = NULL;
- pass->slen = 0;
- }
-
- /* Get the '@' */
- pj_scan_get_char( scanner );
-}
-
-/* Parse all types of URI. */
-static pjsip_uri *int_parse_uri_or_name_addr( pj_scanner *scanner, pj_pool_t *pool,
- unsigned opt)
-{
- pjsip_uri *uri;
- int is_name_addr = 0;
-
- /* Exhaust any whitespaces. */
- pj_scan_skip_whitespace(scanner);
-
- if (*scanner->curptr=='"' || *scanner->curptr=='<') {
- uri = (pjsip_uri*)int_parse_name_addr( scanner, pool );
- is_name_addr = 1;
- } else {
- pj_scan_state backtrack;
- pj_str_t scheme;
- int colon;
-
- pj_scan_save_state( scanner, &backtrack);
- pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &scheme);
- colon = pj_scan_get_char( scanner );
- pj_scan_restore_state( scanner, &backtrack);
-
- if (colon==':' &&
- (parser_stricmp(scheme, pjsip_SIP_STR)==0 ||
- parser_stricmp(scheme, pjsip_SIPS_STR)==0))
- {
- uri = (pjsip_uri*)
- int_parse_sip_url( scanner, pool,
- (opt & PJSIP_PARSE_URI_IN_FROM_TO_HDR)== 0);
-
- } else if (colon==':' && parser_stricmp( scheme, pjsip_TEL_STR)==0) {
-
- /* Not supported. */
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- UNREACHED({return NULL; /* Not reached. */});
-
- } else {
- uri = (pjsip_uri*)int_parse_name_addr( scanner, pool );
- is_name_addr = 1;
- }
- }
-
- /* Should we return the URI object as name address? */
- if (opt & PJSIP_PARSE_URI_AS_NAMEADDR) {
- if (is_name_addr == 0) {
- pjsip_name_addr *name_addr;
-
- name_addr = pjsip_name_addr_create(pool);
- name_addr->uri = uri;
-
- uri = (pjsip_uri*)name_addr;
- }
- }
-
- return uri;
-}
-
-/* Parse URI. */
-static pjsip_uri *int_parse_uri(pj_scanner *scanner, pj_pool_t *pool,
- pj_bool_t parse_params)
-{
- if (*scanner->curptr=='"' || *scanner->curptr=='<') {
- return (pjsip_uri*)int_parse_name_addr( scanner, pool );
- } else {
- pj_str_t scheme;
- int colon;
-
- /* Get scheme. */
- colon = pj_scan_peek(scanner, &pjsip_TOKEN_SPEC, &scheme);
- if (colon != ':') {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- }
-
- if ((parser_stricmp(scheme, pjsip_SIP_STR)==0 ||
- parser_stricmp(scheme, pjsip_SIPS_STR)==0))
- {
- return (pjsip_uri*)int_parse_sip_url( scanner, pool, parse_params);
-
- } else if (parser_stricmp(scheme, pjsip_TEL_STR)==0) {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- UNREACHED({ return NULL; /* Not reached. */ })
-
- } else {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- UNREACHED({ return NULL; /* Not reached. */ })
- }
- }
-}
-
-/* Parse "sip:" and "sips:" URI. */
-static pjsip_url *int_parse_sip_url( pj_scanner *scanner,
- pj_pool_t *pool,
- pj_bool_t parse_params)
-{
- pj_str_t scheme;
- pjsip_url *url;
- int colon;
- int skip_ws = scanner->skip_ws;
- int hsep = '?';
- scanner->skip_ws = 0;
-
- pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &scheme);
- colon = pj_scan_get_char(scanner);
- if (colon != ':') {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- }
-
- if (parser_stricmp(scheme, pjsip_SIP_STR)==0) {
- url = pjsip_url_create(pool, 0);
-
- } else if (parser_stricmp(scheme, pjsip_SIPS_STR)==0) {
- url = pjsip_url_create(pool, 1);
-
- } else {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- /* should not reach here */
- UNREACHED({
- pj_assert(0);
- return 0;
- })
- }
-
- if (int_is_next_user(scanner)) {
- int_parse_user_pass(scanner, &url->user, &url->passwd);
- }
-
- /* Get host:port */
- int_parse_uri_host_port(scanner, &url->host, &url->port);
-
- /* Get URL parameters. */
- while ( parse_params && *scanner->curptr == ';' ) {
- pj_str_t pname, pvalue;
-
- int_parse_param( scanner, &pname, &pvalue);
-
- if (!parser_stricmp(pname, pjsip_USER_STR) && pvalue.slen) {
- url->user_param = pvalue;
-
- } else if (!parser_stricmp(pname, pjsip_METHOD_STR) && pvalue.slen) {
- url->method_param = pvalue;
-
- } else if (!parser_stricmp(pname,pjsip_TRANSPORT_STR) && pvalue.slen) {
- url->transport_param = pvalue;
-
- } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) {
- url->ttl_param = pj_strtoul(&pvalue);
-
- } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) {
- url->maddr_param = pvalue;
-
- } else if (!parser_stricmp(pname, pjsip_LR_STR)) {
- url->lr_param = 1;
-
- } else {
- pjsip_param *p = pj_pool_alloc(pool, sizeof(pjsip_param));
- p->name = pname;
- p->value = pvalue;
- pj_list_insert_before(&url->other_param, p);
- }
- }
-
- /* Get header params. */
- while (parse_params && *scanner->curptr == hsep) {
- pjsip_param *param;
- param = pj_pool_alloc(pool, sizeof(pjsip_param));
- int_parse_hparam(scanner, ¶m->name, ¶m->value);
- pj_list_insert_before(&url->header_param, param);
- hsep = '&';
- }
-
- scanner->skip_ws = skip_ws;
- pj_scan_skip_whitespace(scanner);
- return url;
-}
-
-/* Parse nameaddr. */
-static pjsip_name_addr *int_parse_name_addr( pj_scanner *scanner,
- pj_pool_t *pool )
-{
- int has_bracket;
- pjsip_name_addr *name_addr;
-
- name_addr = pjsip_name_addr_create(pool);
-
- if (*scanner->curptr == '"') {
- pj_scan_get_quote( scanner, '"', '"', &name_addr->display);
-
- } else if (*scanner->curptr != '<') {
- int next;
- pj_str_t dummy;
-
- /* This can be either the start of display name,
- * the start of URL ("sip:", "sips:", "tel:", etc.), or '<' char.
- * We're only interested in display name, because SIP URL
- * will be parser later.
- */
- next = pj_scan_peek_until(scanner, &pjsip_DISPLAY_SCAN_SPEC, &dummy);
- if (next == '<') {
- /* Ok, this is what we're looking for, a display name. */
- pj_scan_get_until_ch( scanner, '<', &name_addr->display);
- pj_strtrim(&name_addr->display);
- }
- }
-
- /* Manually skip whitespace. */
- pj_scan_skip_whitespace(scanner);
-
- /* Get the SIP-URL */
- has_bracket = (*scanner->curptr == '<');
- if (has_bracket)
- pj_scan_get_char(scanner);
- name_addr->uri = int_parse_uri( scanner, pool, PJ_TRUE );
- if (has_bracket)
- pj_scan_get_char(scanner);
-
- return name_addr;
-}
-
-
-/* Parse SIP request line. */
-static void int_parse_req_line( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_request_line *req_line)
-{
- pj_str_t token;
-
- pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &token);
- pjsip_method_init_np( &req_line->method, &token);
-
- req_line->uri = int_parse_uri(scanner, pool, PJ_TRUE);
- if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) != 0)
- PJ_THROW( PJSIP_SYN_ERR_EXCEPTION);
- pj_scan_advance_n (scanner, 7, 1);
- pj_scan_get_newline( scanner );
-}
-
-/* Parse status line. */
-static void int_parse_status_line( pj_scanner *scanner,
- pjsip_status_line *status_line)
-{
- pj_str_t token;
-
- if (pj_scan_stricmp(scanner, PJSIP_VERSION, 7) != 0)
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- pj_scan_advance_n( scanner, 7, 1);
-
- pj_scan_get( scanner, &pjsip_DIGIT_SPEC, &token);
- status_line->code = pj_strtoul(&token);
- pj_scan_get_until( scanner, &pjsip_NEWLINE_OR_EOF_SPEC,
- &status_line->reason);
- pj_scan_get_newline( scanner );
-}
-
-/* Parse ending of header. */
-static void parse_hdr_end( pj_scanner *scanner )
-{
- if (pj_scan_is_eof(scanner)) {
- ; /* Do nothing. */
- } else if (*scanner->curptr == '&') {
- pj_scan_get_char(scanner);
- } else {
- pj_scan_get_newline(scanner);
- }
-}
-
-/* Parse ending of header. */
-void pjsip_parse_end_hdr_imp( pj_scanner *scanner )
-{
- parse_hdr_end(scanner);
-}
-
-/* Parse generic array header. */
-static void parse_generic_array_hdr( pjsip_generic_array_hdr *hdr,
- pj_scanner *scanner)
-{
- pj_scan_get_until( scanner, &pjsip_ARRAY_ELEMENTS, &hdr->values[0]);
- hdr->count++;
-
- while (*scanner->curptr == ',') {
- pj_scan_get_char(scanner);
- pj_scan_get_until( scanner, &pjsip_ARRAY_ELEMENTS,
- &hdr->values[hdr->count]);
- hdr->count++;
- }
- parse_hdr_end(scanner);
-}
-
-/* Parse generic string header. */
-static void parse_generic_string_hdr( pjsip_generic_string_hdr *hdr,
- pj_scanner *scanner )
-{
- pj_scan_get_until( scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &hdr->hvalue);
- parse_hdr_end(scanner);
-}
-
-/* Parse generic integer header. */
-static void parse_generic_int_hdr( pjsip_generic_int_hdr *hdr,
- pj_scanner *scanner )
-{
- pj_str_t tmp;
- pj_scan_get_until( scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &tmp);
- hdr->ivalue = pj_strtoul(&tmp);
- parse_hdr_end(scanner);
-}
-
-
-/* Parse Accept header. */
-static pjsip_hdr* parse_hdr_accept(pjsip_parse_ctx *ctx)
-{
- pjsip_accept_hdr *accept = pjsip_accept_hdr_create(ctx->pool);
- parse_generic_array_hdr(accept, ctx->scanner);
- return (pjsip_hdr*)accept;
-}
-
-/* Parse Allow header. */
-static pjsip_hdr* parse_hdr_allow(pjsip_parse_ctx *ctx)
-{
- pjsip_allow_hdr *allow = pjsip_allow_hdr_create(ctx->pool);
- parse_generic_array_hdr(allow, ctx->scanner);
- return (pjsip_hdr*)allow;
-}
-
-/* Parse Call-ID header. */
-static pjsip_hdr* parse_hdr_call_id(pjsip_parse_ctx *ctx)
-{
- pjsip_cid_hdr *hdr = pjsip_cid_hdr_create(ctx->pool);
- pj_scan_get_until( ctx->scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &hdr->id);
- parse_hdr_end(ctx->scanner);
-
- if (ctx->rdata)
- ctx->rdata->msg_info.call_id = hdr->id;
-
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse and interpret Contact param. */
-static void int_parse_contact_param( pjsip_contact_hdr *hdr,
- pj_scanner *scanner,
- pj_pool_t *pool)
-{
- while ( *scanner->curptr == ';' ) {
- pj_str_t pname, pvalue;
-
- int_parse_param( scanner, &pname, &pvalue);
- if (!parser_stricmp(pname, pjsip_Q_STR) && pvalue.slen) {
- char *dot_pos = memchr(pvalue.ptr, '.', pvalue.slen);
- if (!dot_pos) {
- hdr->q1000 = pj_strtoul(&pvalue);
- } else {
- pvalue.slen = (pvalue.ptr+pvalue.slen) - (dot_pos+1);
- pvalue.ptr = dot_pos + 1;
- hdr->q1000 = pj_strtoul_mindigit(&pvalue, 3);
- }
- } else if (!parser_stricmp(pname, pjsip_EXPIRES_STR) && pvalue.slen) {
- hdr->expires = pj_strtoul(&pvalue);
-
- } else {
- concat_param(&hdr->other_param, pool, &pname, &pvalue);
- }
- }
-}
-
-/* Parse Contact header. */
-static pjsip_hdr* parse_hdr_contact( pjsip_parse_ctx *ctx )
-{
- pjsip_contact_hdr *first = NULL;
- pj_scanner *scanner = ctx->scanner;
-
- do {
- pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(ctx->pool);
- if (first == NULL)
- first = hdr;
- else
- pj_list_insert_before(first, hdr);
-
- if (*scanner->curptr == '*') {
- pj_scan_get_char(scanner);
- hdr->star = 1;
-
- } else {
- hdr->star = 0;
- hdr->uri = int_parse_uri_or_name_addr(scanner, ctx->pool,
- PJSIP_PARSE_URI_AS_NAMEADDR);
-
- int_parse_contact_param(hdr, scanner, ctx->pool);
- }
-
- if (*scanner->curptr != ',')
- break;
-
- pj_scan_get_char(scanner);
-
- } while (1);
-
- parse_hdr_end(scanner);
-
- return (pjsip_hdr*)first;
-}
-
-/* Parse Content-Length header. */
-static pjsip_hdr* parse_hdr_content_len( pjsip_parse_ctx *ctx )
-{
- pj_str_t digit;
- pjsip_clen_hdr *hdr;
-
- hdr = pjsip_clen_hdr_create(ctx->pool);
- pj_scan_get(ctx->scanner, &pjsip_DIGIT_SPEC, &digit);
- hdr->len = pj_strtoul(&digit);
- parse_hdr_end(ctx->scanner);
-
- if (ctx->rdata)
- ctx->rdata->msg_info.clen = hdr;
-
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse Content-Type header. */
-static pjsip_hdr* parse_hdr_content_type( pjsip_parse_ctx *ctx )
-{
- pjsip_ctype_hdr *hdr;
- pj_scanner *scanner = ctx->scanner;
-
- hdr = pjsip_ctype_hdr_create(ctx->pool);
-
- /* Parse media type and subtype. */
- pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->media.type);
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->media.subtype);
-
- /* Parse media parameters */
- while (*scanner->curptr == ';') {
- pj_str_t pname, pvalue;
- int_parse_param(scanner, &pname, &pvalue);
- concat_param(&hdr->media.param, ctx->pool, &pname, &pvalue);
- }
-
- parse_hdr_end(ctx->scanner);
-
- if (ctx->rdata)
- ctx->rdata->msg_info.ctype = hdr;
-
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse CSeq header. */
-static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx )
-{
- pj_str_t cseq, method;
- pjsip_cseq_hdr *hdr;
-
- hdr = pjsip_cseq_hdr_create(ctx->pool);
- pj_scan_get( ctx->scanner, &pjsip_DIGIT_SPEC, &cseq);
- hdr->cseq = pj_strtoul(&cseq);
-
- pj_scan_get( ctx->scanner, &pjsip_TOKEN_SPEC, &method);
- pjsip_method_init_np(&hdr->method, &method);
-
- parse_hdr_end( ctx->scanner );
-
- if (ctx->rdata)
- ctx->rdata->msg_info.cseq = hdr;
-
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse Expires header. */
-static pjsip_hdr* parse_hdr_expires(pjsip_parse_ctx *ctx)
-{
- pjsip_expires_hdr *hdr = pjsip_expires_hdr_create(ctx->pool);
- parse_generic_int_hdr(hdr, ctx->scanner);
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse From: or To: header. */
-static void parse_hdr_fromto( pj_scanner *scanner,
- pj_pool_t *pool,
- pjsip_from_hdr *hdr)
-{
- hdr->uri = int_parse_uri_or_name_addr(scanner, pool,
- PJSIP_PARSE_URI_AS_NAMEADDR |
- PJSIP_PARSE_URI_IN_FROM_TO_HDR);
-
- while ( *scanner->curptr == ';' ) {
- pj_str_t pname, pvalue;
-
- int_parse_param( scanner, &pname, &pvalue);
-
- if (!parser_stricmp(pname, pjsip_TAG_STR)) {
- hdr->tag = pvalue;
-
- } else {
- concat_param(&hdr->other_param, pool, &pname, &pvalue);
- }
- }
-
- parse_hdr_end(scanner);
-}
-
-/* Parse From: header. */
-static pjsip_hdr* parse_hdr_from( pjsip_parse_ctx *ctx )
-{
- pjsip_from_hdr *hdr = pjsip_from_hdr_create(ctx->pool);
- parse_hdr_fromto(ctx->scanner, ctx->pool, hdr);
- if (ctx->rdata)
- ctx->rdata->msg_info.from = hdr;
-
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse Require: header. */
-static pjsip_hdr* parse_hdr_require( pjsip_parse_ctx *ctx )
-{
- pjsip_require_hdr *hdr = pjsip_require_hdr_create(ctx->pool);
- parse_generic_array_hdr(hdr, ctx->scanner);
-
- if (ctx->rdata && ctx->rdata->msg_info.require == NULL)
- ctx->rdata->msg_info.require = hdr;
-
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse Retry-After: header. */
-static pjsip_hdr* parse_hdr_retry_after(pjsip_parse_ctx *ctx)
-{
- pjsip_retry_after_hdr *hdr;
- hdr = pjsip_retry_after_hdr_create(ctx->pool);
- parse_generic_int_hdr(hdr, ctx->scanner);
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse Supported: header. */
-static pjsip_hdr* parse_hdr_supported(pjsip_parse_ctx *ctx)
-{
- pjsip_supported_hdr *hdr = pjsip_supported_hdr_create(ctx->pool);
- parse_generic_array_hdr(hdr, ctx->scanner);
- return (pjsip_hdr*)hdr;
-}
-
-
-/* Parse To: header. */
-static pjsip_hdr* parse_hdr_to( pjsip_parse_ctx *ctx )
-{
- pjsip_to_hdr *hdr = pjsip_to_hdr_create(ctx->pool);
- parse_hdr_fromto(ctx->scanner, ctx->pool, hdr);
-
- if (ctx->rdata)
- ctx->rdata->msg_info.to = hdr;
-
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse Unsupported: header. */
-static pjsip_hdr* parse_hdr_unsupported(pjsip_parse_ctx *ctx)
-{
- pjsip_unsupported_hdr *hdr = pjsip_unsupported_hdr_create(ctx->pool);
- parse_generic_array_hdr(hdr, ctx->scanner);
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse and interpret Via parameters. */
-static void int_parse_via_param( pjsip_via_hdr *hdr, pj_scanner *scanner,
- pj_pool_t *pool)
-{
- while ( *scanner->curptr == ';' ) {
- pj_str_t pname, pvalue;
-
- int_parse_param( scanner, &pname, &pvalue);
-
- if (!parser_stricmp(pname, pjsip_BRANCH_STR) && pvalue.slen) {
- hdr->branch_param = pvalue;
-
- } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) {
- hdr->ttl_param = pj_strtoul(&pvalue);
-
- } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) {
- hdr->maddr_param = pvalue;
-
- } else if (!parser_stricmp(pname, pjsip_PNAME_STR) && pvalue.slen) {
- hdr->recvd_param = pvalue;
-
- } else if (!parser_stricmp(pname, pjsip_RPORT_STR)) {
- if (pvalue.slen)
- hdr->rport_param = pj_strtoul(&pvalue);
- else
- hdr->rport_param = 0;
- } else {
- concat_param( &hdr->other_param, pool, &pname, &pvalue);
- }
- }
-
-}
-
-/* Parse Max-Forwards header. */
-static pjsip_hdr* parse_hdr_max_forwards( pjsip_parse_ctx *ctx )
-{
- pjsip_max_forwards_hdr *hdr;
- hdr = pjsip_max_forwards_hdr_create(ctx->pool);
- parse_generic_int_hdr(hdr, ctx->scanner);
-
- if (ctx->rdata)
- ctx->rdata->msg_info.max_fwd = hdr;
-
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse Min-Expires header. */
-static pjsip_hdr* parse_hdr_min_expires(pjsip_parse_ctx *ctx)
-{
- pjsip_min_expires_hdr *hdr;
- hdr = pjsip_min_expires_hdr_create(ctx->pool);
- parse_generic_int_hdr(hdr, ctx->scanner);
- return (pjsip_hdr*)hdr;
-}
-
-
-/* Parse Route: or Record-Route: header. */
-static void parse_hdr_rr_route( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_routing_hdr *hdr )
-{
- pjsip_name_addr *temp=int_parse_name_addr(scanner, pool);
-
- pj_memcpy(&hdr->name_addr, temp, sizeof(*temp));
- if (*scanner->curptr == ';') {
- pj_scan_get_until(scanner, &pjsip_NEWLINE_OR_EOF_SPEC,
- &hdr->other_param);
- }
-}
-
-/* Parse Record-Route header. */
-static pjsip_hdr* parse_hdr_rr( pjsip_parse_ctx *ctx)
-{
- pjsip_rr_hdr *first = NULL;
- pj_scanner *scanner = ctx->scanner;
-
- do {
- pjsip_rr_hdr *hdr = pjsip_rr_hdr_create(ctx->pool);
- if (!first) {
- first = hdr;
- } else {
- pj_list_insert_before(first, hdr);
- }
- parse_hdr_rr_route(scanner, ctx->pool, hdr);
- if (*scanner->curptr == ',') {
- pj_scan_get_char(scanner);
- } else {
- break;
- }
- } while (1);
- parse_hdr_end(scanner);
-
- if (ctx->rdata && ctx->rdata->msg_info.record_route==NULL)
- ctx->rdata->msg_info.record_route = first;
-
- return (pjsip_hdr*)first;
-}
-
-/* Parse Route: header. */
-static pjsip_hdr* parse_hdr_route( pjsip_parse_ctx *ctx )
-{
- pjsip_route_hdr *first = NULL;
- pj_scanner *scanner = ctx->scanner;
-
- do {
- pjsip_route_hdr *hdr = pjsip_route_hdr_create(ctx->pool);
- if (!first) {
- first = hdr;
- } else {
- pj_list_insert_before(first, hdr);
- }
- parse_hdr_rr_route(scanner, ctx->pool, hdr);
- if (*scanner->curptr == ',') {
- pj_scan_get_char(scanner);
- } else {
- break;
- }
- } while (1);
- parse_hdr_end(scanner);
-
- if (ctx->rdata && ctx->rdata->msg_info.route==NULL)
- ctx->rdata->msg_info.route = first;
-
- return (pjsip_hdr*)first;
-}
-
-/* Parse Via: header. */
-static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx )
-{
- pjsip_via_hdr *first = NULL;
- pj_scanner *scanner = ctx->scanner;
-
- do {
- pjsip_via_hdr *hdr = pjsip_via_hdr_create(ctx->pool);
- if (!first)
- first = hdr;
- else
- pj_list_insert_before(first, hdr);
-
- if (pj_scan_stricmp( scanner, PJSIP_VERSION "/", 8) != 0)
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
-
- pj_scan_advance_n( scanner, 8, 1);
-
- pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &hdr->transport);
- pj_scan_get( scanner, &pjsip_HOST_SPEC, &hdr->sent_by.host);
-
- if (*scanner->curptr==':') {
- pj_str_t digit;
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, &pjsip_DIGIT_SPEC, &digit);
- hdr->sent_by.port = pj_strtoul(&digit);
- } else {
- hdr->sent_by.port = 5060;
- }
-
- int_parse_via_param(hdr, scanner, ctx->pool);
-
- if (*scanner->curptr == '(') {
- pj_scan_get_char(scanner);
- pj_scan_get_until_ch( scanner, ')', &hdr->comment);
- pj_scan_get_char( scanner );
- }
-
- if (*scanner->curptr != ',')
- break;
-
- pj_scan_get_char(scanner);
-
- } while (1);
-
- parse_hdr_end(scanner);
-
- if (ctx->rdata && ctx->rdata->msg_info.via == NULL)
- ctx->rdata->msg_info.via = first;
-
- return (pjsip_hdr*)first;
-}
-
-/* Parse generic header. */
-static pjsip_hdr* parse_hdr_generic_string( pjsip_parse_ctx *ctx )
-{
- pjsip_generic_string_hdr *hdr;
-
- hdr = pjsip_generic_string_hdr_create(ctx->pool, NULL);
- parse_generic_string_hdr(hdr, ctx->scanner);
- return (pjsip_hdr*)hdr;
-
-}
-
-/* Public function to parse a header value. */
-PJ_DEF(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname,
- char *buf, pj_size_t size, int *parsed_len )
-{
- pj_scanner scanner;
- pjsip_hdr *hdr = NULL;
- pjsip_parse_ctx context;
- PJ_USE_EXCEPTION;
-
- init_sip_parser();
-
- pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER,
- &on_syntax_error);
-
- context.scanner = &scanner;
- context.pool = pool;
- context.rdata = NULL;
-
- PJ_TRY {
- pjsip_parse_hdr_func *handler = find_handler(hname);
- if (handler) {
- hdr = (*handler)(&context);
- } else {
- hdr = parse_hdr_generic_string(&context);
- hdr->type = PJSIP_H_OTHER;
- pj_strdup(pool, &hdr->name, hname);
- hdr->sname = hdr->name;
- }
-
- }
- PJ_DEFAULT {
- hdr = NULL;
- }
- PJ_END
-
- if (parsed_len) {
- *parsed_len = (scanner.curptr - scanner.begin);
- }
-
- pj_scan_fini(&scanner);
-
- return hdr;
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip/sip_parser.h> +#include <pjsip/sip_uri.h> +#include <pjsip/sip_msg.h> +#include <pjsip/sip_auth_parser.h> +#include <pjsip/sip_errno.h> +#include <pjsip/sip_transport.h> /* rdata structure */ +#include <pjlib-util/scanner.h> +#include <pjlib-util/string.h> +#include <pj/except.h> +#include <pj/log.h> +#include <pj/hash.h> +#include <pj/os.h> +#include <pj/pool.h> +#include <pj/string.h> +#include <pj/ctype.h> +#include <pj/assert.h> + +#define ALNUM +#define RESERVED ";/?:@&=+$," +#define MARK "-_.!~*'()" +#define UNRESERVED ALNUM MARK +#define ESCAPED "%" +#define USER_UNRESERVED "&=+$,;?/" +#define PASS "&=+$," +#define TOKEN "-.!%*_=`'~+" /* '+' is because of app/pidf+xml + * in Content-Type! */ +#define HOST "_-." +#define HEX_DIGIT "abcdefABCDEF" +#define PARAM_CHAR "[]/:&+$" UNRESERVED ESCAPED +#define HNV_UNRESERVED "[]/?:+$" +#define HDR_CHAR HNV_UNRESERVED UNRESERVED ESCAPED + +#define PJSIP_VERSION "SIP/2.0" +#define PJSIP_SYN_ERR_EXCEPTION 1 + +#define UNREACHED(expr) + +typedef struct handler_rec +{ + char hname[PJSIP_MAX_HNAME_LEN+1]; + pj_size_t hname_len; + pj_uint32_t hname_hash; + pjsip_parse_hdr_func *handler; +} handler_rec; + +static handler_rec handler[127]; +static unsigned handler_count; +static int parser_is_initialized; + + +/* + * Global vars (also extern). + */ +const pj_str_t pjsip_USER_STR = { "user", 4}; +const pj_str_t pjsip_METHOD_STR = { "method", 6}; +const pj_str_t pjsip_TRANSPORT_STR = { "transport", 9}; +const pj_str_t pjsip_MADDR_STR = { "maddr", 5 }; +const pj_str_t pjsip_LR_STR = { "lr", 2 }; +const pj_str_t pjsip_SIP_STR = { "sip", 3 }; +const pj_str_t pjsip_SIPS_STR = { "sips", 4 }; +const pj_str_t pjsip_TEL_STR = { "tel", 3 }; +const pj_str_t pjsip_BRANCH_STR = { "branch", 6 }; +const pj_str_t pjsip_TTL_STR = { "ttl", 3 }; +const pj_str_t pjsip_PNAME_STR = { "received", 8 }; +const pj_str_t pjsip_Q_STR = { "q", 1 }; +const pj_str_t pjsip_EXPIRES_STR = { "expires", 7 }; +const pj_str_t pjsip_TAG_STR = { "tag", 3 }; +const pj_str_t pjsip_RPORT_STR = { "rport", 5}; + +/* Character Input Specification buffer. */ +static pj_cis_buf_t cis_buf; + +/* Character Input Specifications. */ +pj_cis_t pjsip_HOST_SPEC, /* For scanning host part. */ + pjsip_DIGIT_SPEC, /* Decimal digits */ + pjsip_ALPHA_SPEC, /* Alpha (A-Z, a-z) */ + pjsip_ALNUM_SPEC, /* Decimal + Alpha. */ + pjsip_TOKEN_SPEC, /* Token. */ + pjsip_HEX_SPEC, /* Hexadecimal digits. */ + pjsip_PARAM_CHAR_SPEC, /* For scanning pname (or pvalue when + * it's not quoted.) */ + pjsip_HDR_CHAR_SPEC, /* Chars in hname or hvalue */ + pjsip_PROBE_USER_HOST_SPEC, /* Hostname characters. */ + pjsip_PASSWD_SPEC, /* Password. */ + pjsip_USER_SPEC, /* User */ + pjsip_ARRAY_ELEMENTS, /* Array separator. */ + pjsip_NEWLINE_OR_EOF_SPEC, /* For eating up header.*/ + pjsip_DISPLAY_SCAN_SPEC; /* Used when searching for display name + * in URL. */ + + +/* + * Forward decl. + */ +static pjsip_msg * int_parse_msg( pjsip_parse_ctx *ctx, + pjsip_parser_err_report *err_list); +static void int_parse_param( pj_scanner *scanner, + pj_pool_t *pool, + pj_str_t *pname, + pj_str_t *pvalue); +static void int_parse_hparam( pj_scanner *scanner, + pj_pool_t *pool, + pj_str_t *hname, + pj_str_t *hvalue ); +static void int_parse_req_line( pj_scanner *scanner, + pj_pool_t *pool, + pjsip_request_line *req_line); +static int int_is_next_user( pj_scanner *scanner); +static void int_parse_status_line( pj_scanner *scanner, + pjsip_status_line *line); +static void int_parse_user_pass( pj_scanner *scanner, + pj_pool_t *pool, + pj_str_t *user, + pj_str_t *pass); +static void int_parse_uri_host_port( pj_scanner *scanner, + pj_str_t *p_host, + int *p_port); +static pjsip_uri * int_parse_uri_or_name_addr( pj_scanner *scanner, + pj_pool_t *pool, + unsigned option); +static pjsip_url * int_parse_sip_url( pj_scanner *scanner, + pj_pool_t *pool, + pj_bool_t parse_params); +static pjsip_name_addr * + int_parse_name_addr( pj_scanner *scanner, + pj_pool_t *pool ); +static void parse_hdr_end( pj_scanner *scanner ); + +static pjsip_hdr* parse_hdr_accept( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_allow( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_call_id( pjsip_parse_ctx *ctx); +static pjsip_hdr* parse_hdr_contact( pjsip_parse_ctx *ctx); +static pjsip_hdr* parse_hdr_content_len( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_content_type( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_expires( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_from( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_max_forwards( pjsip_parse_ctx *ctx); +static pjsip_hdr* parse_hdr_min_expires( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_rr( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_route( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_require( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_retry_after( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_supported( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_to( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_unsupported( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx ); +static pjsip_hdr* parse_hdr_generic_string( pjsip_parse_ctx *ctx); + +/* Convert non NULL terminated string to integer. */ +static unsigned long pj_strtoul_mindigit(const pj_str_t *str, + unsigned mindig) +{ + unsigned long value; + unsigned i; + + value = 0; + for (i=0; i<(unsigned)str->slen; ++i) { + value = value * 10 + (str->ptr[i] - '0'); + } + for (; i<mindig; ++i) { + value = value * 10; + } + return value; +} + +/* Case insensitive comparison */ +#define parser_stricmp(str1, str2) pj_stricmp(&str1, &str2) + + +/* Syntax error handler for parser. */ +static void on_syntax_error(pj_scanner *scanner) +{ + PJ_UNUSED_ARG(scanner); + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); +} + +/* Concatenate unrecognized params into single string. */ +void pjsip_concat_param_imp( pj_str_t *param, pj_pool_t *pool, + const pj_str_t *pname, const pj_str_t *pvalue, + int sepchar) +{ + char *new_param, *p; + int len; + + len = param->slen + pname->slen + pvalue->slen + 3; + p = new_param = pj_pool_alloc(pool, len); + + if (param->slen) { + int old_len = param->slen; + pj_memcpy(p, param->ptr, old_len); + p += old_len; + } + *p++ = (char)sepchar; + pj_memcpy(p, pname->ptr, pname->slen); + p += pname->slen; + + if (pvalue->slen) { + *p++ = '='; + pj_memcpy(p, pvalue->ptr, pvalue->slen); + p += pvalue->slen; + } + + *p = '\0'; + + param->ptr = new_param; + param->slen = p - new_param; +} + +/* Concatenate unrecognized params into single string. */ +static void concat_param( pj_str_t *param, pj_pool_t *pool, + const pj_str_t *pname, const pj_str_t *pvalue ) +{ + pjsip_concat_param_imp(param, pool, pname, pvalue, ';'); +} + +/* Initialize static properties of the parser. */ +static pj_status_t init_parser() +{ + static int initialized; + pj_status_t status; + + if (initialized) + return PJ_SUCCESS; + + initialized = 1; + + pj_cis_buf_init(&cis_buf); + + status = pj_cis_init(&cis_buf, &pjsip_DIGIT_SPEC); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + pj_cis_add_num(&pjsip_DIGIT_SPEC); + + status = pj_cis_init(&cis_buf, &pjsip_ALPHA_SPEC); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + pj_cis_add_alpha( &pjsip_ALPHA_SPEC ); + + status = pj_cis_init(&cis_buf, &pjsip_ALNUM_SPEC); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + pj_cis_add_alpha( &pjsip_ALNUM_SPEC ); + pj_cis_add_num( &pjsip_ALNUM_SPEC ); + + status = pj_cis_init(&cis_buf, &pjsip_NEWLINE_OR_EOF_SPEC); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + pj_cis_add_str(&pjsip_NEWLINE_OR_EOF_SPEC, "\r\n"); + //pj_cs_set(pjsip_NEWLINE_OR_EOF_SPEC, 0); + + status = pj_cis_init(&cis_buf, &pjsip_ARRAY_ELEMENTS); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + pj_cis_add_str( &pjsip_ARRAY_ELEMENTS, ",\r\n"); + + status = pj_cis_dup(&pjsip_TOKEN_SPEC, &pjsip_ALNUM_SPEC); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + pj_cis_add_str( &pjsip_TOKEN_SPEC, TOKEN); + + status = pj_cis_dup(&pjsip_HOST_SPEC, &pjsip_ALNUM_SPEC); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + pj_cis_add_str( &pjsip_HOST_SPEC, HOST); + + status = pj_cis_dup(&pjsip_HEX_SPEC, &pjsip_DIGIT_SPEC); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + pj_cis_add_str( &pjsip_HEX_SPEC, HEX_DIGIT); + + status = pj_cis_dup(&pjsip_PARAM_CHAR_SPEC, &pjsip_ALNUM_SPEC); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + pj_cis_add_str(&pjsip_PARAM_CHAR_SPEC, PARAM_CHAR); + + status = pj_cis_dup(&pjsip_HDR_CHAR_SPEC, &pjsip_ALNUM_SPEC); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + pj_cis_add_str(&pjsip_HDR_CHAR_SPEC, HDR_CHAR); + + status = pj_cis_dup(&pjsip_USER_SPEC, &pjsip_ALNUM_SPEC); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + pj_cis_add_str( &pjsip_USER_SPEC, UNRESERVED ESCAPED USER_UNRESERVED ); + + status = pj_cis_dup(&pjsip_PASSWD_SPEC, &pjsip_ALNUM_SPEC); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + pj_cis_add_str( &pjsip_PASSWD_SPEC, UNRESERVED ESCAPED PASS); + + status = pj_cis_init(&cis_buf, &pjsip_PROBE_USER_HOST_SPEC); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + pj_cis_add_str( &pjsip_PROBE_USER_HOST_SPEC, "@ \n>"); + pj_cis_invert( &pjsip_PROBE_USER_HOST_SPEC ); + + status = pj_cis_init(&cis_buf, &pjsip_DISPLAY_SCAN_SPEC); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + pj_cis_add_str( &pjsip_DISPLAY_SCAN_SPEC, ":\r\n<"); + + status = pjsip_register_hdr_parser( "Accept", NULL, &parse_hdr_accept); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "Allow", NULL, &parse_hdr_allow); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "Call-ID", NULL, &parse_hdr_call_id); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "Contact", "m", &parse_hdr_contact); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "Content-Length", NULL, + &parse_hdr_content_len); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "Content-Type", NULL, + &parse_hdr_content_type); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "CSeq", NULL, &parse_hdr_cseq); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "Expires", NULL, &parse_hdr_expires); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "From", "f", &parse_hdr_from); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "Max-Forwards", NULL, + &parse_hdr_max_forwards); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "Min-Expires", NULL, + &parse_hdr_min_expires); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "Record-Route", NULL, &parse_hdr_rr); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "Route", NULL, &parse_hdr_route); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "Require", NULL, &parse_hdr_require); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "Retry-After", NULL, + &parse_hdr_retry_after); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "Supported", "k", + &parse_hdr_supported); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "To", "t", &parse_hdr_to); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "Unsupported", NULL, + &parse_hdr_unsupported); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjsip_register_hdr_parser( "Via", NULL, &parse_hdr_via); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + /* Register auth parser. */ + status = pjsip_auth_init_parser(); + + return status; +} + +static void init_sip_parser(void) +{ + if (!parser_is_initialized) { + /* Prevent race cond. */ + pj_enter_critical_section(); + if (!parser_is_initialized) { + init_parser(); + parser_is_initialized = 1; + } + pj_leave_critical_section(); + } +} + +/* Compare the handler record with header name, and return: + * - 0 if handler match. + * - <0 if handler is 'less' than the header name. + * - >0 if handler is 'greater' than header name. + */ +static int compare_handler( const handler_rec *r1, + const char *name, + pj_size_t name_len, + pj_uint32_t hash ) +{ + /* Compare length. */ + if (r1->hname_len < name_len) + return -1; + if (r1->hname_len > name_len) + return 1; + + /* Length is equal, compare hashed value. */ + if (r1->hname_hash < hash) + return -1; + if (r1->hname_hash > hash) + return 1; + + /* Equal length and equal hash. compare the strings. */ + return pj_native_strcmp(r1->hname, name); +} + +/* Register one handler for one header name. */ +static pj_status_t int_register_parser( const char *name, + pjsip_parse_hdr_func *fptr ) +{ + unsigned pos; + handler_rec rec; + unsigned i; + + if (handler_count >= PJ_ARRAY_SIZE(handler)) { + return PJ_ETOOMANY; + } + + /* Initialize temporary handler. */ + rec.handler = fptr; + rec.hname_len = strlen(name); + if (rec.hname_len >= sizeof(rec.hname)) { + return PJ_ENAMETOOLONG; + } + /* Name is copied in lowercase. */ + for (i=0; i<rec.hname_len; ++i) { + rec.hname[i] = (char)pj_tolower(name[i]); + } + rec.hname[i] = '\0'; + /* Hash value is calculated from the lowercase name. */ + rec.hname_hash = pj_hash_calc(0, rec.hname, PJ_HASH_KEY_STRING); + + /* Get the pos to insert the new handler. */ + for (pos=0; pos < handler_count; ++pos) { + int d; + d = compare_handler(&handler[pos], rec.hname, rec.hname_len, + rec.hname_hash); + if (d == 0) { + pj_assert(0); + return PJ_EEXISTS; + } + if (d > 0) { + break; + } + } + + /* Shift handlers. */ + if (pos != handler_count) { + pj_memmove( &handler[pos+1], &handler[pos], + (handler_count-pos)*sizeof(handler_rec)); + } + /* Add new handler. */ + pj_memcpy( &handler[pos], &rec, sizeof(handler_rec)); + ++handler_count; + + return PJ_SUCCESS; +} + +/* Register parser handler. If both header name and short name are valid, + * then two instances of handler will be registered. + */ +PJ_DEF(pj_status_t) pjsip_register_hdr_parser( const char *hname, + const char *hshortname, + pjsip_parse_hdr_func *fptr) +{ + pj_status_t status; + + status = int_register_parser(hname, fptr); + if (status != PJ_SUCCESS) { + return status; + } + if (hshortname) { + status = int_register_parser(hshortname, fptr); + if (status != PJ_SUCCESS) + return status; + } + return PJ_SUCCESS; +} + +/* Find handler to parse the header name. */ +static pjsip_parse_hdr_func * find_handler(const pj_str_t *hname) +{ + handler_rec *first; + char hname_copy[PJSIP_MAX_HNAME_LEN]; + pj_uint32_t hash; + int comp; + unsigned n; + + if (hname->slen >= PJSIP_MAX_HNAME_LEN) { + pj_assert(!"Header name is too long!"); + return NULL; + } + + /* Calculate hash value while converting the header to lowercase. + * Don't assume that 'hname' is NULL terminated. + */ + hash = pj_hash_calc_tolower(0, hname_copy, hname); + hname_copy[hname->slen] = '\0'; + + /* Binary search for the handler. */ + comp = -1; + first = &handler[0]; + n = handler_count; + for (; n > 0; ) { + unsigned half = n / 2; + handler_rec *mid = first + half; + + comp = compare_handler(mid, hname_copy, hname->slen, hash); + if (comp < 0) { + first = ++mid; + n -= half + 1; + } else if (comp==0) { + first = mid; + break; + } else { + n = half; + } + } + + return comp==0 ? first->handler : NULL; +} + +/* Public function to parse SIP message. */ +PJ_DEF(pjsip_msg*) pjsip_parse_msg( pj_pool_t *pool, + char *buf, pj_size_t size, + pjsip_parser_err_report *err_list) +{ + pjsip_msg *msg = NULL; + pj_scanner scanner; + pjsip_parse_ctx context; + PJ_USE_EXCEPTION; + + init_sip_parser(); + + pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, + &on_syntax_error); + + context.scanner = &scanner; + context.pool = pool; + context.rdata = NULL; + + PJ_TRY { + msg = int_parse_msg(&context, err_list); + } + PJ_DEFAULT { + msg = NULL; + } + PJ_END + + pj_scan_fini(&scanner); + return msg; +} + +/* Public function to parse as rdata.*/ +PJ_DEF(pjsip_msg *) pjsip_parse_rdata( char *buf, pj_size_t size, + pjsip_rx_data *rdata ) +{ + pj_scanner scanner; + pjsip_parse_ctx context; + PJ_USE_EXCEPTION; + + init_sip_parser(); + + pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, + &on_syntax_error); + + context.scanner = &scanner; + context.pool = rdata->tp_info.pool; + context.rdata = rdata; + + PJ_TRY { + rdata->msg_info.msg = int_parse_msg(&context, &rdata->msg_info.parse_err); + } + PJ_DEFAULT { + rdata->msg_info.msg = NULL; + } + PJ_END + + pj_scan_fini(&scanner); + return rdata->msg_info.msg; +} + +/* Determine if a message has been received. */ +PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size, + pj_bool_t is_datagram, pj_size_t *msg_size) +{ +#if PJ_HAS_TCP + const char *hdr_end; + const char *body_start; + const char *pos; + const char *line; + int content_length = -1; + + *msg_size = size; + + /* For datagram, the whole datagram IS the message. */ + if (is_datagram) { + return PJ_SUCCESS; + } + + + /* Find the end of header area by finding an empty line. */ + if ((pos = pj_native_strstr(buf, "\n\r\n")) == NULL) { + return PJSIP_EPARTIALMSG; + } + + hdr_end = pos+1; + body_start = pos+3; + + /* Find "Content-Length" header the hard way. */ + line = pj_native_strchr(buf, '\n'); + while (line && line < hdr_end-14) { + ++line; + if ( ((*line=='C' || *line=='c') && + pj_native_strncasecmp(line, "Content-Length", 14) == 0) || + ((*line=='l' || *line=='L') && + (*(line+1)==' ' || *(line+1)=='\t' || *(line+1)==':'))) + { + /* Try to parse the header. */ + pj_scanner scanner; + PJ_USE_EXCEPTION; + + init_sip_parser(); + + pj_scan_init(&scanner, (char*)line, hdr_end-line, + PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error); + + PJ_TRY { + pj_str_t str_clen; + + /* Get "Content-Length" or "L" name */ + if (*line=='C' || *line=='c') + pj_scan_advance_n(&scanner, 14, PJ_TRUE); + else if (*line=='l' || *line=='L') + pj_scan_advance_n(&scanner, 1, PJ_TRUE); + + /* Get colon */ + if (pj_scan_get_char(&scanner) != ':') { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + } + + /* Get number */ + pj_scan_get(&scanner, &pjsip_DIGIT_SPEC, &str_clen); + + /* Get newline. */ + pj_scan_get_newline(&scanner); + + /* Found a valid Content-Length header. */ + content_length = pj_strtoul(&str_clen); + } + PJ_END + + pj_scan_fini(&scanner); + } + + /* Found valid Content-Length? */ + if (content_length != -1) + break; + + /* Go to next line. */ + line = pj_native_strchr(line, '\n'); + } + + /* Found Content-Length? */ + if (content_length == -1) { + return PJSIP_EMISSINGHDR; + } + + /* Enough packet received? */ + *msg_size = (body_start - buf) + content_length; + return (*msg_size) <= size ? PJ_SUCCESS : PJSIP_EPARTIALMSG; +#else + PJ_UNUSED_ARG(buf); + PJ_UNUSED_ARG(is_datagram); + *msg_size = size; + return PJ_SUCCESS; +#endif +} + +/* Public function to parse URI */ +PJ_DEF(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool, + char *buf, pj_size_t size, + unsigned option) +{ + PJ_USE_EXCEPTION; + pj_scanner scanner; + pjsip_uri *uri = NULL; + + init_sip_parser(); + + pj_scan_init(&scanner, buf, size, 0, &on_syntax_error); + + + PJ_TRY { + uri = int_parse_uri_or_name_addr(&scanner, pool, option); + } + PJ_END; + + /* Must have exhausted all inputs. */ + if (pj_scan_is_eof(&scanner) || *scanner.curptr=='\r' || + *scanner.curptr=='\n') + { + /* Success. */ + pj_scan_fini(&scanner); + return uri; + } + + /* Still have some characters unparsed. */ + pj_scan_fini(&scanner); + return NULL; +} + +/* Generic function to print message body. + * This assumes that the 'data' member points to a contigous memory where the + * actual body is laid. + */ +static int generic_print_body (pjsip_msg_body *msg_body, + char *buf, pj_size_t size) +{ + pjsip_msg_body *body = msg_body; + if (size < body->len) + return 0; + + pj_memcpy (buf, body->data, body->len); + return body->len; +} + +/* Internal function to parse SIP message */ +static pjsip_msg *int_parse_msg( pjsip_parse_ctx *ctx, + pjsip_parser_err_report *err_list) +{ + PJ_USE_EXCEPTION; + int ch; + pjsip_msg *msg; + pjsip_ctype_hdr *ctype_hdr = NULL; + pj_scanner *scanner = ctx->scanner; + pj_pool_t *pool = ctx->pool; + + /* Skip leading newlines. */ + ch = *scanner->curptr; + while (ch=='\r' || ch=='\n') { + pj_scan_get_char(scanner); + ch = *scanner->curptr; + } + + msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG); + + /* Parse request or status line */ + if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) == 0) { + msg->type = PJSIP_RESPONSE_MSG; + int_parse_status_line( scanner, &msg->line.status ); + } else { + msg->type = PJSIP_REQUEST_MSG; + int_parse_req_line(scanner, pool, &msg->line.req ); + } + + /* Parse headers. */ + do { + pj_str_t hname; + pjsip_parse_hdr_func * handler; + pjsip_hdr *hdr = NULL; + + /* Get hname. */ + pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &hname); + ch = pj_scan_get_char( scanner ); + if (ch != ':') { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + } + + /* Find handler. */ + handler = find_handler(&hname); + + PJ_TRY { + /* Call the handler if found. + * If no handler is found, then treat the header as generic + * hname/hvalue pair. + */ + if (handler) { + hdr = (*handler)(ctx); + } else { + hdr = parse_hdr_generic_string(ctx); + hdr->type = PJSIP_H_OTHER; + hdr->name = hdr->sname = hname; + } + + /* Check if we've just parsed a Content-Type header. + * We will check for a message body if we've got Content-Type header. + */ + if (hdr->type == PJSIP_H_CONTENT_TYPE) { + ctype_hdr = (pjsip_ctype_hdr*)hdr; + } + + } + PJ_DEFAULT { + /* Exception was thrown during parsing. + * Skip until newline, and parse next header. + */ + pj_str_t token; + hdr = NULL; + + //PJ_LOG(4,("sipparser", + // "Syntax error in line %d col %d (hname=%.*s)", + // scanner->line, scanner->col, hname.slen, hname.ptr)); + + if (err_list) { + pjsip_parser_err_report *err_info; + + err_info = pj_pool_alloc(pool, sizeof(*err_info)); + err_info->exception_code = PJ_GET_EXCEPTION(); + err_info->line = scanner->line; + err_info->col = scanner->col; + err_info->hname = hname; + + pj_list_insert_before(err_list, err_info); + } + + if (!pj_scan_is_eof(scanner)) { + pj_scan_get_until(scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &token); + parse_hdr_end(scanner); + } + } + PJ_END; + + if (hdr) { + /* Single parse of header line can produce multiple headers. + * For example, if one Contact: header contains Contact list + * separated by comma, then these Contacts will be split into + * different Contact headers. + * So here we must insert list instead of just insert one header. + */ + pj_list_insert_nodes_before(&msg->hdr, hdr); + } + + /* Parse until EOF or an empty line is found. */ + } while (!pj_scan_is_eof(scanner) && + *scanner->curptr != '\r' && *scanner->curptr != '\n'); + + /* If empty line is found, eat it. */ + if (!pj_scan_is_eof(scanner)) { + if (*scanner->curptr=='\r' || *scanner->curptr=='\n') { + pj_scan_get_newline(scanner); + } + } + + /* If we have Content-Type header, treat the rest of the message as body.*/ + if (ctype_hdr) { + pjsip_msg_body *body = pj_pool_alloc(pool, sizeof(pjsip_msg_body)); + pj_strdup(pool, &body->content_type.type, &ctype_hdr->media.type); + pj_strdup(pool, &body->content_type.subtype, &ctype_hdr->media.subtype); + pj_strdup(pool, &body->content_type.param, &ctype_hdr->media.param); + body->data = scanner->curptr; + body->len = scanner->end - scanner->curptr; + body->print_body = &generic_print_body; + + msg->body = body; + } + + return msg; +} + +/* Parse parameter (pname ["=" pvalue]). */ +void pjsip_parse_param_imp( pj_scanner *scanner, pj_pool_t *pool, + pj_str_t *pname, pj_str_t *pvalue, + unsigned option) +{ + /* pname */ + pj_scan_get(scanner, &pjsip_PARAM_CHAR_SPEC, pname); + *pname = pj_str_unescape(pool, pname); + + /* pvalue, if any */ + if (*scanner->curptr == '=') { + pj_scan_get_char(scanner); + /* pvalue can be a quoted string. */ + if (*scanner->curptr == '"') { + pj_scan_get_quote( scanner, '"', '"', pvalue); + if (option & PJSIP_PARSE_REMOVE_QUOTE) { + pvalue->ptr++; + pvalue->slen -= 2; + } + } else { + pj_scan_get(scanner, &pjsip_PARAM_CHAR_SPEC, pvalue); + *pvalue = pj_str_unescape(pool, pvalue); + } + } else { + pvalue->ptr = NULL; + pvalue->slen = 0; + } +} + +/* Parse parameter (";" pname ["=" pvalue]). */ +static void int_parse_param( pj_scanner *scanner, pj_pool_t *pool, + pj_str_t *pname, pj_str_t *pvalue) +{ + /* Get ';' character */ + pj_scan_get_char(scanner); + + /* Get pname and optionally pvalue */ + pjsip_parse_param_imp(scanner, pool, pname, pvalue, + PJSIP_PARSE_REMOVE_QUOTE); +} + +/* Parse header parameter. */ +static void int_parse_hparam( pj_scanner *scanner, pj_pool_t *pool, + pj_str_t *hname, pj_str_t *hvalue ) +{ + /* Get '?' or '&' character. */ + pj_scan_get_char(scanner); + + /* hname */ + pj_scan_get(scanner, &pjsip_HDR_CHAR_SPEC, hname); + *hname = pj_str_unescape(pool, hname); + + /* pvalue, if any */ + if (*scanner->curptr == '=') { + pj_scan_get_char(scanner); + pj_scan_get(scanner, &pjsip_HDR_CHAR_SPEC, hvalue); + *hvalue = pj_str_unescape(pool, hvalue); + } else { + hvalue->ptr = NULL; + hvalue->slen = 0; + } +} + +/* Parse host:port in URI. */ +static void int_parse_uri_host_port( pj_scanner *scanner, + pj_str_t *host, int *p_port) +{ + pj_scan_get( scanner, &pjsip_HOST_SPEC, host); + /* RFC3261 section 19.1.2: host don't need to be unescaped */ + if (*scanner->curptr == ':') { + pj_str_t port; + pj_scan_get_char(scanner); + pj_scan_get(scanner, &pjsip_DIGIT_SPEC, &port); + *p_port = pj_strtoul(&port); + } else { + *p_port = 0; + } +} + +/* Determine if the next token in an URI is a user specification. */ +static int int_is_next_user(pj_scanner *scanner) +{ + pj_str_t dummy; + int is_user; + + /* Find character '@'. If this character exist, then the token + * must be a username. + */ + if (pj_scan_peek( scanner, &pjsip_PROBE_USER_HOST_SPEC, &dummy) == '@') + is_user = 1; + else + is_user = 0; + + return is_user; +} + +/* Parse user:pass tokens in an URI. */ +static void int_parse_user_pass( pj_scanner *scanner, pj_pool_t *pool, + pj_str_t *user, pj_str_t *pass) +{ + pj_scan_get( scanner, &pjsip_USER_SPEC, user); + *user = pj_str_unescape(pool, user); + + if ( *scanner->curptr == ':') { + pj_scan_get_char( scanner ); + pj_scan_get( scanner, &pjsip_PASSWD_SPEC, pass); + *pass = pj_str_unescape(pool, pass); + } else { + pass->ptr = NULL; + pass->slen = 0; + } + + /* Get the '@' */ + pj_scan_get_char( scanner ); +} + +/* Parse all types of URI. */ +static pjsip_uri *int_parse_uri_or_name_addr( pj_scanner *scanner, pj_pool_t *pool, + unsigned opt) +{ + pjsip_uri *uri; + int is_name_addr = 0; + + /* Exhaust any whitespaces. */ + pj_scan_skip_whitespace(scanner); + + if (*scanner->curptr=='"' || *scanner->curptr=='<') { + uri = (pjsip_uri*)int_parse_name_addr( scanner, pool ); + is_name_addr = 1; + } else { + pj_scan_state backtrack; + pj_str_t scheme; + int colon; + + pj_scan_save_state( scanner, &backtrack); + pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &scheme); + colon = pj_scan_get_char( scanner ); + pj_scan_restore_state( scanner, &backtrack); + + if (colon==':' && + (parser_stricmp(scheme, pjsip_SIP_STR)==0 || + parser_stricmp(scheme, pjsip_SIPS_STR)==0)) + { + uri = (pjsip_uri*) + int_parse_sip_url( scanner, pool, + (opt & PJSIP_PARSE_URI_IN_FROM_TO_HDR)== 0); + + } else if (colon==':' && parser_stricmp( scheme, pjsip_TEL_STR)==0) { + + /* Not supported. */ + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + UNREACHED({return NULL; /* Not reached. */}); + + } else { + uri = (pjsip_uri*)int_parse_name_addr( scanner, pool ); + is_name_addr = 1; + } + } + + /* Should we return the URI object as name address? */ + if (opt & PJSIP_PARSE_URI_AS_NAMEADDR) { + if (is_name_addr == 0) { + pjsip_name_addr *name_addr; + + name_addr = pjsip_name_addr_create(pool); + name_addr->uri = uri; + + uri = (pjsip_uri*)name_addr; + } + } + + return uri; +} + +/* Parse URI. */ +static pjsip_uri *int_parse_uri(pj_scanner *scanner, pj_pool_t *pool, + pj_bool_t parse_params) +{ + if (*scanner->curptr=='"' || *scanner->curptr=='<') { + return (pjsip_uri*)int_parse_name_addr( scanner, pool ); + } else { + pj_str_t scheme; + int colon; + + /* Get scheme. */ + colon = pj_scan_peek(scanner, &pjsip_TOKEN_SPEC, &scheme); + if (colon != ':') { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + } + + if ((parser_stricmp(scheme, pjsip_SIP_STR)==0 || + parser_stricmp(scheme, pjsip_SIPS_STR)==0)) + { + return (pjsip_uri*)int_parse_sip_url( scanner, pool, parse_params); + + } else if (parser_stricmp(scheme, pjsip_TEL_STR)==0) { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + UNREACHED({ return NULL; /* Not reached. */ }) + + } else { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + UNREACHED({ return NULL; /* Not reached. */ }) + } + } +} + +/* Parse "sip:" and "sips:" URI. */ +static pjsip_url *int_parse_sip_url( pj_scanner *scanner, + pj_pool_t *pool, + pj_bool_t parse_params) +{ + pj_str_t scheme; + pjsip_url *url; + int colon; + int skip_ws = scanner->skip_ws; + int hsep = '?'; + scanner->skip_ws = 0; + + pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &scheme); + colon = pj_scan_get_char(scanner); + if (colon != ':') { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + } + + if (parser_stricmp(scheme, pjsip_SIP_STR)==0) { + url = pjsip_url_create(pool, 0); + + } else if (parser_stricmp(scheme, pjsip_SIPS_STR)==0) { + url = pjsip_url_create(pool, 1); + + } else { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + /* should not reach here */ + UNREACHED({ + pj_assert(0); + return 0; + }) + } + + if (int_is_next_user(scanner)) { + int_parse_user_pass(scanner, pool, &url->user, &url->passwd); + } + + /* Get host:port */ + int_parse_uri_host_port(scanner, &url->host, &url->port); + + /* Get URL parameters. */ + while ( parse_params && *scanner->curptr == ';' ) { + pj_str_t pname, pvalue; + + int_parse_param( scanner, pool, &pname, &pvalue); + + if (!parser_stricmp(pname, pjsip_USER_STR) && pvalue.slen) { + url->user_param = pvalue; + + } else if (!parser_stricmp(pname, pjsip_METHOD_STR) && pvalue.slen) { + url->method_param = pvalue; + + } else if (!parser_stricmp(pname,pjsip_TRANSPORT_STR) && pvalue.slen) { + url->transport_param = pvalue; + + } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) { + url->ttl_param = pj_strtoul(&pvalue); + + } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) { + url->maddr_param = pvalue; + + } else if (!parser_stricmp(pname, pjsip_LR_STR)) { + url->lr_param = 1; + + } else { + pjsip_param *p = pj_pool_alloc(pool, sizeof(pjsip_param)); + p->name = pname; + p->value = pvalue; + pj_list_insert_before(&url->other_param, p); + } + } + + /* Get header params. */ + while (parse_params && *scanner->curptr == hsep) { + pjsip_param *param; + param = pj_pool_alloc(pool, sizeof(pjsip_param)); + int_parse_hparam(scanner, pool, ¶m->name, ¶m->value); + pj_list_insert_before(&url->header_param, param); + hsep = '&'; + } + + scanner->skip_ws = skip_ws; + pj_scan_skip_whitespace(scanner); + return url; +} + +/* Parse nameaddr. */ +static pjsip_name_addr *int_parse_name_addr( pj_scanner *scanner, + pj_pool_t *pool ) +{ + int has_bracket; + pjsip_name_addr *name_addr; + + name_addr = pjsip_name_addr_create(pool); + + if (*scanner->curptr == '"') { + pj_scan_get_quote( scanner, '"', '"', &name_addr->display); + /* Trim the leading and ending quote */ + name_addr->display.ptr++; + name_addr->display.slen -= 2; + + } else if (*scanner->curptr != '<') { + int next; + pj_str_t dummy; + + /* This can be either the start of display name, + * the start of URL ("sip:", "sips:", "tel:", etc.), or '<' char. + * We're only interested in display name, because SIP URL + * will be parser later. + */ + next = pj_scan_peek_until(scanner, &pjsip_DISPLAY_SCAN_SPEC, &dummy); + if (next == '<') { + /* Ok, this is what we're looking for, a display name. */ + pj_scan_get_until_ch( scanner, '<', &name_addr->display); + pj_strtrim(&name_addr->display); + } + } + + /* Manually skip whitespace. */ + pj_scan_skip_whitespace(scanner); + + /* Get the SIP-URL */ + has_bracket = (*scanner->curptr == '<'); + if (has_bracket) + pj_scan_get_char(scanner); + name_addr->uri = int_parse_uri( scanner, pool, PJ_TRUE ); + if (has_bracket) + pj_scan_get_char(scanner); + + return name_addr; +} + + +/* Parse SIP request line. */ +static void int_parse_req_line( pj_scanner *scanner, pj_pool_t *pool, + pjsip_request_line *req_line) +{ + pj_str_t token; + + pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &token); + pjsip_method_init_np( &req_line->method, &token); + + req_line->uri = int_parse_uri(scanner, pool, PJ_TRUE); + if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) != 0) + PJ_THROW( PJSIP_SYN_ERR_EXCEPTION); + pj_scan_advance_n (scanner, 7, 1); + pj_scan_get_newline( scanner ); +} + +/* Parse status line. */ +static void int_parse_status_line( pj_scanner *scanner, + pjsip_status_line *status_line) +{ + pj_str_t token; + + if (pj_scan_stricmp(scanner, PJSIP_VERSION, 7) != 0) + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + pj_scan_advance_n( scanner, 7, 1); + + pj_scan_get( scanner, &pjsip_DIGIT_SPEC, &token); + status_line->code = pj_strtoul(&token); + pj_scan_get_until( scanner, &pjsip_NEWLINE_OR_EOF_SPEC, + &status_line->reason); + pj_scan_get_newline( scanner ); +} + +/* Parse ending of header. */ +static void parse_hdr_end( pj_scanner *scanner ) +{ + if (pj_scan_is_eof(scanner)) { + ; /* Do nothing. */ + } else if (*scanner->curptr == '&') { + pj_scan_get_char(scanner); + } else { + pj_scan_get_newline(scanner); + } +} + +/* Parse ending of header. */ +void pjsip_parse_end_hdr_imp( pj_scanner *scanner ) +{ + parse_hdr_end(scanner); +} + +/* Parse generic array header. */ +static void parse_generic_array_hdr( pjsip_generic_array_hdr *hdr, + pj_scanner *scanner) +{ + pj_scan_get_until( scanner, &pjsip_ARRAY_ELEMENTS, &hdr->values[0]); + hdr->count++; + + while (*scanner->curptr == ',') { + pj_scan_get_char(scanner); + pj_scan_get_until( scanner, &pjsip_ARRAY_ELEMENTS, + &hdr->values[hdr->count]); + hdr->count++; + } + parse_hdr_end(scanner); +} + +/* Parse generic string header. */ +static void parse_generic_string_hdr( pjsip_generic_string_hdr *hdr, + pj_scanner *scanner ) +{ + pj_scan_get_until( scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &hdr->hvalue); + parse_hdr_end(scanner); +} + +/* Parse generic integer header. */ +static void parse_generic_int_hdr( pjsip_generic_int_hdr *hdr, + pj_scanner *scanner ) +{ + pj_str_t tmp; + pj_scan_get_until( scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &tmp); + hdr->ivalue = pj_strtoul(&tmp); + parse_hdr_end(scanner); +} + + +/* Parse Accept header. */ +static pjsip_hdr* parse_hdr_accept(pjsip_parse_ctx *ctx) +{ + pjsip_accept_hdr *accept = pjsip_accept_hdr_create(ctx->pool); + parse_generic_array_hdr(accept, ctx->scanner); + return (pjsip_hdr*)accept; +} + +/* Parse Allow header. */ +static pjsip_hdr* parse_hdr_allow(pjsip_parse_ctx *ctx) +{ + pjsip_allow_hdr *allow = pjsip_allow_hdr_create(ctx->pool); + parse_generic_array_hdr(allow, ctx->scanner); + return (pjsip_hdr*)allow; +} + +/* Parse Call-ID header. */ +static pjsip_hdr* parse_hdr_call_id(pjsip_parse_ctx *ctx) +{ + pjsip_cid_hdr *hdr = pjsip_cid_hdr_create(ctx->pool); + pj_scan_get_until( ctx->scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &hdr->id); + parse_hdr_end(ctx->scanner); + + if (ctx->rdata) + ctx->rdata->msg_info.call_id = hdr->id; + + return (pjsip_hdr*)hdr; +} + +/* Parse and interpret Contact param. */ +static void int_parse_contact_param( pjsip_contact_hdr *hdr, + pj_scanner *scanner, + pj_pool_t *pool) +{ + while ( *scanner->curptr == ';' ) { + pj_str_t pname, pvalue; + + int_parse_param( scanner, pool, &pname, &pvalue); + if (!parser_stricmp(pname, pjsip_Q_STR) && pvalue.slen) { + char *dot_pos = memchr(pvalue.ptr, '.', pvalue.slen); + if (!dot_pos) { + hdr->q1000 = pj_strtoul(&pvalue); + } else { + pvalue.slen = (pvalue.ptr+pvalue.slen) - (dot_pos+1); + pvalue.ptr = dot_pos + 1; + hdr->q1000 = pj_strtoul_mindigit(&pvalue, 3); + } + } else if (!parser_stricmp(pname, pjsip_EXPIRES_STR) && pvalue.slen) { + hdr->expires = pj_strtoul(&pvalue); + + } else { + pjsip_param *p = pj_pool_alloc(pool, sizeof(pjsip_param)); + p->name = pname; + p->value = pvalue; + pj_list_insert_before(&hdr->other_param, p); + } + } +} + +/* Parse Contact header. */ +static pjsip_hdr* parse_hdr_contact( pjsip_parse_ctx *ctx ) +{ + pjsip_contact_hdr *first = NULL; + pj_scanner *scanner = ctx->scanner; + + do { + pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(ctx->pool); + if (first == NULL) + first = hdr; + else + pj_list_insert_before(first, hdr); + + if (*scanner->curptr == '*') { + pj_scan_get_char(scanner); + hdr->star = 1; + + } else { + hdr->star = 0; + hdr->uri = int_parse_uri_or_name_addr(scanner, ctx->pool, + PJSIP_PARSE_URI_AS_NAMEADDR); + + int_parse_contact_param(hdr, scanner, ctx->pool); + } + + if (*scanner->curptr != ',') + break; + + pj_scan_get_char(scanner); + + } while (1); + + parse_hdr_end(scanner); + + return (pjsip_hdr*)first; +} + +/* Parse Content-Length header. */ +static pjsip_hdr* parse_hdr_content_len( pjsip_parse_ctx *ctx ) +{ + pj_str_t digit; + pjsip_clen_hdr *hdr; + + hdr = pjsip_clen_hdr_create(ctx->pool); + pj_scan_get(ctx->scanner, &pjsip_DIGIT_SPEC, &digit); + hdr->len = pj_strtoul(&digit); + parse_hdr_end(ctx->scanner); + + if (ctx->rdata) + ctx->rdata->msg_info.clen = hdr; + + return (pjsip_hdr*)hdr; +} + +/* Parse Content-Type header. */ +static pjsip_hdr* parse_hdr_content_type( pjsip_parse_ctx *ctx ) +{ + pjsip_ctype_hdr *hdr; + pj_scanner *scanner = ctx->scanner; + + hdr = pjsip_ctype_hdr_create(ctx->pool); + + /* Parse media type and subtype. */ + pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->media.type); + pj_scan_get_char(scanner); + pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->media.subtype); + + /* Parse media parameters */ + while (*scanner->curptr == ';') { + pj_str_t pname, pvalue; + int_parse_param(scanner, ctx->pool, &pname, &pvalue); + concat_param(&hdr->media.param, ctx->pool, &pname, &pvalue); + } + + parse_hdr_end(ctx->scanner); + + if (ctx->rdata) + ctx->rdata->msg_info.ctype = hdr; + + return (pjsip_hdr*)hdr; +} + +/* Parse CSeq header. */ +static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx ) +{ + pj_str_t cseq, method; + pjsip_cseq_hdr *hdr; + + hdr = pjsip_cseq_hdr_create(ctx->pool); + pj_scan_get( ctx->scanner, &pjsip_DIGIT_SPEC, &cseq); + hdr->cseq = pj_strtoul(&cseq); + + pj_scan_get( ctx->scanner, &pjsip_TOKEN_SPEC, &method); + pjsip_method_init_np(&hdr->method, &method); + + parse_hdr_end( ctx->scanner ); + + if (ctx->rdata) + ctx->rdata->msg_info.cseq = hdr; + + return (pjsip_hdr*)hdr; +} + +/* Parse Expires header. */ +static pjsip_hdr* parse_hdr_expires(pjsip_parse_ctx *ctx) +{ + pjsip_expires_hdr *hdr = pjsip_expires_hdr_create(ctx->pool); + parse_generic_int_hdr(hdr, ctx->scanner); + return (pjsip_hdr*)hdr; +} + +/* Parse From: or To: header. */ +static void parse_hdr_fromto( pj_scanner *scanner, + pj_pool_t *pool, + pjsip_from_hdr *hdr) +{ + hdr->uri = int_parse_uri_or_name_addr(scanner, pool, + PJSIP_PARSE_URI_AS_NAMEADDR | + PJSIP_PARSE_URI_IN_FROM_TO_HDR); + + while ( *scanner->curptr == ';' ) { + pj_str_t pname, pvalue; + + int_parse_param( scanner, pool, &pname, &pvalue); + + if (!parser_stricmp(pname, pjsip_TAG_STR)) { + hdr->tag = pvalue; + + } else { + pjsip_param *p = pj_pool_alloc(pool, sizeof(pjsip_param)); + p->name = pname; + p->value = pvalue; + pj_list_insert_before(&hdr->other_param, p); + } + } + + parse_hdr_end(scanner); +} + +/* Parse From: header. */ +static pjsip_hdr* parse_hdr_from( pjsip_parse_ctx *ctx ) +{ + pjsip_from_hdr *hdr = pjsip_from_hdr_create(ctx->pool); + parse_hdr_fromto(ctx->scanner, ctx->pool, hdr); + if (ctx->rdata) + ctx->rdata->msg_info.from = hdr; + + return (pjsip_hdr*)hdr; +} + +/* Parse Require: header. */ +static pjsip_hdr* parse_hdr_require( pjsip_parse_ctx *ctx ) +{ + pjsip_require_hdr *hdr = pjsip_require_hdr_create(ctx->pool); + parse_generic_array_hdr(hdr, ctx->scanner); + + if (ctx->rdata && ctx->rdata->msg_info.require == NULL) + ctx->rdata->msg_info.require = hdr; + + return (pjsip_hdr*)hdr; +} + +/* Parse Retry-After: header. */ +static pjsip_hdr* parse_hdr_retry_after(pjsip_parse_ctx *ctx) +{ + pjsip_retry_after_hdr *hdr; + hdr = pjsip_retry_after_hdr_create(ctx->pool); + parse_generic_int_hdr(hdr, ctx->scanner); + return (pjsip_hdr*)hdr; +} + +/* Parse Supported: header. */ +static pjsip_hdr* parse_hdr_supported(pjsip_parse_ctx *ctx) +{ + pjsip_supported_hdr *hdr = pjsip_supported_hdr_create(ctx->pool); + parse_generic_array_hdr(hdr, ctx->scanner); + return (pjsip_hdr*)hdr; +} + + +/* Parse To: header. */ +static pjsip_hdr* parse_hdr_to( pjsip_parse_ctx *ctx ) +{ + pjsip_to_hdr *hdr = pjsip_to_hdr_create(ctx->pool); + parse_hdr_fromto(ctx->scanner, ctx->pool, hdr); + + if (ctx->rdata) + ctx->rdata->msg_info.to = hdr; + + return (pjsip_hdr*)hdr; +} + +/* Parse Unsupported: header. */ +static pjsip_hdr* parse_hdr_unsupported(pjsip_parse_ctx *ctx) +{ + pjsip_unsupported_hdr *hdr = pjsip_unsupported_hdr_create(ctx->pool); + parse_generic_array_hdr(hdr, ctx->scanner); + return (pjsip_hdr*)hdr; +} + +/* Parse and interpret Via parameters. */ +static void int_parse_via_param( pjsip_via_hdr *hdr, pj_scanner *scanner, + pj_pool_t *pool) +{ + while ( *scanner->curptr == ';' ) { + pj_str_t pname, pvalue; + + int_parse_param( scanner, pool, &pname, &pvalue); + + if (!parser_stricmp(pname, pjsip_BRANCH_STR) && pvalue.slen) { + hdr->branch_param = pvalue; + + } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) { + hdr->ttl_param = pj_strtoul(&pvalue); + + } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) { + hdr->maddr_param = pvalue; + + } else if (!parser_stricmp(pname, pjsip_PNAME_STR) && pvalue.slen) { + hdr->recvd_param = pvalue; + + } else if (!parser_stricmp(pname, pjsip_RPORT_STR)) { + if (pvalue.slen) + hdr->rport_param = pj_strtoul(&pvalue); + else + hdr->rport_param = 0; + } else { + pjsip_param *p = pj_pool_alloc(pool, sizeof(pjsip_param)); + p->name = pname; + p->value = pvalue; + pj_list_insert_before(&hdr->other_param, p); + } + } +} + +/* Parse Max-Forwards header. */ +static pjsip_hdr* parse_hdr_max_forwards( pjsip_parse_ctx *ctx ) +{ + pjsip_max_forwards_hdr *hdr; + hdr = pjsip_max_forwards_hdr_create(ctx->pool); + parse_generic_int_hdr(hdr, ctx->scanner); + + if (ctx->rdata) + ctx->rdata->msg_info.max_fwd = hdr; + + return (pjsip_hdr*)hdr; +} + +/* Parse Min-Expires header. */ +static pjsip_hdr* parse_hdr_min_expires(pjsip_parse_ctx *ctx) +{ + pjsip_min_expires_hdr *hdr; + hdr = pjsip_min_expires_hdr_create(ctx->pool); + parse_generic_int_hdr(hdr, ctx->scanner); + return (pjsip_hdr*)hdr; +} + + +/* Parse Route: or Record-Route: header. */ +static void parse_hdr_rr_route( pj_scanner *scanner, pj_pool_t *pool, + pjsip_routing_hdr *hdr ) +{ + pjsip_name_addr *temp=int_parse_name_addr(scanner, pool); + + pj_memcpy(&hdr->name_addr, temp, sizeof(*temp)); + + while (*scanner->curptr == ';') { + pjsip_param *p = pj_pool_alloc(pool, sizeof(pjsip_param)); + int_parse_param(scanner, pool, &p->name, &p->value); + pj_list_insert_before(&hdr->other_param, p); + } +} + +/* Parse Record-Route header. */ +static pjsip_hdr* parse_hdr_rr( pjsip_parse_ctx *ctx) +{ + pjsip_rr_hdr *first = NULL; + pj_scanner *scanner = ctx->scanner; + + do { + pjsip_rr_hdr *hdr = pjsip_rr_hdr_create(ctx->pool); + if (!first) { + first = hdr; + } else { + pj_list_insert_before(first, hdr); + } + parse_hdr_rr_route(scanner, ctx->pool, hdr); + if (*scanner->curptr == ',') { + pj_scan_get_char(scanner); + } else { + break; + } + } while (1); + parse_hdr_end(scanner); + + if (ctx->rdata && ctx->rdata->msg_info.record_route==NULL) + ctx->rdata->msg_info.record_route = first; + + return (pjsip_hdr*)first; +} + +/* Parse Route: header. */ +static pjsip_hdr* parse_hdr_route( pjsip_parse_ctx *ctx ) +{ + pjsip_route_hdr *first = NULL; + pj_scanner *scanner = ctx->scanner; + + do { + pjsip_route_hdr *hdr = pjsip_route_hdr_create(ctx->pool); + if (!first) { + first = hdr; + } else { + pj_list_insert_before(first, hdr); + } + parse_hdr_rr_route(scanner, ctx->pool, hdr); + if (*scanner->curptr == ',') { + pj_scan_get_char(scanner); + } else { + break; + } + } while (1); + parse_hdr_end(scanner); + + if (ctx->rdata && ctx->rdata->msg_info.route==NULL) + ctx->rdata->msg_info.route = first; + + return (pjsip_hdr*)first; +} + +/* Parse Via: header. */ +static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx ) +{ + pjsip_via_hdr *first = NULL; + pj_scanner *scanner = ctx->scanner; + + do { + pjsip_via_hdr *hdr = pjsip_via_hdr_create(ctx->pool); + if (!first) + first = hdr; + else + pj_list_insert_before(first, hdr); + + if (pj_scan_stricmp( scanner, PJSIP_VERSION "/", 8) != 0) + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + + pj_scan_advance_n( scanner, 8, 1); + + pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &hdr->transport); + pj_scan_get( scanner, &pjsip_HOST_SPEC, &hdr->sent_by.host); + + if (*scanner->curptr==':') { + pj_str_t digit; + pj_scan_get_char(scanner); + pj_scan_get(scanner, &pjsip_DIGIT_SPEC, &digit); + hdr->sent_by.port = pj_strtoul(&digit); + } else { + hdr->sent_by.port = 5060; + } + + int_parse_via_param(hdr, scanner, ctx->pool); + + if (*scanner->curptr == '(') { + pj_scan_get_char(scanner); + pj_scan_get_until_ch( scanner, ')', &hdr->comment); + pj_scan_get_char( scanner ); + } + + if (*scanner->curptr != ',') + break; + + pj_scan_get_char(scanner); + + } while (1); + + parse_hdr_end(scanner); + + if (ctx->rdata && ctx->rdata->msg_info.via == NULL) + ctx->rdata->msg_info.via = first; + + return (pjsip_hdr*)first; +} + +/* Parse generic header. */ +static pjsip_hdr* parse_hdr_generic_string( pjsip_parse_ctx *ctx ) +{ + pjsip_generic_string_hdr *hdr; + + hdr = pjsip_generic_string_hdr_create(ctx->pool, NULL); + parse_generic_string_hdr(hdr, ctx->scanner); + return (pjsip_hdr*)hdr; + +} + +/* Public function to parse a header value. */ +PJ_DEF(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname, + char *buf, pj_size_t size, int *parsed_len ) +{ + pj_scanner scanner; + pjsip_hdr *hdr = NULL; + pjsip_parse_ctx context; + PJ_USE_EXCEPTION; + + init_sip_parser(); + + pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, + &on_syntax_error); + + context.scanner = &scanner; + context.pool = pool; + context.rdata = NULL; + + PJ_TRY { + pjsip_parse_hdr_func *handler = find_handler(hname); + if (handler) { + hdr = (*handler)(&context); + } else { + hdr = parse_hdr_generic_string(&context); + hdr->type = PJSIP_H_OTHER; + pj_strdup(pool, &hdr->name, hname); + hdr->sname = hdr->name; + } + + } + PJ_DEFAULT { + hdr = NULL; + } + PJ_END + + if (parsed_len) { + *parsed_len = (scanner.curptr - scanner.begin); + } + + pj_scan_fini(&scanner); + + return hdr; +} + diff --git a/pjsip/src/pjsip/sip_resolve.c b/pjsip/src/pjsip/sip_resolve.c index 2008fa97..b89b7c01 100644 --- a/pjsip/src/pjsip/sip_resolve.c +++ b/pjsip/src/pjsip/sip_resolve.c @@ -1,125 +1,125 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_resolve.h>
-#include <pjsip/sip_transport.h>
-#include <pj/pool.h>
-#include <pj/ctype.h>
-#include <pj/assert.h>
-
-struct pjsip_resolver_t
-{
- void *dummy;
-};
-
-PJ_DEF(pjsip_resolver_t*) pjsip_resolver_create(pj_pool_t *pool)
-{
- pjsip_resolver_t *resolver;
- resolver = (pjsip_resolver_t*) pj_pool_calloc(pool, 1, sizeof(*resolver));
- return resolver;
-}
-
-PJ_DEF(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver)
-{
- PJ_UNUSED_ARG(resolver);
-}
-
-static int is_str_ip(const pj_str_t *host)
-{
- const char *p = host->ptr;
- const char *end = ((const char*)host->ptr) + host->slen;
-
- while (p != end) {
- if (pj_isdigit(*p) || *p=='.') {
- ++p;
- } else {
- return 0;
- }
- }
- return 1;
-}
-
-PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver,
- pj_pool_t *pool,
- pjsip_host_port *target,
- void *token,
- pjsip_resolver_callback *cb)
-{
- struct pjsip_server_addresses svr_addr;
- pj_status_t status;
- int is_ip_addr;
- pjsip_transport_type_e type = target->type;
-
- PJ_UNUSED_ARG(resolver);
- PJ_UNUSED_ARG(pool);
-
- /* We only do synchronous resolving at this moment. */
- PJ_TODO(SUPPORT_RFC3263_SERVER_RESOLUTION)
-
- /* Is it IP address or hostname?. */
- is_ip_addr = is_str_ip(&target->host);
-
- /* Set the transport type if not explicitly specified.
- * RFC 3263 section 4.1 specify rules to set up this.
- */
- if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
- if (is_ip_addr || (target->port != 0)) {
-#if PJ_HAS_TCP
- if (target->flag & PJSIP_TRANSPORT_SECURE)
- {
- type = PJSIP_TRANSPORT_TLS;
- } else if (target->flag & PJSIP_TRANSPORT_RELIABLE)
- {
- type = PJSIP_TRANSPORT_TCP;
- } else
-#endif
- {
- type = PJSIP_TRANSPORT_UDP;
- }
- } else {
- /* No type or explicit port is specified, and the address is
- * not IP address.
- * In this case, full resolution must be performed.
- * But we don't support it (yet).
- */
- type = PJSIP_TRANSPORT_UDP;
- }
-
- }
-
- /* Set the port number if not specified. */
- if (target->port == 0) {
- target->port = pjsip_transport_get_default_port_for_type(type);
- }
-
- /* Resolve hostname. */
- if (!is_ip_addr) {
- status = pj_sockaddr_in_init(&svr_addr.entry[0].addr, &target->host,
- (pj_uint16_t)target->port);
- } else {
- status = pj_sockaddr_in_init(&svr_addr.entry[0].addr, &target->host,
- (pj_uint16_t)target->port);
- pj_assert(status == PJ_SUCCESS);
- }
-
- /* Call the callback. */
- svr_addr.count = (status == PJ_SUCCESS) ? 1 : 0;
- svr_addr.entry[0].type = type;
- (*cb)(status, token, &svr_addr);
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip/sip_resolve.h> +#include <pjsip/sip_transport.h> +#include <pj/pool.h> +#include <pj/ctype.h> +#include <pj/assert.h> + +struct pjsip_resolver_t +{ + void *dummy; +}; + +PJ_DEF(pjsip_resolver_t*) pjsip_resolver_create(pj_pool_t *pool) +{ + pjsip_resolver_t *resolver; + resolver = (pjsip_resolver_t*) pj_pool_calloc(pool, 1, sizeof(*resolver)); + return resolver; +} + +PJ_DEF(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver) +{ + PJ_UNUSED_ARG(resolver); +} + +static int is_str_ip(const pj_str_t *host) +{ + const char *p = host->ptr; + const char *end = ((const char*)host->ptr) + host->slen; + + while (p != end) { + if (pj_isdigit(*p) || *p=='.') { + ++p; + } else { + return 0; + } + } + return 1; +} + +PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, + pj_pool_t *pool, + pjsip_host_port *target, + void *token, + pjsip_resolver_callback *cb) +{ + struct pjsip_server_addresses svr_addr; + pj_status_t status; + int is_ip_addr; + pjsip_transport_type_e type = target->type; + + PJ_UNUSED_ARG(resolver); + PJ_UNUSED_ARG(pool); + + /* We only do synchronous resolving at this moment. */ + PJ_TODO(SUPPORT_RFC3263_SERVER_RESOLUTION) + + /* Is it IP address or hostname?. */ + is_ip_addr = is_str_ip(&target->host); + + /* Set the transport type if not explicitly specified. + * RFC 3263 section 4.1 specify rules to set up this. + */ + if (type == PJSIP_TRANSPORT_UNSPECIFIED) { + if (is_ip_addr || (target->port != 0)) { +#if PJ_HAS_TCP + if (target->flag & PJSIP_TRANSPORT_SECURE) + { + type = PJSIP_TRANSPORT_TLS; + } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) + { + type = PJSIP_TRANSPORT_TCP; + } else +#endif + { + type = PJSIP_TRANSPORT_UDP; + } + } else { + /* No type or explicit port is specified, and the address is + * not IP address. + * In this case, full resolution must be performed. + * But we don't support it (yet). + */ + type = PJSIP_TRANSPORT_UDP; + } + + } + + /* Set the port number if not specified. */ + if (target->port == 0) { + target->port = pjsip_transport_get_default_port_for_type(type); + } + + /* Resolve hostname. */ + if (!is_ip_addr) { + status = pj_sockaddr_in_init(&svr_addr.entry[0].addr, &target->host, + (pj_uint16_t)target->port); + } else { + status = pj_sockaddr_in_init(&svr_addr.entry[0].addr, &target->host, + (pj_uint16_t)target->port); + pj_assert(status == PJ_SUCCESS); + } + + /* Call the callback. */ + svr_addr.count = (status == PJ_SUCCESS) ? 1 : 0; + svr_addr.entry[0].type = type; + (*cb)(status, token, &svr_addr); +} + diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c index bc4f21ff..eade0a4b 100644 --- a/pjsip/src/pjsip/sip_transaction.c +++ b/pjsip/src/pjsip/sip_transaction.c @@ -1,1995 +1,1995 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_config.h>
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_errno.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/os.h>
-#include <pj/guid.h>
-#include <pj/pool.h>
-#include <pj/assert.h>
-
-/* Thread Local Storage ID for transaction lock (initialized by endpoint) */
-long pjsip_tsx_lock_tls_id;
-
-/* State names */
-static const char *state_str[] =
-{
- "Null",
- "Calling",
- "Trying",
- "Proceeding",
- "Completed",
- "Confirmed",
- "Terminated",
- "Destroyed",
-};
-
-/* Role names */
-static const char *role_name[] =
-{
- "Client",
- "Server"
-};
-
-/* Transaction lock. */
-typedef struct tsx_lock_data {
- struct tsx_lock_data *prev;
- pjsip_transaction *tsx;
- int is_alive;
-} tsx_lock_data;
-
-
-/* Timer timeout value constants */
-static const pj_time_val t1_timer_val = { PJSIP_T1_TIMEOUT/1000,
- PJSIP_T1_TIMEOUT%1000 };
-static const pj_time_val t4_timer_val = { PJSIP_T4_TIMEOUT/1000,
- PJSIP_T4_TIMEOUT%1000 };
-static const pj_time_val td_timer_val = { PJSIP_TD_TIMEOUT/1000,
- PJSIP_TD_TIMEOUT%1000 };
-static const pj_time_val timeout_timer_val = { (64*PJSIP_T1_TIMEOUT)/1000,
- (64*PJSIP_T1_TIMEOUT)%1000 };
-
-/* Internal timer IDs */
-enum Transaction_Timer_Id
-{
- TSX_TIMER_RETRANSMISSION,
- TSX_TIMER_TIMEOUT,
-};
-
-/* Function Prototypes */
-static pj_status_t pjsip_tsx_on_state_null( pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_calling( pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_trying( pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_proceeding_uac( pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_confirmed(pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_terminated(pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_destroyed(pjsip_transaction *tsx,
- pjsip_event *event);
-
-static void tsx_timer_callback( pj_timer_heap_t *theap,
- pj_timer_entry *entry);
-static int tsx_send_msg( pjsip_transaction *tsx,
- pjsip_tx_data *tdata);
-static void lock_tsx( pjsip_transaction *tsx, struct
- tsx_lock_data *lck );
-static pj_status_t unlock_tsx( pjsip_transaction *tsx,
- struct tsx_lock_data *lck );
-
-/* State handlers for UAC, indexed by state */
-static int (*tsx_state_handler_uac[PJSIP_TSX_STATE_MAX])(pjsip_transaction *,
- pjsip_event *) =
-{
- &pjsip_tsx_on_state_null,
- &pjsip_tsx_on_state_calling,
- &pjsip_tsx_on_state_trying,
- &pjsip_tsx_on_state_proceeding_uac,
- &pjsip_tsx_on_state_completed_uac,
- &pjsip_tsx_on_state_confirmed,
- &pjsip_tsx_on_state_terminated,
- &pjsip_tsx_on_state_destroyed,
-};
-
-/* State handlers for UAS */
-static int (*tsx_state_handler_uas[PJSIP_TSX_STATE_MAX])(pjsip_transaction *,
- pjsip_event *) =
-{
- &pjsip_tsx_on_state_null,
- &pjsip_tsx_on_state_calling,
- &pjsip_tsx_on_state_trying,
- &pjsip_tsx_on_state_proceeding_uas,
- &pjsip_tsx_on_state_completed_uas,
- &pjsip_tsx_on_state_confirmed,
- &pjsip_tsx_on_state_terminated,
- &pjsip_tsx_on_state_destroyed,
-};
-
-/*
- * Get transaction state name.
- */
-PJ_DEF(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state)
-{
- return state_str[state];
-}
-
-/*
- * Get the role name.
- */
-PJ_DEF(const char *) pjsip_role_name(pjsip_role_e role)
-{
- return role_name[role];
-}
-
-
-/*
- * Create transaction key for RFC2543 compliant messages, which don't have
- * unique branch parameter in the top most Via header.
- *
- * INVITE requests matches a transaction if the following attributes
- * match the original request:
- * - Request-URI
- * - To tag
- * - From tag
- * - Call-ID
- * - CSeq
- * - top Via header
- *
- * CANCEL matching is done similarly as INVITE, except:
- * - CSeq method will differ
- * - To tag is not matched.
- *
- * ACK matching is done similarly, except that:
- * - method of the CSeq will differ,
- * - To tag is matched to the response sent by the server transaction.
- *
- * The transaction key is constructed from the common components of above
- * components. Additional comparison is needed to fully match a transaction.
- */
-static pj_status_t create_tsx_key_2543( pj_pool_t *pool,
- pj_str_t *str,
- pjsip_role_e role,
- const pjsip_method *method,
- const pjsip_rx_data *rdata )
-{
-#define SEPARATOR '$'
- char *key, *p, *end;
- int len;
- pj_size_t len_required;
- pjsip_uri *req_uri;
- pj_str_t *host;
-
- PJ_ASSERT_RETURN(pool && str && method && rdata, PJ_EINVAL);
- PJ_ASSERT_RETURN(rdata->msg_info.msg, PJ_EINVAL);
- PJ_ASSERT_RETURN(rdata->msg_info.via, PJSIP_EMISSINGHDR);
- PJ_ASSERT_RETURN(rdata->msg_info.cseq, PJSIP_EMISSINGHDR);
- PJ_ASSERT_RETURN(rdata->msg_info.from, PJSIP_EMISSINGHDR);
-
- host = &rdata->msg_info.via->sent_by.host;
- req_uri = (pjsip_uri*)rdata->msg_info.msg->line.req.uri;
-
- /* Calculate length required. */
- len_required = 9 + /* CSeq number */
- rdata->msg_info.from->tag.slen + /* From tag. */
- rdata->msg_info.call_id.slen + /* Call-ID */
- host->slen + /* Via host. */
- 9 + /* Via port. */
- 16; /* Separator+Allowance. */
- key = p = pj_pool_alloc(pool, len_required);
- end = p + len_required;
-
- /* Add role. */
- *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's');
- *p++ = SEPARATOR;
-
- /* Add Request-URI */
- /* This is BUG!
- * Response doesn't have Request-URI!
- *
- len = req_uri->vptr->print( PJSIP_URI_IN_REQ_URI, req_uri, p, end-p );
- p += len;
- *p++ = SEPARATOR;
- */
-
- /* Add method, except when method is INVITE or ACK. */
- if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) {
- pj_memcpy(p, method->name.ptr, method->name.slen);
- p += method->name.slen;
- *p++ = '$';
- }
-
- /* Add CSeq (only the number). */
- len = pj_utoa(rdata->msg_info.cseq->cseq, p);
- p += len;
- *p++ = SEPARATOR;
-
- /* Add From tag. */
- len = rdata->msg_info.from->tag.slen;
- pj_memcpy( p, rdata->msg_info.from->tag.ptr, len);
- p += len;
- *p++ = SEPARATOR;
-
- /* Add Call-ID. */
- len = rdata->msg_info.call_id.slen;
- pj_memcpy( p, rdata->msg_info.call_id.ptr, len );
- p += len;
- *p++ = SEPARATOR;
-
- /* Add top Via header.
- * We don't really care whether the port contains the real port (because
- * it can be omited if default port is used). Anyway this function is
- * only used to match request retransmission, and we expect that the
- * request retransmissions will contain the same port.
- */
- pj_memcpy(p, host->ptr, host->slen);
- p += host->slen;
- *p++ = ':';
-
- len = pj_utoa(rdata->msg_info.via->sent_by.port, p);
- p += len;
- *p++ = SEPARATOR;
-
- *p++ = '\0';
-
- /* Done. */
- str->ptr = key;
- str->slen = p-key;
-
- return PJ_SUCCESS;
-}
-
-/*
- * Create transaction key for RFC3161 compliant system.
- */
-static pj_status_t create_tsx_key_3261( pj_pool_t *pool,
- pj_str_t *key,
- pjsip_role_e role,
- const pjsip_method *method,
- const pj_str_t *branch)
-{
- char *p;
-
- PJ_ASSERT_RETURN(pool && key && method && branch, PJ_EINVAL);
-
- p = key->ptr = pj_pool_alloc(pool, branch->slen + method->name.slen + 4 );
-
- /* Add role. */
- *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's');
- *p++ = SEPARATOR;
-
- /* Add method, except when method is INVITE or ACK. */
- if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) {
- pj_memcpy(p, method->name.ptr, method->name.slen);
- p += method->name.slen;
- *p++ = '$';
- }
-
- /* Add branch ID. */
- pj_memcpy(p, branch->ptr, branch->slen);
- p += branch->slen;
-
- /* Set length */
- key->slen = p - key->ptr;
-
- return PJ_SUCCESS;
-}
-
-/*
- * Create key from the incoming data, to be used to search the transaction
- * in the transaction hash table.
- */
-PJ_DEF(pj_status_t) pjsip_tsx_create_key( pj_pool_t *pool, pj_str_t *key,
- pjsip_role_e role,
- const pjsip_method *method,
- const pjsip_rx_data *rdata)
-{
- pj_str_t rfc3261_branch = {PJSIP_RFC3261_BRANCH_ID,
- PJSIP_RFC3261_BRANCH_LEN};
-
-
- /* Get the branch parameter in the top-most Via.
- * If branch parameter is started with "z9hG4bK", then the message was
- * generated by agent compliant with RFC3261. Otherwise, it will be
- * handled as RFC2543.
- */
- const pj_str_t *branch = &rdata->msg_info.via->branch_param;
-
- if (pj_strncmp(branch,&rfc3261_branch,PJSIP_RFC3261_BRANCH_LEN)==0) {
-
- /* Create transaction key. */
- return create_tsx_key_3261(pool, key, role, method, branch);
-
- } else {
- /* Create the key for the message. This key will be matched up
- * with the transaction key. For RFC2563 transactions, the
- * transaction key was created by the same function, so it will
- * match the message.
- */
- return create_tsx_key_2543( pool, key, role, method, rdata );
- }
-}
-
-
-/*
- * Create new transaction.
- */
-PJ_DEF(pj_status_t) pjsip_tsx_create( pj_pool_t *pool,
- pjsip_endpoint *endpt,
- pjsip_transaction **p_tsx)
-{
- pjsip_transaction *tsx;
- pj_status_t status;
-
- tsx = pj_pool_calloc(pool, 1, sizeof(pjsip_transaction));
-
- tsx->pool = pool;
- tsx->endpt = endpt;
- tsx->retransmit_timer.id = TSX_TIMER_RETRANSMISSION;
- tsx->retransmit_timer._timer_id = -1;
- tsx->retransmit_timer.user_data = tsx;
- tsx->retransmit_timer.cb = &tsx_timer_callback;
- tsx->timeout_timer.id = TSX_TIMER_TIMEOUT;
- tsx->timeout_timer._timer_id = -1;
- tsx->timeout_timer.user_data = tsx;
- tsx->timeout_timer.cb = &tsx_timer_callback;
- pj_sprintf(tsx->obj_name, "tsx%p", tsx);
- status = pj_mutex_create_recursive(pool, "mtsx%p", &tsx->mutex);
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- *p_tsx = tsx;
- return PJ_SUCCESS;
-}
-
-/*
- * Lock transaction and set the value of Thread Local Storage.
- */
-static void lock_tsx(pjsip_transaction *tsx, struct tsx_lock_data *lck)
-{
- struct tsx_lock_data *prev_data;
-
- pj_mutex_lock(tsx->mutex);
- prev_data = (struct tsx_lock_data *)
- pj_thread_local_get(pjsip_tsx_lock_tls_id);
- lck->prev = prev_data;
- lck->tsx = tsx;
- lck->is_alive = 1;
- pj_thread_local_set(pjsip_tsx_lock_tls_id, lck);
-}
-
-
-/*
- * Unlock transaction.
- * This will selectively unlock the mutex ONLY IF the transaction has not been
- * destroyed. The function knows whether the transaction has been destroyed
- * because when transaction is destroyed the is_alive flag for the transaction
- * will be set to zero.
- */
-static pj_status_t unlock_tsx( pjsip_transaction *tsx,
- struct tsx_lock_data *lck)
-{
- pj_assert( (void*)pj_thread_local_get(pjsip_tsx_lock_tls_id) == lck);
- pj_assert( lck->tsx == tsx );
- pj_thread_local_set(pjsip_tsx_lock_tls_id, lck->prev);
- if (lck->is_alive)
- pj_mutex_unlock(tsx->mutex);
-
- return lck->is_alive ? PJ_SUCCESS : PJSIP_ETSXDESTROYED;
-}
-
-/*
- * Set transaction state, and inform TU about the transaction state change.
- */
-static void tsx_set_state( pjsip_transaction *tsx,
- pjsip_tsx_state_e state,
- pjsip_event_id_e event_src_type,
- void *event_src )
-{
- pjsip_event e;
-
- PJ_LOG(4, (tsx->obj_name, "STATE %s-->%s, cause = %s",
- state_str[tsx->state], state_str[state],
- pjsip_event_str(event_src_type)));
-
- /* Change state. */
- tsx->state = state;
-
- /* Update the state handlers. */
- if (tsx->role == PJSIP_ROLE_UAC) {
- tsx->state_handler = tsx_state_handler_uac[state];
- } else {
- tsx->state_handler = tsx_state_handler_uas[state];
- }
-
- /* Inform TU */
- PJSIP_EVENT_INIT_TSX_STATE(e, tsx, event_src_type, event_src);
- pjsip_endpt_send_tsx_event( tsx->endpt, &e );
-
- /* When the transaction is terminated, release transport, and free the
- * saved last transmitted message.
- */
- if (state == PJSIP_TSX_STATE_TERMINATED) {
-
- /* Decrement transport reference counter. */
- if (tsx->transport &&
- tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL)
- {
- pjsip_transport_dec_ref( tsx->transport );
- tsx->transport = NULL;
- }
- /* Free last transmitted message. */
- if (tsx->last_tx) {
- pjsip_tx_data_dec_ref( tsx->last_tx );
- tsx->last_tx = NULL;
- }
- /* Cancel timeout timer. */
- if (tsx->timeout_timer._timer_id != -1) {
- pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);
- tsx->timeout_timer._timer_id = -1;
- }
- /* Cancel retransmission timer. */
- if (tsx->retransmit_timer._timer_id != -1) {
- pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
- tsx->retransmit_timer._timer_id = -1;
- }
-
- /* If transport is not pending, reschedule timeout timer to
- * destroy this transaction.
- */
- if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) {
- pj_time_val timeout = {0, 0};
- pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
- &timeout);
- }
-
- } else if (state == PJSIP_TSX_STATE_DESTROYED) {
-
- /* Clear TLS, so that mutex will not be unlocked */
- struct tsx_lock_data *lck = pj_thread_local_get(pjsip_tsx_lock_tls_id);
- while (lck) {
- if (lck->tsx == tsx) {
- lck->is_alive = 0;
- }
- lck = lck->prev;
- }
- }
-}
-
-/*
- * Look-up destination address and select which transport to be used to send
- * the request message. The procedure used here follows the guidelines on
- * sending the request in RFC3261 chapter 8.1.2.
- *
- * This function also modifies the message (request line and Route headers)
- * accordingly.
- */
-static pj_status_t tsx_process_route( pjsip_transaction *tsx,
- pjsip_tx_data *tdata,
- pjsip_host_port *send_addr )
-{
- const pjsip_uri *new_request_uri, *target_uri;
- const pjsip_name_addr *topmost_route_uri;
- pjsip_route_hdr *first_route_hdr, *last_route_hdr;
-
- pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
-
- /* Get the first "Route" header from the message. If the message doesn't
- * have any "Route" headers but the endpoint has, then copy the "Route"
- * headers from the endpoint first.
- */
- last_route_hdr = first_route_hdr =
- pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
- if (first_route_hdr) {
- topmost_route_uri = &first_route_hdr->name_addr;
- while (last_route_hdr->next != (void*)&tdata->msg->hdr) {
- pjsip_route_hdr *hdr;
- hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE,
- last_route_hdr->next);
- if (!hdr)
- break;
- last_route_hdr = hdr;
- }
- } else {
- const pjsip_route_hdr *hdr_list;
- hdr_list = (pjsip_route_hdr*)pjsip_endpt_get_routing(tsx->endpt);
- if (hdr_list->next != hdr_list) {
- const pjsip_route_hdr *hdr = (pjsip_route_hdr*)hdr_list->next;
- first_route_hdr = NULL;
- topmost_route_uri = &hdr->name_addr;
- do {
- last_route_hdr = pjsip_hdr_shallow_clone(tdata->pool, hdr);
- if (first_route_hdr == NULL)
- first_route_hdr = last_route_hdr;
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)last_route_hdr);
- hdr = hdr->next;
- } while (hdr != hdr_list);
- } else {
- topmost_route_uri = NULL;
- }
- }
-
- /* If Route headers exist, and the first element indicates loose-route,
- * the URI is taken from the Request-URI, and we keep all existing Route
- * headers intact.
- * If Route headers exist, and the first element DOESN'T indicate loose
- * route, the URI is taken from the first Route header, and remove the
- * first Route header from the message.
- * Otherwise if there's no Route headers, the URI is taken from the
- * Request-URI.
- */
- if (topmost_route_uri) {
- pj_bool_t has_lr_param;
-
- if (PJSIP_URI_SCHEME_IS_SIP(topmost_route_uri) ||
- PJSIP_URI_SCHEME_IS_SIPS(topmost_route_uri))
- {
- const pjsip_url *url = pjsip_uri_get_uri((void*)topmost_route_uri);
- has_lr_param = url->lr_param;
- } else {
- has_lr_param = 0;
- }
-
- if (has_lr_param) {
- new_request_uri = tdata->msg->line.req.uri;
- /* We shouldn't need to delete topmost Route if it has lr param.
- * But seems like it breaks some proxy implementation, so we
- * delete it anyway.
- */
- /*
- pj_list_erase(first_route_hdr);
- if (first_route_hdr == last_route_hdr)
- last_route_hdr = NULL;
- */
- } else {
- new_request_uri = pjsip_uri_get_uri((void*)topmost_route_uri);
- pj_list_erase(first_route_hdr);
- if (first_route_hdr == last_route_hdr)
- last_route_hdr = NULL;
- }
-
- target_uri = (pjsip_uri*)topmost_route_uri;
-
- } else {
- target_uri = new_request_uri = tdata->msg->line.req.uri;
- }
-
- /* The target URI must be a SIP/SIPS URL so we can resolve it's address.
- * Otherwise we're in trouble (i.e. there's no host part in tel: URL).
- */
- pj_memset(send_addr, 0, sizeof(*send_addr));
-
- if (PJSIP_URI_SCHEME_IS_SIPS(target_uri)) {
- pjsip_uri *uri = (pjsip_uri*) target_uri;
- const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
- send_addr->flag |= (PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_RELIABLE);
- pj_strdup(tdata->pool, &send_addr->host, &url->host);
- send_addr->port = url->port;
- send_addr->type =
- pjsip_transport_get_type_from_name(&url->transport_param);
-
- } else if (PJSIP_URI_SCHEME_IS_SIP(target_uri)) {
- pjsip_uri *uri = (pjsip_uri*) target_uri;
- const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
- pj_strdup(tdata->pool, &send_addr->host, &url->host);
- send_addr->port = url->port;
- send_addr->type =
- pjsip_transport_get_type_from_name(&url->transport_param);
-#if PJ_HAS_TCP
- if (send_addr->type == PJSIP_TRANSPORT_TCP ||
- send_addr->type == PJSIP_TRANSPORT_SCTP)
- {
- send_addr->flag |= PJSIP_TRANSPORT_RELIABLE;
- }
-#endif
- } else {
- pj_assert(!"Unsupported URI scheme!");
- return PJSIP_EINVALIDSCHEME;
- }
-
- /* If target URI is different than request URI, replace
- * request URI add put the original URI in the last Route header.
- */
- if (new_request_uri && new_request_uri!=tdata->msg->line.req.uri) {
- pjsip_route_hdr *route = pjsip_route_hdr_create(tdata->pool);
- route->name_addr.uri = tdata->msg->line.req.uri;
- if (last_route_hdr)
- pj_list_insert_after(last_route_hdr, route);
- else
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route);
- tdata->msg->line.req.uri = (pjsip_uri*)new_request_uri;
- }
-
- /* Success. */
- return PJ_SUCCESS;
-}
-
-
-/*
- * Callback from the transport job.
- * This callback is called when asychronous transport connect() operation
- * has completed, with or without error.
- */
-static void tsx_transport_callback(pjsip_transport *tr,
- void *token,
- pj_status_t status)
-{
- char addr[PJ_MAX_HOSTNAME];
- pjsip_transaction *tsx = token;
- struct tsx_lock_data lck;
-
- pj_memcpy(addr, tsx->dest_name.host.ptr, tsx->dest_name.host.slen);
- addr[tsx->dest_name.host.slen] = '\0';
-
-
- if (status == PJ_SUCCESS) {
- PJ_LOG(4, (tsx->obj_name, "%s connected to %s:%d",
- tr->type_name,
- addr, tsx->dest_name.port));
- } else {
- PJ_LOG(4, (tsx->obj_name, "%s unable to connect to %s:%d, status=%d",
- tr->type_name,
- addr, tsx->dest_name.port, status));
- }
-
- /* Lock transaction. */
- lock_tsx(tsx, &lck);
-
- if (status != PJ_SUCCESS) {
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
- tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
-
- tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
-
- /* Unlock transaction. */
- unlock_tsx(tsx, &lck);
- return;
- }
-
- /* See if transaction has already been terminated.
- * If so, schedule to destroy the transaction.
- */
- if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
- pj_time_val timeout = {0, 0};
- pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
- &timeout);
-
- /* Unlock transaction. */
- unlock_tsx(tsx, &lck);
- return;
- }
-
- /* Add reference counter to the transport. */
- pjsip_transport_add_ref(tr);
-
- /* Mark transport as ready. */
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
- tsx->transport = tr;
-
- /* If there's a pending message to send, send it now. */
- if (tsx->has_unsent_msg) {
- tsx_send_msg( tsx, tsx->last_tx );
- }
-
- /* Unlock transaction. */
- unlock_tsx(tsx, &lck);
-}
-
-/*
- * Callback from the resolver job.
- */
-static void tsx_resolver_callback(pj_status_t status,
- void *token,
- const struct pjsip_server_addresses *addr)
-{
- pjsip_transaction *tsx = token;
- struct tsx_lock_data lck;
- pjsip_transport *tp;
-
- PJ_LOG(4, (tsx->obj_name, "resolver job complete, status=%d", status));
-
- if (status != PJ_SUCCESS || addr->count == 0) {
- lock_tsx(tsx, &lck);
- tsx->status_code = PJSIP_SC_TSX_RESOLVE_ERROR;
- tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
- unlock_tsx(tsx, &lck);
- return;
- }
-
- /* Lock transaction. */
- lock_tsx(tsx, &lck);
-
- /* Copy server addresses. */
- pj_memcpy(&tsx->remote_addr, addr, sizeof(*addr));
-
- /* Create/find the transport for the remote address. */
- PJ_LOG(5,(tsx->obj_name, "tsx getting transport for %s:%d",
- pj_inet_ntoa(addr->entry[0].addr.sin_addr),
- pj_ntohs(addr->entry[0].addr.sin_port)));
-
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_CONNECTING;
- status = pjsip_endpt_alloc_transport( tsx->endpt, addr->entry[0].type,
- &addr->entry[0].addr,
- &tp);
- tsx_transport_callback(tp, tsx, status);
-
- /* Unlock transaction */
- unlock_tsx(tsx, &lck);
-
- /* There should be nothing to do after this point.
- * Execution for the transaction will resume when the callback for the
- * transport is called.
- */
-}
-
-/*
- * Initialize the transaction as UAC transaction.
- */
-PJ_DEF(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx,
- pjsip_tx_data *tdata)
-{
- pjsip_msg *msg;
- pjsip_cseq_hdr *cseq;
- pjsip_via_hdr *via;
- pj_status_t status;
- struct tsx_lock_data lck;
-
- PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAC (tdata=%p)", tdata));
-
- /* Lock transaction. */
- lock_tsx(tsx, &lck);
-
- /* Keep shortcut */
- msg = tdata->msg;
-
- /* Role is UAC. */
- tsx->role = PJSIP_ROLE_UAC;
-
- /* Save method. */
- pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);
-
- /* Generate Via header if it doesn't exist. */
- via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL);
- if (via == NULL) {
- via = pjsip_via_hdr_create(tdata->pool);
- pjsip_msg_insert_first_hdr(msg, (pjsip_hdr*) via);
- }
-
- if (via->branch_param.slen == 0) {
- pj_str_t tmp;
- via->branch_param.ptr = pj_pool_alloc(tsx->pool, PJSIP_MAX_BRANCH_LEN);
- via->branch_param.slen = PJSIP_MAX_BRANCH_LEN;
- pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID,
- PJSIP_RFC3261_BRANCH_LEN);
-
- tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN;
- pj_generate_unique_string( &tmp );
-
- /* Save branch parameter. */
- tsx->branch = via->branch_param;
- } else {
- /* Copy branch parameter. */
- pj_strdup(tsx->pool, &tsx->branch, &via->branch_param);
- }
-
-
- /* Generate transaction key. */
- status = create_tsx_key_3261( tsx->pool, &tsx->transaction_key,
- PJSIP_ROLE_UAC, &tsx->method,
- &via->branch_param);
- if (status != PJ_SUCCESS) {
- unlock_tsx(tsx, &lck);
- return status;
- }
-
- PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
- tsx->transaction_key.ptr));
-
- /* Save CSeq. */
- cseq = pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL);
- if (!cseq) {
- pj_assert(!"CSeq header not present in outgoing message!");
- unlock_tsx(tsx, &lck);
- return PJSIP_EMISSINGHDR;
- }
- tsx->cseq = cseq->cseq;
-
-
- /* Begin with State_Null.
- * Manually set-up the state becase we don't want to call the callback.
- */
- tsx->state = PJSIP_TSX_STATE_NULL;
- tsx->state_handler = &pjsip_tsx_on_state_null;
-
- /* Get destination name from the message. */
- status = tsx_process_route(tsx, tdata, &tsx->dest_name);
- if (status != PJ_SUCCESS) {
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
- tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
- tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
- unlock_tsx(tsx, &lck);
- return status;
- }
-
- /* Resolve destination.
- * This will start asynchronous resolver job, and when it finishes,
- * the callback will be called.
- */
- PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
- tsx->dest_name.host.slen,
- tsx->dest_name.host.ptr,
- tsx->dest_name.port));
-
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
- pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name,
- tsx, &tsx_resolver_callback);
-
- /* There should be nothing to do after this point.
- * Execution for the transaction will resume when the resolver callback is
- * called.
- */
-
- /* Unlock transaction and return.
- * If transaction has been destroyed WITHIN the current thread, the
- * unlock_tsx() function will return -1.
- */
- return unlock_tsx(tsx, &lck);
-}
-
-
-/*
- * Initialize the transaction as UAS transaction.
- */
-PJ_DEF(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx,
- pjsip_rx_data *rdata)
-{
- pjsip_msg *msg = rdata->msg_info.msg;
- pj_str_t *branch;
- pjsip_cseq_hdr *cseq;
- pj_status_t status;
- struct tsx_lock_data lck;
-
- PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAS (rdata=%p)", rdata));
-
- /* Lock transaction. */
- lock_tsx(tsx, &lck);
-
- /* Keep shortcut to message */
- msg = rdata->msg_info.msg;
-
- /* Role is UAS */
- tsx->role = PJSIP_ROLE_UAS;
-
- /* Save method. */
- pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);
-
- /* Get transaction key either from branch for RFC3261 message, or
- * create transaction key.
- */
- status = pjsip_tsx_create_key(tsx->pool, &tsx->transaction_key,
- PJSIP_ROLE_UAS, &tsx->method, rdata);
- if (status != PJ_SUCCESS) {
- unlock_tsx(tsx, &lck);
- return status;
- }
-
- /* Duplicate branch parameter for transaction. */
- branch = &rdata->msg_info.via->branch_param;
- pj_strdup(tsx->pool, &tsx->branch, branch);
-
- PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
- tsx->transaction_key.ptr));
-
- /* Save CSeq */
- cseq = rdata->msg_info.cseq;
- tsx->cseq = cseq->cseq;
-
- /* Begin with state NULL
- * Manually set-up the state becase we don't want to call the callback.
- */
- tsx->state = PJSIP_TSX_STATE_NULL;
- tsx->state_handler = &pjsip_tsx_on_state_null;
-
- /* Get the transport to send the response.
- * According to section 18.2.2 of RFC3261, if the transport is reliable
- * then the response must be sent using that transport.
- */
- /* In addition, RFC 3581 says, if Via has "rport" parameter specified,
- * then return the response using the same transport.
- */
- if (PJSIP_TRANSPORT_IS_RELIABLE(rdata->tp_info.transport) ||
- rdata->msg_info.via->rport_param >= 0)
- {
- tsx->transport = rdata->tp_info.transport;
- pjsip_transport_add_ref(tsx->transport);
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
-
- tsx->current_addr = 0;
- tsx->remote_addr.count = 1;
- tsx->remote_addr.entry[0].type = tsx->transport->type;
- pj_memcpy(&tsx->remote_addr.entry[0].addr,
- &rdata->pkt_info.addr, rdata->pkt_info.addr_len);
-
- } else {
- pj_status_t status;
-
- status = pjsip_get_response_addr(tsx->pool, rdata->tp_info.transport,
- rdata->msg_info.via, &tsx->dest_name);
- if (status != PJ_SUCCESS) {
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
- tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
- tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
- unlock_tsx(tsx, &lck);
- return status;
- }
-
- /* Resolve destination.
- * This will start asynchronous resolver job, and when it finishes,
- * the callback will be called.
- */
- PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
- tsx->dest_name.host.slen,
- tsx->dest_name.host.ptr,
- tsx->dest_name.port));
-
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
- pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name,
- tsx, &tsx_resolver_callback);
- }
-
- /* There should be nothing to do after this point.
- * Execution for the transaction will resume when the resolver callback is
- * called.
- */
-
- /* Unlock transaction and return.
- * If transaction has been destroyed WITHIN the current thread, the
- * unlock_tsx() function will return -1.
- */
- return unlock_tsx(tsx, &lck);
-}
-
-/*
- * Callback when timer expires.
- */
-static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry)
-{
- pjsip_event event;
- pjsip_transaction *tsx = entry->user_data;
- struct tsx_lock_data lck;
-
- PJ_UNUSED_ARG(theap);
-
- PJ_LOG(5,(tsx->obj_name, "got timer event (%s timer)",
- (entry->id==TSX_TIMER_RETRANSMISSION ? "Retransmit" : "Timeout")));
-
-
- if (entry->id == TSX_TIMER_RETRANSMISSION) {
- PJSIP_EVENT_INIT_TIMER(event, &tsx->retransmit_timer);
- } else {
- PJSIP_EVENT_INIT_TIMER(event, &tsx->timeout_timer);
- }
-
- /* Dispatch event to transaction. */
- lock_tsx(tsx, &lck);
- (*tsx->state_handler)(tsx, &event);
- unlock_tsx(tsx, &lck);
-}
-
-/*
- * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for
- * non-2xx/INVITE is automatically sent by the transaction.
- * This operation is only valid if the transaction is configured to handle ACK
- * (tsx->handle_ack is non-zero). If this attribute is not set, then the
- * transaction will comply with RFC-3261, i.e. it will set itself to
- * TERMINATED state when it receives 2xx/INVITE.
- */
-PJ_DEF(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx, pjsip_tx_data *tdata)
-{
- pjsip_msg *msg;
- pjsip_host_port dest_addr;
- pjsip_via_hdr *via;
- struct tsx_lock_data lck;
- pj_status_t status = PJ_SUCCESS;
-
- /* Lock tsx. */
- lock_tsx(tsx, &lck);
-
- pj_assert(tsx->handle_ack != 0);
-
- msg = tdata->msg;
-
- /* Generate branch parameter if it doesn't exist. */
- via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL);
- if (via == NULL) {
- via = pjsip_via_hdr_create(tdata->pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) via);
- }
-
- if (via->branch_param.slen == 0) {
- via->branch_param = tsx->branch;
- } else {
- pj_assert( pj_strcmp(&via->branch_param, &tsx->branch) == 0 );
- }
-
- /* Get destination name from the message. */
- status = tsx_process_route(tsx, tdata, &dest_addr);
- if (status != 0){
- goto on_error;
- }
-
- /* Compare message's destination name with transaction's destination name.
- * If NOT equal, then we'll have to resolve the destination.
- */
- if (dest_addr.type == tsx->dest_name.type &&
- dest_addr.flag == tsx->dest_name.flag &&
- dest_addr.port == tsx->dest_name.port &&
- pj_stricmp(&dest_addr.host, &tsx->dest_name.host) == 0)
- {
- /* Equal destination. We can use current transport. */
- pjsip_tsx_on_tx_msg(tsx, tdata);
- unlock_tsx(tsx, &lck);
- return;
-
- }
-
- /* New destination; we'll have to resolve host and create new transport. */
- pj_memcpy(&tsx->dest_name, &dest_addr, sizeof(dest_addr));
- pj_strdup(tsx->pool, &tsx->dest_name.host, &dest_addr.host);
-
- PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
- tsx->dest_name.host.slen,
- tsx->dest_name.host.ptr,
- tsx->dest_name.port));
-
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
- pjsip_transport_dec_ref(tsx->transport);
- tsx->transport = NULL;
-
- /* Put the message in queue. */
- pjsip_tsx_on_tx_msg(tsx, tdata);
-
- /* This is a bug!
- * We shouldn't change transaction's state before actually sending the
- * message. Otherwise transaction will terminate before message is sent,
- * and timeout timer will be scheduled.
- */
- PJ_TODO(TSX_DONT_CHANGE_STATE_BEFORE_SENDING_ACK)
-
- /*
- * This will start asynchronous resolver job, and when it finishes,
- * the callback will be called.
- */
-
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
- pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name,
- tsx, &tsx_resolver_callback);
-
- unlock_tsx(tsx, &lck);
-
- /* There should be nothing to do after this point.
- * Execution for the transaction will resume when the resolver callback is
- * called.
- */
- return;
-
-on_error:
- /* Failure condition.
- * Send TERMINATED event.
- */
- tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
-
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
-
- unlock_tsx(tsx, &lck);
-}
-
-
-/*
- * This function is called by TU to send a message.
- */
-PJ_DEF(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx,
- pjsip_tx_data *tdata )
-{
- pjsip_event event;
- struct tsx_lock_data lck;
- pj_status_t status;
-
- PJ_LOG(5,(tsx->obj_name, "Request to transmit msg on state %s (tdata=%p)",
- state_str[tsx->state], tdata));
-
- PJSIP_EVENT_INIT_TX_MSG(event, tsx, tdata);
-
- /* Dispatch to transaction. */
- lock_tsx(tsx, &lck);
- status = (*tsx->state_handler)(tsx, &event);
- unlock_tsx(tsx, &lck);
-}
-
-/*
- * This function is called by endpoint when incoming message for the
- * transaction is received.
- */
-PJ_DEF(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx,
- pjsip_rx_data *rdata)
-{
- pjsip_event event;
- struct tsx_lock_data lck;
- pj_status_t status;
-
- PJ_LOG(5,(tsx->obj_name, "Incoming msg on state %s (rdata=%p)",
- state_str[tsx->state], rdata));
-
- PJSIP_EVENT_INIT_RX_MSG(event, tsx, rdata);
-
- /* Dispatch to transaction. */
- lock_tsx(tsx, &lck);
- status = (*tsx->state_handler)(tsx, &event);
- unlock_tsx(tsx, &lck);
-}
-
-/*
- * Forcely terminate transaction.
- */
-PJ_DEF(void) pjsip_tsx_terminate( pjsip_transaction *tsx, int code )
-{
- struct tsx_lock_data lck;
-
- lock_tsx(tsx, &lck);
- tsx->status_code = code;
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_USER, NULL);
-
- unlock_tsx(tsx, &lck);
-}
-
-/*
- * Transport send completion callback.
- */
-static void tsx_on_send_complete(void *token, pjsip_tx_data *tdata,
- pj_ssize_t bytes_sent)
-{
- PJ_UNUSED_ARG(token);
- PJ_UNUSED_ARG(tdata);
-
- if (bytes_sent <= 0) {
- PJ_TODO(HANDLE_TRANSPORT_ERROR);
- }
-}
-
-/*
- * Send message to the transport.
- * If transport is not yet available, then do nothing. The message will be
- * transmitted when transport connection completion callback is called.
- */
-static pj_status_t tsx_send_msg( pjsip_transaction *tsx,
- pjsip_tx_data *tdata)
-{
- pj_status_t status = PJ_SUCCESS;
-
- PJ_LOG(5,(tsx->obj_name, "sending msg (tdata=%p)", tdata));
-
- if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) {
- pjsip_event before_tx_event;
-
- pj_assert(tsx->transport != NULL);
-
- /* Make sure Via transport info is filled up properly for
- * requests.
- */
- if (tdata->msg->type == PJSIP_REQUEST_MSG) {
- const pj_sockaddr_in *addr_name;
- pjsip_via_hdr *via = (pjsip_via_hdr*)
- pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
-
- /* For request message, set "rport" parameter by default. */
- if (tdata->msg->type == PJSIP_REQUEST_MSG)
- via->rport_param = 0;
-
- /* Don't update Via sent-by on retransmission. */
- if (via->sent_by.host.slen == 0) {
- addr_name = &tsx->transport->public_addr;
- pj_strdup2(tdata->pool, &via->transport,
- tsx->transport->type_name);
- pj_strdup2(tdata->pool, &via->sent_by.host,
- pj_inet_ntoa(addr_name->sin_addr));
- via->sent_by.port = pj_ntohs(addr_name->sin_port);
- }
- }
-
- /* Notify everybody we're about to send message. */
- PJSIP_EVENT_INIT_PRE_TX_MSG(before_tx_event, tsx, tdata,
- tsx->retransmit_count);
- pjsip_endpt_send_tsx_event( tsx->endpt, &before_tx_event );
-
- tsx->has_unsent_msg = 0;
- status = pjsip_transport_send(tsx->transport, tdata,
- &tsx->remote_addr.entry[tsx->current_addr].addr,
- tsx, &tsx_on_send_complete);
- if (status != PJ_SUCCESS && status != PJ_EPENDING) {
- PJ_TODO(HANDLE_TRANSPORT_ERROR);
- goto on_error;
- }
- } else {
- tsx->has_unsent_msg = 1;
- }
-
- return 0;
-
-on_error:
- tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
- return status;
-}
-
-/*
- * Retransmit last message sent.
- */
-static pj_status_t pjsip_tsx_retransmit( pjsip_transaction *tsx,
- int should_restart_timer)
-{
- pj_status_t status;
-
- PJ_LOG(4,(tsx->obj_name, "retransmiting (tdata=%p, count=%d, restart?=%d)",
- tsx->last_tx, tsx->retransmit_count, should_restart_timer));
-
- pj_assert(tsx->last_tx != NULL);
-
- ++tsx->retransmit_count;
-
- status = tsx_send_msg( tsx, tsx->last_tx);
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- /* Restart timer T1. */
- if (should_restart_timer) {
- pj_time_val timeout;
- int msec_time = (1 << (tsx->retransmit_count)) * PJSIP_T1_TIMEOUT;
-
- if (tsx->method.id!=PJSIP_INVITE_METHOD && msec_time>PJSIP_T2_TIMEOUT)
- msec_time = PJSIP_T2_TIMEOUT;
-
- timeout.sec = msec_time / 1000;
- timeout.msec = msec_time % 1000;
- pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer,
- &timeout);
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Handler for events in state Null.
- */
-static pj_status_t pjsip_tsx_on_state_null( pjsip_transaction *tsx,
- pjsip_event *event )
-{
- pj_status_t status;
-
- pj_assert( tsx->state == PJSIP_TSX_STATE_NULL);
- pj_assert( tsx->last_tx == NULL );
- pj_assert( tsx->has_unsent_msg == 0);
-
- if (tsx->role == PJSIP_ROLE_UAS) {
-
- /* Set state to Trying. */
- pj_assert(event->type == PJSIP_EVENT_RX_MSG);
- tsx_set_state( tsx, PJSIP_TSX_STATE_TRYING,
- PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
-
- } else {
- pjsip_tx_data *tdata = event->body.tx_msg.tdata;
-
- /* Save the message for retransmission. */
- tsx->last_tx = tdata;
- pjsip_tx_data_add_ref(tdata);
-
- /* Send the message. */
- status = tsx_send_msg( tsx, tdata);
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- /* Start Timer B (or called timer F for non-INVITE) for transaction
- * timeout.
- */
- pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
- &timeout_timer_val);
-
- /* Start Timer A (or timer E) for retransmission only if unreliable
- * transport is being used.
- */
- if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL &&
- PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0)
- {
- pjsip_endpt_schedule_timer(tsx->endpt, &tsx->retransmit_timer,
- &t1_timer_val);
- tsx->retransmit_count = 0;
- }
-
- /* Move state. */
- tsx_set_state( tsx, PJSIP_TSX_STATE_CALLING,
- PJSIP_EVENT_TX_MSG, tdata);
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * State Calling is for UAC after it sends request but before any responses
- * is received.
- */
-static pj_status_t pjsip_tsx_on_state_calling( pjsip_transaction *tsx,
- pjsip_event *event )
-{
- pj_assert(tsx->state == PJSIP_TSX_STATE_CALLING);
- pj_assert(tsx->role == PJSIP_ROLE_UAC);
-
- if (event->type == PJSIP_EVENT_TIMER &&
- event->body.timer.entry == &tsx->retransmit_timer)
- {
- pj_status_t status;
-
- /* Retransmit the request. */
- status = pjsip_tsx_retransmit( tsx, 1 );
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- } else if (event->type == PJSIP_EVENT_TIMER &&
- event->body.timer.entry == &tsx->timeout_timer)
- {
-
- /* Cancel retransmission timer. */
- if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
- pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
- }
-
- /* Set status code */
- tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
-
- /* Inform TU. */
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TIMER, &tsx->timeout_timer);
-
- /* Transaction is destroyed */
- return PJSIP_ETSXDESTROYED;
-
- } else if (event->type == PJSIP_EVENT_RX_MSG) {
- int code;
-
- /* Cancel retransmission timer A. */
- if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0)
- pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
-
- /* Cancel timer B (transaction timeout) */
- pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);
-
- /* Discard retransmission message if it is not INVITE.
- * The INVITE tdata is needed in case we have to generate ACK for
- * the final response.
- */
- /* Keep last_tx for authorization. */
- code = event->body.rx_msg.rdata->msg_info.msg->line.status.code;
- if (tsx->method.id != PJSIP_INVITE_METHOD && code!=401 && code!=407) {
- pjsip_tx_data_dec_ref(tsx->last_tx);
- tsx->last_tx = NULL;
- }
-
- /* Processing is similar to state Proceeding. */
- pjsip_tsx_on_state_proceeding_uac( tsx, event);
-
- } else {
- pj_assert(0);
- return PJ_EBUG;
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * State Trying is for UAS after it received request but before any responses
- * is sent.
- * Note: this is different than RFC3261, which can use Trying state for
- * non-INVITE client transaction (bug in RFC?).
- */
-static pj_status_t pjsip_tsx_on_state_trying( pjsip_transaction *tsx,
- pjsip_event *event)
-{
- pj_status_t status;
-
- pj_assert(tsx->state == PJSIP_TSX_STATE_TRYING);
-
- /* This state is only for UAS */
- pj_assert(tsx->role == PJSIP_ROLE_UAS);
-
- /* Better be transmission of response message.
- * If we've got request retransmission, this means that the TU hasn't
- * transmitted any responses within 500 ms, which is not allowed. If
- * this happens, just ignore the event (we couldn't retransmit last
- * response because we haven't sent any!).
- */
- //pj_assert(event->type == PJSIP_EVENT_TX_MSG);
- if (event->type != PJSIP_EVENT_TX_MSG) {
- return PJ_SUCCESS;
- }
-
- /* The rest of the processing of the event is exactly the same as in
- * "Proceeding" state.
- */
- status = pjsip_tsx_on_state_proceeding_uas( tsx, event);
-
- /* Inform the TU of the state transision if state is still State_Trying */
- if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TRYING) {
- tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING,
- PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata);
- }
-
- return status;
-}
-
-/*
- * Handler for events in Proceeding for UAS
- * This state happens after the TU sends provisional response.
- */
-static pj_status_t pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx,
- pjsip_event *event)
-{
- pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING ||
- tsx->state == PJSIP_TSX_STATE_TRYING);
-
- /* This state is only for UAS. */
- pj_assert(tsx->role == PJSIP_ROLE_UAS);
-
- /* Receive request retransmission. */
- if (event->type == PJSIP_EVENT_RX_MSG) {
-
- pj_status_t status;
-
- /* Send last response. */
- status = pjsip_tsx_retransmit( tsx, 0 );
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- } else if (event->type == PJSIP_EVENT_TX_MSG ) {
- pjsip_tx_data *tdata = event->body.tx_msg.tdata;
- pj_status_t status;
-
- /* The TU sends response message to the request. Save this message so
- * that we can retransmit the last response in case we receive request
- * retransmission.
- */
- pjsip_msg *msg = tdata->msg;
-
- /* This can only be a response message. */
- pj_assert(msg->type == PJSIP_RESPONSE_MSG);
-
- /* Status code must be higher than last sent. */
- pj_assert(msg->line.status.code >= tsx->status_code);
-
- /* Update last status */
- tsx->status_code = msg->line.status.code;
-
- /* Discard the saved last response (it will be updated later as
- * necessary).
- */
- if (tsx->last_tx && tsx->last_tx != tdata) {
- pjsip_tx_data_dec_ref( tsx->last_tx );
- tsx->last_tx = NULL;
- }
-
- /* Send the message. */
- status = tsx_send_msg(tsx, tdata);
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- // Update To tag header for RFC2543 transaction.
- // TODO:
-
- /* Update transaction state */
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) {
-
- if (tsx->last_tx != tdata) {
- tsx->last_tx = tdata;
- pjsip_tx_data_add_ref( tdata );
- }
- tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING,
- PJSIP_EVENT_TX_MSG, tdata );
-
- } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
-
- if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack==0) {
-
- /* 2xx class message is not saved, because retransmission
- * is handled by TU.
- */
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TX_MSG, tdata );
-
- /* Transaction is destroyed. */
- return PJSIP_ETSXDESTROYED;
-
- } else {
- pj_time_val timeout;
-
- if (tsx->method.id == PJSIP_INVITE_METHOD) {
- tsx->retransmit_count = 0;
- pjsip_endpt_schedule_timer( tsx->endpt,
- &tsx->retransmit_timer,
- &t1_timer_val);
- }
-
- /* Save last response sent for retransmission when request
- * retransmission is received.
- */
- if (tsx->last_tx != tdata) {
- tsx->last_tx = tdata;
- pjsip_tx_data_add_ref(tdata);
- }
-
- /* Start timer J at 64*T1 for unreliable transport or zero for
- * reliable transport.
- */
- if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
- timeout = timeout_timer_val;
- } else {
- timeout.sec = timeout.msec = 0;
- }
-
- pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
- &timeout);
-
- /* Set state to "Completed" */
- tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
- PJSIP_EVENT_TX_MSG, tdata );
- }
-
- } else if (tsx->status_code >= 300) {
-
- /* 3xx-6xx class message causes transaction to move to
- * "Completed" state.
- */
- if (tsx->last_tx != tdata) {
- tsx->last_tx = tdata;
- pjsip_tx_data_add_ref( tdata );
- }
-
- /* Start timer H for transaction termination */
- pjsip_endpt_schedule_timer(tsx->endpt,&tsx->timeout_timer,
- &timeout_timer_val);
-
- /* For INVITE, if unreliable transport is used, retransmission
- * timer G will be scheduled (retransmission).
- */
- if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
- pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ,
- NULL);
- if (cseq->method.id == PJSIP_INVITE_METHOD) {
- tsx->retransmit_count = 0;
- pjsip_endpt_schedule_timer(tsx->endpt,
- &tsx->retransmit_timer,
- &t1_timer_val);
- }
- }
-
- /* Inform TU */
- tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
- PJSIP_EVENT_TX_MSG, tdata );
-
- } else {
- pj_assert(0);
- }
-
-
- } else if (event->type == PJSIP_EVENT_TIMER &&
- event->body.timer.entry == &tsx->retransmit_timer) {
- /* Retransmission timer elapsed. */
- pj_status_t status;
-
- /* Must have last response to retransmit. */
- pj_assert(tsx->last_tx != NULL);
-
- /* Retransmit the last response. */
- status = pjsip_tsx_retransmit( tsx, 1 );
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- } else if (event->type == PJSIP_EVENT_TIMER &&
- event->body.timer.entry == &tsx->timeout_timer) {
-
- /* Timeout timer. should not happen? */
- pj_assert(0);
-
- tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
-
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TIMER, &tsx->timeout_timer);
-
- return PJ_EBUG;
-
- } else {
- pj_assert(0);
- return PJ_EBUG;
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Handler for events in Proceeding for UAC
- * This state happens after provisional response(s) has been received from
- * UAS.
- */
-static pj_status_t pjsip_tsx_on_state_proceeding_uac(pjsip_transaction *tsx,
- pjsip_event *event)
-{
-
- pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING ||
- tsx->state == PJSIP_TSX_STATE_CALLING);
-
- if (event->type != PJSIP_EVENT_TIMER) {
- /* Must be incoming response, because we should not retransmit
- * request once response has been received.
- */
- pj_assert(event->type == PJSIP_EVENT_RX_MSG);
- if (event->type != PJSIP_EVENT_RX_MSG) {
- return PJ_EINVALIDOP;
- }
-
- tsx->status_code = event->body.rx_msg.rdata->msg_info.msg->line.status.code;
- } else {
- tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
- }
-
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) {
-
- /* Inform the message to TU. */
- tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING,
- PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
-
- } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) {
-
- /* Stop timeout timer B/F. */
- pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer );
-
- /* For INVITE, the state moves to Terminated state (because ACK is
- * handled in TU). For non-INVITE, state moves to Completed.
- */
- if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack == 0) {
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
- return PJSIP_ETSXDESTROYED;
-
- } else {
- pj_time_val timeout;
-
- /* For unreliable transport, start timer D (for INVITE) or
- * timer K for non-INVITE. */
- if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) {
- if (tsx->method.id == PJSIP_INVITE_METHOD) {
- timeout = td_timer_val;
- } else {
- timeout = t4_timer_val;
- }
- } else {
- timeout.sec = timeout.msec = 0;
- }
- pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
- &timeout);
-
- /* Move state to Completed, inform TU. */
- tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
- PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
- }
-
- } else if (tsx->status_code >= 300 && tsx->status_code <= 699) {
- pj_time_val timeout;
- pj_status_t status;
-
- /* Stop timer B. */
- pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer );
-
- /* Generate and send ACK for INVITE. */
- if (tsx->method.id == PJSIP_INVITE_METHOD) {
- pjsip_endpt_create_ack( tsx->endpt, tsx->last_tx,
- event->body.rx_msg.rdata );
- status = tsx_send_msg( tsx, tsx->last_tx);
- if (status != PJ_SUCCESS) {
- return status;
- }
- }
-
- /* Start Timer D with TD/T4 timer if unreliable transport is used. */
- if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) {
- if (tsx->method.id == PJSIP_INVITE_METHOD) {
- timeout = td_timer_val;
- } else {
- timeout = t4_timer_val;
- }
- } else {
- timeout.sec = timeout.msec = 0;
- }
- pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout);
-
- /* Inform TU. */
- tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
- PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
-
- } else {
- // Shouldn't happen because there's no timer for this state.
- pj_assert(0);
- return PJ_EBUG;
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Handler for events in Completed state for UAS
- */
-static pj_status_t pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx,
- pjsip_event *event)
-{
- pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED);
-
- if (event->type == PJSIP_EVENT_RX_MSG) {
- pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg;
- pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL );
-
- /* On receive request retransmission, retransmit last response. */
- if (cseq->method.id != PJSIP_ACK_METHOD) {
- pj_status_t status;
-
- status = pjsip_tsx_retransmit( tsx, 0 );
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- } else {
- /* Process incoming ACK request. */
-
- /* Cease retransmission. */
- pjsip_endpt_cancel_timer( tsx->endpt, &tsx->retransmit_timer );
-
- /* Start timer I in T4 interval (transaction termination). */
- pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer );
- pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
- &t4_timer_val);
-
- /* Move state to "Confirmed" */
- tsx_set_state( tsx, PJSIP_TSX_STATE_CONFIRMED,
- PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
- }
-
- } else if (event->type == PJSIP_EVENT_TIMER) {
-
- if (event->body.timer.entry == &tsx->retransmit_timer) {
- /* Retransmit message. */
- pj_status_t status;
-
- status = pjsip_tsx_retransmit( tsx, 1 );
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- } else {
- if (tsx->method.id == PJSIP_INVITE_METHOD) {
-
- /* For INVITE, this means that ACK was never received.
- * Set state to Terminated, and inform TU.
- */
-
- tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
-
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TIMER, &tsx->timeout_timer );
-
- return PJSIP_ETSXDESTROYED;
-
- } else {
- /* Transaction terminated, it can now be deleted. */
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TIMER, &tsx->timeout_timer );
- return PJSIP_ETSXDESTROYED;
- }
- }
-
- } else {
- /* Ignore request to transmit. */
- pj_assert(event->body.tx_msg.tdata == tsx->last_tx);
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Handler for events in Completed state for UAC transaction.
- */
-static pj_status_t pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx,
- pjsip_event *event)
-{
- pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED);
-
- if (event->type == PJSIP_EVENT_TIMER) {
- /* Must be the timeout timer. */
- pj_assert(event->body.timer.entry == &tsx->timeout_timer);
-
- /* Move to Terminated state. */
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TIMER, event->body.timer.entry );
-
- /* Transaction has been destroyed. */
- return PJSIP_ETSXDESTROYED;
-
- } else if (event->type == PJSIP_EVENT_RX_MSG) {
- if (tsx->method.id == PJSIP_INVITE_METHOD) {
- /* On received of final response retransmission, retransmit the ACK.
- * TU doesn't need to be informed.
- */
- pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg;
- pj_assert(msg->type == PJSIP_RESPONSE_MSG);
- if (msg->type==PJSIP_RESPONSE_MSG &&
- msg->line.status.code >= 200)
- {
- pj_status_t status;
-
- status = pjsip_tsx_retransmit( tsx, 0 );
- if (status != PJ_SUCCESS) {
- return status;
- }
- } else {
- /* Very late retransmission of privisional response. */
- pj_assert( msg->type == PJSIP_RESPONSE_MSG );
- }
- } else {
- /* Just drop the response. */
- }
- } else if (tsx->method.id == PJSIP_INVITE_METHOD &&
- event->type == PJSIP_EVENT_TX_MSG &&
- event->body.tx_msg.tdata->msg->line.req.method.id==PJSIP_ACK_METHOD) {
-
- pj_status_t status;
-
- /* Set last transmitted message. */
- if (tsx->last_tx != event->body.tx_msg.tdata) {
- pjsip_tx_data_dec_ref( tsx->last_tx );
- tsx->last_tx = event->body.tx_msg.tdata;
- pjsip_tx_data_add_ref( tsx->last_tx );
- }
-
- /* No state changed, but notify app.
- * Must notify now, so app has chance to put SDP in outgoing ACK msg.
- */
- tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
- PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata );
-
- /* Send msg */
- status = tsx_send_msg(tsx, event->body.tx_msg.tdata);
- if (status != PJ_SUCCESS)
- return status;
-
- } else {
- pj_assert(0);
- return PJ_EBUG;
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Handler for events in state Confirmed.
- */
-static pj_status_t pjsip_tsx_on_state_confirmed( pjsip_transaction *tsx,
- pjsip_event *event)
-{
- pj_assert(tsx->state == PJSIP_TSX_STATE_CONFIRMED);
-
- /* This state is only for UAS for INVITE. */
- pj_assert(tsx->role == PJSIP_ROLE_UAS);
- pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
-
- /* Absorb any ACK received. */
- if (event->type == PJSIP_EVENT_RX_MSG) {
-
- pjsip_method_e method_id =
- event->body.rx_msg.rdata->msg_info.msg->line.req.method.id;
-
- /* Must be a request message. */
- pj_assert(event->body.rx_msg.rdata->msg_info.msg->type == PJSIP_REQUEST_MSG);
-
- /* Must be an ACK request or a late INVITE retransmission. */
- pj_assert(method_id == PJSIP_ACK_METHOD ||
- method_id == PJSIP_INVITE_METHOD);
-
- /* Just so that compiler won't complain about unused vars when
- * building release code.
- */
- PJ_UNUSED_ARG(method_id);
-
- } else if (event->type == PJSIP_EVENT_TIMER) {
- /* Must be from timeout_timer_. */
- pj_assert(event->body.timer.entry == &tsx->timeout_timer);
-
- /* Move to Terminated state. */
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TIMER, &tsx->timeout_timer );
-
- /* Transaction has been destroyed. */
- return PJSIP_ETSXDESTROYED;
-
- } else {
- pj_assert(0);
- return PJ_EBUG;
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Handler for events in state Terminated.
- */
-static pj_status_t pjsip_tsx_on_state_terminated( pjsip_transaction *tsx,
- pjsip_event *event)
-{
- pj_assert(tsx->state == PJSIP_TSX_STATE_TERMINATED);
-
- PJ_UNUSED_ARG(event);
-
- /* Destroy this transaction */
- tsx_set_state(tsx, PJSIP_TSX_STATE_DESTROYED,
- event->type, event->body.user.user1 );
-
- return PJ_SUCCESS;
-}
-
-
-static pj_status_t pjsip_tsx_on_state_destroyed(pjsip_transaction *tsx,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(tsx);
- PJ_UNUSED_ARG(event);
- return PJ_SUCCESS;
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip/sip_transaction.h> +#include <pjsip/sip_transport.h> +#include <pjsip/sip_config.h> +#include <pjsip/sip_util.h> +#include <pjsip/sip_event.h> +#include <pjsip/sip_endpoint.h> +#include <pjsip/sip_errno.h> +#include <pj/log.h> +#include <pj/string.h> +#include <pj/os.h> +#include <pj/guid.h> +#include <pj/pool.h> +#include <pj/assert.h> + +/* Thread Local Storage ID for transaction lock (initialized by endpoint) */ +long pjsip_tsx_lock_tls_id; + +/* State names */ +static const char *state_str[] = +{ + "Null", + "Calling", + "Trying", + "Proceeding", + "Completed", + "Confirmed", + "Terminated", + "Destroyed", +}; + +/* Role names */ +static const char *role_name[] = +{ + "Client", + "Server" +}; + +/* Transaction lock. */ +typedef struct tsx_lock_data { + struct tsx_lock_data *prev; + pjsip_transaction *tsx; + int is_alive; +} tsx_lock_data; + + +/* Timer timeout value constants */ +static const pj_time_val t1_timer_val = { PJSIP_T1_TIMEOUT/1000, + PJSIP_T1_TIMEOUT%1000 }; +static const pj_time_val t4_timer_val = { PJSIP_T4_TIMEOUT/1000, + PJSIP_T4_TIMEOUT%1000 }; +static const pj_time_val td_timer_val = { PJSIP_TD_TIMEOUT/1000, + PJSIP_TD_TIMEOUT%1000 }; +static const pj_time_val timeout_timer_val = { (64*PJSIP_T1_TIMEOUT)/1000, + (64*PJSIP_T1_TIMEOUT)%1000 }; + +/* Internal timer IDs */ +enum Transaction_Timer_Id +{ + TSX_TIMER_RETRANSMISSION, + TSX_TIMER_TIMEOUT, +}; + +/* Function Prototypes */ +static pj_status_t pjsip_tsx_on_state_null( pjsip_transaction *tsx, + pjsip_event *event); +static pj_status_t pjsip_tsx_on_state_calling( pjsip_transaction *tsx, + pjsip_event *event); +static pj_status_t pjsip_tsx_on_state_trying( pjsip_transaction *tsx, + pjsip_event *event); +static pj_status_t pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx, + pjsip_event *event); +static pj_status_t pjsip_tsx_on_state_proceeding_uac( pjsip_transaction *tsx, + pjsip_event *event); +static pj_status_t pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx, + pjsip_event *event); +static pj_status_t pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx, + pjsip_event *event); +static pj_status_t pjsip_tsx_on_state_confirmed(pjsip_transaction *tsx, + pjsip_event *event); +static pj_status_t pjsip_tsx_on_state_terminated(pjsip_transaction *tsx, + pjsip_event *event); +static pj_status_t pjsip_tsx_on_state_destroyed(pjsip_transaction *tsx, + pjsip_event *event); + +static void tsx_timer_callback( pj_timer_heap_t *theap, + pj_timer_entry *entry); +static int tsx_send_msg( pjsip_transaction *tsx, + pjsip_tx_data *tdata); +static void lock_tsx( pjsip_transaction *tsx, struct + tsx_lock_data *lck ); +static pj_status_t unlock_tsx( pjsip_transaction *tsx, + struct tsx_lock_data *lck ); + +/* State handlers for UAC, indexed by state */ +static int (*tsx_state_handler_uac[PJSIP_TSX_STATE_MAX])(pjsip_transaction *, + pjsip_event *) = +{ + &pjsip_tsx_on_state_null, + &pjsip_tsx_on_state_calling, + &pjsip_tsx_on_state_trying, + &pjsip_tsx_on_state_proceeding_uac, + &pjsip_tsx_on_state_completed_uac, + &pjsip_tsx_on_state_confirmed, + &pjsip_tsx_on_state_terminated, + &pjsip_tsx_on_state_destroyed, +}; + +/* State handlers for UAS */ +static int (*tsx_state_handler_uas[PJSIP_TSX_STATE_MAX])(pjsip_transaction *, + pjsip_event *) = +{ + &pjsip_tsx_on_state_null, + &pjsip_tsx_on_state_calling, + &pjsip_tsx_on_state_trying, + &pjsip_tsx_on_state_proceeding_uas, + &pjsip_tsx_on_state_completed_uas, + &pjsip_tsx_on_state_confirmed, + &pjsip_tsx_on_state_terminated, + &pjsip_tsx_on_state_destroyed, +}; + +/* + * Get transaction state name. + */ +PJ_DEF(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state) +{ + return state_str[state]; +} + +/* + * Get the role name. + */ +PJ_DEF(const char *) pjsip_role_name(pjsip_role_e role) +{ + return role_name[role]; +} + + +/* + * Create transaction key for RFC2543 compliant messages, which don't have + * unique branch parameter in the top most Via header. + * + * INVITE requests matches a transaction if the following attributes + * match the original request: + * - Request-URI + * - To tag + * - From tag + * - Call-ID + * - CSeq + * - top Via header + * + * CANCEL matching is done similarly as INVITE, except: + * - CSeq method will differ + * - To tag is not matched. + * + * ACK matching is done similarly, except that: + * - method of the CSeq will differ, + * - To tag is matched to the response sent by the server transaction. + * + * The transaction key is constructed from the common components of above + * components. Additional comparison is needed to fully match a transaction. + */ +static pj_status_t create_tsx_key_2543( pj_pool_t *pool, + pj_str_t *str, + pjsip_role_e role, + const pjsip_method *method, + const pjsip_rx_data *rdata ) +{ +#define SEPARATOR '$' + char *key, *p, *end; + int len; + pj_size_t len_required; + pjsip_uri *req_uri; + pj_str_t *host; + + PJ_ASSERT_RETURN(pool && str && method && rdata, PJ_EINVAL); + PJ_ASSERT_RETURN(rdata->msg_info.msg, PJ_EINVAL); + PJ_ASSERT_RETURN(rdata->msg_info.via, PJSIP_EMISSINGHDR); + PJ_ASSERT_RETURN(rdata->msg_info.cseq, PJSIP_EMISSINGHDR); + PJ_ASSERT_RETURN(rdata->msg_info.from, PJSIP_EMISSINGHDR); + + host = &rdata->msg_info.via->sent_by.host; + req_uri = (pjsip_uri*)rdata->msg_info.msg->line.req.uri; + + /* Calculate length required. */ + len_required = 9 + /* CSeq number */ + rdata->msg_info.from->tag.slen + /* From tag. */ + rdata->msg_info.call_id.slen + /* Call-ID */ + host->slen + /* Via host. */ + 9 + /* Via port. */ + 16; /* Separator+Allowance. */ + key = p = pj_pool_alloc(pool, len_required); + end = p + len_required; + + /* Add role. */ + *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's'); + *p++ = SEPARATOR; + + /* Add Request-URI */ + /* This is BUG! + * Response doesn't have Request-URI! + * + len = req_uri->vptr->print( PJSIP_URI_IN_REQ_URI, req_uri, p, end-p ); + p += len; + *p++ = SEPARATOR; + */ + + /* Add method, except when method is INVITE or ACK. */ + if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) { + pj_memcpy(p, method->name.ptr, method->name.slen); + p += method->name.slen; + *p++ = '$'; + } + + /* Add CSeq (only the number). */ + len = pj_utoa(rdata->msg_info.cseq->cseq, p); + p += len; + *p++ = SEPARATOR; + + /* Add From tag. */ + len = rdata->msg_info.from->tag.slen; + pj_memcpy( p, rdata->msg_info.from->tag.ptr, len); + p += len; + *p++ = SEPARATOR; + + /* Add Call-ID. */ + len = rdata->msg_info.call_id.slen; + pj_memcpy( p, rdata->msg_info.call_id.ptr, len ); + p += len; + *p++ = SEPARATOR; + + /* Add top Via header. + * We don't really care whether the port contains the real port (because + * it can be omited if default port is used). Anyway this function is + * only used to match request retransmission, and we expect that the + * request retransmissions will contain the same port. + */ + pj_memcpy(p, host->ptr, host->slen); + p += host->slen; + *p++ = ':'; + + len = pj_utoa(rdata->msg_info.via->sent_by.port, p); + p += len; + *p++ = SEPARATOR; + + *p++ = '\0'; + + /* Done. */ + str->ptr = key; + str->slen = p-key; + + return PJ_SUCCESS; +} + +/* + * Create transaction key for RFC3161 compliant system. + */ +static pj_status_t create_tsx_key_3261( pj_pool_t *pool, + pj_str_t *key, + pjsip_role_e role, + const pjsip_method *method, + const pj_str_t *branch) +{ + char *p; + + PJ_ASSERT_RETURN(pool && key && method && branch, PJ_EINVAL); + + p = key->ptr = pj_pool_alloc(pool, branch->slen + method->name.slen + 4 ); + + /* Add role. */ + *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's'); + *p++ = SEPARATOR; + + /* Add method, except when method is INVITE or ACK. */ + if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) { + pj_memcpy(p, method->name.ptr, method->name.slen); + p += method->name.slen; + *p++ = '$'; + } + + /* Add branch ID. */ + pj_memcpy(p, branch->ptr, branch->slen); + p += branch->slen; + + /* Set length */ + key->slen = p - key->ptr; + + return PJ_SUCCESS; +} + +/* + * Create key from the incoming data, to be used to search the transaction + * in the transaction hash table. + */ +PJ_DEF(pj_status_t) pjsip_tsx_create_key( pj_pool_t *pool, pj_str_t *key, + pjsip_role_e role, + const pjsip_method *method, + const pjsip_rx_data *rdata) +{ + pj_str_t rfc3261_branch = {PJSIP_RFC3261_BRANCH_ID, + PJSIP_RFC3261_BRANCH_LEN}; + + + /* Get the branch parameter in the top-most Via. + * If branch parameter is started with "z9hG4bK", then the message was + * generated by agent compliant with RFC3261. Otherwise, it will be + * handled as RFC2543. + */ + const pj_str_t *branch = &rdata->msg_info.via->branch_param; + + if (pj_strncmp(branch,&rfc3261_branch,PJSIP_RFC3261_BRANCH_LEN)==0) { + + /* Create transaction key. */ + return create_tsx_key_3261(pool, key, role, method, branch); + + } else { + /* Create the key for the message. This key will be matched up + * with the transaction key. For RFC2563 transactions, the + * transaction key was created by the same function, so it will + * match the message. + */ + return create_tsx_key_2543( pool, key, role, method, rdata ); + } +} + + +/* + * Create new transaction. + */ +PJ_DEF(pj_status_t) pjsip_tsx_create( pj_pool_t *pool, + pjsip_endpoint *endpt, + pjsip_transaction **p_tsx) +{ + pjsip_transaction *tsx; + pj_status_t status; + + tsx = pj_pool_calloc(pool, 1, sizeof(pjsip_transaction)); + + tsx->pool = pool; + tsx->endpt = endpt; + tsx->retransmit_timer.id = TSX_TIMER_RETRANSMISSION; + tsx->retransmit_timer._timer_id = -1; + tsx->retransmit_timer.user_data = tsx; + tsx->retransmit_timer.cb = &tsx_timer_callback; + tsx->timeout_timer.id = TSX_TIMER_TIMEOUT; + tsx->timeout_timer._timer_id = -1; + tsx->timeout_timer.user_data = tsx; + tsx->timeout_timer.cb = &tsx_timer_callback; + pj_sprintf(tsx->obj_name, "tsx%p", tsx); + status = pj_mutex_create_recursive(pool, "mtsx%p", &tsx->mutex); + if (status != PJ_SUCCESS) { + return status; + } + + *p_tsx = tsx; + return PJ_SUCCESS; +} + +/* + * Lock transaction and set the value of Thread Local Storage. + */ +static void lock_tsx(pjsip_transaction *tsx, struct tsx_lock_data *lck) +{ + struct tsx_lock_data *prev_data; + + pj_mutex_lock(tsx->mutex); + prev_data = (struct tsx_lock_data *) + pj_thread_local_get(pjsip_tsx_lock_tls_id); + lck->prev = prev_data; + lck->tsx = tsx; + lck->is_alive = 1; + pj_thread_local_set(pjsip_tsx_lock_tls_id, lck); +} + + +/* + * Unlock transaction. + * This will selectively unlock the mutex ONLY IF the transaction has not been + * destroyed. The function knows whether the transaction has been destroyed + * because when transaction is destroyed the is_alive flag for the transaction + * will be set to zero. + */ +static pj_status_t unlock_tsx( pjsip_transaction *tsx, + struct tsx_lock_data *lck) +{ + pj_assert( (void*)pj_thread_local_get(pjsip_tsx_lock_tls_id) == lck); + pj_assert( lck->tsx == tsx ); + pj_thread_local_set(pjsip_tsx_lock_tls_id, lck->prev); + if (lck->is_alive) + pj_mutex_unlock(tsx->mutex); + + return lck->is_alive ? PJ_SUCCESS : PJSIP_ETSXDESTROYED; +} + +/* + * Set transaction state, and inform TU about the transaction state change. + */ +static void tsx_set_state( pjsip_transaction *tsx, + pjsip_tsx_state_e state, + pjsip_event_id_e event_src_type, + void *event_src ) +{ + pjsip_event e; + + PJ_LOG(4, (tsx->obj_name, "STATE %s-->%s, cause = %s", + state_str[tsx->state], state_str[state], + pjsip_event_str(event_src_type))); + + /* Change state. */ + tsx->state = state; + + /* Update the state handlers. */ + if (tsx->role == PJSIP_ROLE_UAC) { + tsx->state_handler = tsx_state_handler_uac[state]; + } else { + tsx->state_handler = tsx_state_handler_uas[state]; + } + + /* Inform TU */ + PJSIP_EVENT_INIT_TSX_STATE(e, tsx, event_src_type, event_src); + pjsip_endpt_send_tsx_event( tsx->endpt, &e ); + + /* When the transaction is terminated, release transport, and free the + * saved last transmitted message. + */ + if (state == PJSIP_TSX_STATE_TERMINATED) { + + /* Decrement transport reference counter. */ + if (tsx->transport && + tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) + { + pjsip_transport_dec_ref( tsx->transport ); + tsx->transport = NULL; + } + /* Free last transmitted message. */ + if (tsx->last_tx) { + pjsip_tx_data_dec_ref( tsx->last_tx ); + tsx->last_tx = NULL; + } + /* Cancel timeout timer. */ + if (tsx->timeout_timer._timer_id != -1) { + pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer); + tsx->timeout_timer._timer_id = -1; + } + /* Cancel retransmission timer. */ + if (tsx->retransmit_timer._timer_id != -1) { + pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer); + tsx->retransmit_timer._timer_id = -1; + } + + /* If transport is not pending, reschedule timeout timer to + * destroy this transaction. + */ + if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) { + pj_time_val timeout = {0, 0}; + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, + &timeout); + } + + } else if (state == PJSIP_TSX_STATE_DESTROYED) { + + /* Clear TLS, so that mutex will not be unlocked */ + struct tsx_lock_data *lck = pj_thread_local_get(pjsip_tsx_lock_tls_id); + while (lck) { + if (lck->tsx == tsx) { + lck->is_alive = 0; + } + lck = lck->prev; + } + } +} + +/* + * Look-up destination address and select which transport to be used to send + * the request message. The procedure used here follows the guidelines on + * sending the request in RFC3261 chapter 8.1.2. + * + * This function also modifies the message (request line and Route headers) + * accordingly. + */ +static pj_status_t tsx_process_route( pjsip_transaction *tsx, + pjsip_tx_data *tdata, + pjsip_host_port *send_addr ) +{ + const pjsip_uri *new_request_uri, *target_uri; + const pjsip_name_addr *topmost_route_uri; + pjsip_route_hdr *first_route_hdr, *last_route_hdr; + + pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG); + + /* Get the first "Route" header from the message. If the message doesn't + * have any "Route" headers but the endpoint has, then copy the "Route" + * headers from the endpoint first. + */ + last_route_hdr = first_route_hdr = + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL); + if (first_route_hdr) { + topmost_route_uri = &first_route_hdr->name_addr; + while (last_route_hdr->next != (void*)&tdata->msg->hdr) { + pjsip_route_hdr *hdr; + hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, + last_route_hdr->next); + if (!hdr) + break; + last_route_hdr = hdr; + } + } else { + const pjsip_route_hdr *hdr_list; + hdr_list = (pjsip_route_hdr*)pjsip_endpt_get_routing(tsx->endpt); + if (hdr_list->next != hdr_list) { + const pjsip_route_hdr *hdr = (pjsip_route_hdr*)hdr_list->next; + first_route_hdr = NULL; + topmost_route_uri = &hdr->name_addr; + do { + last_route_hdr = pjsip_hdr_shallow_clone(tdata->pool, hdr); + if (first_route_hdr == NULL) + first_route_hdr = last_route_hdr; + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)last_route_hdr); + hdr = hdr->next; + } while (hdr != hdr_list); + } else { + topmost_route_uri = NULL; + } + } + + /* If Route headers exist, and the first element indicates loose-route, + * the URI is taken from the Request-URI, and we keep all existing Route + * headers intact. + * If Route headers exist, and the first element DOESN'T indicate loose + * route, the URI is taken from the first Route header, and remove the + * first Route header from the message. + * Otherwise if there's no Route headers, the URI is taken from the + * Request-URI. + */ + if (topmost_route_uri) { + pj_bool_t has_lr_param; + + if (PJSIP_URI_SCHEME_IS_SIP(topmost_route_uri) || + PJSIP_URI_SCHEME_IS_SIPS(topmost_route_uri)) + { + const pjsip_url *url = pjsip_uri_get_uri((void*)topmost_route_uri); + has_lr_param = url->lr_param; + } else { + has_lr_param = 0; + } + + if (has_lr_param) { + new_request_uri = tdata->msg->line.req.uri; + /* We shouldn't need to delete topmost Route if it has lr param. + * But seems like it breaks some proxy implementation, so we + * delete it anyway. + */ + /* + pj_list_erase(first_route_hdr); + if (first_route_hdr == last_route_hdr) + last_route_hdr = NULL; + */ + } else { + new_request_uri = pjsip_uri_get_uri((void*)topmost_route_uri); + pj_list_erase(first_route_hdr); + if (first_route_hdr == last_route_hdr) + last_route_hdr = NULL; + } + + target_uri = (pjsip_uri*)topmost_route_uri; + + } else { + target_uri = new_request_uri = tdata->msg->line.req.uri; + } + + /* The target URI must be a SIP/SIPS URL so we can resolve it's address. + * Otherwise we're in trouble (i.e. there's no host part in tel: URL). + */ + pj_memset(send_addr, 0, sizeof(*send_addr)); + + if (PJSIP_URI_SCHEME_IS_SIPS(target_uri)) { + pjsip_uri *uri = (pjsip_uri*) target_uri; + const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri); + send_addr->flag |= (PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_RELIABLE); + pj_strdup(tdata->pool, &send_addr->host, &url->host); + send_addr->port = url->port; + send_addr->type = + pjsip_transport_get_type_from_name(&url->transport_param); + + } else if (PJSIP_URI_SCHEME_IS_SIP(target_uri)) { + pjsip_uri *uri = (pjsip_uri*) target_uri; + const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri); + pj_strdup(tdata->pool, &send_addr->host, &url->host); + send_addr->port = url->port; + send_addr->type = + pjsip_transport_get_type_from_name(&url->transport_param); +#if PJ_HAS_TCP + if (send_addr->type == PJSIP_TRANSPORT_TCP || + send_addr->type == PJSIP_TRANSPORT_SCTP) + { + send_addr->flag |= PJSIP_TRANSPORT_RELIABLE; + } +#endif + } else { + pj_assert(!"Unsupported URI scheme!"); + return PJSIP_EINVALIDSCHEME; + } + + /* If target URI is different than request URI, replace + * request URI add put the original URI in the last Route header. + */ + if (new_request_uri && new_request_uri!=tdata->msg->line.req.uri) { + pjsip_route_hdr *route = pjsip_route_hdr_create(tdata->pool); + route->name_addr.uri = tdata->msg->line.req.uri; + if (last_route_hdr) + pj_list_insert_after(last_route_hdr, route); + else + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route); + tdata->msg->line.req.uri = (pjsip_uri*)new_request_uri; + } + + /* Success. */ + return PJ_SUCCESS; +} + + +/* + * Callback from the transport job. + * This callback is called when asychronous transport connect() operation + * has completed, with or without error. + */ +static void tsx_transport_callback(pjsip_transport *tr, + void *token, + pj_status_t status) +{ + char addr[PJ_MAX_HOSTNAME]; + pjsip_transaction *tsx = token; + struct tsx_lock_data lck; + + pj_memcpy(addr, tsx->dest_name.host.ptr, tsx->dest_name.host.slen); + addr[tsx->dest_name.host.slen] = '\0'; + + + if (status == PJ_SUCCESS) { + PJ_LOG(4, (tsx->obj_name, "%s connected to %s:%d", + tr->type_name, + addr, tsx->dest_name.port)); + } else { + PJ_LOG(4, (tsx->obj_name, "%s unable to connect to %s:%d, status=%d", + tr->type_name, + addr, tsx->dest_name.port, status)); + } + + /* Lock transaction. */ + lock_tsx(tsx, &lck); + + if (status != PJ_SUCCESS) { + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; + tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; + + tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, + PJSIP_EVENT_TRANSPORT_ERROR, (void*)status); + + /* Unlock transaction. */ + unlock_tsx(tsx, &lck); + return; + } + + /* See if transaction has already been terminated. + * If so, schedule to destroy the transaction. + */ + if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { + pj_time_val timeout = {0, 0}; + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, + &timeout); + + /* Unlock transaction. */ + unlock_tsx(tsx, &lck); + return; + } + + /* Add reference counter to the transport. */ + pjsip_transport_add_ref(tr); + + /* Mark transport as ready. */ + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; + tsx->transport = tr; + + /* If there's a pending message to send, send it now. */ + if (tsx->has_unsent_msg) { + tsx_send_msg( tsx, tsx->last_tx ); + } + + /* Unlock transaction. */ + unlock_tsx(tsx, &lck); +} + +/* + * Callback from the resolver job. + */ +static void tsx_resolver_callback(pj_status_t status, + void *token, + const struct pjsip_server_addresses *addr) +{ + pjsip_transaction *tsx = token; + struct tsx_lock_data lck; + pjsip_transport *tp; + + PJ_LOG(4, (tsx->obj_name, "resolver job complete, status=%d", status)); + + if (status != PJ_SUCCESS || addr->count == 0) { + lock_tsx(tsx, &lck); + tsx->status_code = PJSIP_SC_TSX_RESOLVE_ERROR; + tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, + PJSIP_EVENT_TRANSPORT_ERROR, (void*)status); + unlock_tsx(tsx, &lck); + return; + } + + /* Lock transaction. */ + lock_tsx(tsx, &lck); + + /* Copy server addresses. */ + pj_memcpy(&tsx->remote_addr, addr, sizeof(*addr)); + + /* Create/find the transport for the remote address. */ + PJ_LOG(5,(tsx->obj_name, "tsx getting transport for %s:%d", + pj_inet_ntoa(addr->entry[0].addr.sin_addr), + pj_ntohs(addr->entry[0].addr.sin_port))); + + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_CONNECTING; + status = pjsip_endpt_alloc_transport( tsx->endpt, addr->entry[0].type, + &addr->entry[0].addr, + &tp); + tsx_transport_callback(tp, tsx, status); + + /* Unlock transaction */ + unlock_tsx(tsx, &lck); + + /* There should be nothing to do after this point. + * Execution for the transaction will resume when the callback for the + * transport is called. + */ +} + +/* + * Initialize the transaction as UAC transaction. + */ +PJ_DEF(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx, + pjsip_tx_data *tdata) +{ + pjsip_msg *msg; + pjsip_cseq_hdr *cseq; + pjsip_via_hdr *via; + pj_status_t status; + struct tsx_lock_data lck; + + PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAC (tdata=%p)", tdata)); + + /* Lock transaction. */ + lock_tsx(tsx, &lck); + + /* Keep shortcut */ + msg = tdata->msg; + + /* Role is UAC. */ + tsx->role = PJSIP_ROLE_UAC; + + /* Save method. */ + pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method); + + /* Generate Via header if it doesn't exist. */ + via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL); + if (via == NULL) { + via = pjsip_via_hdr_create(tdata->pool); + pjsip_msg_insert_first_hdr(msg, (pjsip_hdr*) via); + } + + if (via->branch_param.slen == 0) { + pj_str_t tmp; + via->branch_param.ptr = pj_pool_alloc(tsx->pool, PJSIP_MAX_BRANCH_LEN); + via->branch_param.slen = PJSIP_MAX_BRANCH_LEN; + pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID, + PJSIP_RFC3261_BRANCH_LEN); + + tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN; + pj_generate_unique_string( &tmp ); + + /* Save branch parameter. */ + tsx->branch = via->branch_param; + } else { + /* Copy branch parameter. */ + pj_strdup(tsx->pool, &tsx->branch, &via->branch_param); + } + + + /* Generate transaction key. */ + status = create_tsx_key_3261( tsx->pool, &tsx->transaction_key, + PJSIP_ROLE_UAC, &tsx->method, + &via->branch_param); + if (status != PJ_SUCCESS) { + unlock_tsx(tsx, &lck); + return status; + } + + PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen, + tsx->transaction_key.ptr)); + + /* Save CSeq. */ + cseq = pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL); + if (!cseq) { + pj_assert(!"CSeq header not present in outgoing message!"); + unlock_tsx(tsx, &lck); + return PJSIP_EMISSINGHDR; + } + tsx->cseq = cseq->cseq; + + + /* Begin with State_Null. + * Manually set-up the state becase we don't want to call the callback. + */ + tsx->state = PJSIP_TSX_STATE_NULL; + tsx->state_handler = &pjsip_tsx_on_state_null; + + /* Get destination name from the message. */ + status = tsx_process_route(tsx, tdata, &tsx->dest_name); + if (status != PJ_SUCCESS) { + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; + tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; + tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, + PJSIP_EVENT_TRANSPORT_ERROR, (void*)status); + unlock_tsx(tsx, &lck); + return status; + } + + /* Resolve destination. + * This will start asynchronous resolver job, and when it finishes, + * the callback will be called. + */ + PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d", + tsx->dest_name.host.slen, + tsx->dest_name.host.ptr, + tsx->dest_name.port)); + + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING; + pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, + tsx, &tsx_resolver_callback); + + /* There should be nothing to do after this point. + * Execution for the transaction will resume when the resolver callback is + * called. + */ + + /* Unlock transaction and return. + * If transaction has been destroyed WITHIN the current thread, the + * unlock_tsx() function will return -1. + */ + return unlock_tsx(tsx, &lck); +} + + +/* + * Initialize the transaction as UAS transaction. + */ +PJ_DEF(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx, + pjsip_rx_data *rdata) +{ + pjsip_msg *msg = rdata->msg_info.msg; + pj_str_t *branch; + pjsip_cseq_hdr *cseq; + pj_status_t status; + struct tsx_lock_data lck; + + PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAS (rdata=%p)", rdata)); + + /* Lock transaction. */ + lock_tsx(tsx, &lck); + + /* Keep shortcut to message */ + msg = rdata->msg_info.msg; + + /* Role is UAS */ + tsx->role = PJSIP_ROLE_UAS; + + /* Save method. */ + pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method); + + /* Get transaction key either from branch for RFC3261 message, or + * create transaction key. + */ + status = pjsip_tsx_create_key(tsx->pool, &tsx->transaction_key, + PJSIP_ROLE_UAS, &tsx->method, rdata); + if (status != PJ_SUCCESS) { + unlock_tsx(tsx, &lck); + return status; + } + + /* Duplicate branch parameter for transaction. */ + branch = &rdata->msg_info.via->branch_param; + pj_strdup(tsx->pool, &tsx->branch, branch); + + PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen, + tsx->transaction_key.ptr)); + + /* Save CSeq */ + cseq = rdata->msg_info.cseq; + tsx->cseq = cseq->cseq; + + /* Begin with state NULL + * Manually set-up the state becase we don't want to call the callback. + */ + tsx->state = PJSIP_TSX_STATE_NULL; + tsx->state_handler = &pjsip_tsx_on_state_null; + + /* Get the transport to send the response. + * According to section 18.2.2 of RFC3261, if the transport is reliable + * then the response must be sent using that transport. + */ + /* In addition, RFC 3581 says, if Via has "rport" parameter specified, + * then return the response using the same transport. + */ + if (PJSIP_TRANSPORT_IS_RELIABLE(rdata->tp_info.transport) || + rdata->msg_info.via->rport_param >= 0) + { + tsx->transport = rdata->tp_info.transport; + pjsip_transport_add_ref(tsx->transport); + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; + + tsx->current_addr = 0; + tsx->remote_addr.count = 1; + tsx->remote_addr.entry[0].type = tsx->transport->type; + pj_memcpy(&tsx->remote_addr.entry[0].addr, + &rdata->pkt_info.addr, rdata->pkt_info.addr_len); + + } else { + pj_status_t status; + + status = pjsip_get_response_addr(tsx->pool, rdata->tp_info.transport, + rdata->msg_info.via, &tsx->dest_name); + if (status != PJ_SUCCESS) { + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; + tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; + tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, + PJSIP_EVENT_TRANSPORT_ERROR, (void*)status); + unlock_tsx(tsx, &lck); + return status; + } + + /* Resolve destination. + * This will start asynchronous resolver job, and when it finishes, + * the callback will be called. + */ + PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d", + tsx->dest_name.host.slen, + tsx->dest_name.host.ptr, + tsx->dest_name.port)); + + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING; + pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, + tsx, &tsx_resolver_callback); + } + + /* There should be nothing to do after this point. + * Execution for the transaction will resume when the resolver callback is + * called. + */ + + /* Unlock transaction and return. + * If transaction has been destroyed WITHIN the current thread, the + * unlock_tsx() function will return -1. + */ + return unlock_tsx(tsx, &lck); +} + +/* + * Callback when timer expires. + */ +static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry) +{ + pjsip_event event; + pjsip_transaction *tsx = entry->user_data; + struct tsx_lock_data lck; + + PJ_UNUSED_ARG(theap); + + PJ_LOG(5,(tsx->obj_name, "got timer event (%s timer)", + (entry->id==TSX_TIMER_RETRANSMISSION ? "Retransmit" : "Timeout"))); + + + if (entry->id == TSX_TIMER_RETRANSMISSION) { + PJSIP_EVENT_INIT_TIMER(event, &tsx->retransmit_timer); + } else { + PJSIP_EVENT_INIT_TIMER(event, &tsx->timeout_timer); + } + + /* Dispatch event to transaction. */ + lock_tsx(tsx, &lck); + (*tsx->state_handler)(tsx, &event); + unlock_tsx(tsx, &lck); +} + +/* + * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for + * non-2xx/INVITE is automatically sent by the transaction. + * This operation is only valid if the transaction is configured to handle ACK + * (tsx->handle_ack is non-zero). If this attribute is not set, then the + * transaction will comply with RFC-3261, i.e. it will set itself to + * TERMINATED state when it receives 2xx/INVITE. + */ +PJ_DEF(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx, pjsip_tx_data *tdata) +{ + pjsip_msg *msg; + pjsip_host_port dest_addr; + pjsip_via_hdr *via; + struct tsx_lock_data lck; + pj_status_t status = PJ_SUCCESS; + + /* Lock tsx. */ + lock_tsx(tsx, &lck); + + pj_assert(tsx->handle_ack != 0); + + msg = tdata->msg; + + /* Generate branch parameter if it doesn't exist. */ + via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL); + if (via == NULL) { + via = pjsip_via_hdr_create(tdata->pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*) via); + } + + if (via->branch_param.slen == 0) { + via->branch_param = tsx->branch; + } else { + pj_assert( pj_strcmp(&via->branch_param, &tsx->branch) == 0 ); + } + + /* Get destination name from the message. */ + status = tsx_process_route(tsx, tdata, &dest_addr); + if (status != 0){ + goto on_error; + } + + /* Compare message's destination name with transaction's destination name. + * If NOT equal, then we'll have to resolve the destination. + */ + if (dest_addr.type == tsx->dest_name.type && + dest_addr.flag == tsx->dest_name.flag && + dest_addr.port == tsx->dest_name.port && + pj_stricmp(&dest_addr.host, &tsx->dest_name.host) == 0) + { + /* Equal destination. We can use current transport. */ + pjsip_tsx_on_tx_msg(tsx, tdata); + unlock_tsx(tsx, &lck); + return; + + } + + /* New destination; we'll have to resolve host and create new transport. */ + pj_memcpy(&tsx->dest_name, &dest_addr, sizeof(dest_addr)); + pj_strdup(tsx->pool, &tsx->dest_name.host, &dest_addr.host); + + PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d", + tsx->dest_name.host.slen, + tsx->dest_name.host.ptr, + tsx->dest_name.port)); + + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING; + pjsip_transport_dec_ref(tsx->transport); + tsx->transport = NULL; + + /* Put the message in queue. */ + pjsip_tsx_on_tx_msg(tsx, tdata); + + /* This is a bug! + * We shouldn't change transaction's state before actually sending the + * message. Otherwise transaction will terminate before message is sent, + * and timeout timer will be scheduled. + */ + PJ_TODO(TSX_DONT_CHANGE_STATE_BEFORE_SENDING_ACK) + + /* + * This will start asynchronous resolver job, and when it finishes, + * the callback will be called. + */ + + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING; + pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, + tsx, &tsx_resolver_callback); + + unlock_tsx(tsx, &lck); + + /* There should be nothing to do after this point. + * Execution for the transaction will resume when the resolver callback is + * called. + */ + return; + +on_error: + /* Failure condition. + * Send TERMINATED event. + */ + tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; + + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, + PJSIP_EVENT_TRANSPORT_ERROR, (void*)status); + + unlock_tsx(tsx, &lck); +} + + +/* + * This function is called by TU to send a message. + */ +PJ_DEF(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx, + pjsip_tx_data *tdata ) +{ + pjsip_event event; + struct tsx_lock_data lck; + pj_status_t status; + + PJ_LOG(5,(tsx->obj_name, "Request to transmit msg on state %s (tdata=%p)", + state_str[tsx->state], tdata)); + + PJSIP_EVENT_INIT_TX_MSG(event, tsx, tdata); + + /* Dispatch to transaction. */ + lock_tsx(tsx, &lck); + status = (*tsx->state_handler)(tsx, &event); + unlock_tsx(tsx, &lck); +} + +/* + * This function is called by endpoint when incoming message for the + * transaction is received. + */ +PJ_DEF(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx, + pjsip_rx_data *rdata) +{ + pjsip_event event; + struct tsx_lock_data lck; + pj_status_t status; + + PJ_LOG(5,(tsx->obj_name, "Incoming msg on state %s (rdata=%p)", + state_str[tsx->state], rdata)); + + PJSIP_EVENT_INIT_RX_MSG(event, tsx, rdata); + + /* Dispatch to transaction. */ + lock_tsx(tsx, &lck); + status = (*tsx->state_handler)(tsx, &event); + unlock_tsx(tsx, &lck); +} + +/* + * Forcely terminate transaction. + */ +PJ_DEF(void) pjsip_tsx_terminate( pjsip_transaction *tsx, int code ) +{ + struct tsx_lock_data lck; + + lock_tsx(tsx, &lck); + tsx->status_code = code; + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, + PJSIP_EVENT_USER, NULL); + + unlock_tsx(tsx, &lck); +} + +/* + * Transport send completion callback. + */ +static void tsx_on_send_complete(void *token, pjsip_tx_data *tdata, + pj_ssize_t bytes_sent) +{ + PJ_UNUSED_ARG(token); + PJ_UNUSED_ARG(tdata); + + if (bytes_sent <= 0) { + PJ_TODO(HANDLE_TRANSPORT_ERROR); + } +} + +/* + * Send message to the transport. + * If transport is not yet available, then do nothing. The message will be + * transmitted when transport connection completion callback is called. + */ +static pj_status_t tsx_send_msg( pjsip_transaction *tsx, + pjsip_tx_data *tdata) +{ + pj_status_t status = PJ_SUCCESS; + + PJ_LOG(5,(tsx->obj_name, "sending msg (tdata=%p)", tdata)); + + if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) { + pjsip_event before_tx_event; + + pj_assert(tsx->transport != NULL); + + /* Make sure Via transport info is filled up properly for + * requests. + */ + if (tdata->msg->type == PJSIP_REQUEST_MSG) { + const pj_sockaddr_in *addr_name; + pjsip_via_hdr *via = (pjsip_via_hdr*) + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); + + /* For request message, set "rport" parameter by default. */ + if (tdata->msg->type == PJSIP_REQUEST_MSG) + via->rport_param = 0; + + /* Don't update Via sent-by on retransmission. */ + if (via->sent_by.host.slen == 0) { + addr_name = &tsx->transport->public_addr; + pj_strdup2(tdata->pool, &via->transport, + tsx->transport->type_name); + pj_strdup2(tdata->pool, &via->sent_by.host, + pj_inet_ntoa(addr_name->sin_addr)); + via->sent_by.port = pj_ntohs(addr_name->sin_port); + } + } + + /* Notify everybody we're about to send message. */ + PJSIP_EVENT_INIT_PRE_TX_MSG(before_tx_event, tsx, tdata, + tsx->retransmit_count); + pjsip_endpt_send_tsx_event( tsx->endpt, &before_tx_event ); + + tsx->has_unsent_msg = 0; + status = pjsip_transport_send(tsx->transport, tdata, + &tsx->remote_addr.entry[tsx->current_addr].addr, + tsx, &tsx_on_send_complete); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + PJ_TODO(HANDLE_TRANSPORT_ERROR); + goto on_error; + } + } else { + tsx->has_unsent_msg = 1; + } + + return 0; + +on_error: + tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, + PJSIP_EVENT_TRANSPORT_ERROR, (void*)status); + return status; +} + +/* + * Retransmit last message sent. + */ +static pj_status_t pjsip_tsx_retransmit( pjsip_transaction *tsx, + int should_restart_timer) +{ + pj_status_t status; + + PJ_LOG(4,(tsx->obj_name, "retransmiting (tdata=%p, count=%d, restart?=%d)", + tsx->last_tx, tsx->retransmit_count, should_restart_timer)); + + pj_assert(tsx->last_tx != NULL); + + ++tsx->retransmit_count; + + status = tsx_send_msg( tsx, tsx->last_tx); + if (status != PJ_SUCCESS) { + return status; + } + + /* Restart timer T1. */ + if (should_restart_timer) { + pj_time_val timeout; + int msec_time = (1 << (tsx->retransmit_count)) * PJSIP_T1_TIMEOUT; + + if (tsx->method.id!=PJSIP_INVITE_METHOD && msec_time>PJSIP_T2_TIMEOUT) + msec_time = PJSIP_T2_TIMEOUT; + + timeout.sec = msec_time / 1000; + timeout.msec = msec_time % 1000; + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer, + &timeout); + } + + return PJ_SUCCESS; +} + +/* + * Handler for events in state Null. + */ +static pj_status_t pjsip_tsx_on_state_null( pjsip_transaction *tsx, + pjsip_event *event ) +{ + pj_status_t status; + + pj_assert( tsx->state == PJSIP_TSX_STATE_NULL); + pj_assert( tsx->last_tx == NULL ); + pj_assert( tsx->has_unsent_msg == 0); + + if (tsx->role == PJSIP_ROLE_UAS) { + + /* Set state to Trying. */ + pj_assert(event->type == PJSIP_EVENT_RX_MSG); + tsx_set_state( tsx, PJSIP_TSX_STATE_TRYING, + PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); + + } else { + pjsip_tx_data *tdata = event->body.tx_msg.tdata; + + /* Save the message for retransmission. */ + tsx->last_tx = tdata; + pjsip_tx_data_add_ref(tdata); + + /* Send the message. */ + status = tsx_send_msg( tsx, tdata); + if (status != PJ_SUCCESS) { + return status; + } + + /* Start Timer B (or called timer F for non-INVITE) for transaction + * timeout. + */ + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, + &timeout_timer_val); + + /* Start Timer A (or timer E) for retransmission only if unreliable + * transport is being used. + */ + if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL && + PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) + { + pjsip_endpt_schedule_timer(tsx->endpt, &tsx->retransmit_timer, + &t1_timer_val); + tsx->retransmit_count = 0; + } + + /* Move state. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_CALLING, + PJSIP_EVENT_TX_MSG, tdata); + } + + return PJ_SUCCESS; +} + +/* + * State Calling is for UAC after it sends request but before any responses + * is received. + */ +static pj_status_t pjsip_tsx_on_state_calling( pjsip_transaction *tsx, + pjsip_event *event ) +{ + pj_assert(tsx->state == PJSIP_TSX_STATE_CALLING); + pj_assert(tsx->role == PJSIP_ROLE_UAC); + + if (event->type == PJSIP_EVENT_TIMER && + event->body.timer.entry == &tsx->retransmit_timer) + { + pj_status_t status; + + /* Retransmit the request. */ + status = pjsip_tsx_retransmit( tsx, 1 ); + if (status != PJ_SUCCESS) { + return status; + } + + } else if (event->type == PJSIP_EVENT_TIMER && + event->body.timer.entry == &tsx->timeout_timer) + { + + /* Cancel retransmission timer. */ + if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) { + pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer); + } + + /* Set status code */ + tsx->status_code = PJSIP_SC_TSX_TIMEOUT; + + /* Inform TU. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, + PJSIP_EVENT_TIMER, &tsx->timeout_timer); + + /* Transaction is destroyed */ + return PJSIP_ETSXDESTROYED; + + } else if (event->type == PJSIP_EVENT_RX_MSG) { + int code; + + /* Cancel retransmission timer A. */ + if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) + pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer); + + /* Cancel timer B (transaction timeout) */ + pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer); + + /* Discard retransmission message if it is not INVITE. + * The INVITE tdata is needed in case we have to generate ACK for + * the final response. + */ + /* Keep last_tx for authorization. */ + code = event->body.rx_msg.rdata->msg_info.msg->line.status.code; + if (tsx->method.id != PJSIP_INVITE_METHOD && code!=401 && code!=407) { + pjsip_tx_data_dec_ref(tsx->last_tx); + tsx->last_tx = NULL; + } + + /* Processing is similar to state Proceeding. */ + pjsip_tsx_on_state_proceeding_uac( tsx, event); + + } else { + pj_assert(0); + return PJ_EBUG; + } + + return PJ_SUCCESS; +} + +/* + * State Trying is for UAS after it received request but before any responses + * is sent. + * Note: this is different than RFC3261, which can use Trying state for + * non-INVITE client transaction (bug in RFC?). + */ +static pj_status_t pjsip_tsx_on_state_trying( pjsip_transaction *tsx, + pjsip_event *event) +{ + pj_status_t status; + + pj_assert(tsx->state == PJSIP_TSX_STATE_TRYING); + + /* This state is only for UAS */ + pj_assert(tsx->role == PJSIP_ROLE_UAS); + + /* Better be transmission of response message. + * If we've got request retransmission, this means that the TU hasn't + * transmitted any responses within 500 ms, which is not allowed. If + * this happens, just ignore the event (we couldn't retransmit last + * response because we haven't sent any!). + */ + //pj_assert(event->type == PJSIP_EVENT_TX_MSG); + if (event->type != PJSIP_EVENT_TX_MSG) { + return PJ_SUCCESS; + } + + /* The rest of the processing of the event is exactly the same as in + * "Proceeding" state. + */ + status = pjsip_tsx_on_state_proceeding_uas( tsx, event); + + /* Inform the TU of the state transision if state is still State_Trying */ + if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TRYING) { + tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, + PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata); + } + + return status; +} + +/* + * Handler for events in Proceeding for UAS + * This state happens after the TU sends provisional response. + */ +static pj_status_t pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx, + pjsip_event *event) +{ + pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING || + tsx->state == PJSIP_TSX_STATE_TRYING); + + /* This state is only for UAS. */ + pj_assert(tsx->role == PJSIP_ROLE_UAS); + + /* Receive request retransmission. */ + if (event->type == PJSIP_EVENT_RX_MSG) { + + pj_status_t status; + + /* Send last response. */ + status = pjsip_tsx_retransmit( tsx, 0 ); + if (status != PJ_SUCCESS) { + return status; + } + + } else if (event->type == PJSIP_EVENT_TX_MSG ) { + pjsip_tx_data *tdata = event->body.tx_msg.tdata; + pj_status_t status; + + /* The TU sends response message to the request. Save this message so + * that we can retransmit the last response in case we receive request + * retransmission. + */ + pjsip_msg *msg = tdata->msg; + + /* This can only be a response message. */ + pj_assert(msg->type == PJSIP_RESPONSE_MSG); + + /* Status code must be higher than last sent. */ + pj_assert(msg->line.status.code >= tsx->status_code); + + /* Update last status */ + tsx->status_code = msg->line.status.code; + + /* Discard the saved last response (it will be updated later as + * necessary). + */ + if (tsx->last_tx && tsx->last_tx != tdata) { + pjsip_tx_data_dec_ref( tsx->last_tx ); + tsx->last_tx = NULL; + } + + /* Send the message. */ + status = tsx_send_msg(tsx, tdata); + if (status != PJ_SUCCESS) { + return status; + } + + // Update To tag header for RFC2543 transaction. + // TODO: + + /* Update transaction state */ + if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) { + + if (tsx->last_tx != tdata) { + tsx->last_tx = tdata; + pjsip_tx_data_add_ref( tdata ); + } + tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, + PJSIP_EVENT_TX_MSG, tdata ); + + } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { + + if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack==0) { + + /* 2xx class message is not saved, because retransmission + * is handled by TU. + */ + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, + PJSIP_EVENT_TX_MSG, tdata ); + + /* Transaction is destroyed. */ + return PJSIP_ETSXDESTROYED; + + } else { + pj_time_val timeout; + + if (tsx->method.id == PJSIP_INVITE_METHOD) { + tsx->retransmit_count = 0; + pjsip_endpt_schedule_timer( tsx->endpt, + &tsx->retransmit_timer, + &t1_timer_val); + } + + /* Save last response sent for retransmission when request + * retransmission is received. + */ + if (tsx->last_tx != tdata) { + tsx->last_tx = tdata; + pjsip_tx_data_add_ref(tdata); + } + + /* Start timer J at 64*T1 for unreliable transport or zero for + * reliable transport. + */ + if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) { + timeout = timeout_timer_val; + } else { + timeout.sec = timeout.msec = 0; + } + + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, + &timeout); + + /* Set state to "Completed" */ + tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, + PJSIP_EVENT_TX_MSG, tdata ); + } + + } else if (tsx->status_code >= 300) { + + /* 3xx-6xx class message causes transaction to move to + * "Completed" state. + */ + if (tsx->last_tx != tdata) { + tsx->last_tx = tdata; + pjsip_tx_data_add_ref( tdata ); + } + + /* Start timer H for transaction termination */ + pjsip_endpt_schedule_timer(tsx->endpt,&tsx->timeout_timer, + &timeout_timer_val); + + /* For INVITE, if unreliable transport is used, retransmission + * timer G will be scheduled (retransmission). + */ + if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) { + pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, + NULL); + if (cseq->method.id == PJSIP_INVITE_METHOD) { + tsx->retransmit_count = 0; + pjsip_endpt_schedule_timer(tsx->endpt, + &tsx->retransmit_timer, + &t1_timer_val); + } + } + + /* Inform TU */ + tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, + PJSIP_EVENT_TX_MSG, tdata ); + + } else { + pj_assert(0); + } + + + } else if (event->type == PJSIP_EVENT_TIMER && + event->body.timer.entry == &tsx->retransmit_timer) { + /* Retransmission timer elapsed. */ + pj_status_t status; + + /* Must have last response to retransmit. */ + pj_assert(tsx->last_tx != NULL); + + /* Retransmit the last response. */ + status = pjsip_tsx_retransmit( tsx, 1 ); + if (status != PJ_SUCCESS) { + return status; + } + + } else if (event->type == PJSIP_EVENT_TIMER && + event->body.timer.entry == &tsx->timeout_timer) { + + /* Timeout timer. should not happen? */ + pj_assert(0); + + tsx->status_code = PJSIP_SC_TSX_TIMEOUT; + + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, + PJSIP_EVENT_TIMER, &tsx->timeout_timer); + + return PJ_EBUG; + + } else { + pj_assert(0); + return PJ_EBUG; + } + + return PJ_SUCCESS; +} + +/* + * Handler for events in Proceeding for UAC + * This state happens after provisional response(s) has been received from + * UAS. + */ +static pj_status_t pjsip_tsx_on_state_proceeding_uac(pjsip_transaction *tsx, + pjsip_event *event) +{ + + pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING || + tsx->state == PJSIP_TSX_STATE_CALLING); + + if (event->type != PJSIP_EVENT_TIMER) { + /* Must be incoming response, because we should not retransmit + * request once response has been received. + */ + pj_assert(event->type == PJSIP_EVENT_RX_MSG); + if (event->type != PJSIP_EVENT_RX_MSG) { + return PJ_EINVALIDOP; + } + + tsx->status_code = event->body.rx_msg.rdata->msg_info.msg->line.status.code; + } else { + tsx->status_code = PJSIP_SC_TSX_TIMEOUT; + } + + if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) { + + /* Inform the message to TU. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, + PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); + + } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) { + + /* Stop timeout timer B/F. */ + pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer ); + + /* For INVITE, the state moves to Terminated state (because ACK is + * handled in TU). For non-INVITE, state moves to Completed. + */ + if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack == 0) { + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, + PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); + return PJSIP_ETSXDESTROYED; + + } else { + pj_time_val timeout; + + /* For unreliable transport, start timer D (for INVITE) or + * timer K for non-INVITE. */ + if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) { + if (tsx->method.id == PJSIP_INVITE_METHOD) { + timeout = td_timer_val; + } else { + timeout = t4_timer_val; + } + } else { + timeout.sec = timeout.msec = 0; + } + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, + &timeout); + + /* Move state to Completed, inform TU. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, + PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); + } + + } else if (tsx->status_code >= 300 && tsx->status_code <= 699) { + pj_time_val timeout; + pj_status_t status; + + /* Stop timer B. */ + pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer ); + + /* Generate and send ACK for INVITE. */ + if (tsx->method.id == PJSIP_INVITE_METHOD) { + pjsip_endpt_create_ack( tsx->endpt, tsx->last_tx, + event->body.rx_msg.rdata ); + status = tsx_send_msg( tsx, tsx->last_tx); + if (status != PJ_SUCCESS) { + return status; + } + } + + /* Start Timer D with TD/T4 timer if unreliable transport is used. */ + if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) { + if (tsx->method.id == PJSIP_INVITE_METHOD) { + timeout = td_timer_val; + } else { + timeout = t4_timer_val; + } + } else { + timeout.sec = timeout.msec = 0; + } + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout); + + /* Inform TU. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, + PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); + + } else { + // Shouldn't happen because there's no timer for this state. + pj_assert(0); + return PJ_EBUG; + } + + return PJ_SUCCESS; +} + +/* + * Handler for events in Completed state for UAS + */ +static pj_status_t pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx, + pjsip_event *event) +{ + pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED); + + if (event->type == PJSIP_EVENT_RX_MSG) { + pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg; + pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL ); + + /* On receive request retransmission, retransmit last response. */ + if (cseq->method.id != PJSIP_ACK_METHOD) { + pj_status_t status; + + status = pjsip_tsx_retransmit( tsx, 0 ); + if (status != PJ_SUCCESS) { + return status; + } + + } else { + /* Process incoming ACK request. */ + + /* Cease retransmission. */ + pjsip_endpt_cancel_timer( tsx->endpt, &tsx->retransmit_timer ); + + /* Start timer I in T4 interval (transaction termination). */ + pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer ); + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, + &t4_timer_val); + + /* Move state to "Confirmed" */ + tsx_set_state( tsx, PJSIP_TSX_STATE_CONFIRMED, + PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); + } + + } else if (event->type == PJSIP_EVENT_TIMER) { + + if (event->body.timer.entry == &tsx->retransmit_timer) { + /* Retransmit message. */ + pj_status_t status; + + status = pjsip_tsx_retransmit( tsx, 1 ); + if (status != PJ_SUCCESS) { + return status; + } + + } else { + if (tsx->method.id == PJSIP_INVITE_METHOD) { + + /* For INVITE, this means that ACK was never received. + * Set state to Terminated, and inform TU. + */ + + tsx->status_code = PJSIP_SC_TSX_TIMEOUT; + + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, + PJSIP_EVENT_TIMER, &tsx->timeout_timer ); + + return PJSIP_ETSXDESTROYED; + + } else { + /* Transaction terminated, it can now be deleted. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, + PJSIP_EVENT_TIMER, &tsx->timeout_timer ); + return PJSIP_ETSXDESTROYED; + } + } + + } else { + /* Ignore request to transmit. */ + pj_assert(event->body.tx_msg.tdata == tsx->last_tx); + } + + return PJ_SUCCESS; +} + +/* + * Handler for events in Completed state for UAC transaction. + */ +static pj_status_t pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx, + pjsip_event *event) +{ + pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED); + + if (event->type == PJSIP_EVENT_TIMER) { + /* Must be the timeout timer. */ + pj_assert(event->body.timer.entry == &tsx->timeout_timer); + + /* Move to Terminated state. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, + PJSIP_EVENT_TIMER, event->body.timer.entry ); + + /* Transaction has been destroyed. */ + return PJSIP_ETSXDESTROYED; + + } else if (event->type == PJSIP_EVENT_RX_MSG) { + if (tsx->method.id == PJSIP_INVITE_METHOD) { + /* On received of final response retransmission, retransmit the ACK. + * TU doesn't need to be informed. + */ + pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg; + pj_assert(msg->type == PJSIP_RESPONSE_MSG); + if (msg->type==PJSIP_RESPONSE_MSG && + msg->line.status.code >= 200) + { + pj_status_t status; + + status = pjsip_tsx_retransmit( tsx, 0 ); + if (status != PJ_SUCCESS) { + return status; + } + } else { + /* Very late retransmission of privisional response. */ + pj_assert( msg->type == PJSIP_RESPONSE_MSG ); + } + } else { + /* Just drop the response. */ + } + } else if (tsx->method.id == PJSIP_INVITE_METHOD && + event->type == PJSIP_EVENT_TX_MSG && + event->body.tx_msg.tdata->msg->line.req.method.id==PJSIP_ACK_METHOD) { + + pj_status_t status; + + /* Set last transmitted message. */ + if (tsx->last_tx != event->body.tx_msg.tdata) { + pjsip_tx_data_dec_ref( tsx->last_tx ); + tsx->last_tx = event->body.tx_msg.tdata; + pjsip_tx_data_add_ref( tsx->last_tx ); + } + + /* No state changed, but notify app. + * Must notify now, so app has chance to put SDP in outgoing ACK msg. + */ + tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, + PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata ); + + /* Send msg */ + status = tsx_send_msg(tsx, event->body.tx_msg.tdata); + if (status != PJ_SUCCESS) + return status; + + } else { + pj_assert(0); + return PJ_EBUG; + } + + return PJ_SUCCESS; +} + +/* + * Handler for events in state Confirmed. + */ +static pj_status_t pjsip_tsx_on_state_confirmed( pjsip_transaction *tsx, + pjsip_event *event) +{ + pj_assert(tsx->state == PJSIP_TSX_STATE_CONFIRMED); + + /* This state is only for UAS for INVITE. */ + pj_assert(tsx->role == PJSIP_ROLE_UAS); + pj_assert(tsx->method.id == PJSIP_INVITE_METHOD); + + /* Absorb any ACK received. */ + if (event->type == PJSIP_EVENT_RX_MSG) { + + pjsip_method_e method_id = + event->body.rx_msg.rdata->msg_info.msg->line.req.method.id; + + /* Must be a request message. */ + pj_assert(event->body.rx_msg.rdata->msg_info.msg->type == PJSIP_REQUEST_MSG); + + /* Must be an ACK request or a late INVITE retransmission. */ + pj_assert(method_id == PJSIP_ACK_METHOD || + method_id == PJSIP_INVITE_METHOD); + + /* Just so that compiler won't complain about unused vars when + * building release code. + */ + PJ_UNUSED_ARG(method_id); + + } else if (event->type == PJSIP_EVENT_TIMER) { + /* Must be from timeout_timer_. */ + pj_assert(event->body.timer.entry == &tsx->timeout_timer); + + /* Move to Terminated state. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, + PJSIP_EVENT_TIMER, &tsx->timeout_timer ); + + /* Transaction has been destroyed. */ + return PJSIP_ETSXDESTROYED; + + } else { + pj_assert(0); + return PJ_EBUG; + } + + return PJ_SUCCESS; +} + +/* + * Handler for events in state Terminated. + */ +static pj_status_t pjsip_tsx_on_state_terminated( pjsip_transaction *tsx, + pjsip_event *event) +{ + pj_assert(tsx->state == PJSIP_TSX_STATE_TERMINATED); + + PJ_UNUSED_ARG(event); + + /* Destroy this transaction */ + tsx_set_state(tsx, PJSIP_TSX_STATE_DESTROYED, + event->type, event->body.user.user1 ); + + return PJ_SUCCESS; +} + + +static pj_status_t pjsip_tsx_on_state_destroyed(pjsip_transaction *tsx, + pjsip_event *event) +{ + PJ_UNUSED_ARG(tsx); + PJ_UNUSED_ARG(event); + return PJ_SUCCESS; +} + diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index a32fb683..de1d13d9 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -1,843 +1,843 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_parser.h>
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_private.h>
-#include <pjsip/sip_errno.h>
-#include <pj/os.h>
-#include <pj/log.h>
-#include <pj/ioqueue.h>
-#include <pj/hash.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-#include <pj/assert.h>
-#include <pj/lock.h>
-
-
-#define THIS_FILE "transport"
-
-/*
- * Transport manager.
- */
-struct pjsip_tpmgr
-{
- pj_hash_table_t *table;
- pj_lock_t *lock;
- pjsip_endpoint *endpt;
- pjsip_tpfactory factory_list;
- void (*msg_cb)(pjsip_endpoint*, pj_status_t, pjsip_rx_data*);
-};
-
-
-
-/*****************************************************************************
- *
- * GENERAL TRANSPORT (NAMES, TYPES, ETC.)
- *
- *****************************************************************************/
-
-/*
- * Transport names.
- */
-const struct
-{
- pjsip_transport_type_e type;
- pj_uint16_t port;
- pj_str_t name;
- unsigned flag;
-} transport_names[] =
-{
- { PJSIP_TRANSPORT_UNSPECIFIED, 0, {NULL, 0}, 0},
- { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}, PJSIP_TRANSPORT_DATAGRAM},
- { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}, PJSIP_TRANSPORT_RELIABLE},
- { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}, PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE},
- { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}, PJSIP_TRANSPORT_RELIABLE}
-};
-
-
-/*
- * Get transport type from name.
- */
-PJ_DEF(pjsip_transport_type_e)
-pjsip_transport_get_type_from_name(const pj_str_t *name)
-{
- unsigned i;
-
- for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) {
- if (pj_stricmp(name, &transport_names[i].name) == 0) {
- return transport_names[i].type;
- }
- }
-
- pj_assert(!"Invalid transport name");
- return PJSIP_TRANSPORT_UNSPECIFIED;
-}
-
-
-/*
- * Get the transport type for the specified flags.
- */
-PJ_DEF(pjsip_transport_type_e)
-pjsip_transport_get_type_from_flag(unsigned flag)
-{
- unsigned i;
-
- for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) {
- if (transport_names[i].flag == flag) {
- return transport_names[i].type;
- }
- }
-
- pj_assert(!"Invalid transport type");
- return PJSIP_TRANSPORT_UNSPECIFIED;
-}
-
-PJ_DEF(unsigned)
-pjsip_transport_get_flag_from_type( pjsip_transport_type_e type )
-{
- PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(transport_names), 0);
- return transport_names[type].flag;
-}
-
-/*
- * Get the default SIP port number for the specified type.
- */
-PJ_DEF(int)
-pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type)
-{
- PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(transport_names), 5060);
- return transport_names[type].port;
-}
-
-
-/*****************************************************************************
- *
- * TRANSMIT DATA BUFFER MANIPULATION.
- *
- *****************************************************************************/
-
-/*
- * Create new transmit buffer.
- */
-PJ_DEF(pj_status_t) pjsip_tx_data_create( pjsip_tpmgr *mgr,
- pjsip_tx_data **p_tdata )
-{
- pj_pool_t *pool;
- pjsip_tx_data *tdata;
- pj_status_t status;
-
- PJ_ASSERT_RETURN(mgr && p_tdata, PJ_EINVAL);
-
- PJ_LOG(5, ("", "pjsip_tx_data_create"));
-
- pool = pjsip_endpt_create_pool( mgr->endpt, "tdta%p",
- PJSIP_POOL_LEN_TDATA,
- PJSIP_POOL_INC_TDATA );
- if (!pool)
- return PJ_ENOMEM;
-
- tdata = pj_pool_zalloc(pool, sizeof(pjsip_tx_data));
- tdata->pool = pool;
- tdata->mgr = mgr;
- pj_snprintf(tdata->obj_name, PJ_MAX_OBJ_NAME, "tdta%p", tdata);
-
- status = pj_atomic_create(tdata->pool, 0, &tdata->ref_cnt);
- if (status != PJ_SUCCESS) {
- pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool );
- return status;
- }
-
- //status = pj_lock_create_simple_mutex(pool, "tdta%p", &tdata->lock);
- status = pj_lock_create_null_mutex(pool, "tdta%p", &tdata->lock);
- if (status != PJ_SUCCESS) {
- pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool );
- return status;
- }
-
- pj_ioqueue_op_key_init(&tdata->op_key.key, sizeof(tdata->op_key));
-
- *p_tdata = tdata;
- return PJ_SUCCESS;
-}
-
-
-/*
- * Add reference to tx buffer.
- */
-PJ_DEF(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata )
-{
- pj_atomic_inc(tdata->ref_cnt);
-}
-
-/*
- * Decrease transport data reference, destroy it when the reference count
- * reaches zero.
- */
-PJ_DEF(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata )
-{
- pj_assert( pj_atomic_get(tdata->ref_cnt) > 0);
- if (pj_atomic_dec_and_get(tdata->ref_cnt) <= 0) {
- PJ_LOG(5,(tdata->obj_name, "destroying txdata"));
- pj_atomic_destroy( tdata->ref_cnt );
- pj_lock_destroy( tdata->lock );
- pjsip_endpt_destroy_pool( tdata->mgr->endpt, tdata->pool );
- }
-}
-
-/*
- * Invalidate the content of the print buffer to force the message to be
- * re-printed when sent.
- */
-PJ_DEF(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata )
-{
- tdata->buf.cur = tdata->buf.start;
-}
-
-PJ_DEF(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata )
-{
- return tdata->buf.cur != tdata->buf.start;
-}
-
-
-
-/*****************************************************************************
- *
- * TRANSPORT KEY
- *
- *****************************************************************************/
-
-/*
- * Transport key for indexing in the hash table.
- */
-typedef struct transport_key
-{
- pj_uint8_t type;
- pj_uint8_t zero;
- pj_uint16_t port;
- pj_uint32_t addr;
-} transport_key;
-
-
-/*****************************************************************************
- *
- * TRANSPORT
- *
- *****************************************************************************/
-
-static void transport_send_callback(pjsip_transport *transport,
- void *token,
- pj_ssize_t size)
-{
- pjsip_tx_data *tdata = token;
-
- PJ_UNUSED_ARG(transport);
-
- /* Mark pending off so that app can resend/reuse txdata from inside
- * the callback.
- */
- tdata->is_pending = 0;
-
- /* Call callback, if any. */
- if (tdata->cb) {
- (*tdata->cb)(tdata->token, tdata, size);
- }
-
- /* Decrement reference count. */
- pjsip_tx_data_dec_ref(tdata);
-}
-
-/*
- * Send a SIP message using the specified transport.
- */
-PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr,
- pjsip_tx_data *tdata,
- const pj_sockaddr_in *addr,
- void *token,
- void (*cb)(void *token,
- pjsip_tx_data *tdata,
- pj_ssize_t))
-{
- pj_status_t status;
-
- PJ_ASSERT_RETURN(tr && tdata && addr, PJ_EINVAL);
-
- /* Is it currently being sent? */
- if (tdata->is_pending) {
- pj_assert(!"Invalid operation step!");
- return PJSIP_EPENDINGTX;
- }
-
- /* Allocate buffer if necessary. */
- if (tdata->buf.start == NULL) {
- tdata->buf.start = pj_pool_alloc( tdata->pool, PJSIP_MAX_PKT_LEN);
- tdata->buf.cur = tdata->buf.start;
- tdata->buf.end = tdata->buf.start + PJSIP_MAX_PKT_LEN;
- }
-
- /* Do we need to reprint? */
- if (!pjsip_tx_data_is_valid(tdata)) {
- pj_ssize_t size;
-
- size = pjsip_msg_print( tdata->msg, tdata->buf.start,
- tdata->buf.end - tdata->buf.start);
- if (size < 0) {
- return PJSIP_EMSGTOOLONG;
- }
- pj_assert(size != 0);
- tdata->buf.cur += size;
- tdata->buf.cur[size] = '\0';
- }
-
- /* Save callback data. */
- tdata->token = token;
- tdata->cb = cb;
-
- /* Add reference counter. */
- pjsip_tx_data_add_ref(tdata);
-
- /* Mark as pending. */
- tdata->is_pending = 1;
-
- /* Send to transport. */
- status = (*tr->send_msg)(tr, tdata, addr, (void*)tdata,
- &transport_send_callback);
-
- if (status != PJ_EPENDING) {
- tdata->is_pending = 0;
- pjsip_tx_data_dec_ref(tdata);
- }
-
- return status;
-}
-
-static void transport_idle_callback(pj_timer_heap_t *timer_heap,
- struct pj_timer_entry *entry)
-{
- pjsip_transport *tp = entry->user_data;
- pj_assert(tp != NULL);
-
- PJ_UNUSED_ARG(timer_heap);
-
- entry->id = PJ_FALSE;
- pjsip_transport_unregister(tp->tpmgr, tp);
-}
-
-/*
- * Add ref.
- */
-PJ_DEF(pj_status_t) pjsip_transport_add_ref( pjsip_transport *tp )
-{
- PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL);
-
- if (pj_atomic_inc_and_get(tp->ref_cnt) == 1) {
- pj_lock_acquire(tp->tpmgr->lock);
- /* Verify again. */
- if (pj_atomic_get(tp->ref_cnt) == 1) {
- if (tp->idle_timer.id != PJ_FALSE) {
- pjsip_endpt_cancel_timer(tp->tpmgr->endpt, &tp->idle_timer);
- tp->idle_timer.id = PJ_FALSE;
- }
- }
- pj_lock_release(tp->tpmgr->lock);
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Dec ref.
- */
-PJ_DEF(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp )
-{
- PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL);
-
- pj_assert(pj_atomic_get(tp->ref_cnt) > 0);
-
- if (pj_atomic_dec_and_get(tp->ref_cnt) == 0) {
- pj_lock_acquire(tp->tpmgr->lock);
- /* Verify again. */
- if (pj_atomic_get(tp->ref_cnt) == 0) {
- pj_time_val delay = { PJSIP_TRANSPORT_IDLE_TIME, 0 };
-
- pj_assert(tp->idle_timer.id == 0);
- tp->idle_timer.id = PJ_TRUE;
- pjsip_endpt_schedule_timer(tp->tpmgr->endpt, &tp->idle_timer,
- &delay);
- }
- pj_lock_release(tp->tpmgr->lock);
- }
-
- return PJ_SUCCESS;
-}
-
-
-/**
- * Register a transport.
- */
-PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr,
- pjsip_transport *tp )
-{
- transport_key key;
-
- /* Init. */
- tp->tpmgr = mgr;
- pj_memset(&tp->idle_timer, 0, sizeof(tp->idle_timer));
- tp->idle_timer.user_data = tp;
- tp->idle_timer.cb = &transport_idle_callback;
-
- /*
- * Register to hash table.
- */
- key.type = (pj_uint8_t)tp->type;
- key.zero = 0;
- key.addr = pj_ntohl(tp->rem_addr.sin_addr.s_addr);
- key.port = pj_ntohs(tp->rem_addr.sin_port);
-
- pj_lock_acquire(mgr->lock);
- pj_hash_set(tp->pool, mgr->table, &key, sizeof(key), tp);
- pj_lock_release(mgr->lock);
-
- return PJ_SUCCESS;
-}
-
-
-/**
- * Unregister transport.
- */
-PJ_DEF(pj_status_t) pjsip_transport_unregister( pjsip_tpmgr *mgr,
- pjsip_transport *tp)
-{
- transport_key key;
-
- PJ_ASSERT_RETURN(pj_atomic_get(tp->ref_cnt) == 0, PJSIP_EBUSY);
-
- pj_lock_acquire(tp->lock);
- pj_lock_acquire(mgr->lock);
-
- /*
- * Unregister timer, if any.
- */
- pj_assert(tp->idle_timer.id == PJ_FALSE);
- if (tp->idle_timer.id != PJ_FALSE) {
- pjsip_endpt_cancel_timer(mgr->endpt, &tp->idle_timer);
- tp->idle_timer.id = PJ_FALSE;
- }
-
- /*
- * Unregister from hash table.
- */
- key.type = (pj_uint8_t)tp->type;
- key.zero = 0;
- key.addr = pj_ntohl(tp->rem_addr.sin_addr.s_addr);
- key.port = pj_ntohs(tp->rem_addr.sin_port);
-
- pj_hash_set(tp->pool, mgr->table, &key, sizeof(key), NULL);
-
- pj_lock_release(mgr->lock);
-
- /* Destroy. */
- return tp->destroy(tp);
-}
-
-
-
-/*****************************************************************************
- *
- * TRANSPORT FACTORY
- *
- *****************************************************************************/
-
-
-PJ_DEF(pj_status_t) pjsip_tpmgr_register_tpfactory( pjsip_tpmgr *mgr,
- pjsip_tpfactory *tpf)
-{
- pjsip_tpfactory *p;
- pj_status_t status;
-
- pj_lock_acquire(mgr->lock);
-
- /* Check that no factory with the same type has been registered. */
- status = PJ_SUCCESS;
- for (p=mgr->factory_list.next; p!=&mgr->factory_list; p=p->next) {
- if (p->type == tpf->type) {
- status = PJSIP_ETYPEEXISTS;
- break;
- }
- if (p == tpf) {
- status = PJ_EEXISTS;
- break;
- }
- }
-
- if (status != PJ_SUCCESS) {
- pj_lock_release(mgr->lock);
- return status;
- }
-
- pj_list_insert_before(&mgr->factory_list, tpf);
-
- pj_lock_release(mgr->lock);
-
- return PJ_SUCCESS;
-}
-
-
-/**
- * Unregister factory.
- */
-PJ_DEF(pj_status_t) pjsip_tpmgr_unregister_tpfactory( pjsip_tpmgr *mgr,
- pjsip_tpfactory *tpf)
-{
- pj_lock_acquire(mgr->lock);
-
- pj_assert(pj_list_find_node(&mgr->factory_list, tpf) == tpf);
- pj_list_erase(tpf);
-
- pj_lock_release(mgr->lock);
-
- return PJ_SUCCESS;
-}
-
-
-/*****************************************************************************
- *
- * TRANSPORT MANAGER
- *
- *****************************************************************************/
-
-/*
- * Create a new transport manager.
- */
-PJ_DEF(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool,
- pjsip_endpoint *endpt,
- void (*cb)(pjsip_endpoint*,
- pj_status_t,
- pjsip_rx_data *),
- pjsip_tpmgr **p_mgr)
-{
- pjsip_tpmgr *mgr;
- pj_status_t status;
-
- PJ_ASSERT_RETURN(pool && endpt && cb && p_mgr, PJ_EINVAL);
-
- PJ_LOG(5, (THIS_FILE, "pjsip_tpmgr_create()"));
-
- mgr = pj_pool_zalloc(pool, sizeof(*mgr));
- mgr->endpt = endpt;
- mgr->msg_cb = cb;
- pj_list_init(&mgr->factory_list);
-
- mgr->table = pj_hash_create(pool, PJSIP_TPMGR_HTABLE_SIZE);
- if (!mgr->table)
- return PJ_ENOMEM;
-
- status = pj_lock_create_recursive_mutex(pool, "tmgr%p", &mgr->lock);
- if (status != PJ_SUCCESS)
- return status;
-
- *p_mgr = mgr;
- return PJ_SUCCESS;
-}
-
-/*
- * pjsip_tpmgr_destroy()
- *
- * Destroy transport manager.
- */
-PJ_DEF(pj_status_t) pjsip_tpmgr_destroy( pjsip_tpmgr *mgr )
-{
- pj_hash_iterator_t itr_val;
- pj_hash_iterator_t *itr;
-
- PJ_LOG(5, (THIS_FILE, "pjsip_tpmgr_destroy()"));
-
- pj_lock_acquire(mgr->lock);
-
- itr = pj_hash_first(mgr->table, &itr_val);
- while (itr != NULL) {
- pj_hash_iterator_t *next;
- pjsip_transport *transport;
-
- transport = pj_hash_this(mgr->table, itr);
-
- next = pj_hash_next(mgr->table, itr);
-
- pj_atomic_set(transport->ref_cnt, 0);
- pjsip_transport_unregister(mgr, transport);
-
- itr = next;
- }
-
- pj_lock_release(mgr->lock);
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * pjsip_tpmgr_receive_packet()
- *
- * Called by tranports when they receive a new packet.
- */
-PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr,
- pjsip_rx_data *rdata)
-{
- pjsip_transport *tr = rdata->tp_info.transport;
- pj_str_t s;
-
- char *current_pkt;
- pj_size_t remaining_len;
- pj_size_t total_processed = 0;
-
- /* Check size. */
- pj_assert(rdata->pkt_info.len > 0);
- if (rdata->pkt_info.len <= 0)
- return -1;
-
- current_pkt = rdata->pkt_info.packet;
- remaining_len = rdata->pkt_info.len;
-
- /* Must NULL terminate buffer. This is the requirement of the
- * parser etc.
- */
- current_pkt[remaining_len] = '\0';
-
- /* Process all message fragments. */
- while (total_processed < remaining_len) {
-
- pjsip_msg *msg;
- pj_size_t msg_fragment_size = 0;
-
- /* Initialize default fragment size. */
- msg_fragment_size = remaining_len;
-
- /* Null terminate packet. */
-
- /* Clear and init msg_info in rdata.
- * Endpoint might inspect the values there when we call the callback
- * to report some errors.
- */
- pj_memset(&rdata->msg_info, 0, sizeof(rdata->msg_info));
- pj_list_init(&rdata->msg_info.parse_err);
- rdata->msg_info.msg_buf = current_pkt;
- rdata->msg_info.len = remaining_len;
-
- /* For TCP transport, check if the whole message has been received. */
- if ((tr->flag & PJSIP_TRANSPORT_DATAGRAM) == 0) {
- pj_status_t msg_status;
- msg_status = pjsip_find_msg(current_pkt, remaining_len, PJ_FALSE,
- &msg_fragment_size);
- if (msg_status != PJ_SUCCESS) {
- if (remaining_len == PJSIP_MAX_PKT_LEN) {
- mgr->msg_cb(mgr->endpt, PJSIP_ERXOVERFLOW, rdata);
- /* Exhaust all data. */
- return rdata->pkt_info.len;
- } else {
- /* Not enough data in packet. */
- return total_processed;
- }
- }
- }
-
- /* Update msg_info. */
- rdata->msg_info.len = msg_fragment_size;
-
- /* Parse the message. */
- rdata->msg_info.msg = msg =
- pjsip_parse_rdata( current_pkt, msg_fragment_size, rdata);
- if (msg == NULL) {
- mgr->msg_cb(mgr->endpt, PJSIP_EINVALIDMSG, rdata);
- goto finish_process_fragment;
- }
-
- /* Perform basic header checking. */
- if (rdata->msg_info.call_id.ptr == NULL ||
- rdata->msg_info.from == NULL ||
- rdata->msg_info.to == NULL ||
- rdata->msg_info.via == NULL ||
- rdata->msg_info.cseq == NULL)
- {
- mgr->msg_cb(mgr->endpt, PJSIP_EMISSINGHDR, rdata);
- goto finish_process_fragment;
- }
-
- /* If message is received from address that's different from sent-by,
- * MUST add received parameter to the via.
- */
- s = pj_str(pj_inet_ntoa(rdata->pkt_info.addr.sin_addr));
- if (pj_strcmp(&s, &rdata->msg_info.via->sent_by.host) != 0) {
- pj_strdup(rdata->tp_info.pool,
- &rdata->msg_info.via->recvd_param, &s);
- }
-
- /* RFC 3581:
- * If message contains "rport" param, put the received port there.
- */
- if (rdata->msg_info.via->rport_param == 0) {
- rdata->msg_info.via->rport_param =
- pj_ntohs(rdata->pkt_info.addr.sin_port);
- }
-
- /* Drop response message if it has more than one Via.
- */
- if (msg->type == PJSIP_RESPONSE_MSG) {
- pjsip_hdr *hdr;
- hdr = (pjsip_hdr*)rdata->msg_info.via->next;
- if (hdr != &msg->hdr) {
- hdr = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, hdr);
- if (hdr) {
- mgr->msg_cb(mgr->endpt, PJSIP_EMULTIPLEVIA, rdata);
- goto finish_process_fragment;
- }
- }
- }
-
- /* Call the transport manager's upstream message callback.
- */
- mgr->msg_cb(mgr->endpt, PJ_SUCCESS, rdata);
-
-
-finish_process_fragment:
- total_processed += msg_fragment_size;
- current_pkt += msg_fragment_size;
- remaining_len -= msg_fragment_size;
-
- } /* while (rdata->pkt_info.len > 0) */
-
-
- return total_processed;
-}
-
-
-/*
- * pjsip_tpmgr_alloc_transport()
- *
- * Get transport suitable to communicate to remote. Create a new one
- * if necessary.
- */
-PJ_DEF(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr,
- pjsip_transport_type_e type,
- const pj_sockaddr_in *remote,
- pjsip_transport **p_transport)
-{
- transport_key key;
- pjsip_transport *transport;
- pjsip_tpfactory *factory;
- pj_status_t status;
-
- pj_lock_acquire(mgr->lock);
-
- /* First try to get exact destination. */
- key.type = (pj_uint8_t)type;
- key.zero = 0;
- key.addr = pj_ntohl(remote->sin_addr.s_addr);
- key.port = pj_ntohs(remote->sin_port);
-
- transport = pj_hash_get(mgr->table, &key, sizeof(key));
- if (transport != NULL) {
- unsigned flag = pjsip_transport_get_flag_from_type(type);
-
- /* For datagram transports, try lookup with zero address. */
- if (flag & PJSIP_TRANSPORT_DATAGRAM) {
- key.addr = 0;
- key.port = 0;
-
- transport = pj_hash_get(mgr->table, &key, sizeof(key));
- }
- }
-
- if (transport != NULL) {
- /*
- * Transport found!
- */
- pjsip_transport_add_ref(transport);
- pj_lock_release(mgr->lock);
- *p_transport = transport;
- return PJ_SUCCESS;
- }
-
- /*
- * Transport not found!
- * Find factory that can create such transport.
- */
- factory = mgr->factory_list.next;
- while (factory != &mgr->factory_list) {
- if (factory->type == type)
- break;
- factory = factory->next;
- }
-
- if (factory == &mgr->factory_list) {
- /* No factory can create the transport! */
- pj_lock_release(mgr->lock);
- return PJSIP_EUNSUPTRANSPORT;
- }
-
- /* Request factory to create transport. */
- status = factory->create_transport(factory, mgr, mgr->endpt,
- remote, p_transport);
-
- pj_lock_release(mgr->lock);
- return status;
-}
-
-/**
- * Dump transport info.
- */
-PJ_DEF(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr)
-{
-#if PJ_LOG_MAX_LEVEL >= 3
- pj_hash_iterator_t itr_val;
- pj_hash_iterator_t *itr;
-
- pj_lock_acquire(mgr->lock);
-
- itr = pj_hash_first(mgr->table, &itr_val);
- if (itr) {
- PJ_LOG(3, (THIS_FILE, " Dumping transports:"));
-
- do {
- char src_addr[128], dst_addr[128];
- int src_port, dst_port;
- pjsip_transport *t;
-
- t = pj_hash_this(mgr->table, itr);
- pj_native_strcpy(src_addr, pj_inet_ntoa(t->local_addr.sin_addr));
- src_port = pj_ntohs(t->local_addr.sin_port);
-
- pj_native_strcpy(dst_addr, pj_inet_ntoa(t->rem_addr.sin_addr));
- dst_port = pj_ntohs(t->rem_addr.sin_port);
-
- PJ_LOG(3, (THIS_FILE, " %s %s %s:%d --> %s:%d (refcnt=%d)",
- t->type_name,
- t->obj_name,
- src_addr, src_port,
- dst_addr, dst_port,
- pj_atomic_get(t->ref_cnt)));
-
- itr = pj_hash_next(mgr->table, itr);
- } while (itr);
- }
-
- pj_lock_release(mgr->lock);
-#endif
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip/sip_transport.h> +#include <pjsip/sip_endpoint.h> +#include <pjsip/sip_parser.h> +#include <pjsip/sip_msg.h> +#include <pjsip/sip_private.h> +#include <pjsip/sip_errno.h> +#include <pj/os.h> +#include <pj/log.h> +#include <pj/ioqueue.h> +#include <pj/hash.h> +#include <pj/string.h> +#include <pj/pool.h> +#include <pj/assert.h> +#include <pj/lock.h> + + +#define THIS_FILE "transport" + +/* + * Transport manager. + */ +struct pjsip_tpmgr +{ + pj_hash_table_t *table; + pj_lock_t *lock; + pjsip_endpoint *endpt; + pjsip_tpfactory factory_list; + void (*msg_cb)(pjsip_endpoint*, pj_status_t, pjsip_rx_data*); +}; + + + +/***************************************************************************** + * + * GENERAL TRANSPORT (NAMES, TYPES, ETC.) + * + *****************************************************************************/ + +/* + * Transport names. + */ +const struct +{ + pjsip_transport_type_e type; + pj_uint16_t port; + pj_str_t name; + unsigned flag; +} transport_names[] = +{ + { PJSIP_TRANSPORT_UNSPECIFIED, 0, {NULL, 0}, 0}, + { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}, PJSIP_TRANSPORT_DATAGRAM}, + { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}, PJSIP_TRANSPORT_RELIABLE}, + { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}, PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE}, + { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}, PJSIP_TRANSPORT_RELIABLE} +}; + + +/* + * Get transport type from name. + */ +PJ_DEF(pjsip_transport_type_e) +pjsip_transport_get_type_from_name(const pj_str_t *name) +{ + unsigned i; + + for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) { + if (pj_stricmp(name, &transport_names[i].name) == 0) { + return transport_names[i].type; + } + } + + pj_assert(!"Invalid transport name"); + return PJSIP_TRANSPORT_UNSPECIFIED; +} + + +/* + * Get the transport type for the specified flags. + */ +PJ_DEF(pjsip_transport_type_e) +pjsip_transport_get_type_from_flag(unsigned flag) +{ + unsigned i; + + for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) { + if (transport_names[i].flag == flag) { + return transport_names[i].type; + } + } + + pj_assert(!"Invalid transport type"); + return PJSIP_TRANSPORT_UNSPECIFIED; +} + +PJ_DEF(unsigned) +pjsip_transport_get_flag_from_type( pjsip_transport_type_e type ) +{ + PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(transport_names), 0); + return transport_names[type].flag; +} + +/* + * Get the default SIP port number for the specified type. + */ +PJ_DEF(int) +pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type) +{ + PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(transport_names), 5060); + return transport_names[type].port; +} + + +/***************************************************************************** + * + * TRANSMIT DATA BUFFER MANIPULATION. + * + *****************************************************************************/ + +/* + * Create new transmit buffer. + */ +PJ_DEF(pj_status_t) pjsip_tx_data_create( pjsip_tpmgr *mgr, + pjsip_tx_data **p_tdata ) +{ + pj_pool_t *pool; + pjsip_tx_data *tdata; + pj_status_t status; + + PJ_ASSERT_RETURN(mgr && p_tdata, PJ_EINVAL); + + PJ_LOG(5, ("", "pjsip_tx_data_create")); + + pool = pjsip_endpt_create_pool( mgr->endpt, "tdta%p", + PJSIP_POOL_LEN_TDATA, + PJSIP_POOL_INC_TDATA ); + if (!pool) + return PJ_ENOMEM; + + tdata = pj_pool_zalloc(pool, sizeof(pjsip_tx_data)); + tdata->pool = pool; + tdata->mgr = mgr; + pj_snprintf(tdata->obj_name, PJ_MAX_OBJ_NAME, "tdta%p", tdata); + + status = pj_atomic_create(tdata->pool, 0, &tdata->ref_cnt); + if (status != PJ_SUCCESS) { + pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool ); + return status; + } + + //status = pj_lock_create_simple_mutex(pool, "tdta%p", &tdata->lock); + status = pj_lock_create_null_mutex(pool, "tdta%p", &tdata->lock); + if (status != PJ_SUCCESS) { + pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool ); + return status; + } + + pj_ioqueue_op_key_init(&tdata->op_key.key, sizeof(tdata->op_key)); + + *p_tdata = tdata; + return PJ_SUCCESS; +} + + +/* + * Add reference to tx buffer. + */ +PJ_DEF(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata ) +{ + pj_atomic_inc(tdata->ref_cnt); +} + +/* + * Decrease transport data reference, destroy it when the reference count + * reaches zero. + */ +PJ_DEF(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata ) +{ + pj_assert( pj_atomic_get(tdata->ref_cnt) > 0); + if (pj_atomic_dec_and_get(tdata->ref_cnt) <= 0) { + PJ_LOG(5,(tdata->obj_name, "destroying txdata")); + pj_atomic_destroy( tdata->ref_cnt ); + pj_lock_destroy( tdata->lock ); + pjsip_endpt_destroy_pool( tdata->mgr->endpt, tdata->pool ); + } +} + +/* + * Invalidate the content of the print buffer to force the message to be + * re-printed when sent. + */ +PJ_DEF(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata ) +{ + tdata->buf.cur = tdata->buf.start; +} + +PJ_DEF(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata ) +{ + return tdata->buf.cur != tdata->buf.start; +} + + + +/***************************************************************************** + * + * TRANSPORT KEY + * + *****************************************************************************/ + +/* + * Transport key for indexing in the hash table. + */ +typedef struct transport_key +{ + pj_uint8_t type; + pj_uint8_t zero; + pj_uint16_t port; + pj_uint32_t addr; +} transport_key; + + +/***************************************************************************** + * + * TRANSPORT + * + *****************************************************************************/ + +static void transport_send_callback(pjsip_transport *transport, + void *token, + pj_ssize_t size) +{ + pjsip_tx_data *tdata = token; + + PJ_UNUSED_ARG(transport); + + /* Mark pending off so that app can resend/reuse txdata from inside + * the callback. + */ + tdata->is_pending = 0; + + /* Call callback, if any. */ + if (tdata->cb) { + (*tdata->cb)(tdata->token, tdata, size); + } + + /* Decrement reference count. */ + pjsip_tx_data_dec_ref(tdata); +} + +/* + * Send a SIP message using the specified transport. + */ +PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr, + pjsip_tx_data *tdata, + const pj_sockaddr_in *addr, + void *token, + void (*cb)(void *token, + pjsip_tx_data *tdata, + pj_ssize_t)) +{ + pj_status_t status; + + PJ_ASSERT_RETURN(tr && tdata && addr, PJ_EINVAL); + + /* Is it currently being sent? */ + if (tdata->is_pending) { + pj_assert(!"Invalid operation step!"); + return PJSIP_EPENDINGTX; + } + + /* Allocate buffer if necessary. */ + if (tdata->buf.start == NULL) { + tdata->buf.start = pj_pool_alloc( tdata->pool, PJSIP_MAX_PKT_LEN); + tdata->buf.cur = tdata->buf.start; + tdata->buf.end = tdata->buf.start + PJSIP_MAX_PKT_LEN; + } + + /* Do we need to reprint? */ + if (!pjsip_tx_data_is_valid(tdata)) { + pj_ssize_t size; + + size = pjsip_msg_print( tdata->msg, tdata->buf.start, + tdata->buf.end - tdata->buf.start); + if (size < 0) { + return PJSIP_EMSGTOOLONG; + } + pj_assert(size != 0); + tdata->buf.cur += size; + tdata->buf.cur[size] = '\0'; + } + + /* Save callback data. */ + tdata->token = token; + tdata->cb = cb; + + /* Add reference counter. */ + pjsip_tx_data_add_ref(tdata); + + /* Mark as pending. */ + tdata->is_pending = 1; + + /* Send to transport. */ + status = (*tr->send_msg)(tr, tdata, addr, (void*)tdata, + &transport_send_callback); + + if (status != PJ_EPENDING) { + tdata->is_pending = 0; + pjsip_tx_data_dec_ref(tdata); + } + + return status; +} + +static void transport_idle_callback(pj_timer_heap_t *timer_heap, + struct pj_timer_entry *entry) +{ + pjsip_transport *tp = entry->user_data; + pj_assert(tp != NULL); + + PJ_UNUSED_ARG(timer_heap); + + entry->id = PJ_FALSE; + pjsip_transport_unregister(tp->tpmgr, tp); +} + +/* + * Add ref. + */ +PJ_DEF(pj_status_t) pjsip_transport_add_ref( pjsip_transport *tp ) +{ + PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL); + + if (pj_atomic_inc_and_get(tp->ref_cnt) == 1) { + pj_lock_acquire(tp->tpmgr->lock); + /* Verify again. */ + if (pj_atomic_get(tp->ref_cnt) == 1) { + if (tp->idle_timer.id != PJ_FALSE) { + pjsip_endpt_cancel_timer(tp->tpmgr->endpt, &tp->idle_timer); + tp->idle_timer.id = PJ_FALSE; + } + } + pj_lock_release(tp->tpmgr->lock); + } + + return PJ_SUCCESS; +} + +/* + * Dec ref. + */ +PJ_DEF(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp ) +{ + PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL); + + pj_assert(pj_atomic_get(tp->ref_cnt) > 0); + + if (pj_atomic_dec_and_get(tp->ref_cnt) == 0) { + pj_lock_acquire(tp->tpmgr->lock); + /* Verify again. */ + if (pj_atomic_get(tp->ref_cnt) == 0) { + pj_time_val delay = { PJSIP_TRANSPORT_IDLE_TIME, 0 }; + + pj_assert(tp->idle_timer.id == 0); + tp->idle_timer.id = PJ_TRUE; + pjsip_endpt_schedule_timer(tp->tpmgr->endpt, &tp->idle_timer, + &delay); + } + pj_lock_release(tp->tpmgr->lock); + } + + return PJ_SUCCESS; +} + + +/** + * Register a transport. + */ +PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr, + pjsip_transport *tp ) +{ + transport_key key; + + /* Init. */ + tp->tpmgr = mgr; + pj_memset(&tp->idle_timer, 0, sizeof(tp->idle_timer)); + tp->idle_timer.user_data = tp; + tp->idle_timer.cb = &transport_idle_callback; + + /* + * Register to hash table. + */ + key.type = (pj_uint8_t)tp->type; + key.zero = 0; + key.addr = pj_ntohl(tp->rem_addr.sin_addr.s_addr); + key.port = pj_ntohs(tp->rem_addr.sin_port); + + pj_lock_acquire(mgr->lock); + pj_hash_set(tp->pool, mgr->table, &key, sizeof(key), tp); + pj_lock_release(mgr->lock); + + return PJ_SUCCESS; +} + + +/** + * Unregister transport. + */ +PJ_DEF(pj_status_t) pjsip_transport_unregister( pjsip_tpmgr *mgr, + pjsip_transport *tp) +{ + transport_key key; + + PJ_ASSERT_RETURN(pj_atomic_get(tp->ref_cnt) == 0, PJSIP_EBUSY); + + pj_lock_acquire(tp->lock); + pj_lock_acquire(mgr->lock); + + /* + * Unregister timer, if any. + */ + pj_assert(tp->idle_timer.id == PJ_FALSE); + if (tp->idle_timer.id != PJ_FALSE) { + pjsip_endpt_cancel_timer(mgr->endpt, &tp->idle_timer); + tp->idle_timer.id = PJ_FALSE; + } + + /* + * Unregister from hash table. + */ + key.type = (pj_uint8_t)tp->type; + key.zero = 0; + key.addr = pj_ntohl(tp->rem_addr.sin_addr.s_addr); + key.port = pj_ntohs(tp->rem_addr.sin_port); + + pj_hash_set(tp->pool, mgr->table, &key, sizeof(key), NULL); + + pj_lock_release(mgr->lock); + + /* Destroy. */ + return tp->destroy(tp); +} + + + +/***************************************************************************** + * + * TRANSPORT FACTORY + * + *****************************************************************************/ + + +PJ_DEF(pj_status_t) pjsip_tpmgr_register_tpfactory( pjsip_tpmgr *mgr, + pjsip_tpfactory *tpf) +{ + pjsip_tpfactory *p; + pj_status_t status; + + pj_lock_acquire(mgr->lock); + + /* Check that no factory with the same type has been registered. */ + status = PJ_SUCCESS; + for (p=mgr->factory_list.next; p!=&mgr->factory_list; p=p->next) { + if (p->type == tpf->type) { + status = PJSIP_ETYPEEXISTS; + break; + } + if (p == tpf) { + status = PJ_EEXISTS; + break; + } + } + + if (status != PJ_SUCCESS) { + pj_lock_release(mgr->lock); + return status; + } + + pj_list_insert_before(&mgr->factory_list, tpf); + + pj_lock_release(mgr->lock); + + return PJ_SUCCESS; +} + + +/** + * Unregister factory. + */ +PJ_DEF(pj_status_t) pjsip_tpmgr_unregister_tpfactory( pjsip_tpmgr *mgr, + pjsip_tpfactory *tpf) +{ + pj_lock_acquire(mgr->lock); + + pj_assert(pj_list_find_node(&mgr->factory_list, tpf) == tpf); + pj_list_erase(tpf); + + pj_lock_release(mgr->lock); + + return PJ_SUCCESS; +} + + +/***************************************************************************** + * + * TRANSPORT MANAGER + * + *****************************************************************************/ + +/* + * Create a new transport manager. + */ +PJ_DEF(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool, + pjsip_endpoint *endpt, + void (*cb)(pjsip_endpoint*, + pj_status_t, + pjsip_rx_data *), + pjsip_tpmgr **p_mgr) +{ + pjsip_tpmgr *mgr; + pj_status_t status; + + PJ_ASSERT_RETURN(pool && endpt && cb && p_mgr, PJ_EINVAL); + + PJ_LOG(5, (THIS_FILE, "pjsip_tpmgr_create()")); + + mgr = pj_pool_zalloc(pool, sizeof(*mgr)); + mgr->endpt = endpt; + mgr->msg_cb = cb; + pj_list_init(&mgr->factory_list); + + mgr->table = pj_hash_create(pool, PJSIP_TPMGR_HTABLE_SIZE); + if (!mgr->table) + return PJ_ENOMEM; + + status = pj_lock_create_recursive_mutex(pool, "tmgr%p", &mgr->lock); + if (status != PJ_SUCCESS) + return status; + + *p_mgr = mgr; + return PJ_SUCCESS; +} + +/* + * pjsip_tpmgr_destroy() + * + * Destroy transport manager. + */ +PJ_DEF(pj_status_t) pjsip_tpmgr_destroy( pjsip_tpmgr *mgr ) +{ + pj_hash_iterator_t itr_val; + pj_hash_iterator_t *itr; + + PJ_LOG(5, (THIS_FILE, "pjsip_tpmgr_destroy()")); + + pj_lock_acquire(mgr->lock); + + itr = pj_hash_first(mgr->table, &itr_val); + while (itr != NULL) { + pj_hash_iterator_t *next; + pjsip_transport *transport; + + transport = pj_hash_this(mgr->table, itr); + + next = pj_hash_next(mgr->table, itr); + + pj_atomic_set(transport->ref_cnt, 0); + pjsip_transport_unregister(mgr, transport); + + itr = next; + } + + pj_lock_release(mgr->lock); + + return PJ_SUCCESS; +} + + +/* + * pjsip_tpmgr_receive_packet() + * + * Called by tranports when they receive a new packet. + */ +PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, + pjsip_rx_data *rdata) +{ + pjsip_transport *tr = rdata->tp_info.transport; + pj_str_t s; + + char *current_pkt; + pj_size_t remaining_len; + pj_size_t total_processed = 0; + + /* Check size. */ + pj_assert(rdata->pkt_info.len > 0); + if (rdata->pkt_info.len <= 0) + return -1; + + current_pkt = rdata->pkt_info.packet; + remaining_len = rdata->pkt_info.len; + + /* Must NULL terminate buffer. This is the requirement of the + * parser etc. + */ + current_pkt[remaining_len] = '\0'; + + /* Process all message fragments. */ + while (total_processed < remaining_len) { + + pjsip_msg *msg; + pj_size_t msg_fragment_size = 0; + + /* Initialize default fragment size. */ + msg_fragment_size = remaining_len; + + /* Null terminate packet. */ + + /* Clear and init msg_info in rdata. + * Endpoint might inspect the values there when we call the callback + * to report some errors. + */ + pj_memset(&rdata->msg_info, 0, sizeof(rdata->msg_info)); + pj_list_init(&rdata->msg_info.parse_err); + rdata->msg_info.msg_buf = current_pkt; + rdata->msg_info.len = remaining_len; + + /* For TCP transport, check if the whole message has been received. */ + if ((tr->flag & PJSIP_TRANSPORT_DATAGRAM) == 0) { + pj_status_t msg_status; + msg_status = pjsip_find_msg(current_pkt, remaining_len, PJ_FALSE, + &msg_fragment_size); + if (msg_status != PJ_SUCCESS) { + if (remaining_len == PJSIP_MAX_PKT_LEN) { + mgr->msg_cb(mgr->endpt, PJSIP_ERXOVERFLOW, rdata); + /* Exhaust all data. */ + return rdata->pkt_info.len; + } else { + /* Not enough data in packet. */ + return total_processed; + } + } + } + + /* Update msg_info. */ + rdata->msg_info.len = msg_fragment_size; + + /* Parse the message. */ + rdata->msg_info.msg = msg = + pjsip_parse_rdata( current_pkt, msg_fragment_size, rdata); + if (msg == NULL) { + mgr->msg_cb(mgr->endpt, PJSIP_EINVALIDMSG, rdata); + goto finish_process_fragment; + } + + /* Perform basic header checking. */ + if (rdata->msg_info.call_id.ptr == NULL || + rdata->msg_info.from == NULL || + rdata->msg_info.to == NULL || + rdata->msg_info.via == NULL || + rdata->msg_info.cseq == NULL) + { + mgr->msg_cb(mgr->endpt, PJSIP_EMISSINGHDR, rdata); + goto finish_process_fragment; + } + + /* If message is received from address that's different from sent-by, + * MUST add received parameter to the via. + */ + s = pj_str(pj_inet_ntoa(rdata->pkt_info.addr.sin_addr)); + if (pj_strcmp(&s, &rdata->msg_info.via->sent_by.host) != 0) { + pj_strdup(rdata->tp_info.pool, + &rdata->msg_info.via->recvd_param, &s); + } + + /* RFC 3581: + * If message contains "rport" param, put the received port there. + */ + if (rdata->msg_info.via->rport_param == 0) { + rdata->msg_info.via->rport_param = + pj_ntohs(rdata->pkt_info.addr.sin_port); + } + + /* Drop response message if it has more than one Via. + */ + if (msg->type == PJSIP_RESPONSE_MSG) { + pjsip_hdr *hdr; + hdr = (pjsip_hdr*)rdata->msg_info.via->next; + if (hdr != &msg->hdr) { + hdr = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, hdr); + if (hdr) { + mgr->msg_cb(mgr->endpt, PJSIP_EMULTIPLEVIA, rdata); + goto finish_process_fragment; + } + } + } + + /* Call the transport manager's upstream message callback. + */ + mgr->msg_cb(mgr->endpt, PJ_SUCCESS, rdata); + + +finish_process_fragment: + total_processed += msg_fragment_size; + current_pkt += msg_fragment_size; + remaining_len -= msg_fragment_size; + + } /* while (rdata->pkt_info.len > 0) */ + + + return total_processed; +} + + +/* + * pjsip_tpmgr_alloc_transport() + * + * Get transport suitable to communicate to remote. Create a new one + * if necessary. + */ +PJ_DEF(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr, + pjsip_transport_type_e type, + const pj_sockaddr_in *remote, + pjsip_transport **p_transport) +{ + transport_key key; + pjsip_transport *transport; + pjsip_tpfactory *factory; + pj_status_t status; + + pj_lock_acquire(mgr->lock); + + /* First try to get exact destination. */ + key.type = (pj_uint8_t)type; + key.zero = 0; + key.addr = pj_ntohl(remote->sin_addr.s_addr); + key.port = pj_ntohs(remote->sin_port); + + transport = pj_hash_get(mgr->table, &key, sizeof(key)); + if (transport != NULL) { + unsigned flag = pjsip_transport_get_flag_from_type(type); + + /* For datagram transports, try lookup with zero address. */ + if (flag & PJSIP_TRANSPORT_DATAGRAM) { + key.addr = 0; + key.port = 0; + + transport = pj_hash_get(mgr->table, &key, sizeof(key)); + } + } + + if (transport != NULL) { + /* + * Transport found! + */ + pjsip_transport_add_ref(transport); + pj_lock_release(mgr->lock); + *p_transport = transport; + return PJ_SUCCESS; + } + + /* + * Transport not found! + * Find factory that can create such transport. + */ + factory = mgr->factory_list.next; + while (factory != &mgr->factory_list) { + if (factory->type == type) + break; + factory = factory->next; + } + + if (factory == &mgr->factory_list) { + /* No factory can create the transport! */ + pj_lock_release(mgr->lock); + return PJSIP_EUNSUPTRANSPORT; + } + + /* Request factory to create transport. */ + status = factory->create_transport(factory, mgr, mgr->endpt, + remote, p_transport); + + pj_lock_release(mgr->lock); + return status; +} + +/** + * Dump transport info. + */ +PJ_DEF(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr) +{ +#if PJ_LOG_MAX_LEVEL >= 3 + pj_hash_iterator_t itr_val; + pj_hash_iterator_t *itr; + + pj_lock_acquire(mgr->lock); + + itr = pj_hash_first(mgr->table, &itr_val); + if (itr) { + PJ_LOG(3, (THIS_FILE, " Dumping transports:")); + + do { + char src_addr[128], dst_addr[128]; + int src_port, dst_port; + pjsip_transport *t; + + t = pj_hash_this(mgr->table, itr); + pj_native_strcpy(src_addr, pj_inet_ntoa(t->local_addr.sin_addr)); + src_port = pj_ntohs(t->local_addr.sin_port); + + pj_native_strcpy(dst_addr, pj_inet_ntoa(t->rem_addr.sin_addr)); + dst_port = pj_ntohs(t->rem_addr.sin_port); + + PJ_LOG(3, (THIS_FILE, " %s %s %s:%d --> %s:%d (refcnt=%d)", + t->type_name, + t->obj_name, + src_addr, src_port, + dst_addr, dst_port, + pj_atomic_get(t->ref_cnt))); + + itr = pj_hash_next(mgr->table, itr); + } while (itr); + } + + pj_lock_release(mgr->lock); +#endif +} + diff --git a/pjsip/src/pjsip/sip_transport_udp.c b/pjsip/src/pjsip/sip_transport_udp.c index a6729ffa..2dc835a3 100644 --- a/pjsip/src/pjsip/sip_transport_udp.c +++ b/pjsip/src/pjsip/sip_transport_udp.c @@ -1,410 +1,410 @@ -/* $Id: $ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_errno.h>
-#include <pj/pool.h>
-#include <pj/sock.h>
-#include <pj/os.h>
-#include <pj/lock.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-
-
-/* Struct udp_transport "inherits" struct pjsip_transport */
-struct udp_transport
-{
- pjsip_transport base;
- pj_sock_t sock;
- pj_ioqueue_key_t *key;
- int rdata_cnt;
- pjsip_rx_data **rdata;
- int is_closing;
-};
-
-
-/*
- * on_read_complete()
- *
- * This is callback notification from ioqueue that a pending recvfrom()
- * operation has completed.
- */
-static void on_read_complete( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_read)
-{
- enum { MAX_IMMEDIATE_PACKET = 10 };
- pjsip_rx_data_op_key *rdata_op_key = (pjsip_rx_data_op_key*) op_key;
- pjsip_rx_data *rdata = rdata_op_key->rdata;
- struct udp_transport *tp = (struct udp_transport*)rdata->tp_info.transport;
- int i;
- pj_status_t status;
-
- /* Don't do anything if transport is closing. */
- if (tp->is_closing)
- return;
-
- /*
- * The idea of the loop is to process immediate data received by
- * pj_ioqueue_recvfrom(), as long as i < MAX_IMMEDIATE_PACKET. When
- * i is >= MAX_IMMEDIATE_PACKET, we force the recvfrom() operation to
- * complete asynchronously, to allow other sockets to get their data.
- */
- for (i=0;; ++i) {
- pj_uint32_t flags;
-
- /* Report the packet to transport manager. */
- if (bytes_read > 0) {
- pj_size_t size_eaten;
-
- rdata->pkt_info.len = bytes_read;
- rdata->pkt_info.zero = 0;
- pj_gettimeofday(&rdata->pkt_info.timestamp);
-
- size_eaten =
- pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr,
- rdata);
-
- if (size_eaten < 0) {
- pj_assert(!"It shouldn't happen!");
- size_eaten = rdata->pkt_info.len;
- }
-
- /* Since this is UDP, the whole buffer is the message. */
- rdata->pkt_info.len = 0;
-
- } else if (bytes_read == 0) {
- /* TODO: */
- } else {
- /* Report error to endpoint. */
- PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt,
- rdata->tp_info.transport->obj_name,
- -bytes_read,
- "Warning: pj_ioqueue_recvfrom()"
- " callback error"));
- }
-
- if (i >= MAX_IMMEDIATE_PACKET) {
- /* Force ioqueue_recvfrom() to return PJ_EPENDING */
- flags = PJ_IOQUEUE_ALWAYS_ASYNC;
- } else {
- flags = 0;
- }
-
- /* Read next packet. */
- bytes_read = sizeof(rdata->pkt_info.packet);
- rdata->pkt_info.addr_len = sizeof(rdata->pkt_info.addr);
- status = pj_ioqueue_recvfrom(key, op_key,
- rdata->pkt_info.packet,
- &bytes_read, flags,
- &rdata->pkt_info.addr,
- &rdata->pkt_info.addr_len);
-
- if (status == PJ_SUCCESS) {
- /* Continue loop. */
- pj_assert(i < MAX_IMMEDIATE_PACKET);
-
- } else if (status == PJ_EPENDING) {
- break;
-
- } else {
-
- if (i < MAX_IMMEDIATE_PACKET) {
- /* Report error to endpoint. */
- PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt,
- rdata->tp_info.transport->obj_name,
- status,
- "Warning: pj_ioqueue_recvfrom error"));
- /* Continue loop. */
- bytes_read = 0;
- } else {
- /* This is fatal error.
- * Ioqueue operation will stop for this transport!
- */
- PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt,
- rdata->tp_info.transport->obj_name,
- status,
- "FATAL: pj_ioqueue_recvfrom() error, "
- "UDP transport stopping! Error"));
- break;
- }
- }
- }
-}
-
-/*
- * on_write_complete()
- *
- * This is callback notification from ioqueue that a pending sendto()
- * operation has completed.
- */
-static void on_write_complete( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_sent)
-{
- struct udp_transport *tp = pj_ioqueue_get_user_data(key);
- pjsip_tx_data_op_key *tdata_op_key = (pjsip_tx_data_op_key*)op_key;
-
- tdata_op_key->tdata = NULL;
-
- if (tdata_op_key->callback) {
- tdata_op_key->callback(&tp->base, tdata_op_key->token, bytes_sent);
- }
-}
-
-/*
- * transport_send_msg()
- *
- * This function is called by transport manager (by transport->send_msg())
- * to send outgoing message.
- */
-static pj_status_t transport_send_msg( pjsip_transport *transport,
- pjsip_tx_data *tdata,
- const pj_sockaddr_in *rem_addr,
- void *token,
- void (*callback)(pjsip_transport*,
- void *token,
- pj_ssize_t))
-{
- struct udp_transport *tp = (struct udp_transport*)transport;
- pj_ssize_t size;
-
- PJ_ASSERT_RETURN(transport && tdata, PJ_EINVAL);
- PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX);
-
- /* Init op key. */
- tdata->op_key.tdata = tdata;
- tdata->op_key.token = token;
- tdata->op_key.callback = callback;
-
- /* Send to ioqueue! */
- size = tdata->buf.cur - tdata->buf.start;
- return pj_ioqueue_sendto(tp->key, (pj_ioqueue_op_key_t*)&tdata->op_key,
- tdata->buf.start, &size, 0,
- rem_addr, (rem_addr ? sizeof(pj_sockaddr_in):0));
-}
-
-/*
- * transport_destroy()
- *
- * This function is called by transport manager (by transport->destroy()).
- */
-static pj_status_t transport_destroy( pjsip_transport *transport )
-{
- struct udp_transport *tp = (struct udp_transport*)transport;
- int i;
-
- /* Mark this transport as closing. */
- tp->is_closing = 1;
-
- /* Cancel all pending operations. */
- for (i=0; i<tp->rdata_cnt; ++i) {
- pj_ioqueue_post_completion(tp->key,
- &tp->rdata[i]->tp_info.op_key.op_key, -1);
- }
-
- /* Unregister from ioqueue. */
- if (tp->key)
- pj_ioqueue_unregister(tp->key);
-
- /* Close socket. */
- if (tp->sock && tp->sock != PJ_INVALID_SOCKET)
- pj_sock_close(tp->sock);
-
- /* Destroy reference counter. */
- if (tp->base.ref_cnt)
- pj_atomic_destroy(tp->base.ref_cnt);
-
- /* Destroy lock */
- if (tp->base.lock)
- pj_lock_destroy(tp->base.lock);
-
- /* Destroy pool. */
- pjsip_endpt_destroy_pool(tp->base.endpt, tp->base.pool);
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * pjsip_udp_transport_attach()
- *
- * Attach UDP socket and start transport.
- */
-PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt,
- pj_sock_t sock,
- const pj_sockaddr_in *pub_addr,
- unsigned async_cnt,
- pjsip_transport **p_transport)
-{
- pj_pool_t *pool;
- struct udp_transport *tp;
- pj_ioqueue_t *ioqueue;
- pj_ioqueue_callback ioqueue_cb;
- unsigned i;
- int addrlen;
- pj_status_t status;
-
- /* Create pool. */
- pool = pjsip_endpt_create_pool(endpt, "udp%p", PJSIP_POOL_LEN_TRANSPORT,
- PJSIP_POOL_INC_TRANSPORT);
- if (!pool)
- return PJ_ENOMEM;
-
- tp = pj_pool_zalloc(pool, sizeof(struct udp_transport));
- tp->base.pool = pool;
- tp->base.endpt = endpt;
-
- /* Init type, type_name, and flag */
- tp->base.type = PJSIP_TRANSPORT_UDP;
- pj_native_strcpy(tp->base.type_name, "UDP");
- tp->base.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_UDP);
-
- /* Init addresses. */
- addrlen = sizeof(tp->base.local_addr);
- status = pj_sock_getsockname(sock, &tp->base.local_addr, &addrlen);
- if (status != PJ_SUCCESS) {
- pjsip_endpt_destroy_pool(endpt, pool);
- return status;
- }
- pj_memcpy(&tp->base.public_addr, pub_addr, sizeof(pj_sockaddr_in));
- tp->base.rem_addr.sin_family = PJ_AF_INET;
-
- /* Init reference counter. */
- status = pj_atomic_create(pool, 0, &tp->base.ref_cnt);
- if (status != PJ_SUCCESS)
- goto on_error;
-
- /* Init lock. */
- status = pj_lock_create_recursive_mutex(pool, "udp%p", &tp->base.lock);
- if (status != PJ_SUCCESS)
- goto on_error;
-
- /* Attach socket. */
- tp->sock = sock;
-
- /* Register to ioqueue. */
- ioqueue = pjsip_endpt_get_ioqueue(endpt);
- pj_memset(&ioqueue_cb, 0, sizeof(ioqueue_cb));
- ioqueue_cb.on_read_complete = &on_read_complete;
- ioqueue_cb.on_write_complete = &on_write_complete;
- status = pj_ioqueue_register_sock(pool, ioqueue, tp->sock, tp,
- &ioqueue_cb, &tp->key);
- if (status != PJ_SUCCESS)
- goto on_error;
-
- /* Set functions. */
- tp->base.send_msg = &transport_send_msg;
- tp->base.destroy = &transport_destroy;
-
- /* This is a permanent transport, so we initialize the ref count
- * to one so that transport manager don't destroy this transport
- * when there's no user!
- */
- pj_atomic_inc(tp->base.ref_cnt);
-
- /* Register to transport manager. */
- tp->base.tpmgr = pjsip_endpt_get_tpmgr(endpt);
- status = pjsip_transport_register( tp->base.tpmgr, (pjsip_transport*)tp);
- if (status != PJ_SUCCESS)
- goto on_error;
-
-
- /* Create rdata and put it in the array. */
- tp->rdata_cnt = 0;
- for (i=0; i<async_cnt; ++i) {
- pj_pool_t *rdata_pool = pjsip_endpt_create_pool(endpt, "rtd%p",
- PJSIP_POOL_LEN_RDATA,
- PJSIP_POOL_INC_RDATA);
- if (!rdata_pool) {
- pj_atomic_set(tp->base.ref_cnt, 0);
- pjsip_transport_unregister(tp->base.tpmgr, &tp->base);
- return PJ_ENOMEM;
- }
-
- tp->rdata[i] = pj_pool_zalloc(rdata_pool, sizeof(pjsip_rx_data));
- tp->rdata[i]->tp_info.pool = rdata_pool;
- tp->rdata[i]->tp_info.transport = &tp->base;
- pj_ioqueue_op_key_init(&tp->rdata[i]->tp_info.op_key.op_key,
- sizeof(pj_ioqueue_op_key_t));
-
- tp->rdata_cnt++;
- }
-
- /* Start reading the ioqueue. */
- for (i=0; i<async_cnt; ++i) {
- pj_ssize_t size;
-
- size = sizeof(tp->rdata[i]->pkt_info.packet);
- tp->rdata[i]->pkt_info.addr_len = sizeof(tp->rdata[i]->pkt_info.addr);
- status = pj_ioqueue_recvfrom(tp->key,
- &tp->rdata[i]->tp_info.op_key.op_key,
- tp->rdata[i]->pkt_info.packet,
- &size, PJ_IOQUEUE_ALWAYS_ASYNC,
- &tp->rdata[i]->pkt_info.addr,
- &tp->rdata[i]->pkt_info.addr_len);
- if (status == PJ_SUCCESS) {
- pj_assert(!"Shouldn't happen because PJ_IOQUEUE_ALWAYS_ASYNC!");
- on_read_complete(tp->key, &tp->rdata[i]->tp_info.op_key.op_key,
- size);
- } else if (status != PJ_EPENDING) {
- /* Error! */
- pjsip_transport_unregister(tp->base.tpmgr, &tp->base);
- return status;
- }
- }
-
- /* Done. */
- *p_transport = &tp->base;
- return PJ_SUCCESS;
-
-on_error:
- transport_destroy((pjsip_transport*)tp);
- return status;
-}
-
-/*
- * pjsip_udp_transport_start()
- *
- * Start an UDP transport/listener.
- */
-PJ_DEF(pj_status_t) pjsip_udp_transport_start( pjsip_endpoint *endpt,
- const pj_sockaddr_in *local,
- const pj_sockaddr_in *pub_addr,
- unsigned async_cnt,
- pjsip_transport **p_transport)
-{
- pj_sock_t sock;
- pj_status_t status;
-
- status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock);
- if (status != PJ_SUCCESS)
- return status;
-
- status = pj_sock_bind(sock, local, sizeof(*local));
- if (status != PJ_SUCCESS) {
- pj_sock_close(sock);
- return status;
- }
-
- return pjsip_udp_transport_attach( endpt, sock, pub_addr, async_cnt,
- p_transport );
-}
-
-
+/* $Id: $ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip/sip_transport.h> +#include <pjsip/sip_endpoint.h> +#include <pjsip/sip_errno.h> +#include <pj/pool.h> +#include <pj/sock.h> +#include <pj/os.h> +#include <pj/lock.h> +#include <pj/string.h> +#include <pj/assert.h> + + +/* Struct udp_transport "inherits" struct pjsip_transport */ +struct udp_transport +{ + pjsip_transport base; + pj_sock_t sock; + pj_ioqueue_key_t *key; + int rdata_cnt; + pjsip_rx_data **rdata; + int is_closing; +}; + + +/* + * on_read_complete() + * + * This is callback notification from ioqueue that a pending recvfrom() + * operation has completed. + */ +static void on_read_complete( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_read) +{ + enum { MAX_IMMEDIATE_PACKET = 10 }; + pjsip_rx_data_op_key *rdata_op_key = (pjsip_rx_data_op_key*) op_key; + pjsip_rx_data *rdata = rdata_op_key->rdata; + struct udp_transport *tp = (struct udp_transport*)rdata->tp_info.transport; + int i; + pj_status_t status; + + /* Don't do anything if transport is closing. */ + if (tp->is_closing) + return; + + /* + * The idea of the loop is to process immediate data received by + * pj_ioqueue_recvfrom(), as long as i < MAX_IMMEDIATE_PACKET. When + * i is >= MAX_IMMEDIATE_PACKET, we force the recvfrom() operation to + * complete asynchronously, to allow other sockets to get their data. + */ + for (i=0;; ++i) { + pj_uint32_t flags; + + /* Report the packet to transport manager. */ + if (bytes_read > 0) { + pj_size_t size_eaten; + + rdata->pkt_info.len = bytes_read; + rdata->pkt_info.zero = 0; + pj_gettimeofday(&rdata->pkt_info.timestamp); + + size_eaten = + pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, + rdata); + + if (size_eaten < 0) { + pj_assert(!"It shouldn't happen!"); + size_eaten = rdata->pkt_info.len; + } + + /* Since this is UDP, the whole buffer is the message. */ + rdata->pkt_info.len = 0; + + } else if (bytes_read == 0) { + /* TODO: */ + } else { + /* Report error to endpoint. */ + PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt, + rdata->tp_info.transport->obj_name, + -bytes_read, + "Warning: pj_ioqueue_recvfrom()" + " callback error")); + } + + if (i >= MAX_IMMEDIATE_PACKET) { + /* Force ioqueue_recvfrom() to return PJ_EPENDING */ + flags = PJ_IOQUEUE_ALWAYS_ASYNC; + } else { + flags = 0; + } + + /* Read next packet. */ + bytes_read = sizeof(rdata->pkt_info.packet); + rdata->pkt_info.addr_len = sizeof(rdata->pkt_info.addr); + status = pj_ioqueue_recvfrom(key, op_key, + rdata->pkt_info.packet, + &bytes_read, flags, + &rdata->pkt_info.addr, + &rdata->pkt_info.addr_len); + + if (status == PJ_SUCCESS) { + /* Continue loop. */ + pj_assert(i < MAX_IMMEDIATE_PACKET); + + } else if (status == PJ_EPENDING) { + break; + + } else { + + if (i < MAX_IMMEDIATE_PACKET) { + /* Report error to endpoint. */ + PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt, + rdata->tp_info.transport->obj_name, + status, + "Warning: pj_ioqueue_recvfrom error")); + /* Continue loop. */ + bytes_read = 0; + } else { + /* This is fatal error. + * Ioqueue operation will stop for this transport! + */ + PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt, + rdata->tp_info.transport->obj_name, + status, + "FATAL: pj_ioqueue_recvfrom() error, " + "UDP transport stopping! Error")); + break; + } + } + } +} + +/* + * on_write_complete() + * + * This is callback notification from ioqueue that a pending sendto() + * operation has completed. + */ +static void on_write_complete( pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_sent) +{ + struct udp_transport *tp = pj_ioqueue_get_user_data(key); + pjsip_tx_data_op_key *tdata_op_key = (pjsip_tx_data_op_key*)op_key; + + tdata_op_key->tdata = NULL; + + if (tdata_op_key->callback) { + tdata_op_key->callback(&tp->base, tdata_op_key->token, bytes_sent); + } +} + +/* + * transport_send_msg() + * + * This function is called by transport manager (by transport->send_msg()) + * to send outgoing message. + */ +static pj_status_t transport_send_msg( pjsip_transport *transport, + pjsip_tx_data *tdata, + const pj_sockaddr_in *rem_addr, + void *token, + void (*callback)(pjsip_transport*, + void *token, + pj_ssize_t)) +{ + struct udp_transport *tp = (struct udp_transport*)transport; + pj_ssize_t size; + + PJ_ASSERT_RETURN(transport && tdata, PJ_EINVAL); + PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX); + + /* Init op key. */ + tdata->op_key.tdata = tdata; + tdata->op_key.token = token; + tdata->op_key.callback = callback; + + /* Send to ioqueue! */ + size = tdata->buf.cur - tdata->buf.start; + return pj_ioqueue_sendto(tp->key, (pj_ioqueue_op_key_t*)&tdata->op_key, + tdata->buf.start, &size, 0, + rem_addr, (rem_addr ? sizeof(pj_sockaddr_in):0)); +} + +/* + * transport_destroy() + * + * This function is called by transport manager (by transport->destroy()). + */ +static pj_status_t transport_destroy( pjsip_transport *transport ) +{ + struct udp_transport *tp = (struct udp_transport*)transport; + int i; + + /* Mark this transport as closing. */ + tp->is_closing = 1; + + /* Cancel all pending operations. */ + for (i=0; i<tp->rdata_cnt; ++i) { + pj_ioqueue_post_completion(tp->key, + &tp->rdata[i]->tp_info.op_key.op_key, -1); + } + + /* Unregister from ioqueue. */ + if (tp->key) + pj_ioqueue_unregister(tp->key); + + /* Close socket. */ + if (tp->sock && tp->sock != PJ_INVALID_SOCKET) + pj_sock_close(tp->sock); + + /* Destroy reference counter. */ + if (tp->base.ref_cnt) + pj_atomic_destroy(tp->base.ref_cnt); + + /* Destroy lock */ + if (tp->base.lock) + pj_lock_destroy(tp->base.lock); + + /* Destroy pool. */ + pjsip_endpt_destroy_pool(tp->base.endpt, tp->base.pool); + + return PJ_SUCCESS; +} + + +/* + * pjsip_udp_transport_attach() + * + * Attach UDP socket and start transport. + */ +PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt, + pj_sock_t sock, + const pj_sockaddr_in *pub_addr, + unsigned async_cnt, + pjsip_transport **p_transport) +{ + pj_pool_t *pool; + struct udp_transport *tp; + pj_ioqueue_t *ioqueue; + pj_ioqueue_callback ioqueue_cb; + unsigned i; + int addrlen; + pj_status_t status; + + /* Create pool. */ + pool = pjsip_endpt_create_pool(endpt, "udp%p", PJSIP_POOL_LEN_TRANSPORT, + PJSIP_POOL_INC_TRANSPORT); + if (!pool) + return PJ_ENOMEM; + + tp = pj_pool_zalloc(pool, sizeof(struct udp_transport)); + tp->base.pool = pool; + tp->base.endpt = endpt; + + /* Init type, type_name, and flag */ + tp->base.type = PJSIP_TRANSPORT_UDP; + pj_native_strcpy(tp->base.type_name, "UDP"); + tp->base.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_UDP); + + /* Init addresses. */ + addrlen = sizeof(tp->base.local_addr); + status = pj_sock_getsockname(sock, &tp->base.local_addr, &addrlen); + if (status != PJ_SUCCESS) { + pjsip_endpt_destroy_pool(endpt, pool); + return status; + } + pj_memcpy(&tp->base.public_addr, pub_addr, sizeof(pj_sockaddr_in)); + tp->base.rem_addr.sin_family = PJ_AF_INET; + + /* Init reference counter. */ + status = pj_atomic_create(pool, 0, &tp->base.ref_cnt); + if (status != PJ_SUCCESS) + goto on_error; + + /* Init lock. */ + status = pj_lock_create_recursive_mutex(pool, "udp%p", &tp->base.lock); + if (status != PJ_SUCCESS) + goto on_error; + + /* Attach socket. */ + tp->sock = sock; + + /* Register to ioqueue. */ + ioqueue = pjsip_endpt_get_ioqueue(endpt); + pj_memset(&ioqueue_cb, 0, sizeof(ioqueue_cb)); + ioqueue_cb.on_read_complete = &on_read_complete; + ioqueue_cb.on_write_complete = &on_write_complete; + status = pj_ioqueue_register_sock(pool, ioqueue, tp->sock, tp, + &ioqueue_cb, &tp->key); + if (status != PJ_SUCCESS) + goto on_error; + + /* Set functions. */ + tp->base.send_msg = &transport_send_msg; + tp->base.destroy = &transport_destroy; + + /* This is a permanent transport, so we initialize the ref count + * to one so that transport manager don't destroy this transport + * when there's no user! + */ + pj_atomic_inc(tp->base.ref_cnt); + + /* Register to transport manager. */ + tp->base.tpmgr = pjsip_endpt_get_tpmgr(endpt); + status = pjsip_transport_register( tp->base.tpmgr, (pjsip_transport*)tp); + if (status != PJ_SUCCESS) + goto on_error; + + + /* Create rdata and put it in the array. */ + tp->rdata_cnt = 0; + for (i=0; i<async_cnt; ++i) { + pj_pool_t *rdata_pool = pjsip_endpt_create_pool(endpt, "rtd%p", + PJSIP_POOL_LEN_RDATA, + PJSIP_POOL_INC_RDATA); + if (!rdata_pool) { + pj_atomic_set(tp->base.ref_cnt, 0); + pjsip_transport_unregister(tp->base.tpmgr, &tp->base); + return PJ_ENOMEM; + } + + tp->rdata[i] = pj_pool_zalloc(rdata_pool, sizeof(pjsip_rx_data)); + tp->rdata[i]->tp_info.pool = rdata_pool; + tp->rdata[i]->tp_info.transport = &tp->base; + pj_ioqueue_op_key_init(&tp->rdata[i]->tp_info.op_key.op_key, + sizeof(pj_ioqueue_op_key_t)); + + tp->rdata_cnt++; + } + + /* Start reading the ioqueue. */ + for (i=0; i<async_cnt; ++i) { + pj_ssize_t size; + + size = sizeof(tp->rdata[i]->pkt_info.packet); + tp->rdata[i]->pkt_info.addr_len = sizeof(tp->rdata[i]->pkt_info.addr); + status = pj_ioqueue_recvfrom(tp->key, + &tp->rdata[i]->tp_info.op_key.op_key, + tp->rdata[i]->pkt_info.packet, + &size, PJ_IOQUEUE_ALWAYS_ASYNC, + &tp->rdata[i]->pkt_info.addr, + &tp->rdata[i]->pkt_info.addr_len); + if (status == PJ_SUCCESS) { + pj_assert(!"Shouldn't happen because PJ_IOQUEUE_ALWAYS_ASYNC!"); + on_read_complete(tp->key, &tp->rdata[i]->tp_info.op_key.op_key, + size); + } else if (status != PJ_EPENDING) { + /* Error! */ + pjsip_transport_unregister(tp->base.tpmgr, &tp->base); + return status; + } + } + + /* Done. */ + *p_transport = &tp->base; + return PJ_SUCCESS; + +on_error: + transport_destroy((pjsip_transport*)tp); + return status; +} + +/* + * pjsip_udp_transport_start() + * + * Start an UDP transport/listener. + */ +PJ_DEF(pj_status_t) pjsip_udp_transport_start( pjsip_endpoint *endpt, + const pj_sockaddr_in *local, + const pj_sockaddr_in *pub_addr, + unsigned async_cnt, + pjsip_transport **p_transport) +{ + pj_sock_t sock; + pj_status_t status; + + status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock); + if (status != PJ_SUCCESS) + return status; + + status = pj_sock_bind(sock, local, sizeof(*local)); + if (status != PJ_SUCCESS) { + pj_sock_close(sock); + return status; + } + + return pjsip_udp_transport_attach( endpt, sock, pub_addr, async_cnt, + p_transport ); +} + + diff --git a/pjsip/src/pjsip/sip_uri.c b/pjsip/src/pjsip/sip_uri.c index b292fea0..ab25880a 100644 --- a/pjsip/src/pjsip/sip_uri.c +++ b/pjsip/src/pjsip/sip_uri.c @@ -1,535 +1,570 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_uri.h>
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_parser.h>
-#include <pjsip/print_util.h>
-#include <pjsip/sip_errno.h>
-#include <pjlib-util/string.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-#include <pj/assert.h>
-
-/*
- * Generic parameter manipulation.
- */
-PJ_DEF(pjsip_param*) pjsip_param_find( pjsip_param *param_list,
- const pj_str_t *name )
-{
- pjsip_param *p = param_list->next;
- while (p != param_list) {
- if (pj_stricmp(&p->name, name)==0)
- return p;
- p = p->next;
- }
- return NULL;
-}
-
-PJ_DEF(const pjsip_param*) pjsip_param_cfind( const pjsip_param *param_list,
- const pj_str_t *name )
-{
- const pjsip_param *p = param_list->next;
- while (p != param_list) {
- if (pj_stricmp(&p->name, name)==0)
- return p;
- p = p->next;
- }
- return NULL;
-}
-
-PJ_DEF(void) pjsip_param_clone( pj_pool_t *pool, pjsip_param *dst_list,
- const pjsip_param *src_list)
-{
- const pjsip_param *p = src_list->next;
-
- pj_list_init(dst_list);
- while (p != src_list) {
- pjsip_param *new_param = pj_pool_alloc(pool, sizeof(pjsip_param));
- pj_strdup(pool, &new_param->name, &p->name);
- pj_strdup(pool, &new_param->value, &p->value);
- pj_list_insert_before(dst_list, new_param);
- p = p->next;
- }
-}
-
-/*
- * URI stuffs
- */
-#define IS_SIPS(url) ((url)->vptr==&sips_url_vptr)
-
-static const pj_str_t *pjsip_url_get_scheme( const pjsip_url* );
-static const pj_str_t *pjsips_url_get_scheme( const pjsip_url* );
-static const pj_str_t *pjsip_name_addr_get_scheme( const pjsip_name_addr * );
-static void *pjsip_get_uri( pjsip_uri *uri );
-static void *pjsip_name_addr_get_uri( pjsip_name_addr *name );
-
-static pj_str_t sip_str = { "sip", 3 };
-static pj_str_t sips_str = { "sips", 4 };
-
-#ifdef __GNUC__
-# define HAPPY_FLAG (void*)
-#else
-# define HAPPY_FLAG
-#endif
-
-static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool,
- const pjsip_name_addr *rhs);
-static int pjsip_name_addr_print( pjsip_uri_context_e context,
- const pjsip_name_addr *name,
- char *buf, pj_size_t size);
-static int pjsip_name_addr_compare( pjsip_uri_context_e context,
- const pjsip_name_addr *naddr1,
- const pjsip_name_addr *naddr2);
-static int pjsip_url_print( pjsip_uri_context_e context,
- const pjsip_url *url,
- char *buf, pj_size_t size);
-static int pjsip_url_compare( pjsip_uri_context_e context,
- const pjsip_url *url1, const pjsip_url *url2);
-static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs);
-
-static pjsip_uri_vptr sip_url_vptr =
-{
- HAPPY_FLAG &pjsip_url_get_scheme,
- HAPPY_FLAG &pjsip_get_uri,
- HAPPY_FLAG &pjsip_url_print,
- HAPPY_FLAG &pjsip_url_compare,
- HAPPY_FLAG &pjsip_url_clone
-};
-
-static pjsip_uri_vptr sips_url_vptr =
-{
- HAPPY_FLAG &pjsips_url_get_scheme,
- HAPPY_FLAG &pjsip_get_uri,
- HAPPY_FLAG &pjsip_url_print,
- HAPPY_FLAG &pjsip_url_compare,
- HAPPY_FLAG &pjsip_url_clone
-};
-
-static pjsip_uri_vptr name_addr_vptr =
-{
- HAPPY_FLAG &pjsip_name_addr_get_scheme,
- HAPPY_FLAG &pjsip_name_addr_get_uri,
- HAPPY_FLAG &pjsip_name_addr_print,
- HAPPY_FLAG &pjsip_name_addr_compare,
- HAPPY_FLAG &pjsip_name_addr_clone
-};
-
-static const pj_str_t *pjsip_url_get_scheme(const pjsip_url *url)
-{
- PJ_UNUSED_ARG(url);
- return &sip_str;
-}
-
-static const pj_str_t *pjsips_url_get_scheme(const pjsip_url *url)
-{
- PJ_UNUSED_ARG(url);
- return &sips_str;
-}
-
-static void *pjsip_get_uri( pjsip_uri *uri )
-{
- return uri;
-}
-
-static void *pjsip_name_addr_get_uri( pjsip_name_addr *name )
-{
- return name->uri;
-}
-
-PJ_DEF(void) pjsip_url_init(pjsip_url *url, int secure)
-{
- pj_memset(url, 0, sizeof(*url));
- url->ttl_param = -1;
- url->vptr = secure ? &sips_url_vptr : &sip_url_vptr;
- pj_list_init(&url->other_param);
- pj_list_init(&url->header_param);
-}
-
-PJ_DEF(pjsip_url*) pjsip_url_create( pj_pool_t *pool, int secure )
-{
- pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url));
- pjsip_url_init(url, secure);
- return url;
-}
-
-static int pjsip_url_print( pjsip_uri_context_e context,
- const pjsip_url *url,
- char *buf, pj_size_t size)
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf+size;
- const pj_str_t *scheme;
- pjsip_param *param;
- char hparam_char = '?';
-
- *buf = '\0';
-
- /* Print scheme ("sip:" or "sips:") */
- scheme = pjsip_uri_get_scheme(url);
- copy_advance_check(buf, *scheme);
- *buf++ = ':';
-
- /* Print "user:password@", if any. */
- if (url->user.slen) {
- copy_advance_escape(buf, url->user, pjsip_USER_SPEC);
- if (url->passwd.slen) {
- *buf++ = ':';
- copy_advance_escape(buf, url->passwd, pjsip_PASSWD_SPEC);
- }
-
- *buf++ = '@';
- }
-
- /* Print host. */
- pj_assert(url->host.slen != 0);
- copy_advance_check(buf, url->host);
-
- /* Only print port if it is explicitly specified.
- * Port is not allowed in To and From header.
- */
- /* Unfortunately some UA requires us to send back the port
- * number exactly as it was sent. We don't remember whether an
- * UA has sent us port, so we'll just send the port indiscrimately
- */
- //PJ_TODO(SHOULD_DISALLOW_URI_PORT_IN_FROM_TO_HEADER)
- if (url->port && context != PJSIP_URI_IN_FROMTO_HDR) {
- *buf++ = ':';
- printed = pj_utoa(url->port, buf);
- buf += printed;
- }
-
- /* User param is allowed in all contexes */
- copy_advance_pair_check(buf, ";user=", 6, url->user_param);
-
- /* Method param is only allowed in external/other context. */
- if (context == PJSIP_URI_IN_OTHER) {
- copy_advance_pair_escape(buf, ";method=", 8, url->method_param,
- pjsip_PARAM_CHAR_SPEC);
- }
-
- /* Transport is not allowed in From/To header. */
- if (context != PJSIP_URI_IN_FROMTO_HDR) {
- copy_advance_pair_escape(buf, ";transport=", 11, url->transport_param,
- pjsip_PARAM_CHAR_SPEC);
- }
-
- /* TTL param is not allowed in From, To, Route, and Record-Route header. */
- if (url->ttl_param >= 0 && context != PJSIP_URI_IN_FROMTO_HDR &&
- context != PJSIP_URI_IN_ROUTING_HDR && (endbuf-buf) > 15)
- {
- pj_memcpy(buf, ";ttl=", 5);
- printed = pj_utoa(url->ttl_param, buf+5);
- buf += printed + 5;
- }
-
- /* maddr param is not allowed in From and To header. */
- if (context != PJSIP_URI_IN_FROMTO_HDR) {
- copy_advance_pair_escape(buf, ";maddr=", 7, url->maddr_param,
- pjsip_PARAM_CHAR_SPEC);
- }
-
- /* lr param is not allowed in From, To, and Contact header. */
- if (url->lr_param && context != PJSIP_URI_IN_FROMTO_HDR &&
- context != PJSIP_URI_IN_CONTACT_HDR)
- {
- pj_str_t lr = { ";lr", 3 };
- copy_advance_check(buf, lr);
- }
-
- /* Other param. */
- param = url->other_param.next;
- while (param != &url->other_param) {
- *buf++ = ';';
- copy_advance_escape(buf, param->name, pjsip_PARAM_CHAR_SPEC);
- if (param->value.slen) {
- *buf++ = '=';
- copy_advance_escape(buf, param->value, pjsip_PARAM_CHAR_SPEC);
- }
- param = param->next;
- }
-
- /* Header param. */
- param = url->header_param.next;
- while (param != &url->header_param) {
- *buf++ = hparam_char;
- copy_advance_escape(buf, param->name, pjsip_HDR_CHAR_SPEC);
- if (param->value.slen) {
- *buf++ = '=';
- copy_advance_escape(buf, param->value, pjsip_HDR_CHAR_SPEC);
- }
- param = param->next;
- hparam_char = '&';
- }
-
- *buf = '\0';
- return buf-startbuf;
-}
-
-static pj_status_t pjsip_url_compare( pjsip_uri_context_e context,
- const pjsip_url *url1,
- const pjsip_url *url2)
-{
- const pjsip_param *p1;
-
- /*
- * Compare two SIP URL's according to Section 19.1.4 of RFC 3261.
- */
-
- /* SIP and SIPS URI are never equivalent.
- * Note: just compare the vptr to avoid string comparison.
- * Pretty neat huh!!
- */
- if (url1->vptr != url2->vptr)
- return PJSIP_ECMPSCHEME;
-
- /* Comparison of the userinfo of SIP and SIPS URIs is case-sensitive.
- * This includes userinfo containing passwords or formatted as
- * telephone-subscribers.
- */
- if (pj_strcmp(&url1->user, &url2->user) != 0)
- return PJSIP_ECMPUSER;
- if (pj_strcmp(&url1->passwd, &url2->passwd) != 0)
- return PJSIP_ECMPPASSWD;
-
- /* Comparison of all other components of the URI is
- * case-insensitive unless explicitly defined otherwise.
- */
-
- /* The ordering of parameters and header fields is not significant
- * in comparing SIP and SIPS URIs.
- */
-
- /* Characters other than those in the “reserved” set (see RFC 2396 [5])
- * are equivalent to their “encoding.
- */
-
- /* An IP address that is the result of a DNS lookup of a host name
- * does not match that host name.
- */
- if (pj_stricmp(&url1->host, &url2->host) != 0)
- return PJSIP_ECMPHOST;
-
- /* A URI omitting any component with a default value will not match a URI
- * explicitly containing that component with its default value.
- * For instance, a URI omitting the optional port component will not match
- * a URI explicitly declaring port 5060.
- * The same is true for the transport-parameter, ttl-parameter,
- * user-parameter, and method components.
- */
-
- /* Port is not allowed in To and From header.
- */
- if (context != PJSIP_URI_IN_FROMTO_HDR) {
- if (url1->port != url2->port)
- return PJSIP_ECMPPORT;
- }
- /* Transport is not allowed in From/To header. */
- if (context != PJSIP_URI_IN_FROMTO_HDR) {
- if (pj_stricmp(&url1->transport_param, &url2->transport_param) != 0)
- return PJSIP_ECMPTRANSPORTPRM;
- }
- /* TTL param is not allowed in From, To, Route, and Record-Route header. */
- if (context != PJSIP_URI_IN_FROMTO_HDR &&
- context != PJSIP_URI_IN_ROUTING_HDR)
- {
- if (url1->ttl_param != url2->ttl_param)
- return PJSIP_ECMPTTLPARAM;
- }
- /* User param is allowed in all contexes */
- if (pj_stricmp(&url1->user_param, &url2->user_param) != 0)
- return PJSIP_ECMPUSERPARAM;
- /* Method param is only allowed in external/other context. */
- if (context == PJSIP_URI_IN_OTHER) {
- if (pj_stricmp(&url1->method_param, &url2->method_param) != 0)
- return PJSIP_ECMPMETHODPARAM;
- }
- /* maddr param is not allowed in From and To header. */
- if (context != PJSIP_URI_IN_FROMTO_HDR) {
- if (pj_stricmp(&url1->maddr_param, &url2->maddr_param) != 0)
- return PJSIP_ECMPMADDRPARAM;
- }
-
- /* lr parameter is ignored (?) */
- /* lr param is not allowed in From, To, and Contact header. */
-
-
- /* All other uri-parameters appearing in only one URI are ignored when
- * comparing the URIs.
- */
- p1 = url1->other_param.next;
- while (p1 != &url1->other_param) {
- const pjsip_param *p2;
- p2 = pjsip_param_cfind(&url2->other_param, &p1->name);
- if (p2 ) {
- if (pj_stricmp(&p1->value, &p2->value) != 0)
- return PJSIP_ECMPOTHERPARAM;
- }
-
- p1 = p1->next;
- }
-
- /* URI header components are never ignored. Any present header component
- * MUST be present in both URIs and match for the URIs to match.
- * The matching rules are defined for each header field in Section 20.
- */
- p1 = url1->header_param.next;
- while (p1 != &url1->header_param) {
- const pjsip_param *p2;
- p2 = pjsip_param_cfind(&url2->header_param, &p1->name);
- if (p2) {
- /* It seems too much to compare two header params according to
- * the rule of each header. We'll just compare them string to
- * string..
- */
- PJ_TODO(MORE_COMPLIANT_HEADER_PARAM_COMPARISON_IN_URL);
-
- if (pj_stricmp(&p1->value, &p2->value) != 0)
- return PJSIP_ECMPHEADERPARAM;
- } else {
- return PJSIP_ECMPHEADERPARAM;
- }
- p1 = p1->next;
- }
-
- /* Equal!! Pheuww.. */
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(void) pjsip_url_assign(pj_pool_t *pool, pjsip_url *url,
- const pjsip_url *rhs)
-{
- pj_strdup( pool, &url->user, &rhs->user);
- pj_strdup( pool, &url->passwd, &rhs->passwd);
- pj_strdup( pool, &url->host, &rhs->host);
- url->port = rhs->port;
- pj_strdup( pool, &url->user_param, &rhs->user_param);
- pj_strdup( pool, &url->method_param, &rhs->method_param);
- pj_strdup( pool, &url->transport_param, &rhs->transport_param);
- url->ttl_param = rhs->ttl_param;
- pj_strdup( pool, &url->maddr_param, &rhs->maddr_param);
- pjsip_param_clone(pool, &url->other_param, &rhs->other_param);
- pjsip_param_clone(pool, &url->header_param, &rhs->header_param);
- url->lr_param = rhs->lr_param;
-}
-
-static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs)
-{
- pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url));
- if (!url)
- return NULL;
-
- pjsip_url_init(url, IS_SIPS(rhs));
- pjsip_url_assign(pool, url, rhs);
- return url;
-}
-
-static const pj_str_t *pjsip_name_addr_get_scheme(const pjsip_name_addr *name)
-{
- pj_assert(name->uri != NULL);
- return pjsip_uri_get_scheme(name->uri);
-}
-
-PJ_DEF(void) pjsip_name_addr_init(pjsip_name_addr *name)
-{
- name->vptr = &name_addr_vptr;
- name->uri = NULL;
- name->display.slen = 0;
-}
-
-PJ_DEF(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool)
-{
- pjsip_name_addr *name_addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr));
- pjsip_name_addr_init(name_addr);
- return name_addr;
-}
-
-static int pjsip_name_addr_print( pjsip_uri_context_e context,
- const pjsip_name_addr *name,
- char *buf, pj_size_t size)
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
-
- pj_assert(name->uri != NULL);
-
- if (context != PJSIP_URI_IN_REQ_URI) {
- copy_advance(buf, name->display);
- if (name->display.slen) {
- *buf++ = ' ';
- }
- *buf++ = '<';
- }
-
- printed = pjsip_uri_print(context,name->uri, buf, size-(buf-startbuf));
- if (printed < 1)
- return -1;
- buf += printed;
-
- if (context != PJSIP_URI_IN_REQ_URI) {
- *buf++ = '>';
- }
-
- *buf = '\0';
- return buf-startbuf;
-}
-
-PJ_DEF(void) pjsip_name_addr_assign(pj_pool_t *pool, pjsip_name_addr *dst,
- const pjsip_name_addr *src)
-{
- pj_strdup( pool, &dst->display, &src->display);
- dst->uri = pjsip_uri_clone(pool, src->uri);
-}
-
-static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool,
- const pjsip_name_addr *rhs)
-{
- pjsip_name_addr *addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr));
- if (!addr)
- return NULL;
-
- pjsip_name_addr_init(addr);
- pjsip_name_addr_assign(pool, addr, rhs);
- return addr;
-}
-
-static int pjsip_name_addr_compare( pjsip_uri_context_e context,
- const pjsip_name_addr *naddr1,
- const pjsip_name_addr *naddr2)
-{
- int d;
-
- /* I'm not sure whether display name is included in the comparison. */
- if (pj_strcmp(&naddr1->display, &naddr2->display) != 0) {
- return -1;
- }
-
- pj_assert( naddr1->uri != NULL );
- pj_assert( naddr2->uri != NULL );
-
- /* Compare name-addr as URL */
- d = pjsip_uri_cmp( context, naddr1->uri, naddr2->uri);
- if (d)
- return d;
-
- return 0;
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip/sip_uri.h> +#include <pjsip/sip_msg.h> +#include <pjsip/sip_parser.h> +#include <pjsip/print_util.h> +#include <pjsip/sip_errno.h> +#include <pjlib-util/string.h> +#include <pj/string.h> +#include <pj/pool.h> +#include <pj/assert.h> + +/* + * Generic parameter manipulation. + */ +PJ_DEF(pjsip_param*) pjsip_param_find( pjsip_param *param_list, + const pj_str_t *name ) +{ + pjsip_param *p = param_list->next; + while (p != param_list) { + if (pj_stricmp(&p->name, name)==0) + return p; + p = p->next; + } + return NULL; +} + +PJ_DEF(const pjsip_param*) pjsip_param_cfind( const pjsip_param *param_list, + const pj_str_t *name ) +{ + const pjsip_param *p = param_list->next; + while (p != param_list) { + if (pj_stricmp(&p->name, name)==0) + return p; + p = p->next; + } + return NULL; +} + +PJ_DEF(void) pjsip_param_clone( pj_pool_t *pool, pjsip_param *dst_list, + const pjsip_param *src_list) +{ + const pjsip_param *p = src_list->next; + + pj_list_init(dst_list); + while (p != src_list) { + pjsip_param *new_param = pj_pool_alloc(pool, sizeof(pjsip_param)); + pj_strdup(pool, &new_param->name, &p->name); + pj_strdup(pool, &new_param->value, &p->value); + pj_list_insert_before(dst_list, new_param); + p = p->next; + } +} + + +PJ_DEF(void) pjsip_param_shallow_clone( pj_pool_t *pool, + pjsip_param *dst_list, + const pjsip_param *src_list) +{ + const pjsip_param *p = src_list->next; + + pj_list_init(dst_list); + while (p != src_list) { + pjsip_param *new_param = pj_pool_alloc(pool, sizeof(pjsip_param)); + new_param->name = p->name; + new_param->value = p->value; + pj_list_insert_before(dst_list, new_param); + p = p->next; + } +} + +PJ_DEF(pj_ssize_t) pjsip_param_print_on( const pjsip_param *param_list, + char *buf, pj_size_t size, + int sep) +{ + const pjsip_param *p = param_list->next; + char *startbuf = buf; + char *endbuf = buf + size; + + while (p != param_list) { + *buf++ = (char)sep; + copy_advance_escape(buf, p->name, pjsip_PARAM_CHAR_SPEC); + if (p->value.slen) { + *buf++ = '='; + copy_advance_escape(buf, p->value, pjsip_PARAM_CHAR_SPEC); + } + p = p->next; + } + return buf-startbuf; +} + + +/* + * URI stuffs + */ +#define IS_SIPS(url) ((url)->vptr==&sips_url_vptr) + +static const pj_str_t *pjsip_url_get_scheme( const pjsip_url* ); +static const pj_str_t *pjsips_url_get_scheme( const pjsip_url* ); +static const pj_str_t *pjsip_name_addr_get_scheme( const pjsip_name_addr * ); +static void *pjsip_get_uri( pjsip_uri *uri ); +static void *pjsip_name_addr_get_uri( pjsip_name_addr *name ); + +static pj_str_t sip_str = { "sip", 3 }; +static pj_str_t sips_str = { "sips", 4 }; + +#ifdef __GNUC__ +# define HAPPY_FLAG (void*) +#else +# define HAPPY_FLAG +#endif + +static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool, + const pjsip_name_addr *rhs); +static int pjsip_name_addr_print( pjsip_uri_context_e context, + const pjsip_name_addr *name, + char *buf, pj_size_t size); +static int pjsip_name_addr_compare( pjsip_uri_context_e context, + const pjsip_name_addr *naddr1, + const pjsip_name_addr *naddr2); +static int pjsip_url_print( pjsip_uri_context_e context, + const pjsip_url *url, + char *buf, pj_size_t size); +static int pjsip_url_compare( pjsip_uri_context_e context, + const pjsip_url *url1, const pjsip_url *url2); +static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs); + +static pjsip_uri_vptr sip_url_vptr = +{ + HAPPY_FLAG &pjsip_url_get_scheme, + HAPPY_FLAG &pjsip_get_uri, + HAPPY_FLAG &pjsip_url_print, + HAPPY_FLAG &pjsip_url_compare, + HAPPY_FLAG &pjsip_url_clone +}; + +static pjsip_uri_vptr sips_url_vptr = +{ + HAPPY_FLAG &pjsips_url_get_scheme, + HAPPY_FLAG &pjsip_get_uri, + HAPPY_FLAG &pjsip_url_print, + HAPPY_FLAG &pjsip_url_compare, + HAPPY_FLAG &pjsip_url_clone +}; + +static pjsip_uri_vptr name_addr_vptr = +{ + HAPPY_FLAG &pjsip_name_addr_get_scheme, + HAPPY_FLAG &pjsip_name_addr_get_uri, + HAPPY_FLAG &pjsip_name_addr_print, + HAPPY_FLAG &pjsip_name_addr_compare, + HAPPY_FLAG &pjsip_name_addr_clone +}; + +static const pj_str_t *pjsip_url_get_scheme(const pjsip_url *url) +{ + PJ_UNUSED_ARG(url); + return &sip_str; +} + +static const pj_str_t *pjsips_url_get_scheme(const pjsip_url *url) +{ + PJ_UNUSED_ARG(url); + return &sips_str; +} + +static void *pjsip_get_uri( pjsip_uri *uri ) +{ + return uri; +} + +static void *pjsip_name_addr_get_uri( pjsip_name_addr *name ) +{ + return name->uri; +} + +PJ_DEF(void) pjsip_url_init(pjsip_url *url, int secure) +{ + pj_memset(url, 0, sizeof(*url)); + url->ttl_param = -1; + url->vptr = secure ? &sips_url_vptr : &sip_url_vptr; + pj_list_init(&url->other_param); + pj_list_init(&url->header_param); +} + +PJ_DEF(pjsip_url*) pjsip_url_create( pj_pool_t *pool, int secure ) +{ + pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url)); + pjsip_url_init(url, secure); + return url; +} + +static int pjsip_url_print( pjsip_uri_context_e context, + const pjsip_url *url, + char *buf, pj_size_t size) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf+size; + const pj_str_t *scheme; + pjsip_param *param; + char hparam_char = '?'; + + *buf = '\0'; + + /* Print scheme ("sip:" or "sips:") */ + scheme = pjsip_uri_get_scheme(url); + copy_advance_check(buf, *scheme); + *buf++ = ':'; + + /* Print "user:password@", if any. */ + if (url->user.slen) { + copy_advance_escape(buf, url->user, pjsip_USER_SPEC); + if (url->passwd.slen) { + *buf++ = ':'; + copy_advance_escape(buf, url->passwd, pjsip_PASSWD_SPEC); + } + + *buf++ = '@'; + } + + /* Print host. */ + pj_assert(url->host.slen != 0); + copy_advance_check(buf, url->host); + + /* Only print port if it is explicitly specified. + * Port is not allowed in To and From header. + */ + /* Unfortunately some UA requires us to send back the port + * number exactly as it was sent. We don't remember whether an + * UA has sent us port, so we'll just send the port indiscrimately + */ + //PJ_TODO(SHOULD_DISALLOW_URI_PORT_IN_FROM_TO_HEADER) + if (url->port && context != PJSIP_URI_IN_FROMTO_HDR) { + *buf++ = ':'; + printed = pj_utoa(url->port, buf); + buf += printed; + } + + /* User param is allowed in all contexes */ + copy_advance_pair_check(buf, ";user=", 6, url->user_param); + + /* Method param is only allowed in external/other context. */ + if (context == PJSIP_URI_IN_OTHER) { + copy_advance_pair_escape(buf, ";method=", 8, url->method_param, + pjsip_PARAM_CHAR_SPEC); + } + + /* Transport is not allowed in From/To header. */ + if (context != PJSIP_URI_IN_FROMTO_HDR) { + copy_advance_pair_escape(buf, ";transport=", 11, url->transport_param, + pjsip_PARAM_CHAR_SPEC); + } + + /* TTL param is not allowed in From, To, Route, and Record-Route header. */ + if (url->ttl_param >= 0 && context != PJSIP_URI_IN_FROMTO_HDR && + context != PJSIP_URI_IN_ROUTING_HDR && (endbuf-buf) > 15) + { + pj_memcpy(buf, ";ttl=", 5); + printed = pj_utoa(url->ttl_param, buf+5); + buf += printed + 5; + } + + /* maddr param is not allowed in From and To header. */ + if (context != PJSIP_URI_IN_FROMTO_HDR) { + copy_advance_pair_escape(buf, ";maddr=", 7, url->maddr_param, + pjsip_PARAM_CHAR_SPEC); + } + + /* lr param is not allowed in From, To, and Contact header. */ + if (url->lr_param && context != PJSIP_URI_IN_FROMTO_HDR && + context != PJSIP_URI_IN_CONTACT_HDR) + { + pj_str_t lr = { ";lr", 3 }; + copy_advance_check(buf, lr); + } + + /* Other param. */ + printed = pjsip_param_print_on(&url->other_param, buf, endbuf-buf, ';'); + if (printed < 0) + return -1; + buf += printed; + + /* Header param. */ + param = url->header_param.next; + while (param != &url->header_param) { + *buf++ = hparam_char; + copy_advance_escape(buf, param->name, pjsip_HDR_CHAR_SPEC); + if (param->value.slen) { + *buf++ = '='; + copy_advance_escape(buf, param->value, pjsip_HDR_CHAR_SPEC); + } + param = param->next; + hparam_char = '&'; + } + + *buf = '\0'; + return buf-startbuf; +} + +static pj_status_t pjsip_url_compare( pjsip_uri_context_e context, + const pjsip_url *url1, + const pjsip_url *url2) +{ + const pjsip_param *p1; + + /* + * Compare two SIP URL's according to Section 19.1.4 of RFC 3261. + */ + + /* SIP and SIPS URI are never equivalent. + * Note: just compare the vptr to avoid string comparison. + * Pretty neat huh!! + */ + if (url1->vptr != url2->vptr) + return PJSIP_ECMPSCHEME; + + /* Comparison of the userinfo of SIP and SIPS URIs is case-sensitive. + * This includes userinfo containing passwords or formatted as + * telephone-subscribers. + */ + if (pj_strcmp(&url1->user, &url2->user) != 0) + return PJSIP_ECMPUSER; + if (pj_strcmp(&url1->passwd, &url2->passwd) != 0) + return PJSIP_ECMPPASSWD; + + /* Comparison of all other components of the URI is + * case-insensitive unless explicitly defined otherwise. + */ + + /* The ordering of parameters and header fields is not significant + * in comparing SIP and SIPS URIs. + */ + + /* Characters other than those in the “reserved” set (see RFC 2396 [5]) + * are equivalent to their “encoding. + */ + + /* An IP address that is the result of a DNS lookup of a host name + * does not match that host name. + */ + if (pj_stricmp(&url1->host, &url2->host) != 0) + return PJSIP_ECMPHOST; + + /* A URI omitting any component with a default value will not match a URI + * explicitly containing that component with its default value. + * For instance, a URI omitting the optional port component will not match + * a URI explicitly declaring port 5060. + * The same is true for the transport-parameter, ttl-parameter, + * user-parameter, and method components. + */ + + /* Port is not allowed in To and From header. + */ + if (context != PJSIP_URI_IN_FROMTO_HDR) { + if (url1->port != url2->port) + return PJSIP_ECMPPORT; + } + /* Transport is not allowed in From/To header. */ + if (context != PJSIP_URI_IN_FROMTO_HDR) { + if (pj_stricmp(&url1->transport_param, &url2->transport_param) != 0) + return PJSIP_ECMPTRANSPORTPRM; + } + /* TTL param is not allowed in From, To, Route, and Record-Route header. */ + if (context != PJSIP_URI_IN_FROMTO_HDR && + context != PJSIP_URI_IN_ROUTING_HDR) + { + if (url1->ttl_param != url2->ttl_param) + return PJSIP_ECMPTTLPARAM; + } + /* User param is allowed in all contexes */ + if (pj_stricmp(&url1->user_param, &url2->user_param) != 0) + return PJSIP_ECMPUSERPARAM; + /* Method param is only allowed in external/other context. */ + if (context == PJSIP_URI_IN_OTHER) { + if (pj_stricmp(&url1->method_param, &url2->method_param) != 0) + return PJSIP_ECMPMETHODPARAM; + } + /* maddr param is not allowed in From and To header. */ + if (context != PJSIP_URI_IN_FROMTO_HDR) { + if (pj_stricmp(&url1->maddr_param, &url2->maddr_param) != 0) + return PJSIP_ECMPMADDRPARAM; + } + + /* lr parameter is ignored (?) */ + /* lr param is not allowed in From, To, and Contact header. */ + + + /* All other uri-parameters appearing in only one URI are ignored when + * comparing the URIs. + */ + p1 = url1->other_param.next; + while (p1 != &url1->other_param) { + const pjsip_param *p2; + p2 = pjsip_param_cfind(&url2->other_param, &p1->name); + if (p2 ) { + if (pj_stricmp(&p1->value, &p2->value) != 0) + return PJSIP_ECMPOTHERPARAM; + } + + p1 = p1->next; + } + + /* URI header components are never ignored. Any present header component + * MUST be present in both URIs and match for the URIs to match. + * The matching rules are defined for each header field in Section 20. + */ + p1 = url1->header_param.next; + while (p1 != &url1->header_param) { + const pjsip_param *p2; + p2 = pjsip_param_cfind(&url2->header_param, &p1->name); + if (p2) { + /* It seems too much to compare two header params according to + * the rule of each header. We'll just compare them string to + * string.. + */ + PJ_TODO(MORE_COMPLIANT_HEADER_PARAM_COMPARISON_IN_URL); + + if (pj_stricmp(&p1->value, &p2->value) != 0) + return PJSIP_ECMPHEADERPARAM; + } else { + return PJSIP_ECMPHEADERPARAM; + } + p1 = p1->next; + } + + /* Equal!! Pheuww.. */ + return PJ_SUCCESS; +} + + +PJ_DEF(void) pjsip_url_assign(pj_pool_t *pool, pjsip_url *url, + const pjsip_url *rhs) +{ + pj_strdup( pool, &url->user, &rhs->user); + pj_strdup( pool, &url->passwd, &rhs->passwd); + pj_strdup( pool, &url->host, &rhs->host); + url->port = rhs->port; + pj_strdup( pool, &url->user_param, &rhs->user_param); + pj_strdup( pool, &url->method_param, &rhs->method_param); + pj_strdup( pool, &url->transport_param, &rhs->transport_param); + url->ttl_param = rhs->ttl_param; + pj_strdup( pool, &url->maddr_param, &rhs->maddr_param); + pjsip_param_clone(pool, &url->other_param, &rhs->other_param); + pjsip_param_clone(pool, &url->header_param, &rhs->header_param); + url->lr_param = rhs->lr_param; +} + +static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs) +{ + pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url)); + if (!url) + return NULL; + + pjsip_url_init(url, IS_SIPS(rhs)); + pjsip_url_assign(pool, url, rhs); + return url; +} + +static const pj_str_t *pjsip_name_addr_get_scheme(const pjsip_name_addr *name) +{ + pj_assert(name->uri != NULL); + return pjsip_uri_get_scheme(name->uri); +} + +PJ_DEF(void) pjsip_name_addr_init(pjsip_name_addr *name) +{ + name->vptr = &name_addr_vptr; + name->uri = NULL; + name->display.slen = 0; +} + +PJ_DEF(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool) +{ + pjsip_name_addr *name_addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr)); + pjsip_name_addr_init(name_addr); + return name_addr; +} + +static int pjsip_name_addr_print( pjsip_uri_context_e context, + const pjsip_name_addr *name, + char *buf, pj_size_t size) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + + pj_assert(name->uri != NULL); + + if (context != PJSIP_URI_IN_REQ_URI) { + if (name->display.slen) { + if (endbuf-buf < 8) return -1; + *buf++ = '"'; + copy_advance(buf, name->display); + *buf++ = '"'; + *buf++ = ' '; + } + *buf++ = '<'; + } + + printed = pjsip_uri_print(context,name->uri, buf, size-(buf-startbuf)); + if (printed < 1) + return -1; + buf += printed; + + if (context != PJSIP_URI_IN_REQ_URI) { + *buf++ = '>'; + } + + *buf = '\0'; + return buf-startbuf; +} + +PJ_DEF(void) pjsip_name_addr_assign(pj_pool_t *pool, pjsip_name_addr *dst, + const pjsip_name_addr *src) +{ + pj_strdup( pool, &dst->display, &src->display); + dst->uri = pjsip_uri_clone(pool, src->uri); +} + +static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool, + const pjsip_name_addr *rhs) +{ + pjsip_name_addr *addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr)); + if (!addr) + return NULL; + + pjsip_name_addr_init(addr); + pjsip_name_addr_assign(pool, addr, rhs); + return addr; +} + +static int pjsip_name_addr_compare( pjsip_uri_context_e context, + const pjsip_name_addr *naddr1, + const pjsip_name_addr *naddr2) +{ + int d; + + /* I'm not sure whether display name is included in the comparison. */ + if (pj_strcmp(&naddr1->display, &naddr2->display) != 0) { + return -1; + } + + pj_assert( naddr1->uri != NULL ); + pj_assert( naddr2->uri != NULL ); + + /* Compare name-addr as URL */ + d = pjsip_uri_cmp( context, naddr1->uri, naddr2->uri); + if (d) + return d; + + return 0; +} + diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c index 5f59a12e..4531117f 100644 --- a/pjsip/src/pjsip/sip_util.c +++ b/pjsip/src/pjsip/sip_util.c @@ -1,724 +1,724 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_module.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/guid.h>
-#include <pj/pool.h>
-#include <pj/except.h>
-#include <pj/rand.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
-
-#define THIS_FILE "endpoint"
-
-static const char *event_str[] =
-{
- "UNIDENTIFIED",
- "TIMER",
- "TX_MSG",
- "RX_MSG",
- "TRANSPORT_ERROR",
- "TSX_STATE",
- "RX_2XX_RESPONSE",
- "RX_ACK",
- "DISCARD_MSG",
- "USER",
- "BEFORE_TX",
-};
-
-static pj_str_t str_TEXT = { "text", 4},
- str_PLAIN = { "plain", 5 };
-static int aux_mod_id;
-
-struct aux_tsx_data
-{
- void *token;
- void (*cb)(void*,pjsip_event*);
-};
-
-static pj_status_t aux_tsx_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id )
-{
- PJ_UNUSED_ARG(endpt);
- PJ_UNUSED_ARG(mod);
-
- aux_mod_id = id;
- return 0;
-}
-
-static void aux_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
-{
- pjsip_transaction *tsx;
- struct aux_tsx_data *tsx_data;
-
- PJ_UNUSED_ARG(mod);
-
- if (event->type != PJSIP_EVENT_TSX_STATE)
- return;
-
- pj_assert(event->body.tsx_state.tsx != NULL);
- tsx = event->body.tsx_state.tsx;
- if (tsx == NULL)
- return;
- if (tsx->module_data[aux_mod_id] == NULL)
- return;
- if (tsx->status_code < 200)
- return;
-
- /* Call the callback, if any, and prevent the callback to be called again
- * by clearing the transaction's module_data.
- */
- tsx_data = tsx->module_data[aux_mod_id];
- tsx->module_data[aux_mod_id] = NULL;
-
- if (tsx_data->cb) {
- (*tsx_data->cb)(tsx_data->token, event);
- }
-}
-
-pjsip_module aux_tsx_module =
-{
- { "Aux-Tsx", 7}, /* Name. */
- 0, /* Flag */
- 128, /* Priority */
- NULL, /* Arbitrary data. */
- 0, /* Number of methods supported (none). */
- { 0 }, /* Array of methods (none) */
- &aux_tsx_init, /* init_module() */
- NULL, /* start_module() */
- NULL, /* deinit_module() */
- &aux_tsx_handler, /* tsx_handler() */
-};
-
-PJ_DEF(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- int timeout,
- void *token,
- void (*cb)(void*,pjsip_event*))
-{
- pjsip_transaction *tsx;
- struct aux_tsx_data *tsx_data;
- pj_status_t status;
-
- status = pjsip_endpt_create_tsx(endpt, &tsx);
- if (!tsx) {
- pjsip_tx_data_dec_ref(tdata);
- return -1;
- }
-
- tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct aux_tsx_data));
- tsx_data->token = token;
- tsx_data->cb = cb;
- tsx->module_data[aux_mod_id] = tsx_data;
-
- if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
- pjsip_endpt_destroy_tsx(endpt, tsx);
- pjsip_tx_data_dec_ref(tdata);
- return -1;
- }
-
- pjsip_endpt_register_tsx(endpt, tsx);
- pjsip_tx_data_invalidate_msg(tdata);
- pjsip_tsx_on_tx_msg(tsx, tdata);
- pjsip_tx_data_dec_ref(tdata);
- return 0;
-}
-
-/*
- * Initialize transmit data (msg) with the headers and optional body.
- * This will just put the headers in the message as it is. Be carefull
- * when calling this function because once a header is put in a message,
- * it CAN NOT be put in other message until the first message is deleted,
- * because the way the header is put in the list.
- * That's why the session will shallow_clone it's headers before calling
- * this function.
- */
-static void init_request_throw( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- pjsip_method *method,
- pjsip_uri *param_target,
- pjsip_from_hdr *param_from,
- pjsip_to_hdr *param_to,
- pjsip_contact_hdr *param_contact,
- pjsip_cid_hdr *param_call_id,
- pjsip_cseq_hdr *param_cseq,
- const pj_str_t *param_text)
-{
- pjsip_msg *msg;
- pjsip_msg_body *body;
- const pjsip_hdr *endpt_hdr;
-
- /* Create the message. */
- msg = tdata->msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
-
- /* Init request URI. */
- pj_memcpy(&msg->line.req.method, method, sizeof(*method));
- msg->line.req.uri = param_target;
-
- /* Add additional request headers from endpoint. */
- endpt_hdr = pjsip_endpt_get_request_headers(endpt)->next;
- while (endpt_hdr != pjsip_endpt_get_request_headers(endpt)) {
- pjsip_hdr *hdr = pjsip_hdr_shallow_clone(tdata->pool, endpt_hdr);
- pjsip_msg_add_hdr( tdata->msg, hdr );
- endpt_hdr = endpt_hdr->next;
- }
-
- /* Add From header. */
- if (param_from->tag.slen == 0)
- pj_create_unique_string(tdata->pool, ¶m_from->tag);
- pjsip_msg_add_hdr(msg, (void*)param_from);
-
- /* Add To header. */
- pjsip_msg_add_hdr(msg, (void*)param_to);
-
- /* Add Contact header. */
- if (param_contact) {
- pjsip_msg_add_hdr(msg, (void*)param_contact);
- }
-
- /* Add Call-ID header. */
- pjsip_msg_add_hdr(msg, (void*)param_call_id);
-
- /* Add CSeq header. */
- pjsip_msg_add_hdr(msg, (void*)param_cseq);
-
- /* Create message body. */
- if (param_text) {
- body = pj_pool_calloc(tdata->pool, 1, sizeof(pjsip_msg_body));
- body->content_type.type = str_TEXT;
- body->content_type.subtype = str_PLAIN;
- body->data = pj_pool_alloc(tdata->pool, param_text->slen );
- pj_memcpy(body->data, param_text->ptr, param_text->slen);
- body->len = param_text->slen;
- body->print_body = &pjsip_print_text_body;
- msg->body = body;
- }
-}
-
-/*
- * Create arbitrary request.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt,
- const pjsip_method *method,
- const pj_str_t *param_target,
- const pj_str_t *param_from,
- const pj_str_t *param_to,
- const pj_str_t *param_contact,
- const pj_str_t *param_call_id,
- int param_cseq,
- const pj_str_t *param_text,
- pjsip_tx_data **p_tdata)
-{
- pjsip_uri *target;
- pjsip_tx_data *tdata;
- pjsip_from_hdr *from;
- pjsip_to_hdr *to;
- pjsip_contact_hdr *contact;
- pjsip_cseq_hdr *cseq = NULL; /* = NULL, warning in VC6 */
- pjsip_cid_hdr *call_id;
- pj_str_t tmp;
- pj_status_t status;
- PJ_USE_EXCEPTION;
-
- PJ_LOG(5,(THIS_FILE, "Entering pjsip_endpt_create_request()"));
-
- status = pjsip_endpt_create_tdata(endpt, &tdata);
- if (status != PJ_SUCCESS)
- return status;
-
- /* Init reference counter to 1. */
- pjsip_tx_data_add_ref(tdata);
-
- PJ_TRY {
- /* Request target. */
- pj_strdup_with_null(tdata->pool, &tmp, param_target);
- target = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, 0);
- if (target == NULL) {
- PJ_LOG(4,(THIS_FILE, "Error creating request: invalid target %s",
- tmp.ptr));
- goto on_error;
- }
-
- /* From */
- from = pjsip_from_hdr_create(tdata->pool);
- pj_strdup_with_null(tdata->pool, &tmp, param_from);
- from->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (from->uri == NULL) {
- PJ_LOG(4,(THIS_FILE, "Error creating request: invalid 'From' URI '%s'",
- tmp.ptr));
- goto on_error;
- }
- pj_create_unique_string(tdata->pool, &from->tag);
-
- /* To */
- to = pjsip_to_hdr_create(tdata->pool);
- pj_strdup_with_null(tdata->pool, &tmp, param_to);
- to->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (to->uri == NULL) {
- PJ_LOG(4,(THIS_FILE, "Error creating request: invalid 'To' URI '%s'",
- tmp.ptr));
- goto on_error;
- }
-
- /* Contact. */
- if (param_contact) {
- contact = pjsip_contact_hdr_create(tdata->pool);
- pj_strdup_with_null(tdata->pool, &tmp, param_contact);
- contact->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (contact->uri == NULL) {
- PJ_LOG(4,(THIS_FILE,
- "Error creating request: invalid 'Contact' URI '%s'",
- tmp.ptr));
- goto on_error;
- }
- } else {
- contact = NULL;
- }
-
- /* Call-ID */
- call_id = pjsip_cid_hdr_create(tdata->pool);
- if (param_call_id != NULL && param_call_id->slen)
- pj_strdup(tdata->pool, &call_id->id, param_call_id);
- else
- pj_create_unique_string(tdata->pool, &call_id->id);
-
- /* CSeq */
- cseq = pjsip_cseq_hdr_create(tdata->pool);
- if (param_cseq >= 0)
- cseq->cseq = param_cseq;
- else
- cseq->cseq = pj_rand() & 0xFFFF;
-
- /* Method */
- pjsip_method_copy(tdata->pool, &cseq->method, method);
-
- /* Create the request. */
- init_request_throw( endpt, tdata, &cseq->method, target, from, to,
- contact, call_id, cseq, param_text);
- }
- PJ_DEFAULT {
- status = PJ_ENOMEM;
- goto on_error;
- }
- PJ_END
-
- PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.",
- tdata->obj_name,
- cseq->cseq,
- cseq->method.name.slen,
- cseq->method.name.ptr));
-
- *p_tdata = tdata;
- return PJ_SUCCESS;
-
-on_error:
- pjsip_tx_data_dec_ref(tdata);
- return status;
-}
-
-PJ_DEF(pj_status_t)
-pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
- const pjsip_method *method,
- const pjsip_uri *param_target,
- const pjsip_from_hdr *param_from,
- const pjsip_to_hdr *param_to,
- const pjsip_contact_hdr *param_contact,
- const pjsip_cid_hdr *param_call_id,
- int param_cseq,
- const pj_str_t *param_text,
- pjsip_tx_data **p_tdata)
-{
- pjsip_uri *target;
- pjsip_tx_data *tdata;
- pjsip_from_hdr *from;
- pjsip_to_hdr *to;
- pjsip_contact_hdr *contact;
- pjsip_cid_hdr *call_id;
- pjsip_cseq_hdr *cseq = NULL; /* The NULL because warning in VC6 */
- pj_status_t status;
- PJ_USE_EXCEPTION;
-
- PJ_LOG(5,(THIS_FILE, "Entering pjsip_endpt_create_request_from_hdr()"));
-
- status = pjsip_endpt_create_tdata(endpt, &tdata);
- if (status != PJ_SUCCESS)
- return status;
-
- pjsip_tx_data_add_ref(tdata);
-
- PJ_TRY {
- target = pjsip_uri_clone(tdata->pool, param_target);
- from = pjsip_hdr_shallow_clone(tdata->pool, param_from);
- pjsip_fromto_set_from(from);
- to = pjsip_hdr_shallow_clone(tdata->pool, param_to);
- pjsip_fromto_set_to(to);
- if (param_contact)
- contact = pjsip_hdr_shallow_clone(tdata->pool, param_contact);
- else
- contact = NULL;
- call_id = pjsip_hdr_shallow_clone(tdata->pool, param_call_id);
- cseq = pjsip_cseq_hdr_create(tdata->pool);
- if (param_cseq >= 0)
- cseq->cseq = param_cseq;
- else
- cseq->cseq = pj_rand() % 0xFFFF;
- pjsip_method_copy(tdata->pool, &cseq->method, method);
-
- init_request_throw(endpt, tdata, &cseq->method, target, from, to,
- contact, call_id, cseq, param_text);
- }
- PJ_DEFAULT {
- status = PJ_ENOMEM;
- goto on_error;
- }
- PJ_END;
-
- PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.",
- tdata->obj_name,
- cseq->cseq,
- cseq->method.name.slen,
- cseq->method.name.ptr));
-
- *p_tdata = tdata;
- return PJ_SUCCESS;
-
-on_error:
- pjsip_tx_data_dec_ref(tdata);
- return status;
-}
-
-/*
- * Construct a minimal response message for the received request.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
- const pjsip_rx_data *rdata,
- int code,
- pjsip_tx_data **p_tdata)
-{
- pjsip_tx_data *tdata;
- pjsip_msg *msg, *req_msg;
- pjsip_hdr *hdr;
- pjsip_via_hdr *via;
- pjsip_rr_hdr *rr;
- pj_status_t status;
-
- /* rdata must be a request message. */
- req_msg = rdata->msg_info.msg;
- pj_assert(req_msg->type == PJSIP_REQUEST_MSG);
-
- /* Log this action. */
- PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_response(rdata=%p, code=%d)",
- rdata, code));
-
- /* Create a new transmit buffer. */
- status = pjsip_endpt_create_tdata( endpt, &tdata);
- if (status != PJ_SUCCESS)
- return status;
-
- /* Create new response message. */
- tdata->msg = msg = pjsip_msg_create(tdata->pool, PJSIP_RESPONSE_MSG);
-
- /* Set status code and reason text. */
- msg->line.status.code = code;
- msg->line.status.reason = *pjsip_get_status_text(code);
-
- /* Set TX data attributes. */
- tdata->rx_timestamp = rdata->pkt_info.timestamp;
-
- /* Copy all the via headers, in order. */
- via = rdata->msg_info.via;
- while (via) {
- pjsip_msg_add_hdr( msg, pjsip_hdr_clone(tdata->pool, via));
- via = via->next;
- if (via != (void*)&req_msg->hdr)
- via = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, via);
- else
- break;
- }
-
- /* Copy all Record-Route headers, in order. */
- rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, NULL);
- while (rr) {
- pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, rr));
- rr = rr->next;
- if (rr != (void*)&req_msg->hdr)
- rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, rr);
- else
- break;
- }
-
- /* Copy Call-ID header. */
- hdr = pjsip_msg_find_hdr( req_msg, PJSIP_H_CALL_ID, NULL);
- pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, hdr));
-
- /* Copy From header. */
- hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.from);
- pjsip_msg_add_hdr( msg, hdr);
-
- /* Copy To header. */
- hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.to);
- pjsip_msg_add_hdr( msg, hdr);
-
- /* Copy CSeq header. */
- hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.cseq);
- pjsip_msg_add_hdr( msg, hdr);
-
- /* All done. */
- *p_tdata = tdata;
- return PJ_SUCCESS;
-}
-
-
-/*
- * Construct ACK for 3xx-6xx final response (according to chapter 17.1.1 of
- * RFC3261). Note that the generation of ACK for 2xx response is different,
- * and one must not use this function to generate such ACK.
- */
-PJ_DEF(void) pjsip_endpt_create_ack(pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- const pjsip_rx_data *rdata )
-{
- pjsip_msg *ack_msg, *invite_msg;
- pjsip_to_hdr *to;
- pjsip_from_hdr *from;
- pjsip_cseq_hdr *cseq;
- pjsip_hdr *hdr;
-
- /* Make compiler happy. */
- PJ_UNUSED_ARG(endpt);
-
- /* rdata must be a final response. */
- pj_assert(rdata->msg_info.msg->type==PJSIP_RESPONSE_MSG &&
- rdata->msg_info.msg->line.status.code >= 300);
-
- /* Log this action. */
- PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_ack(rdata=%p)", rdata));
-
- /* Create new request message. */
- ack_msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
- pjsip_method_set( &ack_msg->line.req.method, PJSIP_ACK_METHOD );
-
- /* The original INVITE message. */
- invite_msg = tdata->msg;
-
- /* Copy Request-Uri from the original INVITE. */
- ack_msg->line.req.uri = invite_msg->line.req.uri;
-
- /* Copy Call-ID from the original INVITE */
- hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_CALL_ID, NULL);
- pjsip_msg_add_hdr( ack_msg, hdr );
-
- /* Copy From header from the original INVITE. */
- from = (pjsip_from_hdr*)pjsip_msg_find_remove_hdr(invite_msg,
- PJSIP_H_FROM, NULL);
- pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)from );
-
- /* Copy To header from the original INVITE. */
- to = (pjsip_to_hdr*)pjsip_msg_find_remove_hdr( invite_msg,
- PJSIP_H_TO, NULL);
- pj_strdup(tdata->pool, &to->tag, &rdata->msg_info.to->tag);
- pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)to );
-
- /* Must contain single Via, just as the original INVITE. */
- hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_VIA, NULL);
- pjsip_msg_insert_first_hdr( ack_msg, hdr );
-
- /* Must have the same CSeq value as the original INVITE, but method
- * changed to ACK
- */
- cseq = (pjsip_cseq_hdr*) pjsip_msg_find_remove_hdr( invite_msg,
- PJSIP_H_CSEQ, NULL);
- pjsip_method_set( &cseq->method, PJSIP_ACK_METHOD );
- pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*) cseq );
-
- /* If the original INVITE has Route headers, those header fields MUST
- * appear in the ACK.
- */
- hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
- while (hdr != NULL) {
- pjsip_msg_add_hdr( ack_msg, hdr );
- hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
- }
-
- /* Set the message in the "tdata" to point to the ACK message. */
- tdata->msg = ack_msg;
-
- /* Reset transmit packet buffer, to force 're-printing' of message. */
- tdata->buf.cur = tdata->buf.start;
-
- /* We're done.
- * "tdata" parameter now contains the ACK message.
- */
-}
-
-
-/*
- * Construct CANCEL request for the previously sent request, according to
- * chapter 9.1 of RFC3261.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
- pjsip_tx_data *req_tdata,
- pjsip_tx_data **p_tdata)
-{
- pjsip_msg *req_msg; /* the original request. */
- pjsip_tx_data *cancel_tdata;
- pjsip_msg *cancel_msg;
- pjsip_hdr *hdr;
- pjsip_cseq_hdr *req_cseq, *cseq;
- pjsip_uri *req_uri;
- pj_status_t status;
-
- /* Log this action. */
- PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_cancel(tdata=%p)", req_tdata));
-
- /* Get the original request. */
- req_msg = req_tdata->msg;
-
- /* The transmit buffer must INVITE request. */
- PJ_ASSERT_RETURN(req_msg->type == PJSIP_REQUEST_MSG &&
- req_msg->line.req.method.id == PJSIP_INVITE_METHOD,
- PJ_EINVAL);
-
- /* Create new transmit buffer. */
- status = pjsip_endpt_create_tdata( endpt, &cancel_tdata);
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- /* Create CANCEL request message. */
- cancel_msg = pjsip_msg_create(cancel_tdata->pool, PJSIP_REQUEST_MSG);
- cancel_tdata->msg = cancel_msg;
-
- /* Request-URI, Call-ID, From, To, and the numeric part of the CSeq are
- * copied from the original request.
- */
- /* Set request line. */
- pjsip_method_set(&cancel_msg->line.req.method, PJSIP_CANCEL_METHOD);
- req_uri = req_msg->line.req.uri;
- cancel_msg->line.req.uri = pjsip_uri_clone(cancel_tdata->pool, req_uri);
-
- /* Copy Call-ID */
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_CALL_ID, NULL);
- pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
-
- /* Copy From header. */
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_FROM, NULL);
- pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
-
- /* Copy To header. */
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_TO, NULL);
- pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
-
- /* Create new CSeq with equal number, but method set to CANCEL. */
- req_cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(req_msg, PJSIP_H_CSEQ, NULL);
- cseq = pjsip_cseq_hdr_create(cancel_tdata->pool);
- cseq->cseq = req_cseq->cseq;
- pjsip_method_set(&cseq->method, PJSIP_CANCEL_METHOD);
- pjsip_msg_add_hdr(cancel_msg, (pjsip_hdr*)cseq);
-
- /* Must only have single Via which matches the top-most Via in the
- * request being cancelled.
- */
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, NULL);
- pjsip_msg_insert_first_hdr(cancel_msg,
- pjsip_hdr_clone(cancel_tdata->pool, hdr));
-
- /* If the original request has Route header, the CANCEL request must also
- * has exactly the same.
- * Copy "Route" header from the request.
- */
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, NULL);
- while (hdr != NULL) {
- pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
- hdr = hdr->next;
- if (hdr != &cancel_msg->hdr)
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, hdr);
- else
- break;
- }
-
- /* Done.
- * Return the transmit buffer containing the CANCEL request.
- */
- *p_tdata = cancel_tdata;
- return PJ_SUCCESS;
-}
-
-/* Get the address parameters (host, port, flag, TTL, etc) to send the
- * response.
- */
-PJ_DEF(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool,
- const pjsip_transport *req_transport,
- const pjsip_via_hdr *via,
- pjsip_host_port *send_addr)
-{
- /* Determine the destination address (section 18.2.2):
- * - for TCP, SCTP, or TLS, send the response using the transport where
- * the request was received.
- * - if maddr parameter is present, send to this address using the port
- * in sent-by or 5060. If multicast is used, the TTL in the Via must
- * be used, or 1 if ttl parameter is not present.
- * - otherwise if received parameter is present, set to this address.
- * - otherwise send to the address in sent-by.
- */
- send_addr->flag = req_transport->flag;
- send_addr->type = req_transport->type;
-
- if (PJSIP_TRANSPORT_IS_RELIABLE(req_transport)) {
- const pj_sockaddr_in *remote_addr;
- remote_addr = &req_transport->rem_addr;
- pj_strdup2(pool, &send_addr->host,
- pj_inet_ntoa(remote_addr->sin_addr));
- send_addr->port = pj_sockaddr_in_get_port(remote_addr);
-
- } else {
- /* Set the host part */
- if (via->maddr_param.slen) {
- pj_strdup(pool, &send_addr->host, &via->maddr_param);
- } else if (via->recvd_param.slen) {
- pj_strdup(pool, &send_addr->host, &via->recvd_param);
- } else {
- pj_strdup(pool, &send_addr->host, &via->sent_by.host);
- }
-
- /* Set the port */
- send_addr->port = via->sent_by.port;
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Get the event string from the event ID.
- */
-PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e)
-{
- return event_str[e];
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjsip/sip_util.h> +#include <pjsip/sip_transport.h> +#include <pjsip/sip_msg.h> +#include <pjsip/sip_endpoint.h> +#include <pjsip/sip_event.h> +#include <pjsip/sip_transaction.h> +#include <pjsip/sip_module.h> +#include <pj/log.h> +#include <pj/string.h> +#include <pj/guid.h> +#include <pj/pool.h> +#include <pj/except.h> +#include <pj/rand.h> +#include <pj/assert.h> +#include <pj/errno.h> + +#define THIS_FILE "endpoint" + +static const char *event_str[] = +{ + "UNIDENTIFIED", + "TIMER", + "TX_MSG", + "RX_MSG", + "TRANSPORT_ERROR", + "TSX_STATE", + "RX_2XX_RESPONSE", + "RX_ACK", + "DISCARD_MSG", + "USER", + "BEFORE_TX", +}; + +static pj_str_t str_TEXT = { "text", 4}, + str_PLAIN = { "plain", 5 }; +static int aux_mod_id; + +struct aux_tsx_data +{ + void *token; + void (*cb)(void*,pjsip_event*); +}; + +static pj_status_t aux_tsx_init( pjsip_endpoint *endpt, + struct pjsip_module *mod, pj_uint32_t id ) +{ + PJ_UNUSED_ARG(endpt); + PJ_UNUSED_ARG(mod); + + aux_mod_id = id; + return 0; +} + +static void aux_tsx_handler( struct pjsip_module *mod, pjsip_event *event ) +{ + pjsip_transaction *tsx; + struct aux_tsx_data *tsx_data; + + PJ_UNUSED_ARG(mod); + + if (event->type != PJSIP_EVENT_TSX_STATE) + return; + + pj_assert(event->body.tsx_state.tsx != NULL); + tsx = event->body.tsx_state.tsx; + if (tsx == NULL) + return; + if (tsx->module_data[aux_mod_id] == NULL) + return; + if (tsx->status_code < 200) + return; + + /* Call the callback, if any, and prevent the callback to be called again + * by clearing the transaction's module_data. + */ + tsx_data = tsx->module_data[aux_mod_id]; + tsx->module_data[aux_mod_id] = NULL; + + if (tsx_data->cb) { + (*tsx_data->cb)(tsx_data->token, event); + } +} + +pjsip_module aux_tsx_module = +{ + { "Aux-Tsx", 7}, /* Name. */ + 0, /* Flag */ + 128, /* Priority */ + NULL, /* Arbitrary data. */ + 0, /* Number of methods supported (none). */ + { 0 }, /* Array of methods (none) */ + &aux_tsx_init, /* init_module() */ + NULL, /* start_module() */ + NULL, /* deinit_module() */ + &aux_tsx_handler, /* tsx_handler() */ +}; + +PJ_DEF(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt, + pjsip_tx_data *tdata, + int timeout, + void *token, + void (*cb)(void*,pjsip_event*)) +{ + pjsip_transaction *tsx; + struct aux_tsx_data *tsx_data; + pj_status_t status; + + status = pjsip_endpt_create_tsx(endpt, &tsx); + if (!tsx) { + pjsip_tx_data_dec_ref(tdata); + return -1; + } + + tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct aux_tsx_data)); + tsx_data->token = token; + tsx_data->cb = cb; + tsx->module_data[aux_mod_id] = tsx_data; + + if (pjsip_tsx_init_uac(tsx, tdata) != 0) { + pjsip_endpt_destroy_tsx(endpt, tsx); + pjsip_tx_data_dec_ref(tdata); + return -1; + } + + pjsip_endpt_register_tsx(endpt, tsx); + pjsip_tx_data_invalidate_msg(tdata); + pjsip_tsx_on_tx_msg(tsx, tdata); + pjsip_tx_data_dec_ref(tdata); + return 0; +} + +/* + * Initialize transmit data (msg) with the headers and optional body. + * This will just put the headers in the message as it is. Be carefull + * when calling this function because once a header is put in a message, + * it CAN NOT be put in other message until the first message is deleted, + * because the way the header is put in the list. + * That's why the session will shallow_clone it's headers before calling + * this function. + */ +static void init_request_throw( pjsip_endpoint *endpt, + pjsip_tx_data *tdata, + pjsip_method *method, + pjsip_uri *param_target, + pjsip_from_hdr *param_from, + pjsip_to_hdr *param_to, + pjsip_contact_hdr *param_contact, + pjsip_cid_hdr *param_call_id, + pjsip_cseq_hdr *param_cseq, + const pj_str_t *param_text) +{ + pjsip_msg *msg; + pjsip_msg_body *body; + const pjsip_hdr *endpt_hdr; + + /* Create the message. */ + msg = tdata->msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); + + /* Init request URI. */ + pj_memcpy(&msg->line.req.method, method, sizeof(*method)); + msg->line.req.uri = param_target; + + /* Add additional request headers from endpoint. */ + endpt_hdr = pjsip_endpt_get_request_headers(endpt)->next; + while (endpt_hdr != pjsip_endpt_get_request_headers(endpt)) { + pjsip_hdr *hdr = pjsip_hdr_shallow_clone(tdata->pool, endpt_hdr); + pjsip_msg_add_hdr( tdata->msg, hdr ); + endpt_hdr = endpt_hdr->next; + } + + /* Add From header. */ + if (param_from->tag.slen == 0) + pj_create_unique_string(tdata->pool, ¶m_from->tag); + pjsip_msg_add_hdr(msg, (void*)param_from); + + /* Add To header. */ + pjsip_msg_add_hdr(msg, (void*)param_to); + + /* Add Contact header. */ + if (param_contact) { + pjsip_msg_add_hdr(msg, (void*)param_contact); + } + + /* Add Call-ID header. */ + pjsip_msg_add_hdr(msg, (void*)param_call_id); + + /* Add CSeq header. */ + pjsip_msg_add_hdr(msg, (void*)param_cseq); + + /* Create message body. */ + if (param_text) { + body = pj_pool_calloc(tdata->pool, 1, sizeof(pjsip_msg_body)); + body->content_type.type = str_TEXT; + body->content_type.subtype = str_PLAIN; + body->data = pj_pool_alloc(tdata->pool, param_text->slen ); + pj_memcpy(body->data, param_text->ptr, param_text->slen); + body->len = param_text->slen; + body->print_body = &pjsip_print_text_body; + msg->body = body; + } +} + +/* + * Create arbitrary request. + */ +PJ_DEF(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt, + const pjsip_method *method, + const pj_str_t *param_target, + const pj_str_t *param_from, + const pj_str_t *param_to, + const pj_str_t *param_contact, + const pj_str_t *param_call_id, + int param_cseq, + const pj_str_t *param_text, + pjsip_tx_data **p_tdata) +{ + pjsip_uri *target; + pjsip_tx_data *tdata; + pjsip_from_hdr *from; + pjsip_to_hdr *to; + pjsip_contact_hdr *contact; + pjsip_cseq_hdr *cseq = NULL; /* = NULL, warning in VC6 */ + pjsip_cid_hdr *call_id; + pj_str_t tmp; + pj_status_t status; + PJ_USE_EXCEPTION; + + PJ_LOG(5,(THIS_FILE, "Entering pjsip_endpt_create_request()")); + + status = pjsip_endpt_create_tdata(endpt, &tdata); + if (status != PJ_SUCCESS) + return status; + + /* Init reference counter to 1. */ + pjsip_tx_data_add_ref(tdata); + + PJ_TRY { + /* Request target. */ + pj_strdup_with_null(tdata->pool, &tmp, param_target); + target = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, 0); + if (target == NULL) { + PJ_LOG(4,(THIS_FILE, "Error creating request: invalid target %s", + tmp.ptr)); + goto on_error; + } + + /* From */ + from = pjsip_from_hdr_create(tdata->pool); + pj_strdup_with_null(tdata->pool, &tmp, param_from); + from->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (from->uri == NULL) { + PJ_LOG(4,(THIS_FILE, "Error creating request: invalid 'From' URI '%s'", + tmp.ptr)); + goto on_error; + } + pj_create_unique_string(tdata->pool, &from->tag); + + /* To */ + to = pjsip_to_hdr_create(tdata->pool); + pj_strdup_with_null(tdata->pool, &tmp, param_to); + to->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (to->uri == NULL) { + PJ_LOG(4,(THIS_FILE, "Error creating request: invalid 'To' URI '%s'", + tmp.ptr)); + goto on_error; + } + + /* Contact. */ + if (param_contact) { + contact = pjsip_contact_hdr_create(tdata->pool); + pj_strdup_with_null(tdata->pool, &tmp, param_contact); + contact->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (contact->uri == NULL) { + PJ_LOG(4,(THIS_FILE, + "Error creating request: invalid 'Contact' URI '%s'", + tmp.ptr)); + goto on_error; + } + } else { + contact = NULL; + } + + /* Call-ID */ + call_id = pjsip_cid_hdr_create(tdata->pool); + if (param_call_id != NULL && param_call_id->slen) + pj_strdup(tdata->pool, &call_id->id, param_call_id); + else + pj_create_unique_string(tdata->pool, &call_id->id); + + /* CSeq */ + cseq = pjsip_cseq_hdr_create(tdata->pool); + if (param_cseq >= 0) + cseq->cseq = param_cseq; + else + cseq->cseq = pj_rand() & 0xFFFF; + + /* Method */ + pjsip_method_copy(tdata->pool, &cseq->method, method); + + /* Create the request. */ + init_request_throw( endpt, tdata, &cseq->method, target, from, to, + contact, call_id, cseq, param_text); + } + PJ_DEFAULT { + status = PJ_ENOMEM; + goto on_error; + } + PJ_END + + PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.", + tdata->obj_name, + cseq->cseq, + cseq->method.name.slen, + cseq->method.name.ptr)); + + *p_tdata = tdata; + return PJ_SUCCESS; + +on_error: + pjsip_tx_data_dec_ref(tdata); + return status; +} + +PJ_DEF(pj_status_t) +pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt, + const pjsip_method *method, + const pjsip_uri *param_target, + const pjsip_from_hdr *param_from, + const pjsip_to_hdr *param_to, + const pjsip_contact_hdr *param_contact, + const pjsip_cid_hdr *param_call_id, + int param_cseq, + const pj_str_t *param_text, + pjsip_tx_data **p_tdata) +{ + pjsip_uri *target; + pjsip_tx_data *tdata; + pjsip_from_hdr *from; + pjsip_to_hdr *to; + pjsip_contact_hdr *contact; + pjsip_cid_hdr *call_id; + pjsip_cseq_hdr *cseq = NULL; /* The NULL because warning in VC6 */ + pj_status_t status; + PJ_USE_EXCEPTION; + + PJ_LOG(5,(THIS_FILE, "Entering pjsip_endpt_create_request_from_hdr()")); + + status = pjsip_endpt_create_tdata(endpt, &tdata); + if (status != PJ_SUCCESS) + return status; + + pjsip_tx_data_add_ref(tdata); + + PJ_TRY { + target = pjsip_uri_clone(tdata->pool, param_target); + from = pjsip_hdr_shallow_clone(tdata->pool, param_from); + pjsip_fromto_set_from(from); + to = pjsip_hdr_shallow_clone(tdata->pool, param_to); + pjsip_fromto_set_to(to); + if (param_contact) + contact = pjsip_hdr_shallow_clone(tdata->pool, param_contact); + else + contact = NULL; + call_id = pjsip_hdr_shallow_clone(tdata->pool, param_call_id); + cseq = pjsip_cseq_hdr_create(tdata->pool); + if (param_cseq >= 0) + cseq->cseq = param_cseq; + else + cseq->cseq = pj_rand() % 0xFFFF; + pjsip_method_copy(tdata->pool, &cseq->method, method); + + init_request_throw(endpt, tdata, &cseq->method, target, from, to, + contact, call_id, cseq, param_text); + } + PJ_DEFAULT { + status = PJ_ENOMEM; + goto on_error; + } + PJ_END; + + PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.", + tdata->obj_name, + cseq->cseq, + cseq->method.name.slen, + cseq->method.name.ptr)); + + *p_tdata = tdata; + return PJ_SUCCESS; + +on_error: + pjsip_tx_data_dec_ref(tdata); + return status; +} + +/* + * Construct a minimal response message for the received request. + */ +PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt, + const pjsip_rx_data *rdata, + int code, + pjsip_tx_data **p_tdata) +{ + pjsip_tx_data *tdata; + pjsip_msg *msg, *req_msg; + pjsip_hdr *hdr; + pjsip_via_hdr *via; + pjsip_rr_hdr *rr; + pj_status_t status; + + /* rdata must be a request message. */ + req_msg = rdata->msg_info.msg; + pj_assert(req_msg->type == PJSIP_REQUEST_MSG); + + /* Log this action. */ + PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_response(rdata=%p, code=%d)", + rdata, code)); + + /* Create a new transmit buffer. */ + status = pjsip_endpt_create_tdata( endpt, &tdata); + if (status != PJ_SUCCESS) + return status; + + /* Create new response message. */ + tdata->msg = msg = pjsip_msg_create(tdata->pool, PJSIP_RESPONSE_MSG); + + /* Set status code and reason text. */ + msg->line.status.code = code; + msg->line.status.reason = *pjsip_get_status_text(code); + + /* Set TX data attributes. */ + tdata->rx_timestamp = rdata->pkt_info.timestamp; + + /* Copy all the via headers, in order. */ + via = rdata->msg_info.via; + while (via) { + pjsip_msg_add_hdr( msg, pjsip_hdr_clone(tdata->pool, via)); + via = via->next; + if (via != (void*)&req_msg->hdr) + via = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, via); + else + break; + } + + /* Copy all Record-Route headers, in order. */ + rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, NULL); + while (rr) { + pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, rr)); + rr = rr->next; + if (rr != (void*)&req_msg->hdr) + rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, rr); + else + break; + } + + /* Copy Call-ID header. */ + hdr = pjsip_msg_find_hdr( req_msg, PJSIP_H_CALL_ID, NULL); + pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, hdr)); + + /* Copy From header. */ + hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.from); + pjsip_msg_add_hdr( msg, hdr); + + /* Copy To header. */ + hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.to); + pjsip_msg_add_hdr( msg, hdr); + + /* Copy CSeq header. */ + hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.cseq); + pjsip_msg_add_hdr( msg, hdr); + + /* All done. */ + *p_tdata = tdata; + return PJ_SUCCESS; +} + + +/* + * Construct ACK for 3xx-6xx final response (according to chapter 17.1.1 of + * RFC3261). Note that the generation of ACK for 2xx response is different, + * and one must not use this function to generate such ACK. + */ +PJ_DEF(void) pjsip_endpt_create_ack(pjsip_endpoint *endpt, + pjsip_tx_data *tdata, + const pjsip_rx_data *rdata ) +{ + pjsip_msg *ack_msg, *invite_msg; + pjsip_to_hdr *to; + pjsip_from_hdr *from; + pjsip_cseq_hdr *cseq; + pjsip_hdr *hdr; + + /* Make compiler happy. */ + PJ_UNUSED_ARG(endpt); + + /* rdata must be a final response. */ + pj_assert(rdata->msg_info.msg->type==PJSIP_RESPONSE_MSG && + rdata->msg_info.msg->line.status.code >= 300); + + /* Log this action. */ + PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_ack(rdata=%p)", rdata)); + + /* Create new request message. */ + ack_msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); + pjsip_method_set( &ack_msg->line.req.method, PJSIP_ACK_METHOD ); + + /* The original INVITE message. */ + invite_msg = tdata->msg; + + /* Copy Request-Uri from the original INVITE. */ + ack_msg->line.req.uri = invite_msg->line.req.uri; + + /* Copy Call-ID from the original INVITE */ + hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_CALL_ID, NULL); + pjsip_msg_add_hdr( ack_msg, hdr ); + + /* Copy From header from the original INVITE. */ + from = (pjsip_from_hdr*)pjsip_msg_find_remove_hdr(invite_msg, + PJSIP_H_FROM, NULL); + pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)from ); + + /* Copy To header from the original INVITE. */ + to = (pjsip_to_hdr*)pjsip_msg_find_remove_hdr( invite_msg, + PJSIP_H_TO, NULL); + pj_strdup(tdata->pool, &to->tag, &rdata->msg_info.to->tag); + pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)to ); + + /* Must contain single Via, just as the original INVITE. */ + hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_VIA, NULL); + pjsip_msg_insert_first_hdr( ack_msg, hdr ); + + /* Must have the same CSeq value as the original INVITE, but method + * changed to ACK + */ + cseq = (pjsip_cseq_hdr*) pjsip_msg_find_remove_hdr( invite_msg, + PJSIP_H_CSEQ, NULL); + pjsip_method_set( &cseq->method, PJSIP_ACK_METHOD ); + pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*) cseq ); + + /* If the original INVITE has Route headers, those header fields MUST + * appear in the ACK. + */ + hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL); + while (hdr != NULL) { + pjsip_msg_add_hdr( ack_msg, hdr ); + hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL); + } + + /* Set the message in the "tdata" to point to the ACK message. */ + tdata->msg = ack_msg; + + /* Reset transmit packet buffer, to force 're-printing' of message. */ + tdata->buf.cur = tdata->buf.start; + + /* We're done. + * "tdata" parameter now contains the ACK message. + */ +} + + +/* + * Construct CANCEL request for the previously sent request, according to + * chapter 9.1 of RFC3261. + */ +PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt, + pjsip_tx_data *req_tdata, + pjsip_tx_data **p_tdata) +{ + pjsip_msg *req_msg; /* the original request. */ + pjsip_tx_data *cancel_tdata; + pjsip_msg *cancel_msg; + pjsip_hdr *hdr; + pjsip_cseq_hdr *req_cseq, *cseq; + pjsip_uri *req_uri; + pj_status_t status; + + /* Log this action. */ + PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_cancel(tdata=%p)", req_tdata)); + + /* Get the original request. */ + req_msg = req_tdata->msg; + + /* The transmit buffer must INVITE request. */ + PJ_ASSERT_RETURN(req_msg->type == PJSIP_REQUEST_MSG && + req_msg->line.req.method.id == PJSIP_INVITE_METHOD, + PJ_EINVAL); + + /* Create new transmit buffer. */ + status = pjsip_endpt_create_tdata( endpt, &cancel_tdata); + if (status != PJ_SUCCESS) { + return status; + } + + /* Create CANCEL request message. */ + cancel_msg = pjsip_msg_create(cancel_tdata->pool, PJSIP_REQUEST_MSG); + cancel_tdata->msg = cancel_msg; + + /* Request-URI, Call-ID, From, To, and the numeric part of the CSeq are + * copied from the original request. + */ + /* Set request line. */ + pjsip_method_set(&cancel_msg->line.req.method, PJSIP_CANCEL_METHOD); + req_uri = req_msg->line.req.uri; + cancel_msg->line.req.uri = pjsip_uri_clone(cancel_tdata->pool, req_uri); + + /* Copy Call-ID */ + hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_CALL_ID, NULL); + pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr)); + + /* Copy From header. */ + hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_FROM, NULL); + pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr)); + + /* Copy To header. */ + hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_TO, NULL); + pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr)); + + /* Create new CSeq with equal number, but method set to CANCEL. */ + req_cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(req_msg, PJSIP_H_CSEQ, NULL); + cseq = pjsip_cseq_hdr_create(cancel_tdata->pool); + cseq->cseq = req_cseq->cseq; + pjsip_method_set(&cseq->method, PJSIP_CANCEL_METHOD); + pjsip_msg_add_hdr(cancel_msg, (pjsip_hdr*)cseq); + + /* Must only have single Via which matches the top-most Via in the + * request being cancelled. + */ + hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, NULL); + pjsip_msg_insert_first_hdr(cancel_msg, + pjsip_hdr_clone(cancel_tdata->pool, hdr)); + + /* If the original request has Route header, the CANCEL request must also + * has exactly the same. + * Copy "Route" header from the request. + */ + hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, NULL); + while (hdr != NULL) { + pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr)); + hdr = hdr->next; + if (hdr != &cancel_msg->hdr) + hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, hdr); + else + break; + } + + /* Done. + * Return the transmit buffer containing the CANCEL request. + */ + *p_tdata = cancel_tdata; + return PJ_SUCCESS; +} + +/* Get the address parameters (host, port, flag, TTL, etc) to send the + * response. + */ +PJ_DEF(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool, + const pjsip_transport *req_transport, + const pjsip_via_hdr *via, + pjsip_host_port *send_addr) +{ + /* Determine the destination address (section 18.2.2): + * - for TCP, SCTP, or TLS, send the response using the transport where + * the request was received. + * - if maddr parameter is present, send to this address using the port + * in sent-by or 5060. If multicast is used, the TTL in the Via must + * be used, or 1 if ttl parameter is not present. + * - otherwise if received parameter is present, set to this address. + * - otherwise send to the address in sent-by. + */ + send_addr->flag = req_transport->flag; + send_addr->type = req_transport->type; + + if (PJSIP_TRANSPORT_IS_RELIABLE(req_transport)) { + const pj_sockaddr_in *remote_addr; + remote_addr = &req_transport->rem_addr; + pj_strdup2(pool, &send_addr->host, + pj_inet_ntoa(remote_addr->sin_addr)); + send_addr->port = pj_sockaddr_in_get_port(remote_addr); + + } else { + /* Set the host part */ + if (via->maddr_param.slen) { + pj_strdup(pool, &send_addr->host, &via->maddr_param); + } else if (via->recvd_param.slen) { + pj_strdup(pool, &send_addr->host, &via->recvd_param); + } else { + pj_strdup(pool, &send_addr->host, &via->sent_by.host); + } + + /* Set the port */ + send_addr->port = via->sent_by.port; + } + + return PJ_SUCCESS; +} + +/* + * Get the event string from the event ID. + */ +PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e) +{ + return event_str[e]; +} + diff --git a/pjsip/src/pjsua/getopt.c b/pjsip/src/pjsua/getopt.c index d2812f2b..b522c579 100644 --- a/pjsip/src/pjsua/getopt.c +++ b/pjsip/src/pjsua/getopt.c @@ -1,1061 +1,1061 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifdef _MSC_VER
-/* in VC this file will generate a lot of warning about old style function
- * declarations.
- */
-# pragma warning(push, 3)
-#endif
-
-/*
- * getopt entry points
- *
- * modified by Mike Borella <mike_borella@mw.3com.com>
- *
- * $Id: getopt.c,v 1.4 2000/10/30 22:06:03 mborella Exp $
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#ifndef HAVE_GETOPT_LONG
-
-/* getopt_long and getopt_long_only entry points for GNU getopt.
- Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
-
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with the GNU C Library; see the file COPYING.LIB. If not,
- write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
-
-#include "getopt.h"
-
-
-#include <stdio.h>
-
-/* Comment out all this code if we are using the GNU C Library, and are not
- actually compiling the library itself. This code is part of the GNU C
- Library, but also included in many other GNU distributions. Compiling
- and linking in this code is a waste when using the GNU C library
- (especially if it is a shared library). Rather than having every GNU
- program understand `configure --with-gnu-libc' and omit the object files,
- it is simpler to just do this in the source for each such file. */
-
-#define GETOPT_INTERFACE_VERSION 2
-#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
-#include <gnu-versions.h>
-#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
-#define ELIDE_CODE
-#endif
-#endif
-
-#ifndef ELIDE_CODE
-
-
-/* This needs to come after some library #include
- to get __GNU_LIBRARY__ defined. */
-#ifdef __GNU_LIBRARY__
-#include <stdlib.h>
-#endif
-
-#ifndef NULL
-#define NULL 0
-#endif
-
-int
-getopt_long (int argc, char *const *argv, const char *options,
- const struct option *long_options, int *opt_index)
-{
- return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
-}
-
-/* Like getopt_long, but '-' as well as '--' can indicate a long option.
- If an option that starts with '-' (not '--') doesn't match a long option,
- but does match a short option, it is parsed as a short option
- instead. */
-
-int
-getopt_long_only (argc, argv, options, long_options, opt_index)
- int argc;
- char *const *argv;
- const char *options;
- const struct option *long_options;
- int *opt_index;
-{
- return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
-}
-
-int
-getopt (int argc, char * const * argv, const char * optstring)
-{
- return _getopt_internal (argc, argv, optstring,
- (const struct option *) 0,
- (int *) 0,
- 0);
-}
-
-#endif /* Not ELIDE_CODE. */
-
-
-#ifndef _NO_PROTO
-#define _NO_PROTO
-#endif
-
-
-//#include <strings.h>
-
-/* Comment out all this code if we are using the GNU C Library, and are not
- actually compiling the library itself. This code is part of the GNU C
- Library, but also included in many other GNU distributions. Compiling
- and linking in this code is a waste when using the GNU C library
- (especially if it is a shared library). Rather than having every GNU
- program understand `configure --with-gnu-libc' and omit the object files,
- it is simpler to just do this in the source for each such file. */
-
-#define GETOPT_INTERFACE_VERSION 2
-#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
-#include <gnu-versions.h>
-#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
-#define ELIDE_CODE
-#endif
-#endif
-
-#ifndef ELIDE_CODE
-
-
-/* This needs to come after some library #include
- to get __GNU_LIBRARY__ defined. */
-#ifdef __GNU_LIBRARY__
-/* Don't include stdlib.h for non-GNU C libraries because some of them
- contain conflicting prototypes for getopt. */
-#include <stdlib.h>
-#include <unistd.h>
-#endif /* GNU C library. */
-
-#ifdef VMS
-#include <unixlib.h>
-#if HAVE_STRING_H - 0
-#include <string.h>
-#endif
-#endif
-
-#if defined (WIN32) && !defined (__CYGWIN32__)
-/* It's not Unix, really. See? Capital letters. */
-#include <windows.h>
-#define getpid() GetCurrentProcessId()
-#endif
-
-#ifndef _
-/* This is for other GNU distributions with internationalized messages.
- When compiling libc, the _ macro is predefined. */
-#ifdef HAVE_LIBINTL_H
-# include <libintl.h>
-# define _(msgid) gettext (msgid)
-#else
-# define _(msgid) (msgid)
-#endif
-#endif
-
-/* This version of `getopt' appears to the caller like standard Unix `getopt'
- but it behaves differently for the user, since it allows the user
- to intersperse the options with the other arguments.
-
- As `getopt' works, it permutes the elements of ARGV so that,
- when it is done, all the options precede everything else. Thus
- all application programs are extended to handle flexible argument order.
-
- Setting the environment variable POSIXLY_CORRECT disables permutation.
- Then the behavior is completely standard.
-
- GNU application programs can use a third alternative mode in which
- they can distinguish the relative order of options and other arguments. */
-
-#include "getopt.h"
-
-/* For communication from `getopt' to the caller.
- When `getopt' finds an option that takes an argument,
- the argument value is returned here.
- Also, when `ordering' is RETURN_IN_ORDER,
- each non-option ARGV-element is returned here. */
-
-char *optarg = NULL;
-
-/* Index in ARGV of the next element to be scanned.
- This is used for communication to and from the caller
- and for communication between successive calls to `getopt'.
-
- On entry to `getopt', zero means this is the first call; initialize.
-
- When `getopt' returns -1, this is the index of the first of the
- non-option elements that the caller should itself scan.
-
- Otherwise, `optind' communicates from one call to the next
- how much of ARGV has been scanned so far. */
-
-/* 1003.2 says this must be 1 before any call. */
-int optind = 1;
-
-/* Formerly, initialization of getopt depended on optind==0, which
- causes problems with re-calling getopt as programs generally don't
- know that. */
-
-int __getopt_initialized = 0;
-
-/* The next char to be scanned in the option-element
- in which the last option character we returned was found.
- This allows us to pick up the scan where we left off.
-
- If this is zero, or a null string, it means resume the scan
- by advancing to the next ARGV-element. */
-
-static char *nextchar;
-
-/* Callers store zero here to inhibit the error message
- for unrecognized options. */
-
-int opterr = 1;
-
-/* Set to an option character which was unrecognized.
- This must be initialized on some systems to avoid linking in the
- system's own getopt implementation. */
-
-int optopt = '?';
-
-/* Describe how to deal with options that follow non-option ARGV-elements.
-
- If the caller did not specify anything,
- the default is REQUIRE_ORDER if the environment variable
- POSIXLY_CORRECT is defined, PERMUTE otherwise.
-
- REQUIRE_ORDER means don't recognize them as options;
- stop option processing when the first non-option is seen.
- This is what Unix does.
- This mode of operation is selected by either setting the environment
- variable POSIXLY_CORRECT, or using `+' as the first character
- of the list of option characters.
-
- PERMUTE is the default. We permute the contents of ARGV as we scan,
- so that eventually all the non-options are at the end. This allows options
- to be given in any order, even with programs that were not written to
- expect this.
-
- RETURN_IN_ORDER is an option available to programs that were written
- to expect options and other ARGV-elements in any order and that care about
- the ordering of the two. We describe each non-option ARGV-element
- as if it were the argument of an option with character code 1.
- Using `-' as the first character of the list of option characters
- selects this mode of operation.
-
- The special argument `--' forces an end of option-scanning regardless
- of the value of `ordering'. In the case of RETURN_IN_ORDER, only
- `--' can cause `getopt' to return -1 with `optind' != ARGC. */
-
-static enum
-{
- REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
-} ordering;
-
-/* Value of POSIXLY_CORRECT environment variable. */
-static char *posixly_correct;
-
-#ifdef __GNU_LIBRARY__
-/* We want to avoid inclusion of string.h with non-GNU libraries
- because there are many ways it can cause trouble.
- On some systems, it contains special magic macros that don't work
- in GCC. */
-#include <string.h>
-#define my_index pj_native_strchr
-#else
-
-static char *
-my_index (const char *str, int chr)
-{
- while (*str)
- {
- if (*str == chr)
- return (char *) str;
- str++;
- }
- return 0;
-}
-
-/* If using GCC, we can safely declare strlen this way.
- If not using GCC, it is ok not to declare it. */
-#ifdef __GNUC__
-/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
- That was relevant to code that was here before. */
-#if !defined (__STDC__) || !__STDC__
-/* gcc with -traditional declares the built-in strlen to return int,
- and has done so at least since version 2.4.5. -- rms. */
-extern int strlen (const char *);
-#endif /* not __STDC__ */
-#endif /* __GNUC__ */
-
-#endif /* not __GNU_LIBRARY__ */
-
-/* Handle permutation of arguments. */
-
-/* Describe the part of ARGV that contains non-options that have
- been skipped. `first_nonopt' is the index in ARGV of the first of them;
- `last_nonopt' is the index after the last of them. */
-
-static int first_nonopt;
-static int last_nonopt;
-
-#ifdef _LIBC
-/* Bash 2.0 gives us an environment variable containing flags
- indicating ARGV elements that should not be considered arguments. */
-
-/* Defined in getopt_init.c */
-extern char *__getopt_nonoption_flags;
-
-static int nonoption_flags_max_len;
-static int nonoption_flags_len;
-
-static int original_argc;
-static char *const *original_argv;
-
-extern pid_t __libc_pid;
-
-/* Make sure the environment variable bash 2.0 puts in the environment
- is valid for the getopt call we must make sure that the ARGV passed
- to getopt is that one passed to the process. */
-static void
-__attribute__ ((unused))
-store_args_and_env (int argc, char *const *argv)
-{
- /* XXX This is no good solution. We should rather copy the args so
- that we can compare them later. But we must not use malloc(3). */
- original_argc = argc;
- original_argv = argv;
-}
-text_set_element (__libc_subinit, store_args_and_env);
-
-# define SWAP_FLAGS(ch1, ch2) \
- if (nonoption_flags_len > 0) \
- { \
- char __tmp = __getopt_nonoption_flags[ch1]; \
- __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
- __getopt_nonoption_flags[ch2] = __tmp; \
- }
-#else /* !_LIBC */
-# define SWAP_FLAGS(ch1, ch2)
-#endif /* _LIBC */
-
-/* Exchange two adjacent subsequences of ARGV.
- One subsequence is elements [first_nonopt,last_nonopt)
- which contains all the non-options that have been skipped so far.
- The other is elements [last_nonopt,optind), which contains all
- the options processed since those non-options were skipped.
-
- `first_nonopt' and `last_nonopt' are relocated so that they describe
- the new indices of the non-options in ARGV after they are moved. */
-
-#if defined (__STDC__) && __STDC__
-static void exchange (char **);
-#endif
-
-static void
-exchange (argv)
- char **argv;
-{
- int bottom = first_nonopt;
- int middle = last_nonopt;
- int top = optind;
- char *tem;
-
- /* Exchange the shorter segment with the far end of the longer segment.
- That puts the shorter segment into the right place.
- It leaves the longer segment in the right place overall,
- but it consists of two parts that need to be swapped next. */
-
-#ifdef _LIBC
- /* First make sure the handling of the `__getopt_nonoption_flags'
- string can work normally. Our top argument must be in the range
- of the string. */
- if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
- {
- /* We must extend the array. The user plays games with us and
- presents new arguments. */
- char *new_str = malloc (top + 1);
- if (new_str == NULL)
- nonoption_flags_len = nonoption_flags_max_len = 0;
- else
- {
- memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len);
- memset (&new_str[nonoption_flags_max_len], '\0',
- top + 1 - nonoption_flags_max_len);
- nonoption_flags_max_len = top + 1;
- __getopt_nonoption_flags = new_str;
- }
- }
-#endif
-
- while (top > middle && middle > bottom)
- {
- if (top - middle > middle - bottom)
- {
- /* Bottom segment is the short one. */
- int len = middle - bottom;
- register int i;
-
- /* Swap it with the top part of the top segment. */
- for (i = 0; i < len; i++)
- {
- tem = argv[bottom + i];
- argv[bottom + i] = argv[top - (middle - bottom) + i];
- argv[top - (middle - bottom) + i] = tem;
- SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
- }
- /* Exclude the moved bottom segment from further swapping. */
- top -= len;
- }
- else
- {
- /* Top segment is the short one. */
- int len = top - middle;
- register int i;
-
- /* Swap it with the bottom part of the bottom segment. */
- for (i = 0; i < len; i++)
- {
- tem = argv[bottom + i];
- argv[bottom + i] = argv[middle + i];
- argv[middle + i] = tem;
- SWAP_FLAGS (bottom + i, middle + i);
- }
- /* Exclude the moved top segment from further swapping. */
- bottom += len;
- }
- }
-
- /* Update records for the slots the non-options now occupy. */
-
- first_nonopt += (optind - last_nonopt);
- last_nonopt = optind;
-}
-
-/* Initialize the internal data when the first call is made. */
-
-#if defined (__STDC__) && __STDC__
-static const char *_getopt_initialize (int, char *const *, const char *);
-#endif
-static const char *
-_getopt_initialize (argc, argv, optstring)
- int argc;
- char *const *argv;
- const char *optstring;
-{
- /* Start processing options with ARGV-element 1 (since ARGV-element 0
- is the program name); the sequence of previously skipped
- non-option ARGV-elements is empty. */
-
- first_nonopt = last_nonopt = optind;
-
- nextchar = NULL;
-
- posixly_correct = getenv ("POSIXLY_CORRECT");
-
- /* Determine how to handle the ordering of options and nonoptions. */
-
- if (optstring[0] == '-')
- {
- ordering = RETURN_IN_ORDER;
- ++optstring;
- }
- else if (optstring[0] == '+')
- {
- ordering = REQUIRE_ORDER;
- ++optstring;
- }
- else if (posixly_correct != NULL)
- ordering = REQUIRE_ORDER;
- else
- ordering = PERMUTE;
-
-#ifdef _LIBC
- if (posixly_correct == NULL
- && argc == original_argc && argv == original_argv)
- {
- if (nonoption_flags_max_len == 0)
- {
- if (__getopt_nonoption_flags == NULL
- || __getopt_nonoption_flags[0] == '\0')
- nonoption_flags_max_len = -1;
- else
- {
- const char *orig_str = __getopt_nonoption_flags;
- int len = nonoption_flags_max_len = strlen (orig_str);
- if (nonoption_flags_max_len < argc)
- nonoption_flags_max_len = argc;
- __getopt_nonoption_flags =
- (char *) malloc (nonoption_flags_max_len);
- if (__getopt_nonoption_flags == NULL)
- nonoption_flags_max_len = -1;
- else
- {
- memcpy (__getopt_nonoption_flags, orig_str, len);
- memset (&__getopt_nonoption_flags[len], '\0',
- nonoption_flags_max_len - len);
- }
- }
- }
- nonoption_flags_len = nonoption_flags_max_len;
- }
- else
- nonoption_flags_len = 0;
-#endif
-
- return optstring;
-}
-
-/* Scan elements of ARGV (whose length is ARGC) for option characters
- given in OPTSTRING.
-
- If an element of ARGV starts with '-', and is not exactly "-" or "--",
- then it is an option element. The characters of this element
- (aside from the initial '-') are option characters. If `getopt'
- is called repeatedly, it returns successively each of the option characters
- from each of the option elements.
-
- If `getopt' finds another option character, it returns that character,
- updating `optind' and `nextchar' so that the next call to `getopt' can
- resume the scan with the following option character or ARGV-element.
-
- If there are no more option characters, `getopt' returns -1.
- Then `optind' is the index in ARGV of the first ARGV-element
- that is not an option. (The ARGV-elements have been permuted
- so that those that are not options now come last.)
-
- OPTSTRING is a string containing the legitimate option characters.
- If an option character is seen that is not listed in OPTSTRING,
- return '?' after printing an error message. If you set `opterr' to
- zero, the error message is suppressed but we still return '?'.
-
- If a char in OPTSTRING is followed by a colon, that means it wants an arg,
- so the following text in the same ARGV-element, or the text of the following
- ARGV-element, is returned in `optarg'. Two colons mean an option that
- wants an optional arg; if there is text in the current ARGV-element,
- it is returned in `optarg', otherwise `optarg' is set to zero.
-
- If OPTSTRING starts with `-' or `+', it requests different methods of
- handling the non-option ARGV-elements.
- See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
-
- Long-named options begin with `--' instead of `-'.
- Their names may be abbreviated as long as the abbreviation is unique
- or is an exact match for some defined option. If they have an
- argument, it follows the option name in the same ARGV-element, separated
- from the option name by a `=', or else the in next ARGV-element.
- When `getopt' finds a long-named option, it returns 0 if that option's
- `flag' field is nonzero, the value of the option's `val' field
- if the `flag' field is zero.
-
- The elements of ARGV aren't really const, because we permute them.
- But we pretend they're const in the prototype to be compatible
- with other systems.
-
- LONGOPTS is a vector of `struct option' terminated by an
- element containing a name which is zero.
-
- LONGIND returns the index in LONGOPT of the long-named option found.
- It is only valid when a long-named option has been found by the most
- recent call.
-
- If LONG_ONLY is nonzero, '-' as well as '--' can introduce
- long-named options. */
-
-int
-_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
- int argc;
- char *const *argv;
- const char *optstring;
- const struct option *longopts;
- int *longind;
- int long_only;
-{
- optarg = NULL;
-
- if (optind == 0 || !__getopt_initialized)
- {
- if (optind == 0)
- optind = 1; /* Don't scan ARGV[0], the program name. */
- optstring = _getopt_initialize (argc, argv, optstring);
- __getopt_initialized = 1;
- }
-
- /* Test whether ARGV[optind] points to a non-option argument.
- Either it does not have option syntax, or there is an environment flag
- from the shell indicating it is not an option. The later information
- is only used when the used in the GNU libc. */
-#ifdef _LIBC
-#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \
- || (optind < nonoption_flags_len \
- && __getopt_nonoption_flags[optind] == '1'))
-#else
-#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
-#endif
-
- if (nextchar == NULL || *nextchar == '\0')
- {
- /* Advance to the next ARGV-element. */
-
- /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
- moved back by the user (who may also have changed the arguments). */
- if (last_nonopt > optind)
- last_nonopt = optind;
- if (first_nonopt > optind)
- first_nonopt = optind;
-
- if (ordering == PERMUTE)
- {
- /* If we have just processed some options following some non-options,
- exchange them so that the options come first. */
-
- if (first_nonopt != last_nonopt && last_nonopt != optind)
- exchange ((char **) argv);
- else if (last_nonopt != optind)
- first_nonopt = optind;
-
- /* Skip any additional non-options
- and extend the range of non-options previously skipped. */
-
- while (optind < argc && NONOPTION_P)
- optind++;
- last_nonopt = optind;
- }
-
- /* The special ARGV-element `--' means premature end of options.
- Skip it like a null option,
- then exchange with previous non-options as if it were an option,
- then skip everything else like a non-option. */
-
- if (optind != argc && !pj_native_strcmp(argv[optind], "--"))
- {
- optind++;
-
- if (first_nonopt != last_nonopt && last_nonopt != optind)
- exchange ((char **) argv);
- else if (first_nonopt == last_nonopt)
- first_nonopt = optind;
- last_nonopt = argc;
-
- optind = argc;
- }
-
- /* If we have done all the ARGV-elements, stop the scan
- and back over any non-options that we skipped and permuted. */
-
- if (optind == argc)
- {
- /* Set the next-arg-index to point at the non-options
- that we previously skipped, so the caller will digest them. */
- if (first_nonopt != last_nonopt)
- optind = first_nonopt;
- return -1;
- }
-
- /* If we have come to a non-option and did not permute it,
- either stop the scan or describe it to the caller and pass it by. */
-
- if (NONOPTION_P)
- {
- if (ordering == REQUIRE_ORDER)
- return -1;
- optarg = argv[optind++];
- return 1;
- }
-
- /* We have found another option-ARGV-element.
- Skip the initial punctuation. */
-
- nextchar = (argv[optind] + 1
- + (longopts != NULL && argv[optind][1] == '-'));
- }
-
- /* Decode the current option-ARGV-element. */
-
- /* Check whether the ARGV-element is a long option.
-
- If long_only and the ARGV-element has the form "-f", where f is
- a valid short option, don't consider it an abbreviated form of
- a long option that starts with f. Otherwise there would be no
- way to give the -f short option.
-
- On the other hand, if there's a long option "fubar" and
- the ARGV-element is "-fu", do consider that an abbreviation of
- the long option, just like "--fu", and not "-f" with arg "u".
-
- This distinction seems to be the most useful approach. */
-
- if (longopts != NULL
- && (argv[optind][1] == '-'
- || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
- {
- char *nameend;
- const struct option *p;
- const struct option *pfound = NULL;
- int exact = 0;
- int ambig = 0;
- int indfound = -1;
- int option_index;
-
- for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
- /* Do nothing. */ ;
-
- /* Test all long options for either exact match
- or abbreviated matches. */
- for (p = longopts, option_index = 0; p->name; p++, option_index++)
- if (!strncmp (p->name, nextchar, nameend - nextchar))
- {
- if ((unsigned int) (nameend - nextchar)
- == (unsigned int) strlen (p->name))
- {
- /* Exact match found. */
- pfound = p;
- indfound = option_index;
- exact = 1;
- break;
- }
- else if (pfound == NULL)
- {
- /* First nonexact match found. */
- pfound = p;
- indfound = option_index;
- }
- else
- /* Second or later nonexact match found. */
- ambig = 1;
- }
-
- if (ambig && !exact)
- {
- if (opterr)
- fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
- argv[0], argv[optind]);
- nextchar += strlen (nextchar);
- optind++;
- optopt = 0;
- return '?';
- }
-
- if (pfound != NULL)
- {
- option_index = indfound;
- optind++;
- if (*nameend)
- {
- /* Don't test has_arg with >, because some C compilers don't
- allow it to be used on enums. */
- if (pfound->has_arg)
- optarg = nameend + 1;
- else
- {
- if (opterr)
- {
- if (argv[optind - 1][1] == '-')
- /* --option */
- fprintf (stderr,
- _("%s: option `--%s' doesn't allow an argument\n"),
- argv[0], pfound->name);
- else
- {
- /* +option or -option */
- fprintf (stderr,
- _("%s: option `%c%s' doesn't allow an argument\n"),
- argv[0], argv[optind - 1][0], pfound->name);
- }
- }
- nextchar += strlen (nextchar);
-
- optopt = pfound->val;
- return '?';
- }
- }
- else if (pfound->has_arg == 1)
- {
- if (optind < argc)
- optarg = argv[optind++];
- else
- {
- if (opterr)
- fprintf (stderr,
- _("%s: option `%s' requires an argument\n"),
- argv[0], argv[optind - 1]);
- nextchar += strlen (nextchar);
- optopt = pfound->val;
- return optstring[0] == ':' ? ':' : '?';
- }
- }
- nextchar += strlen (nextchar);
- if (longind != NULL)
- *longind = option_index;
- if (pfound->flag)
- {
- *(pfound->flag) = pfound->val;
- return 0;
- }
- return pfound->val;
- }
-
- /* Can't find it as a long option. If this is not getopt_long_only,
- or the option starts with '--' or is not a valid short
- option, then it's an error.
- Otherwise interpret it as a short option. */
- if (!long_only || argv[optind][1] == '-'
- || my_index (optstring, *nextchar) == NULL)
- {
- if (opterr)
- {
- if (argv[optind][1] == '-')
- /* --option */
- fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
- argv[0], nextchar);
- else
- /* +option or -option */
- fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
- argv[0], argv[optind][0], nextchar);
- }
- nextchar = (char *) "";
- optind++;
- optopt = 0;
- return '?';
- }
- }
-
- /* Look at and handle the next short option-character. */
-
- {
- char c = *nextchar++;
- char *temp = my_index (optstring, c);
-
- /* Increment `optind' when we start to process its last character. */
- if (*nextchar == '\0')
- ++optind;
-
- if (temp == NULL || c == ':')
- {
- if (opterr)
- {
- if (posixly_correct)
- /* 1003.2 specifies the format of this message. */
- fprintf (stderr, _("%s: illegal option -- %c\n"),
- argv[0], c);
- else
- fprintf (stderr, _("%s: invalid option -- %c\n"),
- argv[0], c);
- }
- optopt = c;
- return '?';
- }
- /* Convenience. Treat POSIX -W foo same as long option --foo */
- if (temp[0] == 'W' && temp[1] == ';')
- {
- char *nameend;
- const struct option *p;
- const struct option *pfound = NULL;
- int exact = 0;
- int ambig = 0;
- int indfound = 0;
- int option_index;
-
- /* This is an option that requires an argument. */
- if (*nextchar != '\0')
- {
- optarg = nextchar;
- /* If we end this ARGV-element by taking the rest as an arg,
- we must advance to the next element now. */
- optind++;
- }
- else if (optind == argc)
- {
- if (opterr)
- {
- /* 1003.2 specifies the format of this message. */
- fprintf (stderr, _("%s: option requires an argument -- %c\n"),
- argv[0], c);
- }
- optopt = c;
- if (optstring[0] == ':')
- c = ':';
- else
- c = '?';
- return c;
- }
- else
- /* We already incremented `optind' once;
- increment it again when taking next ARGV-elt as argument. */
- optarg = argv[optind++];
-
- /* optarg is now the argument, see if it's in the
- table of longopts. */
-
- for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
- /* Do nothing. */ ;
-
- /* Test all long options for either exact match
- or abbreviated matches. */
- for (p = longopts, option_index = 0; p->name; p++, option_index++)
- if (!strncmp (p->name, nextchar, nameend - nextchar))
- {
- if ((unsigned int) (nameend - nextchar) == strlen (p->name))
- {
- /* Exact match found. */
- pfound = p;
- indfound = option_index;
- exact = 1;
- break;
- }
- else if (pfound == NULL)
- {
- /* First nonexact match found. */
- pfound = p;
- indfound = option_index;
- }
- else
- /* Second or later nonexact match found. */
- ambig = 1;
- }
- if (ambig && !exact)
- {
- if (opterr)
- fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
- argv[0], argv[optind]);
- nextchar += strlen (nextchar);
- optind++;
- return '?';
- }
- if (pfound != NULL)
- {
- option_index = indfound;
- if (*nameend)
- {
- /* Don't test has_arg with >, because some C compilers don't
- allow it to be used on enums. */
- if (pfound->has_arg)
- optarg = nameend + 1;
- else
- {
- if (opterr)
- fprintf (stderr, _("\
-%s: option `-W %s' doesn't allow an argument\n"),
- argv[0], pfound->name);
-
- nextchar += strlen (nextchar);
- return '?';
- }
- }
- else if (pfound->has_arg == 1)
- {
- if (optind < argc)
- optarg = argv[optind++];
- else
- {
- if (opterr)
- fprintf (stderr,
- _("%s: option `%s' requires an argument\n"),
- argv[0], argv[optind - 1]);
- nextchar += strlen (nextchar);
- return optstring[0] == ':' ? ':' : '?';
- }
- }
- nextchar += strlen (nextchar);
- if (longind != NULL)
- *longind = option_index;
- if (pfound->flag)
- {
- *(pfound->flag) = pfound->val;
- return 0;
- }
- return pfound->val;
- }
- nextchar = NULL;
- return 'W'; /* Let the application handle it. */
- }
- if (temp[1] == ':')
- {
- if (temp[2] == ':')
- {
- /* This is an option that accepts an argument optionally. */
- if (*nextchar != '\0')
- {
- optarg = nextchar;
- optind++;
- }
- else
- optarg = NULL;
- nextchar = NULL;
- }
- else
- {
- /* This is an option that requires an argument. */
- if (*nextchar != '\0')
- {
- optarg = nextchar;
- /* If we end this ARGV-element by taking the rest as an arg,
- we must advance to the next element now. */
- optind++;
- }
- else if (optind == argc)
- {
- if (opterr)
- {
- /* 1003.2 specifies the format of this message. */
- fprintf (stderr,
- _("%s: option requires an argument -- %c\n"),
- argv[0], c);
- }
- optopt = c;
- if (optstring[0] == ':')
- c = ':';
- else
- c = '?';
- }
- else
- /* We already incremented `optind' once;
- increment it again when taking next ARGV-elt as argument. */
- optarg = argv[optind++];
- nextchar = NULL;
- }
- }
- return c;
- }
-}
-
-#endif /* Not ELIDE_CODE. */
-
-#endif
-
-#ifdef _MSC_VER
-# pragma warning(pop)
-#endif
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef _MSC_VER +/* in VC this file will generate a lot of warning about old style function + * declarations. + */ +# pragma warning(push, 3) +#endif + +/* + * getopt entry points + * + * modified by Mike Borella <mike_borella@mw.3com.com> + * + * $Id: getopt.c,v 1.4 2000/10/30 22:06:03 mborella Exp $ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef HAVE_GETOPT_LONG + +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "getopt.h" + + +#include <stdio.h> + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 +#include <gnu-versions.h> +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include <stdlib.h> +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (int argc, char *const *argv, const char *options, + const struct option *long_options, int *opt_index) +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + +int +getopt (int argc, char * const * argv, const char * optstring) +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + + +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + + +//#include <strings.h> + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 +#include <gnu-versions.h> +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include <stdlib.h> +#include <unistd.h> +#endif /* GNU C library. */ + +#ifdef VMS +#include <unixlib.h> +#if HAVE_STRING_H - 0 +#include <string.h> +#endif +#endif + +#if defined (WIN32) && !defined (__CYGWIN32__) +/* It's not Unix, really. See? Capital letters. */ +#include <windows.h> +#define getpid() GetCurrentProcessId() +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +#ifdef HAVE_LIBINTL_H +# include <libintl.h> +# define _(msgid) gettext (msgid) +#else +# define _(msgid) (msgid) +#endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include <string.h> +#define my_index pj_native_strchr +#else + +static char * +my_index (const char *str, int chr) +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +#if !defined (__STDC__) || !__STDC__ +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +#endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +extern pid_t __libc_pid; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void +__attribute__ ((unused)) +store_args_and_env (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} +text_set_element (__libc_subinit, store_args_and_env); + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined (__STDC__) && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len); + memset (&new_str[nonoption_flags_max_len], '\0', + top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined (__STDC__) && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + { + memcpy (__getopt_nonoption_flags, orig_str, len); + memset (&__getopt_nonoption_flags[len], '\0', + nonoption_flags_max_len - len); + } + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !pj_native_strcmp(argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + { + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + } + } + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +#endif /* Not ELIDE_CODE. */ + +#endif + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + diff --git a/pjsip/src/pjsua/getopt.h b/pjsip/src/pjsua/getopt.h index fbd8a3dd..3acdd5fc 100644 --- a/pjsip/src/pjsua/getopt.h +++ b/pjsip/src/pjsua/getopt.h @@ -1,141 +1,141 @@ -/* $Id$ */
-/* This file has now become GPL. */
-/* Declarations for getopt.
- Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
-
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with the GNU C Library; see the file COPYING.LIB. If not,
- write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
-
-#ifndef _GETOPT_H
-#define _GETOPT_H 1
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* For communication from `getopt' to the caller.
- When `getopt' finds an option that takes an argument,
- the argument value is returned here.
- Also, when `ordering' is RETURN_IN_ORDER,
- each non-option ARGV-element is returned here. */
-
-extern char *optarg;
-
-/* Index in ARGV of the next element to be scanned.
- This is used for communication to and from the caller
- and for communication between successive calls to `getopt'.
-
- On entry to `getopt', zero means this is the first call; initialize.
-
- When `getopt' returns -1, this is the index of the first of the
- non-option elements that the caller should itself scan.
-
- Otherwise, `optind' communicates from one call to the next
- how much of ARGV has been scanned so far. */
-
-extern int optind;
-
-/* Callers store zero here to inhibit the error message `getopt' prints
- for unrecognized options. */
-
-extern int opterr;
-
-/* Set to an option character which was unrecognized. */
-
-extern int optopt;
-
-/* Describe the long-named options requested by the application.
- The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
- of `struct option' terminated by an element containing a name which is
- zero.
-
- The field `has_arg' is:
- no_argument (or 0) if the option does not take an argument,
- required_argument (or 1) if the option requires an argument,
- optional_argument (or 2) if the option takes an optional argument.
-
- If the field `flag' is not NULL, it points to a variable that is set
- to the value given in the field `val' when the option is found, but
- left unchanged if the option is not found.
-
- To have a long-named option do something other than set an `int' to
- a compiled-in constant, such as set a value from `optarg', set the
- option's `flag' field to zero and its `val' field to a nonzero
- value (the equivalent single-letter option character, if there is
- one). For long options that have a zero `flag' field, `getopt'
- returns the contents of the `val' field. */
-
-struct option
-{
- const char *name;
- /* has_arg can't be an enum because some compilers complain about
- type mismatches in all the code that assumes it is an int. */
- int has_arg;
- int *flag;
- int val;
-};
-
-/* Names for the values of the `has_arg' field of `struct option'. */
-
-# define no_argument 0
-# define required_argument 1
-# define optional_argument 2
-
-
-/* Get definitions and prototypes for functions to process the
- arguments in ARGV (ARGC of them, minus the program name) for
- options given in OPTS.
-
- Return the option character from OPTS just read. Return -1 when
- there are no more options. For unrecognized options, or options
- missing arguments, `optopt' is set to the option letter, and '?' is
- returned.
-
- The OPTS string is a list of characters which are recognized option
- letters, optionally followed by colons, specifying that that letter
- takes an argument, to be placed in `optarg'.
-
- If a letter in OPTS is followed by two colons, its argument is
- optional. This behavior is specific to the GNU `getopt'.
-
- The argument `--' causes premature termination of argument
- scanning, explicitly telling `getopt' that there are no more
- options.
-
- If OPTS begins with `--', then non-option arguments are treated as
- arguments to the option '\0'. This behavior is specific to the GNU
- `getopt'. */
-
-int getopt (int argc, char *const *argv, const char *shortopts);
-
-int getopt_long (int argc, char *const *argv, const char *options,
- const struct option *longopts, int *longind);
-int getopt_long_only (int argc, char *const *argv,
- const char *shortopts,
- const struct option *longopts, int *longind);
-
-/* Internal only. Users should not call this directly. */
-int _getopt_internal (int argc, char *const *argv,
- const char *shortopts,
- const struct option *longopts, int *longind,
- int long_only);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* getopt.h */
-
+/* $Id$ */ +/* This file has now become GPL. */ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ + const char *name; + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +# define no_argument 0 +# define required_argument 1 +# define optional_argument 2 + + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, `optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in `optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `getopt' that there are no more + options. + + If OPTS begins with `--', then non-option arguments are treated as + arguments to the option '\0'. This behavior is specific to the GNU + `getopt'. */ + +int getopt (int argc, char *const *argv, const char *shortopts); + +int getopt_long (int argc, char *const *argv, const char *options, + const struct option *longopts, int *longind); +int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); + +#ifdef __cplusplus +} +#endif + +#endif /* getopt.h */ + diff --git a/pjsip/src/pjsua/main.c b/pjsip/src/pjsua/main.c index e4d1e1c8..09f9f8f3 100644 --- a/pjsip/src/pjsua/main.c +++ b/pjsip/src/pjsua/main.c @@ -1,1827 +1,1827 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjlib.h>
-#include <pjsip_core.h>
-#include <pjsip_ua.h>
-#include <pjsip_simple.h>
-#include <pjmedia.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <pj/stun.h>
-
-#define START_PORT 5060
-#define MAX_BUDDIES 32
-#define THIS_FILE "main.c"
-#define MAX_PRESENTITY 32
-#define PRESENCE_TIMEOUT 60
-
-/* By default we'll have one worker thread, except when threading
- * is disabled.
- */
-#if PJ_HAS_THREADS
-# define WORKER_COUNT 1
-#else
-# define WORKER_COUNT 0
-#endif
-
-/* Global variable. */
-static struct
-{
- /* Control. */
- pj_pool_factory *pf;
- pjsip_endpoint *endpt;
- pj_pool_t *pool;
- pjsip_user_agent *user_agent;
- int worker_cnt;
- int worker_quit_flag;
-
- /* User info. */
- char user_id[64];
- pj_str_t local_uri;
- pj_str_t contact;
- pj_str_t real_contact;
-
- /* Dialog. */
- pjsip_dlg *cur_dlg;
-
- /* Authentication. */
- int cred_count;
- pjsip_cred_info cred_info[4];
-
- /* Media stack. */
- pj_bool_t null_audio;
- pj_med_mgr_t *mmgr;
-
- /* Misc. */
- int app_log_level;
- char *log_filename;
- FILE *log_file;
-
- /* Proxy URLs */
- pj_str_t proxy;
- pj_str_t outbound_proxy;
-
- /* UA auto options. */
- int auto_answer; /* -1 to disable. */
- int auto_hangup; /* -1 to disable */
-
- /* Registration. */
- pj_str_t registrar_uri;
- pjsip_regc *regc;
- pj_int32_t reg_timeout;
- pj_timer_entry regc_timer;
-
- /* STUN */
- pj_str_t stun_srv1;
- int stun_port1;
- pj_str_t stun_srv2;
- int stun_port2;
-
- /* UDP sockets and their public address. */
- int sip_port;
- pj_sock_t sip_sock;
- pj_sockaddr_in sip_sock_name;
- pj_sock_t rtp_sock;
- pj_sockaddr_in rtp_sock_name;
- pj_sock_t rtcp_sock;
- pj_sockaddr_in rtcp_sock_name;
-
- /* SIMPLE */
- pj_bool_t hide_status;
- pj_bool_t offer_x_ms_msg;
- int im_counter;
- int buddy_cnt;
- pj_str_t buddy[MAX_BUDDIES];
- pj_bool_t buddy_status[MAX_BUDDIES];
- pj_bool_t no_presence;
- pjsip_presentity *buddy_pres[MAX_BUDDIES];
-
- int pres_cnt;
- pjsip_presentity *pres[MAX_PRESENTITY];
-
-} global;
-
-enum { AUTO_ANSWER, AUTO_HANGUP };
-
-/* This is the data that will be 'attached' on per dialog basis. */
-struct dialog_data
-{
- /* Media session. */
- pj_media_session_t *msession;
-
- /* x-ms-chat session. */
- pj_bool_t x_ms_msg_session;
-
- /* Cached SDP body, updated when media session changed. */
- pjsip_msg_body *body;
-
- /* Timer. */
- pj_bool_t has_auto_timer;
- pj_timer_entry auto_timer;
-};
-
-/*
- * These are the callbacks to be registered to dialog to receive notifications
- * about various events in the dialog.
- */
-static void dlg_on_all_events (pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt,
- pjsip_event *event );
-static void dlg_on_before_tx (pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata, int retransmission);
-static void dlg_on_tx_msg (pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata);
-static void dlg_on_rx_msg (pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_rx_data *rdata);
-static void dlg_on_incoming (pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_rx_data *rdata);
-static void dlg_on_calling (pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata);
-static void dlg_on_provisional (pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_event *event);
-static void dlg_on_connecting (pjsip_dlg *dlg, pjsip_event *event);
-static void dlg_on_established (pjsip_dlg *dlg, pjsip_event *event);
-static void dlg_on_disconnected (pjsip_dlg *dlg, pjsip_event *event);
-static void dlg_on_terminated (pjsip_dlg *dlg);
-static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event);
-
-/* The callback structure that will be registered to UA layer. */
-struct pjsip_dlg_callback dlg_callback = {
- &dlg_on_all_events,
- &dlg_on_before_tx,
- &dlg_on_tx_msg,
- &dlg_on_rx_msg,
- &dlg_on_incoming,
- &dlg_on_calling,
- &dlg_on_provisional,
- &dlg_on_connecting,
- &dlg_on_established,
- &dlg_on_disconnected,
- &dlg_on_terminated,
- &dlg_on_mid_call_evt
-};
-
-
-/*
- * Auxiliary things are put in misc.c, so that this main.c file is more
- * readable.
- */
-#include "misc.c"
-
-static void dlg_auto_timer_callback( pj_timer_heap_t *timer_heap,
- struct pj_timer_entry *entry)
-{
- pjsip_dlg *dlg = entry->user_data;
- struct dialog_data *dlg_data = dlg->user_data;
-
- PJ_UNUSED_ARG(timer_heap)
-
- dlg_data->has_auto_timer = 0;
-
- if (entry->id == AUTO_ANSWER) {
- pjsip_tx_data *tdata = pjsip_dlg_answer(dlg, 200);
- if (tdata) {
- struct dialog_data *dlg_data = global.cur_dlg->user_data;
- tdata->msg->body = dlg_data->body;
- pjsip_dlg_send_msg(dlg, tdata);
- }
- } else {
- pjsip_tx_data *tdata = pjsip_dlg_disconnect(dlg, 500);
- if (tdata)
- pjsip_dlg_send_msg(dlg, tdata);
- }
-}
-
-static void update_registration(pjsip_regc *regc, int renew)
-{
- pjsip_tx_data *tdata;
-
- PJ_LOG(3,(THIS_FILE, "Performing SIP registration..."));
-
- if (renew) {
- tdata = pjsip_regc_register(regc, 1);
- } else {
- tdata = pjsip_regc_unregister(regc);
- }
-
- pjsip_regc_send( regc, tdata );
-}
-
-static void regc_cb(struct pjsip_regc_cbparam *param)
-{
- /*
- * Print registration status.
- */
- if (param->code < 0 || param->code >= 300) {
- PJ_LOG(2, (THIS_FILE, "SIP registration failed, status=%d (%s)",
- param->code, pjsip_get_status_text(param->code)->ptr));
- global.regc = NULL;
-
- } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
- PJ_LOG(3, (THIS_FILE, "SIP registration success, status=%d (%s), "
- "will re-register in %d seconds",
- param->code,
- pjsip_get_status_text(param->code)->ptr,
- param->expiration));
-
- } else {
- PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code));
- }
-}
-
-static void pres_on_received_request(pjsip_presentity *pres, pjsip_rx_data *rdata,
- int *timeout)
-{
- int state;
- int i;
- char url[PJSIP_MAX_URL_SIZE];
- int urllen;
-
- PJ_UNUSED_ARG(rdata)
-
- if (*timeout > 0) {
- state = PJSIP_EVENT_SUB_STATE_ACTIVE;
- if (*timeout > 300)
- *timeout = 300;
- } else {
- state = PJSIP_EVENT_SUB_STATE_TERMINATED;
- }
-
- urllen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->from->uri, url, sizeof(url)-1);
- if (urllen < 1) {
- pj_native_strcpy(url, "<unknown>");
- } else {
- url[urllen] = '\0';
- }
- PJ_LOG(3,(THIS_FILE, "Received presence request from %s, sub_state=%s",
- url,
- (state==PJSIP_EVENT_SUB_STATE_ACTIVE?"active":"terminated")));
-
- for (i=0; i<global.pres_cnt; ++i)
- if (global.pres[i] == pres)
- break;
- if (i == global.pres_cnt)
- global.pres[global.pres_cnt++] = pres;
-
- pjsip_presence_set_credentials( pres, global.cred_count, global.cred_info );
- pjsip_presence_notify(pres, state, !global.hide_status);
-
-}
-
-static void pres_on_received_refresh(pjsip_presentity *pres, pjsip_rx_data *rdata)
-{
- pres_on_received_request(pres, rdata, &pres->sub->default_interval);
-}
-
-/* This is called by presence framework when we receives presence update
- * of a resource (buddy).
- */
-static void pres_on_received_update(pjsip_presentity *pres, pj_bool_t is_open)
-{
- int buddy_index = (int)pres->user_data;
-
- global.buddy_status[buddy_index] = is_open;
- PJ_LOG(3,(THIS_FILE, "Presence update: %s is %s",
- global.buddy[buddy_index].ptr,
- (is_open ? "Online" : "Offline")));
-}
-
-/* This is called when the subscription is terminated. */
-static void pres_on_terminated(pjsip_presentity *pres, const pj_str_t *reason)
-{
- if (pres->sub->role == PJSIP_ROLE_UAC) {
- int buddy_index = (int)pres->user_data;
- PJ_LOG(3,(THIS_FILE, "Presence subscription for %s is terminated (reason=%.*s)",
- global.buddy[buddy_index].ptr,
- reason->slen, reason->ptr));
- global.buddy_pres[buddy_index] = NULL;
- global.buddy_status[buddy_index] = 0;
- } else {
- int i;
- PJ_LOG(3,(THIS_FILE, "Notifier terminated (reason=%.*s)",
- reason->slen, reason->ptr));
- pjsip_presence_notify(pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 1);
- for (i=0; i<global.pres_cnt; ++i) {
- if (global.pres[i] == pres) {
- int j;
- global.pres[i] = NULL;
- for (j=i+1; j<global.pres_cnt; ++j)
- global.pres[j-1] = global.pres[j];
- global.pres_cnt--;
- break;
- }
- }
- }
- pjsip_presence_destroy(pres);
-}
-
-
-/* Callback attached to SIP body to print the body to message buffer. */
-static int print_msg_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size)
-{
- pjsip_msg_body *body = msg_body;
- return pjsdp_print ((pjsdp_session_desc*)body->data, buf, size);
-}
-
-/* When media session has changed, call this function to update the cached body
- * information in the dialog.
- */
-static pjsip_msg_body *create_msg_body (pjsip_dlg *dlg, pj_bool_t is_ack_msg)
-{
- struct dialog_data *dlg_data = dlg->user_data;
- pjsdp_session_desc *sdp;
-
- sdp = pj_media_session_create_sdp (dlg_data->msession, dlg->pool, is_ack_msg);
- if (!sdp) {
- dlg_data->body = NULL;
- return NULL;
- }
-
- /* For outgoing INVITE, if we offer "x-ms-message" line, then add a new
- * "m=" line in the SDP.
- */
- if (dlg_data->x_ms_msg_session >= 0 &&
- dlg_data->x_ms_msg_session >= (int)sdp->media_count)
- {
- pjsdp_media_desc *m = pj_pool_calloc(dlg->pool, 1, sizeof(*m));
- sdp->media[sdp->media_count] = m;
- dlg_data->x_ms_msg_session = sdp->media_count++;
- }
-
- /*
- * For "x-ms-message" line, remove all attributes and connection line etc.
- */
- if (dlg_data->x_ms_msg_session >= 0) {
- pjsdp_media_desc *m = sdp->media[dlg_data->x_ms_msg_session];
- if (m) {
- m->desc.media = pj_str("x-ms-message");
- m->desc.port = 5060;
- m->desc.transport = pj_str("sip");
- m->desc.fmt_count = 1;
- m->desc.fmt[0] = pj_str("null");
- m->attr_count = 0;
- m->conn = NULL;
- }
- }
-
- dlg_data->body = pj_pool_calloc(dlg->pool, 1, sizeof(*dlg_data->body));
- dlg_data->body->content_type.type = pj_str("application");
- dlg_data->body->content_type.subtype = pj_str("sdp");
- dlg_data->body->len = 0; /* ignored */
- dlg_data->body->print_body = &print_msg_body;
-
- dlg_data->body->data = sdp;
- return dlg_data->body;
-}
-
-/* This callback will be called on every occurence of events in dialogs */
-static void dlg_on_all_events(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt,
- pjsip_event *event )
-{
- PJ_UNUSED_ARG(dlg_evt)
- PJ_UNUSED_ARG(event)
-
- PJ_LOG(4, (THIS_FILE, "dlg_on_all_events %p", dlg));
-}
-
-/* This callback is called before each outgoing msg is sent (including
- * retransmission). Application can override this notification if it wants
- * to modify the message before transmission or if it wants to do something
- * else for each transmission.
- */
-static void dlg_on_before_tx(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata, int ret_cnt)
-{
- PJ_UNUSED_ARG(tsx)
- PJ_UNUSED_ARG(tdata)
-
- if (ret_cnt > 0) {
- PJ_LOG(3, (THIS_FILE, "Dialog %s: retransmitting message (cnt=%d)",
- dlg->obj_name, ret_cnt));
- }
-}
-
-/* This callback is called after a message is sent. */
-static void dlg_on_tx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata)
-{
- PJ_UNUSED_ARG(tsx)
- PJ_UNUSED_ARG(tdata)
-
- PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg));
-}
-
-/* This callback is called on receipt of incoming message. */
-static void dlg_on_rx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_rx_data *rdata)
-{
- PJ_UNUSED_ARG(tsx)
- PJ_UNUSED_ARG(rdata)
- PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg));
-}
-
-/* This callback is called after dialog has sent INVITE */
-static void dlg_on_calling(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata)
-{
- PJ_UNUSED_ARG(tsx)
- PJ_UNUSED_ARG(tdata)
-
- pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG &&
- tdata->msg->line.req.method.id == PJSIP_INVITE_METHOD &&
- tsx->method.id == PJSIP_INVITE_METHOD);
-
- PJ_LOG(3, (THIS_FILE, "Dialog %s: start calling...", dlg->obj_name));
-}
-
-static void create_session_from_sdp( pjsip_dlg *dlg, pjsdp_session_desc *sdp)
-{
- struct dialog_data *dlg_data = dlg->user_data;
- pj_bool_t sdp_x_ms_msg_index = -1;
- int i;
- int mcnt;
- const pj_media_stream_info *mi[PJSDP_MAX_MEDIA];
- int has_active;
- pj_media_sock_info sock_info;
-
- /* Find "m=x-ms-message" line in the SDP. */
- for (i=0; i<(int)sdp->media_count; ++i) {
- if (pj_stricmp2(&sdp->media[i]->desc.media, "x-ms-message")==0)
- sdp_x_ms_msg_index = i;
- }
-
- /*
- * Create media session.
- */
- pj_memset(&sock_info, 0, sizeof(sock_info));
- sock_info.rtp_sock = global.rtp_sock;
- sock_info.rtcp_sock = global.rtcp_sock;
- pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in));
-
- dlg_data->msession = pj_media_session_create_from_sdp (global.mmgr, sdp, &sock_info);
-
- /* A session will always be created, unless there is memory
- * alloc problem.
- */
- pj_assert(dlg_data->msession);
-
- /* See if we can take the offer by checking that we have at least
- * one media stream active.
- */
- mcnt = pj_media_session_enum_streams(dlg_data->msession, PJSDP_MAX_MEDIA, mi);
- for (i=0, has_active=0; i<mcnt; ++i) {
- if (mi[i]->fmt_cnt>0 && mi[i]->dir!=PJ_MEDIA_DIR_NONE) {
- has_active = 1;
- break;
- }
- }
-
- if (!has_active && sdp_x_ms_msg_index==-1) {
- pjsip_tx_data *tdata;
-
- /* Unable to accept remote's SDP.
- * Answer with 488 (Not Acceptable Here)
- */
- /* Create 488 response. */
- tdata = pjsip_dlg_answer(dlg, PJSIP_SC_NOT_ACCEPTABLE_HERE);
-
- /* Send response. */
- if (tdata)
- pjsip_dlg_send_msg(dlg, tdata);
- return;
- }
-
- dlg_data->x_ms_msg_session = sdp_x_ms_msg_index;
-
- /* Create msg body to be used later in 2xx/response */
- create_msg_body(dlg, 0);
-
-}
-
-/* This callback is called after an INVITE is received. */
-static void dlg_on_incoming(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_rx_data *rdata)
-{
- struct dialog_data *dlg_data;
- pjsip_msg *msg;
- pjsip_tx_data *tdata;
- char buf[128];
- int len;
-
- PJ_UNUSED_ARG(tsx)
-
- pj_assert(rdata->msg->type == PJSIP_REQUEST_MSG &&
- rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD &&
- tsx->method.id == PJSIP_INVITE_METHOD);
-
- /*
- * Notify user!
- */
- PJ_LOG(3, (THIS_FILE, ""));
- PJ_LOG(3, (THIS_FILE, "INCOMING CALL ON DIALOG %s!!", dlg->obj_name));
- PJ_LOG(3, (THIS_FILE, ""));
- len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR,
- (pjsip_name_addr*)dlg->remote.info->uri,
- buf, sizeof(buf)-1);
- if (len > 0) {
- buf[len] = '\0';
- PJ_LOG(3,(THIS_FILE, "From:\t%s", buf));
- }
- len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR,
- (pjsip_name_addr*)dlg->local.info->uri,
- buf, sizeof(buf)-1);
- if (len > 0) {
- buf[len] = '\0';
- PJ_LOG(3,(THIS_FILE, "To:\t%s", buf));
- }
- PJ_LOG(3, (THIS_FILE, "Press 'a' to answer, or 'h' to hangup!!", dlg->obj_name));
- PJ_LOG(3, (THIS_FILE, ""));
-
- /*
- * Process incoming dialog.
- */
-
- dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data));
- dlg->user_data = dlg_data;
-
- /* Update contact. */
- pjsip_dlg_set_contact(dlg, &global.contact);
-
- /* Initialize credentials. */
- pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info);
-
- /* Create media session if the request has "application/sdp" body. */
- msg = rdata->msg;
- if (msg->body &&
- pj_stricmp2(&msg->body->content_type.type, "application")==0 &&
- pj_stricmp2(&msg->body->content_type.subtype, "sdp")==0)
- {
- pjsdp_session_desc *sdp;
-
- /* Parse SDP body, and instantiate media session based on remote's SDP.
- * Then create our SDP body from the session.
- */
- sdp = pjsdp_parse (msg->body->data, msg->body->len, rdata->pool);
- if (!sdp)
- goto send_answer;
-
- create_session_from_sdp(dlg, sdp);
-
- } else if (msg->body) {
- /* The request has a message body other than "application/sdp" */
- pjsip_accept_hdr *accept;
-
- /* Create response. */
- tdata = pjsip_dlg_answer(dlg, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
-
- /* Add "Accept" header. */
- accept = pjsip_accept_hdr_create(tdata->pool);
- accept->values[0] = pj_str("application/sdp");
- accept->count = 1;
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)accept);
-
- /* Send response. */
- pjsip_dlg_send_msg(dlg, tdata);
- return;
-
- } else {
- /* The request has no message body. We can take this request, but
- * no media session will be activated.
- */
- /* Nothing to do here. */
- }
-
-send_answer:
- /* Immediately answer with 100 (or 180? */
- tdata = pjsip_dlg_answer( dlg, PJSIP_SC_RINGING );
- pjsip_dlg_send_msg(dlg, tdata);
-
- /* Set current dialog to this dialog if we don't currently have
- * current dialog.
- */
- if (global.cur_dlg == NULL) {
- global.cur_dlg = dlg;
- }
-
- /* Auto-answer if option is specified. */
- if (global.auto_answer >= 0) {
- pj_time_val delay = { 0, 0};
- struct dialog_data *dlg_data = dlg->user_data;
-
- PJ_LOG(4, (THIS_FILE, "Scheduling auto-answer in %d seconds",
- global.auto_answer));
-
- delay.sec = global.auto_answer;
- dlg_data->auto_timer.user_data = dlg;
- dlg_data->auto_timer.id = AUTO_ANSWER;
- dlg_data->auto_timer.cb = &dlg_auto_timer_callback;
- dlg_data->has_auto_timer = 1;
- pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay);
- }
-}
-
-/* This callback is called when dialog has sent/received a provisional response
- * to INVITE.
- */
-static void dlg_on_provisional(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_event *event)
-{
- const char *action;
-
- pj_assert((event->src_type == PJSIP_EVENT_TX_MSG &&
- event->src.tdata->msg->type == PJSIP_RESPONSE_MSG &&
- event->src.tdata->msg->line.status.code/100 == 1 &&
- tsx->method.id == PJSIP_INVITE_METHOD)
- ||
- (event->src_type == PJSIP_EVENT_RX_MSG &&
- event->src.rdata->msg->type == PJSIP_RESPONSE_MSG &&
- event->src.rdata->msg->line.status.code/100 == 1 &&
- tsx->method.id == PJSIP_INVITE_METHOD));
-
- if (event->src_type == PJSIP_EVENT_TX_MSG)
- action = "Sending";
- else
- action = "Received";
-
- PJ_LOG(3, (THIS_FILE, "Dialog %s: %s %d (%s)",
- dlg->obj_name, action, tsx->status_code,
- pjsip_get_status_text(tsx->status_code)->ptr));
-}
-
-/* This callback is called when 200 response to INVITE is sent/received. */
-static void dlg_on_connecting(pjsip_dlg *dlg, pjsip_event *event)
-{
- struct dialog_data *dlg_data = dlg->user_data;
- const char *action;
-
- pj_assert((event->src_type == PJSIP_EVENT_TX_MSG &&
- event->src.tdata->msg->type == PJSIP_RESPONSE_MSG &&
- event->src.tdata->msg->line.status.code/100 == 2)
- ||
- (event->src_type == PJSIP_EVENT_RX_MSG &&
- event->src.rdata->msg->type == PJSIP_RESPONSE_MSG &&
- event->src.rdata->msg->line.status.code/100 == 2));
-
- if (event->src_type == PJSIP_EVENT_RX_MSG)
- action = "Received";
- else
- action = "Sending";
-
- PJ_LOG(3, (THIS_FILE, "Dialog %s: %s 200 (OK)", dlg->obj_name, action));
-
- if (event->src_type == PJSIP_EVENT_RX_MSG) {
- /* On receipt of 2xx response, negotiate our media capability
- * and start media.
- */
- pjsip_msg *msg = event->src.rdata->msg;
- pjsip_msg_body *body;
- pjsdp_session_desc *sdp;
-
- /* Get SDP from message. */
-
- /* Ignore if no SDP body is present. */
- body = msg->body;
- if (!body) {
- PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no body!",
- dlg->obj_name));
- return;
- }
-
- if (pj_stricmp2(&body->content_type.type, "application") != 0 &&
- pj_stricmp2(&body->content_type.subtype, "sdp") != 0)
- {
- PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no SDP body!",
- dlg->obj_name));
- return;
- }
-
- /* Got what seems to be a SDP content. Parse it. */
- sdp = pjsdp_parse (body->data, body->len, event->src.rdata->pool);
- if (!sdp) {
- PJ_LOG(3, (THIS_FILE, "Dialog %s: SDP syntax error!",
- dlg->obj_name));
- return;
- }
-
- /* Negotiate media session with remote's media capability. */
- if (pj_media_session_update (dlg_data->msession, sdp) != 0) {
- PJ_LOG(3, (THIS_FILE, "Dialog %s: media session update error!",
- dlg->obj_name));
- return;
- }
-
- /* Update the saved SDP body because media session has changed.
- * Also set ack flag to '1', because we only want to send one format/
- * codec for each media streams.
- */
- create_msg_body(dlg, 1);
-
- /* Activate media. */
- pj_media_session_activate (dlg_data->msession);
-
- } else {
- pjsip_msg *msg = event->src.tdata->msg;
-
- if (msg->body) {
- /* On transmission of 2xx response, start media session. */
- pj_media_session_activate (dlg_data->msession);
- }
- }
-
-}
-
-/* This callback is called when ACK to initial INVITE is sent/received. */
-static void dlg_on_established(pjsip_dlg *dlg, pjsip_event *event)
-{
- const char *action;
-
- pj_assert((event->src_type == PJSIP_EVENT_TX_MSG &&
- event->src.tdata->msg->type == PJSIP_REQUEST_MSG &&
- event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD)
- ||
- (event->src_type == PJSIP_EVENT_RX_MSG &&
- event->src.rdata->msg->type == PJSIP_REQUEST_MSG &&
- event->src.rdata->msg->line.req.method.id == PJSIP_ACK_METHOD));
-
- if (event->src_type == PJSIP_EVENT_RX_MSG)
- action = "Received";
- else
- action = "Sending";
-
- PJ_LOG(3, (THIS_FILE, "Dialog %s: %s ACK, dialog is ESTABLISHED",
- dlg->obj_name, action));
-
- /* Attach SDP body for outgoing ACK. */
- if (event->src_type == PJSIP_EVENT_TX_MSG &&
- event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD)
- {
- struct dialog_data *dlg_data = dlg->user_data;
- event->src.tdata->msg->body = dlg_data->body;
- }
-
- /* Auto-hangup if option is specified. */
- if (global.auto_hangup >= 0) {
- pj_time_val delay = { 0, 0};
- struct dialog_data *dlg_data = dlg->user_data;
-
- PJ_LOG(4, (THIS_FILE, "Scheduling auto-hangup in %d seconds",
- global.auto_hangup));
-
- delay.sec = global.auto_hangup;
- dlg_data->auto_timer.user_data = dlg;
- dlg_data->auto_timer.id = AUTO_HANGUP;
- dlg_data->auto_timer.cb = &dlg_auto_timer_callback;
- dlg_data->has_auto_timer = 1;
- pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay);
- }
-}
-
-
-/* This callback is called when dialog is disconnected (because of final
- * response, BYE, or timer).
- */
-static void dlg_on_disconnected(pjsip_dlg *dlg, pjsip_event *event)
-{
- struct dialog_data *dlg_data = dlg->user_data;
- int status_code;
- const pj_str_t *reason;
-
- PJ_UNUSED_ARG(event)
-
- /* Cancel auto-answer/auto-hangup timer. */
- if (dlg_data->has_auto_timer) {
- pjsip_endpt_cancel_timer(dlg->ua->endpt, &dlg_data->auto_timer);
- dlg_data->has_auto_timer = 0;
- }
-
- if (dlg->invite_tsx)
- status_code = dlg->invite_tsx->status_code;
- else
- status_code = 200;
-
- if (event->obj.tsx->method.id == PJSIP_INVITE_METHOD) {
- if (event->src_type == PJSIP_EVENT_RX_MSG)
- reason = &event->src.rdata->msg->line.status.reason;
- else if (event->src_type == PJSIP_EVENT_TX_MSG)
- reason = &event->src.tdata->msg->line.status.reason;
- else
- reason = pjsip_get_status_text(event->obj.tsx->status_code);
- } else {
- reason = &event->obj.tsx->method.name;
- }
-
- PJ_LOG(3, (THIS_FILE, "Dialog %s: DISCONNECTED! Reason=%d (%.*s)",
- dlg->obj_name, status_code,
- reason->slen, reason->ptr));
-
- if (dlg_data->msession) {
- pj_media_session_destroy (dlg_data->msession);
- dlg_data->msession = NULL;
- }
-}
-
-/* This callback is called when dialog is about to be destroyed. */
-static void dlg_on_terminated(pjsip_dlg *dlg)
-{
- PJ_LOG(3, (THIS_FILE, "Dialog %s: terminated!", dlg->obj_name));
-
- /* If current dialog is equal to this dialog, update it. */
- if (global.cur_dlg == dlg) {
- global.cur_dlg = global.cur_dlg->next;
- if (global.cur_dlg == (void*)&global.user_agent->dlg_list) {
- global.cur_dlg = NULL;
- }
- }
-}
-
-/* This callback is called for any requests when dialog is established. */
-static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event)
-{
- pjsip_transaction *tsx = event->obj.tsx;
-
- if (event->src_type == PJSIP_EVENT_RX_MSG &&
- event->src.rdata->msg->type == PJSIP_REQUEST_MSG)
- {
- if (event->src.rdata->cseq->method.id == PJSIP_INVITE_METHOD) {
- /* Re-invitation. */
- pjsip_tx_data *tdata;
-
- PJ_LOG(3,(THIS_FILE, "Dialog %s: accepting re-invitation (dummy)",
- dlg->obj_name));
- tdata = pjsip_dlg_answer(dlg, 200);
- if (tdata) {
- struct dialog_data *dlg_data = dlg->user_data;
- tdata->msg->body = dlg_data->body;
- pjsip_dlg_send_msg(dlg, tdata);
- }
- } else {
- /* Don't worry, endpoint will answer with 500 or whetever. */
- }
-
- } else if (tsx->status_code/100 == 2) {
- PJ_LOG(3,(THIS_FILE, "Dialog %s: outgoing %.*s success: %d (%s)",
- dlg->obj_name,
- tsx->method.name.slen, tsx->method.name.ptr,
- tsx->status_code,
- pjsip_get_status_text(tsx->status_code)->ptr));
-
-
- } else if (tsx->status_code >= 300) {
- pj_bool_t report_failure = PJ_TRUE;
-
- /* Check for authentication failures. */
- if (tsx->status_code==401 || tsx->status_code==407) {
- pjsip_tx_data *tdata;
- tdata = pjsip_auth_reinit_req( global.endpt,
- dlg->pool, &dlg->auth_sess,
- dlg->cred_count, dlg->cred_info,
- tsx->last_tx, event->src.rdata );
- if (tdata) {
- int rc;
- rc = pjsip_dlg_send_msg( dlg, tdata);
- report_failure = (rc != 0);
- }
- }
- if (report_failure) {
- const pj_str_t *reason;
- if (event->src_type == PJSIP_EVENT_RX_MSG) {
- reason = &event->src.rdata->msg->line.status.reason;
- } else {
- reason = pjsip_get_status_text(tsx->status_code);
- }
- PJ_LOG(2,(THIS_FILE, "Dialog %s: outgoing request failed: %d (%.*s)",
- dlg->obj_name, tsx->status_code,
- reason->slen, reason->ptr));
- }
- }
-}
-
-/* Initialize sockets and optionally get the public address via STUN. */
-static pj_status_t init_sockets()
-{
- enum {
- RTP_START_PORT = 4000,
- RTP_RANDOM_START = 2,
- RTP_RETRY = 10
- };
- enum {
- SIP_SOCK,
- RTP_SOCK,
- RTCP_SOCK,
- };
- int i;
- int rtp_port;
- pj_sock_t sock[3];
- pj_sockaddr_in mapped_addr[3];
-
- for (i=0; i<3; ++i)
- sock[i] = PJ_INVALID_SOCKET;
-
- /* Create and bind SIP UDP socket. */
- sock[SIP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
- if (sock[SIP_SOCK] == PJ_INVALID_SOCKET) {
- PJ_LOG(2,(THIS_FILE, "Unable to create socket"));
- goto on_error;
- }
- if (pj_sock_bind_in(sock[SIP_SOCK], 0, (pj_uint16_t)global.sip_port) != 0) {
- PJ_LOG(2,(THIS_FILE, "Unable to bind to SIP port"));
- goto on_error;
- }
-
- /* Initialize start of RTP port to try. */
- rtp_port = RTP_START_PORT + (pj_rand() % RTP_RANDOM_START) / 2;
-
- /* Loop retry to bind RTP and RTCP sockets. */
- for (i=0; i<RTP_RETRY; ++i, rtp_port += 2) {
-
- /* Create and bind RTP socket. */
- sock[RTP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
- if (sock[RTP_SOCK] == PJ_INVALID_SOCKET)
- goto on_error;
- if (pj_sock_bind_in(sock[RTP_SOCK], 0, (pj_uint16_t)rtp_port) != 0) {
- pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET;
- continue;
- }
-
- /* Create and bind RTCP socket. */
- sock[RTCP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
- if (sock[RTCP_SOCK] == PJ_INVALID_SOCKET)
- goto on_error;
- if (pj_sock_bind_in(sock[RTCP_SOCK], 0, (pj_uint16_t)(rtp_port+1)) != 0) {
- pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET;
- pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
- continue;
- }
-
- /*
- * If we're configured to use STUN, then find out the mapped address,
- * and make sure that the mapped RTCP port is adjacent with the RTP.
- */
- if (global.stun_port1 == 0) {
- pj_str_t hostname;
- pj_sockaddr_in addr;
-
- /* Get local IP address. */
- char hostname_buf[PJ_MAX_HOSTNAME];
- if (gethostname(hostname_buf, sizeof(hostname_buf)))
- goto on_error;
- hostname = pj_str(hostname_buf);
-
- pj_memset( &addr, 0, sizeof(addr));
- addr.sin_family = PJ_AF_INET;
- if (pj_sockaddr_set_str_addr( &addr, &hostname) != PJ_SUCCESS)
- goto on_error;
-
- for (i=0; i<3; ++i)
- pj_memcpy(&mapped_addr[i], &addr, sizeof(addr));
-
- mapped_addr[SIP_SOCK].sin_port = pj_htons((pj_uint16_t)global.sip_port);
- mapped_addr[RTP_SOCK].sin_port = pj_htons((pj_uint16_t)rtp_port);
- mapped_addr[RTCP_SOCK].sin_port = pj_htons((pj_uint16_t)(rtp_port+1));
- break;
- } else {
- pj_status_t rc;
- rc = pj_stun_get_mapped_addr( global.pf, 3, sock,
- &global.stun_srv1, global.stun_port1,
- &global.stun_srv2, global.stun_port2,
- mapped_addr);
- if (rc != 0) {
- PJ_LOG(3,(THIS_FILE, "Error: %s", pj_stun_get_err_msg(rc)));
- goto on_error;
- }
-
- if (pj_ntohs(mapped_addr[2].sin_port) == pj_ntohs(mapped_addr[1].sin_port)+1)
- break;
-
- pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET;
- pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
- }
- }
-
- if (sock[RTP_SOCK] == PJ_INVALID_SOCKET) {
- PJ_LOG(2,(THIS_FILE, "Unable to find appropriate RTP/RTCP ports combination"));
- goto on_error;
- }
-
- global.sip_sock = sock[SIP_SOCK];
- pj_memcpy(&global.sip_sock_name, &mapped_addr[SIP_SOCK], sizeof(pj_sockaddr_in));
- global.rtp_sock = sock[RTP_SOCK];
- pj_memcpy(&global.rtp_sock_name, &mapped_addr[RTP_SOCK], sizeof(pj_sockaddr_in));
- global.rtcp_sock = sock[RTCP_SOCK];
- pj_memcpy(&global.rtcp_sock_name, &mapped_addr[RTCP_SOCK], sizeof(pj_sockaddr_in));
-
- PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
- pj_inet_ntoa(global.sip_sock_name.sin_addr),
- pj_ntohs(global.sip_sock_name.sin_port)));
- PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s:%d",
- pj_inet_ntoa(global.rtp_sock_name.sin_addr),
- pj_ntohs(global.rtp_sock_name.sin_port)));
- PJ_LOG(4,(THIS_FILE, "RTCP UDP socket reachable at %s:%d",
- pj_inet_ntoa(global.rtcp_sock_name.sin_addr),
- pj_ntohs(global.rtcp_sock_name.sin_port)));
- return 0;
-
-on_error:
- for (i=0; i<3; ++i) {
- if (sock[i] != PJ_INVALID_SOCKET)
- pj_sock_close(sock[i]);
- }
- return -1;
-}
-
-static void log_function(int level, const char *buffer, int len)
-{
- /* Write to both stdout and file. */
- if (level <= global.app_log_level)
- pj_log_to_stdout(level, buffer, len);
- if (global.log_file) {
- fwrite(buffer, len, 1, global.log_file);
- fflush(global.log_file);
- }
-}
-
-/* Initialize stack. */
-static pj_status_t init_stack()
-{
- pj_status_t status;
- pj_sockaddr_in bind_addr;
- pj_sockaddr_in bind_name;
- const char *local_addr;
- static char local_uri[128];
-
- /* Optionally set logging file. */
- if (global.log_filename) {
- global.log_file = fopen(global.log_filename, "wt");
- }
-
- /* Initialize endpoint. This will also call initialization to all the
- * modules.
- */
- global.endpt = pjsip_endpt_create(global.pf);
- if (global.endpt == NULL) {
- return -1;
- }
-
- /* Set dialog callback. */
- pjsip_ua_set_dialog_callback(global.user_agent, &dlg_callback);
-
- /* Init listener's bound address and port. */
- pj_sockaddr_init2(&bind_addr, "0.0.0.0", global.sip_port);
- pj_sockaddr_init(&bind_name, pj_gethostname(), global.sip_port);
-
- /* Add UDP transport listener. */
- status = pjsip_endpt_create_udp_listener( global.endpt, global.sip_sock,
- &global.sip_sock_name);
- if (status != 0)
- return -1;
-
- local_addr = pj_inet_ntoa(global.sip_sock_name.sin_addr);
-
-#if PJ_HAS_TCP
- /* Add TCP transport listener. */
- status = pjsip_endpt_create_listener( global.endpt, PJSIP_TRANSPORT_TCP,
- &bind_addr, &bind_name);
- if (status != 0)
- return -1;
-#endif
-
- /* Determine user_id to be put in Contact */
- if (global.local_uri.slen) {
- pj_pool_t *pool = pj_pool_create(global.pf, "parser", 1024, 0, NULL);
- pjsip_uri *uri;
-
- uri = pjsip_parse_uri(pool, global.local_uri.ptr, global.local_uri.slen, 0);
- if (uri) {
- if (pj_stricmp2(pjsip_uri_get_scheme(uri), "sip")==0) {
- pjsip_url *url = (pjsip_url*)pjsip_uri_get_uri(uri);
- if (url->user.slen)
- strncpy(global.user_id, url->user.ptr, url->user.slen);
- }
- }
- pj_pool_release(pool);
- }
-
- if (global.user_id[0]=='\0') {
- pj_native_strcpy(global.user_id, "user");
- }
-
- /* build contact */
- global.real_contact.ptr = local_uri;
- global.real_contact.slen =
- sprintf(local_uri, "<sip:%s@%s:%d>", global.user_id, local_addr, global.sip_port);
-
- if (global.contact.slen == 0)
- global.contact = global.real_contact;
-
- /* initialize local_uri with contact if it's not specified in cmdline */
- if (global.local_uri.slen == 0)
- global.local_uri = global.contact;
-
- /* Init proxy. */
- if (global.proxy.slen || global.outbound_proxy.slen) {
- int count = 0;
- pj_str_t proxy_url[2];
-
- if (global.outbound_proxy.slen) {
- proxy_url[count++] = global.outbound_proxy;
- }
- if (global.proxy.slen) {
- proxy_url[count++] = global.proxy;
- }
-
- if (pjsip_endpt_set_proxies(global.endpt, count, proxy_url) != 0) {
- PJ_LOG(2,(THIS_FILE, "Error setting proxy address!"));
- return -1;
- }
- }
-
- /* initialize SIP registration if registrar is configured */
- if (global.registrar_uri.slen) {
- global.regc = pjsip_regc_create( global.endpt, NULL, ®c_cb);
- pjsip_regc_init( global.regc, &global.registrar_uri,
- &global.local_uri,
- &global.local_uri,
- 1, &global.contact,
- global.reg_timeout);
- pjsip_regc_set_credentials( global.regc, global.cred_count, global.cred_info );
- }
-
- return PJ_SUCCESS;
-}
-
-/* Worker thread function, only used when threading is enabled. */
-static void *PJ_THREAD_FUNC worker_thread(void *unused)
-{
- PJ_UNUSED_ARG(unused)
-
- while (!global.worker_quit_flag) {
- pj_time_val timeout = { 0, 10 };
- pjsip_endpt_handle_events (global.endpt, &timeout);
- }
- return NULL;
-}
-
-
-/* Make call to the specified URI. */
-static pjsip_dlg *make_call(pj_str_t *remote_uri)
-{
- pjsip_dlg *dlg;
- pj_str_t local = global.contact;
- pj_str_t remote = *remote_uri;
- struct dialog_data *dlg_data;
- pjsip_tx_data *tdata;
- pj_media_sock_info sock_info;
-
- /* Create new dialog instance. */
- dlg = pjsip_ua_create_dialog(global.user_agent, PJSIP_ROLE_UAC);
-
- /* Attach our own user data. */
- dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data));
- dlg->user_data = dlg_data;
-
- /* Create media session. */
- pj_memset(&sock_info, 0, sizeof(sock_info));
- sock_info.rtp_sock = global.rtp_sock;
- sock_info.rtcp_sock = global.rtcp_sock;
- pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in));
-
- dlg_data->msession = pj_media_session_create (global.mmgr, &sock_info);
- dlg_data->x_ms_msg_session = -1;
-
- if (global.offer_x_ms_msg) {
- const pj_media_stream_info *minfo[32];
- unsigned cnt;
-
- cnt = pj_media_session_enum_streams(dlg_data->msession, 32, minfo);
- if (cnt > 0)
- dlg_data->x_ms_msg_session = cnt;
- }
-
- /* Initialize dialog with local and remote URI. */
- if (pjsip_dlg_init(dlg, &local, &remote, NULL) != PJ_SUCCESS) {
- pjsip_ua_destroy_dialog(dlg);
- return NULL;
- }
-
- /* Initialize credentials. */
- pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info);
-
- /* Send INVITE! */
- tdata = pjsip_dlg_invite(dlg);
- tdata->msg->body = create_msg_body (dlg, 0);
-
- if (pjsip_dlg_send_msg(dlg, tdata) != PJ_SUCCESS) {
- pjsip_ua_destroy_dialog(dlg);
- return NULL;
- }
-
- return dlg;
-}
-
-/*
- * Callback to receive incoming IM message.
- */
-static int on_incoming_im_msg(pjsip_rx_data *rdata)
-{
- pjsip_msg *msg = rdata->msg;
- pjsip_msg_body *body = msg->body;
- int len;
- char to[128], from[128];
-
-
- len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
- rdata->from->uri, from, sizeof(from));
- if (len > 0) from[len] = '\0';
- else pj_native_strcpy(from, "<URL too long..>");
-
- len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
- rdata->to->uri, to, sizeof(to));
- if (len > 0) to[len] = '\0';
- else pj_native_strcpy(to, "<URL too long..>");
-
- PJ_LOG(3,(THIS_FILE, "Incoming instant message:"));
-
- printf("----- BEGIN INSTANT MESSAGE ----->\n");
- printf("From:\t%s\n", from);
- printf("To:\t%s\n", to);
- printf("Body:\n%.*s\n", (body ? body->len : 0), (body ? (char*)body->data : ""));
- printf("<------ END INSTANT MESSAGE ------\n");
-
- fflush(stdout);
-
- /* Must answer with final response. */
- return 200;
-}
-
-/*
- * Input URL.
- */
-static pj_str_t *ui_input_url(pj_str_t *out, char *buf, int len, int *selection)
-{
- int i;
-
- *selection = -1;
-
- printf("\nBuddy list:\n");
- printf("---------------------------------------\n");
- for (i=0; i<global.buddy_cnt; ++i) {
- printf(" %d\t%s <%s>\n", i+1, global.buddy[i].ptr,
- (global.buddy_status[i]?"Online":"Offline"));
- }
- printf("-------------------------------------\n");
-
- printf("Choices\n"
- "\t0 For current dialog.\n"
- "\t[1-%02d] Select from buddy list\n"
- "\tURL An URL\n"
- , global.buddy_cnt);
- printf("Input: ");
-
- fflush(stdout);
- fgets(buf, len, stdin);
- buf[strlen(buf)-1] = '\0'; /* remove trailing newline. */
-
- while (isspace(*buf)) ++buf;
-
- if (!*buf || *buf=='\n' || *buf=='\r')
- return NULL;
-
- i = atoi(buf);
-
- if (i == 0) {
- if (isdigit(*buf)) {
- *selection = 0;
- *out = pj_str("0");
- return out;
- } else {
- if (verify_sip_url(buf) != 0) {
- puts("Invalid URL specified!");
- return NULL;
- }
- *out = pj_str(buf);
- return out;
- }
- } else if (i > global.buddy_cnt || i < 0) {
- printf("Error: invalid selection!\n");
- return NULL;
- } else {
- *out = global.buddy[i-1];
- *selection = i;
- return out;
- }
-}
-
-
-static void generic_request_callback( void *token, pjsip_event *event )
-{
- pjsip_transaction *tsx = event->obj.tsx;
-
- PJ_UNUSED_ARG(token)
-
- if (tsx->status_code/100 == 2) {
- PJ_LOG(3,(THIS_FILE, "Outgoing %.*s %d (%s)",
- event->obj.tsx->method.name.slen,
- event->obj.tsx->method.name.ptr,
- tsx->status_code,
- pjsip_get_status_text(tsx->status_code)->ptr));
- } else if (tsx->status_code==401 || tsx->status_code==407) {
- pjsip_tx_data *tdata;
- tdata = pjsip_auth_reinit_req( global.endpt,
- global.pool, NULL, global.cred_count, global.cred_info,
- tsx->last_tx, event->src.rdata);
- if (tdata) {
- int rc;
- pjsip_cseq_hdr *cseq;
- cseq = (pjsip_cseq_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
- cseq->cseq++;
- rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL,
- &generic_request_callback);
- if (rc == 0)
- return;
- }
- PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%s)",
- event->obj.tsx->method.name.slen,
- event->obj.tsx->method.name.ptr,
- event->obj.tsx->status_code,
- pjsip_get_status_text(event->obj.tsx->status_code)->ptr));
- } else {
- const pj_str_t *reason;
- if (event->src_type == PJSIP_EVENT_RX_MSG)
- reason = &event->src.rdata->msg->line.status.reason;
- else
- reason = pjsip_get_status_text(tsx->status_code);
- PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%.*s)",
- event->obj.tsx->method.name.slen,
- event->obj.tsx->method.name.ptr,
- event->obj.tsx->status_code,
- reason->slen, reason->ptr));
- }
-}
-
-
-static void ui_send_im_message()
-{
- char line[100];
- char text_buf[100];
- pj_str_t str;
- pj_str_t text_msg;
- int selection, rc;
- pjsip_tx_data *tdata;
-
- if (ui_input_url(&str, line, sizeof(line), &selection) == NULL)
- return;
-
-
- printf("Enter text to send (empty to cancel): "); fflush(stdout);
- fgets(text_buf, sizeof(text_buf), stdin);
- text_buf[strlen(text_buf)-1] = '\0';
- if (!*text_buf)
- return;
-
- text_msg = pj_str(text_buf);
-
- if (selection==0) {
- pjsip_method message_method;
- pj_str_t str_MESSAGE = { "MESSAGE", 7 };
-
- /* Send IM to current dialog. */
- if (global.cur_dlg == NULL || global.cur_dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
- printf("No current dialog or dialog state is not ESTABLISHED!\n");
- return;
- }
-
- pjsip_method_init( &message_method, global.cur_dlg->pool, &str_MESSAGE);
- tdata = pjsip_dlg_create_request( global.cur_dlg, &message_method, -1 );
-
- if (tdata) {
- /* Create message body for the text. */
- pjsip_msg_body *body = pj_pool_calloc(tdata->pool, 1, sizeof(*body));
- body->content_type.type = pj_str("text");
- body->content_type.subtype = pj_str("plain");
- body->data = pj_pool_alloc(tdata->pool, text_msg.slen);
- pj_memcpy(body->data, text_msg.ptr, text_msg.slen);
- body->len = text_msg.slen;
- body->print_body = &pjsip_print_text_body;
-
- /* Assign body to message, and send the message! */
- tdata->msg->body = body;
- pjsip_dlg_send_msg( global.cur_dlg, tdata );
- }
-
- } else {
- /* Send IM to buddy list. */
- pjsip_method message;
- static pj_str_t MESSAGE = { "MESSAGE", 7 };
- pjsip_method_init_np(&message, &MESSAGE);
- tdata = pjsip_endpt_create_request(global.endpt, &message,
- &str,
- &global.real_contact,
- &str, &global.real_contact, NULL, -1,
- &text_msg);
- if (!tdata) {
- puts("Error creating request");
- return;
- }
- rc = pjsip_endpt_send_request(global.endpt, tdata, -1, NULL, &generic_request_callback);
- if (rc == 0) {
- printf("Sending IM message %d\n", global.im_counter);
- ++global.im_counter;
- } else {
- printf("Error: unable to send IM message!\n");
- }
- }
-}
-
-static void ui_send_options()
-{
- char line[100];
- pj_str_t str;
- int selection, rc;
- pjsip_tx_data *tdata;
- pjsip_method options;
-
- if (ui_input_url(&str, line, sizeof(line), &selection) == NULL)
- return;
-
- pjsip_method_set( &options, PJSIP_OPTIONS_METHOD );
-
- if (selection == 0) {
- /* Send OPTIONS to current dialog. */
- tdata = pjsip_dlg_create_request(global.cur_dlg, &options, -1);
- if (tdata)
- pjsip_dlg_send_msg( global.cur_dlg, tdata );
- } else {
- /* Send OPTIONS to arbitrary party. */
- tdata = pjsip_endpt_create_request( global.endpt, &options,
- &str,
- &global.local_uri, &str,
- &global.real_contact,
- NULL, -1, NULL);
- if (tdata) {
- rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL,
- &generic_request_callback);
- if (rc != 0)
- PJ_LOG(2,(THIS_FILE, "Error sending OPTIONS!"));
- }
- }
-}
-
-static void init_presence()
-{
- const pjsip_presence_cb pres_cb = {
- NULL,
- &pres_on_received_request,
- &pres_on_received_refresh,
- &pres_on_received_update,
- &pres_on_terminated
- };
-
- pjsip_presence_init(&pres_cb);
-}
-
-/* Subscribe presence information for all buddies. */
-static void subscribe_buddies_presence()
-{
- int i;
- for (i=0; i<global.buddy_cnt; ++i) {
- pjsip_presentity *pres;
- if (global.buddy_pres[i])
- continue;
- pres = pjsip_presence_create( global.endpt, &global.local_uri,
- &global.buddy[i], PRESENCE_TIMEOUT, (void*)i);
- if (pres) {
- pjsip_presence_set_credentials( pres, global.cred_count, global.cred_info );
- pjsip_presence_subscribe( pres );
- }
- global.buddy_pres[i] = pres;
- }
-}
-
-/* Unsubscribe presence information for all buddies. */
-static void unsubscribe_buddies_presence()
-{
- int i;
- for (i=0; i<global.buddy_cnt; ++i) {
- pjsip_presentity *pres = global.buddy_pres[i];
- if (pres) {
- pjsip_presence_unsubscribe(pres);
- pjsip_presence_destroy(pres);
- global.buddy_pres[i] = NULL;
- }
- }
-}
-
-/* Unsubscribe presence. */
-static void unsubscribe_presence()
-{
- int i;
-
- unsubscribe_buddies_presence();
- for (i=0; i<global.pres_cnt; ++i) {
- pjsip_presentity *pres = global.pres[i];
- pjsip_presence_notify( pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 0);
- pjsip_presence_destroy( pres );
- }
-}
-
-/* Advertise online status to subscribers. */
-static void update_im_status()
-{
- int i;
- for (i=0; i<global.pres_cnt; ++i) {
- pjsip_presentity *pres = global.pres[i];
- pjsip_presence_notify( pres, PJSIP_EVENT_SUB_STATE_ACTIVE,
- !global.hide_status);
- }
-}
-
-/*
- * Main program.
- */
-int main(int argc, char *argv[])
-{
- /* set to WORKER_COUNT+1 to avoid zero size warning
- * when threading is disabled. */
- pj_thread_t *thread[WORKER_COUNT+1];
- pj_caching_pool cp;
- int i;
-
- global.sip_port = 5060;
- global.auto_answer = -1;
- global.auto_hangup = -1;
- global.app_log_level = 3;
-
- pj_log_set_level(4);
- pj_log_set_log_func(&log_function);
-
- /* Init PJLIB */
- if (pj_init() != PJ_SUCCESS)
- return 1;
-
- /* Init caching pool. */
- pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
- global.pf = &cp.factory;
-
- /* Create memory pool for application. */
- global.pool = pj_pool_create(global.pf, "main", 1024, 0, NULL);
-
- /* Parse command line arguments. */
- if (parse_args(global.pool, argc, argv) != PJ_SUCCESS) {
- pj_caching_pool_destroy(&cp);
- return 1;
- }
-
- /* Init sockets */
- if (init_sockets() != 0) {
- pj_caching_pool_destroy(&cp);
- return 1;
- }
-
- /* Initialize stack. */
- if (init_stack() != PJ_SUCCESS) {
- pj_caching_pool_destroy(&cp);
- return 1;
- }
-
- /* Set callback to receive incoming IM */
- pjsip_messaging_set_incoming_callback( &on_incoming_im_msg );
-
- /* Set default worker count (can be zero) */
- global.worker_cnt = WORKER_COUNT;
-
- /* Create user worker thread(s), only when threading is enabled. */
- for (i=0; i<global.worker_cnt; ++i) {
- thread[i] = pj_thread_create( global.pool, "sip%p",
- &worker_thread,
- NULL, 0, NULL, 0);
- if (thread == NULL) {
- global.worker_quit_flag = 1;
- for (--i; i>=0; --i) {
- pj_thread_join(thread[i]);
- pj_thread_destroy(thread[i]);
- }
- pj_caching_pool_destroy(&cp);
- return 1;
- }
- }
-
- printf("Worker thread count: %d\n", global.worker_cnt);
-
- /* Perform registration, if required. */
- if (global.regc) {
- update_registration(global.regc, 1);
- }
-
- /* Initialize media manager. */
- global.mmgr = pj_med_mgr_create(global.pf);
-
- /* Init presence. */
- init_presence();
-
- /* Subscribe presence information of all buddies. */
- if (!global.no_presence)
- subscribe_buddies_presence();
-
- /* Initializatio completes, loop waiting for commands. */
- for (;!global.worker_quit_flag;) {
- pj_str_t str;
- char line[128];
-
-#if WORKER_COUNT==0
- /* If worker thread does not exist, main thread must poll for evetns.
- * But this won't work very well since main thread is blocked by
- * fgets(). So keep pressing the ENTER key to get the events!
- */
- pj_time_val timeout = { 0, 100 };
- pjsip_endpt_handle_events(global.endpt, &timeout);
- puts("Keep pressing ENTER key to get the events!");
-#endif
-
- printf("\nCurrent dialog: ");
- print_dialog(global.cur_dlg);
- puts("");
-
- keystroke_help();
-
- fgets(line, sizeof(line), stdin);
-
- switch (*line) {
- case 'm':
- puts("Make outgoing call");
- if (ui_input_url(&str, line, sizeof(line), &i) != NULL) {
- pjsip_dlg *dlg = make_call(&str);
- if (global.cur_dlg == NULL) {
- global.cur_dlg = dlg;
- }
- }
- break;
- case 'i':
- puts("Send Instant Messaging");
- ui_send_im_message();
- break;
- case 'o':
- puts("Send OPTIONS");
- ui_send_options();
- break;
- case 'a':
- if (global.cur_dlg) {
- unsigned code;
- pjsip_tx_data *tdata;
- struct dialog_data *dlg_data = global.cur_dlg->user_data;
-
- printf("Answer with status code (1xx-6xx): ");
- fflush(stdout);
- fgets(line, sizeof(line), stdin);
- str = pj_str(line);
- str.slen -= 1;
-
- code = pj_strtoul(&str);
- tdata = pjsip_dlg_answer(global.cur_dlg, code);
- if (tdata) {
- if (code/100 == 2) {
- tdata->msg->body = dlg_data->body;
- }
- pjsip_dlg_send_msg(global.cur_dlg, tdata);
-
- }
- } else {
- puts("No current dialog");
- }
- break;
- case 'h':
- if (global.cur_dlg) {
- pjsip_tx_data *tdata;
- tdata = pjsip_dlg_disconnect(global.cur_dlg, PJSIP_SC_DECLINE);
- if (tdata) {
- pjsip_dlg_send_msg(global.cur_dlg, tdata);
- }
- } else {
- puts("No current dialog");
- }
- break;
- case ']':
- if (global.cur_dlg) {
- global.cur_dlg = global.cur_dlg->next;
- if (global.cur_dlg == (void*)&global.user_agent->dlg_list) {
- global.cur_dlg = global.cur_dlg->next;
- }
- } else {
- puts("No current dialog");
- }
- break;
- case '[':
- if (global.cur_dlg) {
- global.cur_dlg = global.cur_dlg->prev;
- if (global.cur_dlg == (void*)&global.user_agent->dlg_list) {
- global.cur_dlg = global.cur_dlg->prev;
- }
- } else {
- puts("No current dialog");
- }
- break;
- case 'd':
- pjsip_endpt_dump(global.endpt, *(line+1)=='1');
- pjsip_ua_dump(global.user_agent);
- break;
- case 's':
- if (*(line+1) == 'u')
- subscribe_buddies_presence();
- break;
- case 'u':
- if (*(line+1) == 's')
- unsubscribe_presence();
- break;
- case 't':
- global.hide_status = !global.hide_status;
- update_im_status();
- break;
- case 'q':
- goto on_exit;
- case 'l':
- print_all_dialogs();
- break;
- }
- }
-
-on_exit:
- /* Unregister, if required. */
- if (global.regc) {
- update_registration(global.regc, 0);
- }
-
- /* Unsubscribe presence. */
- unsubscribe_presence();
-
- /* Allow one second to get all events. */
- if (1) {
- pj_time_val end_time;
-
- pj_gettimeofday(&end_time);
- end_time.sec++;
-
- PJ_LOG(3,(THIS_FILE, "Shutting down.."));
- for (;;) {
- pj_time_val timeout = { 0, 20 }, now;
- pjsip_endpt_handle_events (global.endpt, &timeout);
- pj_gettimeofday(&now);
- PJ_TIME_VAL_SUB(now, end_time);
- if (now.sec >= 1)
- break;
- }
- }
-
- global.worker_quit_flag = 1;
-
- pj_med_mgr_destroy(global.mmgr);
-
- /* Wait all threads to quit. */
- for (i=0; i<global.worker_cnt; ++i) {
- pj_thread_join(thread[i]);
- pj_thread_destroy(thread[i]);
- }
-
- /* Destroy endpoint. */
- pjsip_endpt_destroy(global.endpt);
-
- /* Destroy caching pool. */
- pj_caching_pool_destroy(&cp);
-
- /* Close log file, if any. */
- if (global.log_file)
- fclose(global.log_file);
-
- return 0;
-}
-
-/*
- * Register static modules to the endpoint.
- */
-pj_status_t register_static_modules( pj_size_t *count,
- pjsip_module **modules )
-{
- /* Reset count. */
- *count = 0;
-
- /* Register user agent module. */
- modules[(*count)++] = pjsip_ua_get_module();
- global.user_agent = modules[0]->mod_data;
- modules[(*count)++] = pjsip_messaging_get_module();
- modules[(*count)++] = pjsip_event_sub_get_module();
-
- return PJ_SUCCESS;
-}
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjlib.h> +#include <pjsip_core.h> +#include <pjsip_ua.h> +#include <pjsip_simple.h> +#include <pjmedia.h> +#include <ctype.h> +#include <stdlib.h> +#include <pj/stun.h> + +#define START_PORT 5060 +#define MAX_BUDDIES 32 +#define THIS_FILE "main.c" +#define MAX_PRESENTITY 32 +#define PRESENCE_TIMEOUT 60 + +/* By default we'll have one worker thread, except when threading + * is disabled. + */ +#if PJ_HAS_THREADS +# define WORKER_COUNT 1 +#else +# define WORKER_COUNT 0 +#endif + +/* Global variable. */ +static struct +{ + /* Control. */ + pj_pool_factory *pf; + pjsip_endpoint *endpt; + pj_pool_t *pool; + pjsip_user_agent *user_agent; + int worker_cnt; + int worker_quit_flag; + + /* User info. */ + char user_id[64]; + pj_str_t local_uri; + pj_str_t contact; + pj_str_t real_contact; + + /* Dialog. */ + pjsip_dlg *cur_dlg; + + /* Authentication. */ + int cred_count; + pjsip_cred_info cred_info[4]; + + /* Media stack. */ + pj_bool_t null_audio; + pj_med_mgr_t *mmgr; + + /* Misc. */ + int app_log_level; + char *log_filename; + FILE *log_file; + + /* Proxy URLs */ + pj_str_t proxy; + pj_str_t outbound_proxy; + + /* UA auto options. */ + int auto_answer; /* -1 to disable. */ + int auto_hangup; /* -1 to disable */ + + /* Registration. */ + pj_str_t registrar_uri; + pjsip_regc *regc; + pj_int32_t reg_timeout; + pj_timer_entry regc_timer; + + /* STUN */ + pj_str_t stun_srv1; + int stun_port1; + pj_str_t stun_srv2; + int stun_port2; + + /* UDP sockets and their public address. */ + int sip_port; + pj_sock_t sip_sock; + pj_sockaddr_in sip_sock_name; + pj_sock_t rtp_sock; + pj_sockaddr_in rtp_sock_name; + pj_sock_t rtcp_sock; + pj_sockaddr_in rtcp_sock_name; + + /* SIMPLE */ + pj_bool_t hide_status; + pj_bool_t offer_x_ms_msg; + int im_counter; + int buddy_cnt; + pj_str_t buddy[MAX_BUDDIES]; + pj_bool_t buddy_status[MAX_BUDDIES]; + pj_bool_t no_presence; + pjsip_presentity *buddy_pres[MAX_BUDDIES]; + + int pres_cnt; + pjsip_presentity *pres[MAX_PRESENTITY]; + +} global; + +enum { AUTO_ANSWER, AUTO_HANGUP }; + +/* This is the data that will be 'attached' on per dialog basis. */ +struct dialog_data +{ + /* Media session. */ + pj_media_session_t *msession; + + /* x-ms-chat session. */ + pj_bool_t x_ms_msg_session; + + /* Cached SDP body, updated when media session changed. */ + pjsip_msg_body *body; + + /* Timer. */ + pj_bool_t has_auto_timer; + pj_timer_entry auto_timer; +}; + +/* + * These are the callbacks to be registered to dialog to receive notifications + * about various events in the dialog. + */ +static void dlg_on_all_events (pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt, + pjsip_event *event ); +static void dlg_on_before_tx (pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata, int retransmission); +static void dlg_on_tx_msg (pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata); +static void dlg_on_rx_msg (pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_rx_data *rdata); +static void dlg_on_incoming (pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_rx_data *rdata); +static void dlg_on_calling (pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata); +static void dlg_on_provisional (pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_event *event); +static void dlg_on_connecting (pjsip_dlg *dlg, pjsip_event *event); +static void dlg_on_established (pjsip_dlg *dlg, pjsip_event *event); +static void dlg_on_disconnected (pjsip_dlg *dlg, pjsip_event *event); +static void dlg_on_terminated (pjsip_dlg *dlg); +static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event); + +/* The callback structure that will be registered to UA layer. */ +struct pjsip_dlg_callback dlg_callback = { + &dlg_on_all_events, + &dlg_on_before_tx, + &dlg_on_tx_msg, + &dlg_on_rx_msg, + &dlg_on_incoming, + &dlg_on_calling, + &dlg_on_provisional, + &dlg_on_connecting, + &dlg_on_established, + &dlg_on_disconnected, + &dlg_on_terminated, + &dlg_on_mid_call_evt +}; + + +/* + * Auxiliary things are put in misc.c, so that this main.c file is more + * readable. + */ +#include "misc.c" + +static void dlg_auto_timer_callback( pj_timer_heap_t *timer_heap, + struct pj_timer_entry *entry) +{ + pjsip_dlg *dlg = entry->user_data; + struct dialog_data *dlg_data = dlg->user_data; + + PJ_UNUSED_ARG(timer_heap) + + dlg_data->has_auto_timer = 0; + + if (entry->id == AUTO_ANSWER) { + pjsip_tx_data *tdata = pjsip_dlg_answer(dlg, 200); + if (tdata) { + struct dialog_data *dlg_data = global.cur_dlg->user_data; + tdata->msg->body = dlg_data->body; + pjsip_dlg_send_msg(dlg, tdata); + } + } else { + pjsip_tx_data *tdata = pjsip_dlg_disconnect(dlg, 500); + if (tdata) + pjsip_dlg_send_msg(dlg, tdata); + } +} + +static void update_registration(pjsip_regc *regc, int renew) +{ + pjsip_tx_data *tdata; + + PJ_LOG(3,(THIS_FILE, "Performing SIP registration...")); + + if (renew) { + tdata = pjsip_regc_register(regc, 1); + } else { + tdata = pjsip_regc_unregister(regc); + } + + pjsip_regc_send( regc, tdata ); +} + +static void regc_cb(struct pjsip_regc_cbparam *param) +{ + /* + * Print registration status. + */ + if (param->code < 0 || param->code >= 300) { + PJ_LOG(2, (THIS_FILE, "SIP registration failed, status=%d (%s)", + param->code, pjsip_get_status_text(param->code)->ptr)); + global.regc = NULL; + + } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) { + PJ_LOG(3, (THIS_FILE, "SIP registration success, status=%d (%s), " + "will re-register in %d seconds", + param->code, + pjsip_get_status_text(param->code)->ptr, + param->expiration)); + + } else { + PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code)); + } +} + +static void pres_on_received_request(pjsip_presentity *pres, pjsip_rx_data *rdata, + int *timeout) +{ + int state; + int i; + char url[PJSIP_MAX_URL_SIZE]; + int urllen; + + PJ_UNUSED_ARG(rdata) + + if (*timeout > 0) { + state = PJSIP_EVENT_SUB_STATE_ACTIVE; + if (*timeout > 300) + *timeout = 300; + } else { + state = PJSIP_EVENT_SUB_STATE_TERMINATED; + } + + urllen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->from->uri, url, sizeof(url)-1); + if (urllen < 1) { + pj_native_strcpy(url, "<unknown>"); + } else { + url[urllen] = '\0'; + } + PJ_LOG(3,(THIS_FILE, "Received presence request from %s, sub_state=%s", + url, + (state==PJSIP_EVENT_SUB_STATE_ACTIVE?"active":"terminated"))); + + for (i=0; i<global.pres_cnt; ++i) + if (global.pres[i] == pres) + break; + if (i == global.pres_cnt) + global.pres[global.pres_cnt++] = pres; + + pjsip_presence_set_credentials( pres, global.cred_count, global.cred_info ); + pjsip_presence_notify(pres, state, !global.hide_status); + +} + +static void pres_on_received_refresh(pjsip_presentity *pres, pjsip_rx_data *rdata) +{ + pres_on_received_request(pres, rdata, &pres->sub->default_interval); +} + +/* This is called by presence framework when we receives presence update + * of a resource (buddy). + */ +static void pres_on_received_update(pjsip_presentity *pres, pj_bool_t is_open) +{ + int buddy_index = (int)pres->user_data; + + global.buddy_status[buddy_index] = is_open; + PJ_LOG(3,(THIS_FILE, "Presence update: %s is %s", + global.buddy[buddy_index].ptr, + (is_open ? "Online" : "Offline"))); +} + +/* This is called when the subscription is terminated. */ +static void pres_on_terminated(pjsip_presentity *pres, const pj_str_t *reason) +{ + if (pres->sub->role == PJSIP_ROLE_UAC) { + int buddy_index = (int)pres->user_data; + PJ_LOG(3,(THIS_FILE, "Presence subscription for %s is terminated (reason=%.*s)", + global.buddy[buddy_index].ptr, + reason->slen, reason->ptr)); + global.buddy_pres[buddy_index] = NULL; + global.buddy_status[buddy_index] = 0; + } else { + int i; + PJ_LOG(3,(THIS_FILE, "Notifier terminated (reason=%.*s)", + reason->slen, reason->ptr)); + pjsip_presence_notify(pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 1); + for (i=0; i<global.pres_cnt; ++i) { + if (global.pres[i] == pres) { + int j; + global.pres[i] = NULL; + for (j=i+1; j<global.pres_cnt; ++j) + global.pres[j-1] = global.pres[j]; + global.pres_cnt--; + break; + } + } + } + pjsip_presence_destroy(pres); +} + + +/* Callback attached to SIP body to print the body to message buffer. */ +static int print_msg_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size) +{ + pjsip_msg_body *body = msg_body; + return pjsdp_print ((pjsdp_session_desc*)body->data, buf, size); +} + +/* When media session has changed, call this function to update the cached body + * information in the dialog. + */ +static pjsip_msg_body *create_msg_body (pjsip_dlg *dlg, pj_bool_t is_ack_msg) +{ + struct dialog_data *dlg_data = dlg->user_data; + pjsdp_session_desc *sdp; + + sdp = pj_media_session_create_sdp (dlg_data->msession, dlg->pool, is_ack_msg); + if (!sdp) { + dlg_data->body = NULL; + return NULL; + } + + /* For outgoing INVITE, if we offer "x-ms-message" line, then add a new + * "m=" line in the SDP. + */ + if (dlg_data->x_ms_msg_session >= 0 && + dlg_data->x_ms_msg_session >= (int)sdp->media_count) + { + pjsdp_media_desc *m = pj_pool_calloc(dlg->pool, 1, sizeof(*m)); + sdp->media[sdp->media_count] = m; + dlg_data->x_ms_msg_session = sdp->media_count++; + } + + /* + * For "x-ms-message" line, remove all attributes and connection line etc. + */ + if (dlg_data->x_ms_msg_session >= 0) { + pjsdp_media_desc *m = sdp->media[dlg_data->x_ms_msg_session]; + if (m) { + m->desc.media = pj_str("x-ms-message"); + m->desc.port = 5060; + m->desc.transport = pj_str("sip"); + m->desc.fmt_count = 1; + m->desc.fmt[0] = pj_str("null"); + m->attr_count = 0; + m->conn = NULL; + } + } + + dlg_data->body = pj_pool_calloc(dlg->pool, 1, sizeof(*dlg_data->body)); + dlg_data->body->content_type.type = pj_str("application"); + dlg_data->body->content_type.subtype = pj_str("sdp"); + dlg_data->body->len = 0; /* ignored */ + dlg_data->body->print_body = &print_msg_body; + + dlg_data->body->data = sdp; + return dlg_data->body; +} + +/* This callback will be called on every occurence of events in dialogs */ +static void dlg_on_all_events(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt, + pjsip_event *event ) +{ + PJ_UNUSED_ARG(dlg_evt) + PJ_UNUSED_ARG(event) + + PJ_LOG(4, (THIS_FILE, "dlg_on_all_events %p", dlg)); +} + +/* This callback is called before each outgoing msg is sent (including + * retransmission). Application can override this notification if it wants + * to modify the message before transmission or if it wants to do something + * else for each transmission. + */ +static void dlg_on_before_tx(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata, int ret_cnt) +{ + PJ_UNUSED_ARG(tsx) + PJ_UNUSED_ARG(tdata) + + if (ret_cnt > 0) { + PJ_LOG(3, (THIS_FILE, "Dialog %s: retransmitting message (cnt=%d)", + dlg->obj_name, ret_cnt)); + } +} + +/* This callback is called after a message is sent. */ +static void dlg_on_tx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata) +{ + PJ_UNUSED_ARG(tsx) + PJ_UNUSED_ARG(tdata) + + PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg)); +} + +/* This callback is called on receipt of incoming message. */ +static void dlg_on_rx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_rx_data *rdata) +{ + PJ_UNUSED_ARG(tsx) + PJ_UNUSED_ARG(rdata) + PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg)); +} + +/* This callback is called after dialog has sent INVITE */ +static void dlg_on_calling(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata) +{ + PJ_UNUSED_ARG(tsx) + PJ_UNUSED_ARG(tdata) + + pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG && + tdata->msg->line.req.method.id == PJSIP_INVITE_METHOD && + tsx->method.id == PJSIP_INVITE_METHOD); + + PJ_LOG(3, (THIS_FILE, "Dialog %s: start calling...", dlg->obj_name)); +} + +static void create_session_from_sdp( pjsip_dlg *dlg, pjsdp_session_desc *sdp) +{ + struct dialog_data *dlg_data = dlg->user_data; + pj_bool_t sdp_x_ms_msg_index = -1; + int i; + int mcnt; + const pj_media_stream_info *mi[PJSDP_MAX_MEDIA]; + int has_active; + pj_media_sock_info sock_info; + + /* Find "m=x-ms-message" line in the SDP. */ + for (i=0; i<(int)sdp->media_count; ++i) { + if (pj_stricmp2(&sdp->media[i]->desc.media, "x-ms-message")==0) + sdp_x_ms_msg_index = i; + } + + /* + * Create media session. + */ + pj_memset(&sock_info, 0, sizeof(sock_info)); + sock_info.rtp_sock = global.rtp_sock; + sock_info.rtcp_sock = global.rtcp_sock; + pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in)); + + dlg_data->msession = pj_media_session_create_from_sdp (global.mmgr, sdp, &sock_info); + + /* A session will always be created, unless there is memory + * alloc problem. + */ + pj_assert(dlg_data->msession); + + /* See if we can take the offer by checking that we have at least + * one media stream active. + */ + mcnt = pj_media_session_enum_streams(dlg_data->msession, PJSDP_MAX_MEDIA, mi); + for (i=0, has_active=0; i<mcnt; ++i) { + if (mi[i]->fmt_cnt>0 && mi[i]->dir!=PJ_MEDIA_DIR_NONE) { + has_active = 1; + break; + } + } + + if (!has_active && sdp_x_ms_msg_index==-1) { + pjsip_tx_data *tdata; + + /* Unable to accept remote's SDP. + * Answer with 488 (Not Acceptable Here) + */ + /* Create 488 response. */ + tdata = pjsip_dlg_answer(dlg, PJSIP_SC_NOT_ACCEPTABLE_HERE); + + /* Send response. */ + if (tdata) + pjsip_dlg_send_msg(dlg, tdata); + return; + } + + dlg_data->x_ms_msg_session = sdp_x_ms_msg_index; + + /* Create msg body to be used later in 2xx/response */ + create_msg_body(dlg, 0); + +} + +/* This callback is called after an INVITE is received. */ +static void dlg_on_incoming(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_rx_data *rdata) +{ + struct dialog_data *dlg_data; + pjsip_msg *msg; + pjsip_tx_data *tdata; + char buf[128]; + int len; + + PJ_UNUSED_ARG(tsx) + + pj_assert(rdata->msg->type == PJSIP_REQUEST_MSG && + rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD && + tsx->method.id == PJSIP_INVITE_METHOD); + + /* + * Notify user! + */ + PJ_LOG(3, (THIS_FILE, "")); + PJ_LOG(3, (THIS_FILE, "INCOMING CALL ON DIALOG %s!!", dlg->obj_name)); + PJ_LOG(3, (THIS_FILE, "")); + len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR, + (pjsip_name_addr*)dlg->remote.info->uri, + buf, sizeof(buf)-1); + if (len > 0) { + buf[len] = '\0'; + PJ_LOG(3,(THIS_FILE, "From:\t%s", buf)); + } + len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR, + (pjsip_name_addr*)dlg->local.info->uri, + buf, sizeof(buf)-1); + if (len > 0) { + buf[len] = '\0'; + PJ_LOG(3,(THIS_FILE, "To:\t%s", buf)); + } + PJ_LOG(3, (THIS_FILE, "Press 'a' to answer, or 'h' to hangup!!", dlg->obj_name)); + PJ_LOG(3, (THIS_FILE, "")); + + /* + * Process incoming dialog. + */ + + dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data)); + dlg->user_data = dlg_data; + + /* Update contact. */ + pjsip_dlg_set_contact(dlg, &global.contact); + + /* Initialize credentials. */ + pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info); + + /* Create media session if the request has "application/sdp" body. */ + msg = rdata->msg; + if (msg->body && + pj_stricmp2(&msg->body->content_type.type, "application")==0 && + pj_stricmp2(&msg->body->content_type.subtype, "sdp")==0) + { + pjsdp_session_desc *sdp; + + /* Parse SDP body, and instantiate media session based on remote's SDP. + * Then create our SDP body from the session. + */ + sdp = pjsdp_parse (msg->body->data, msg->body->len, rdata->pool); + if (!sdp) + goto send_answer; + + create_session_from_sdp(dlg, sdp); + + } else if (msg->body) { + /* The request has a message body other than "application/sdp" */ + pjsip_accept_hdr *accept; + + /* Create response. */ + tdata = pjsip_dlg_answer(dlg, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); + + /* Add "Accept" header. */ + accept = pjsip_accept_hdr_create(tdata->pool); + accept->values[0] = pj_str("application/sdp"); + accept->count = 1; + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)accept); + + /* Send response. */ + pjsip_dlg_send_msg(dlg, tdata); + return; + + } else { + /* The request has no message body. We can take this request, but + * no media session will be activated. + */ + /* Nothing to do here. */ + } + +send_answer: + /* Immediately answer with 100 (or 180? */ + tdata = pjsip_dlg_answer( dlg, PJSIP_SC_RINGING ); + pjsip_dlg_send_msg(dlg, tdata); + + /* Set current dialog to this dialog if we don't currently have + * current dialog. + */ + if (global.cur_dlg == NULL) { + global.cur_dlg = dlg; + } + + /* Auto-answer if option is specified. */ + if (global.auto_answer >= 0) { + pj_time_val delay = { 0, 0}; + struct dialog_data *dlg_data = dlg->user_data; + + PJ_LOG(4, (THIS_FILE, "Scheduling auto-answer in %d seconds", + global.auto_answer)); + + delay.sec = global.auto_answer; + dlg_data->auto_timer.user_data = dlg; + dlg_data->auto_timer.id = AUTO_ANSWER; + dlg_data->auto_timer.cb = &dlg_auto_timer_callback; + dlg_data->has_auto_timer = 1; + pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay); + } +} + +/* This callback is called when dialog has sent/received a provisional response + * to INVITE. + */ +static void dlg_on_provisional(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_event *event) +{ + const char *action; + + pj_assert((event->src_type == PJSIP_EVENT_TX_MSG && + event->src.tdata->msg->type == PJSIP_RESPONSE_MSG && + event->src.tdata->msg->line.status.code/100 == 1 && + tsx->method.id == PJSIP_INVITE_METHOD) + || + (event->src_type == PJSIP_EVENT_RX_MSG && + event->src.rdata->msg->type == PJSIP_RESPONSE_MSG && + event->src.rdata->msg->line.status.code/100 == 1 && + tsx->method.id == PJSIP_INVITE_METHOD)); + + if (event->src_type == PJSIP_EVENT_TX_MSG) + action = "Sending"; + else + action = "Received"; + + PJ_LOG(3, (THIS_FILE, "Dialog %s: %s %d (%s)", + dlg->obj_name, action, tsx->status_code, + pjsip_get_status_text(tsx->status_code)->ptr)); +} + +/* This callback is called when 200 response to INVITE is sent/received. */ +static void dlg_on_connecting(pjsip_dlg *dlg, pjsip_event *event) +{ + struct dialog_data *dlg_data = dlg->user_data; + const char *action; + + pj_assert((event->src_type == PJSIP_EVENT_TX_MSG && + event->src.tdata->msg->type == PJSIP_RESPONSE_MSG && + event->src.tdata->msg->line.status.code/100 == 2) + || + (event->src_type == PJSIP_EVENT_RX_MSG && + event->src.rdata->msg->type == PJSIP_RESPONSE_MSG && + event->src.rdata->msg->line.status.code/100 == 2)); + + if (event->src_type == PJSIP_EVENT_RX_MSG) + action = "Received"; + else + action = "Sending"; + + PJ_LOG(3, (THIS_FILE, "Dialog %s: %s 200 (OK)", dlg->obj_name, action)); + + if (event->src_type == PJSIP_EVENT_RX_MSG) { + /* On receipt of 2xx response, negotiate our media capability + * and start media. + */ + pjsip_msg *msg = event->src.rdata->msg; + pjsip_msg_body *body; + pjsdp_session_desc *sdp; + + /* Get SDP from message. */ + + /* Ignore if no SDP body is present. */ + body = msg->body; + if (!body) { + PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no body!", + dlg->obj_name)); + return; + } + + if (pj_stricmp2(&body->content_type.type, "application") != 0 && + pj_stricmp2(&body->content_type.subtype, "sdp") != 0) + { + PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no SDP body!", + dlg->obj_name)); + return; + } + + /* Got what seems to be a SDP content. Parse it. */ + sdp = pjsdp_parse (body->data, body->len, event->src.rdata->pool); + if (!sdp) { + PJ_LOG(3, (THIS_FILE, "Dialog %s: SDP syntax error!", + dlg->obj_name)); + return; + } + + /* Negotiate media session with remote's media capability. */ + if (pj_media_session_update (dlg_data->msession, sdp) != 0) { + PJ_LOG(3, (THIS_FILE, "Dialog %s: media session update error!", + dlg->obj_name)); + return; + } + + /* Update the saved SDP body because media session has changed. + * Also set ack flag to '1', because we only want to send one format/ + * codec for each media streams. + */ + create_msg_body(dlg, 1); + + /* Activate media. */ + pj_media_session_activate (dlg_data->msession); + + } else { + pjsip_msg *msg = event->src.tdata->msg; + + if (msg->body) { + /* On transmission of 2xx response, start media session. */ + pj_media_session_activate (dlg_data->msession); + } + } + +} + +/* This callback is called when ACK to initial INVITE is sent/received. */ +static void dlg_on_established(pjsip_dlg *dlg, pjsip_event *event) +{ + const char *action; + + pj_assert((event->src_type == PJSIP_EVENT_TX_MSG && + event->src.tdata->msg->type == PJSIP_REQUEST_MSG && + event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD) + || + (event->src_type == PJSIP_EVENT_RX_MSG && + event->src.rdata->msg->type == PJSIP_REQUEST_MSG && + event->src.rdata->msg->line.req.method.id == PJSIP_ACK_METHOD)); + + if (event->src_type == PJSIP_EVENT_RX_MSG) + action = "Received"; + else + action = "Sending"; + + PJ_LOG(3, (THIS_FILE, "Dialog %s: %s ACK, dialog is ESTABLISHED", + dlg->obj_name, action)); + + /* Attach SDP body for outgoing ACK. */ + if (event->src_type == PJSIP_EVENT_TX_MSG && + event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD) + { + struct dialog_data *dlg_data = dlg->user_data; + event->src.tdata->msg->body = dlg_data->body; + } + + /* Auto-hangup if option is specified. */ + if (global.auto_hangup >= 0) { + pj_time_val delay = { 0, 0}; + struct dialog_data *dlg_data = dlg->user_data; + + PJ_LOG(4, (THIS_FILE, "Scheduling auto-hangup in %d seconds", + global.auto_hangup)); + + delay.sec = global.auto_hangup; + dlg_data->auto_timer.user_data = dlg; + dlg_data->auto_timer.id = AUTO_HANGUP; + dlg_data->auto_timer.cb = &dlg_auto_timer_callback; + dlg_data->has_auto_timer = 1; + pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay); + } +} + + +/* This callback is called when dialog is disconnected (because of final + * response, BYE, or timer). + */ +static void dlg_on_disconnected(pjsip_dlg *dlg, pjsip_event *event) +{ + struct dialog_data *dlg_data = dlg->user_data; + int status_code; + const pj_str_t *reason; + + PJ_UNUSED_ARG(event) + + /* Cancel auto-answer/auto-hangup timer. */ + if (dlg_data->has_auto_timer) { + pjsip_endpt_cancel_timer(dlg->ua->endpt, &dlg_data->auto_timer); + dlg_data->has_auto_timer = 0; + } + + if (dlg->invite_tsx) + status_code = dlg->invite_tsx->status_code; + else + status_code = 200; + + if (event->obj.tsx->method.id == PJSIP_INVITE_METHOD) { + if (event->src_type == PJSIP_EVENT_RX_MSG) + reason = &event->src.rdata->msg->line.status.reason; + else if (event->src_type == PJSIP_EVENT_TX_MSG) + reason = &event->src.tdata->msg->line.status.reason; + else + reason = pjsip_get_status_text(event->obj.tsx->status_code); + } else { + reason = &event->obj.tsx->method.name; + } + + PJ_LOG(3, (THIS_FILE, "Dialog %s: DISCONNECTED! Reason=%d (%.*s)", + dlg->obj_name, status_code, + reason->slen, reason->ptr)); + + if (dlg_data->msession) { + pj_media_session_destroy (dlg_data->msession); + dlg_data->msession = NULL; + } +} + +/* This callback is called when dialog is about to be destroyed. */ +static void dlg_on_terminated(pjsip_dlg *dlg) +{ + PJ_LOG(3, (THIS_FILE, "Dialog %s: terminated!", dlg->obj_name)); + + /* If current dialog is equal to this dialog, update it. */ + if (global.cur_dlg == dlg) { + global.cur_dlg = global.cur_dlg->next; + if (global.cur_dlg == (void*)&global.user_agent->dlg_list) { + global.cur_dlg = NULL; + } + } +} + +/* This callback is called for any requests when dialog is established. */ +static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event) +{ + pjsip_transaction *tsx = event->obj.tsx; + + if (event->src_type == PJSIP_EVENT_RX_MSG && + event->src.rdata->msg->type == PJSIP_REQUEST_MSG) + { + if (event->src.rdata->cseq->method.id == PJSIP_INVITE_METHOD) { + /* Re-invitation. */ + pjsip_tx_data *tdata; + + PJ_LOG(3,(THIS_FILE, "Dialog %s: accepting re-invitation (dummy)", + dlg->obj_name)); + tdata = pjsip_dlg_answer(dlg, 200); + if (tdata) { + struct dialog_data *dlg_data = dlg->user_data; + tdata->msg->body = dlg_data->body; + pjsip_dlg_send_msg(dlg, tdata); + } + } else { + /* Don't worry, endpoint will answer with 500 or whetever. */ + } + + } else if (tsx->status_code/100 == 2) { + PJ_LOG(3,(THIS_FILE, "Dialog %s: outgoing %.*s success: %d (%s)", + dlg->obj_name, + tsx->method.name.slen, tsx->method.name.ptr, + tsx->status_code, + pjsip_get_status_text(tsx->status_code)->ptr)); + + + } else if (tsx->status_code >= 300) { + pj_bool_t report_failure = PJ_TRUE; + + /* Check for authentication failures. */ + if (tsx->status_code==401 || tsx->status_code==407) { + pjsip_tx_data *tdata; + tdata = pjsip_auth_reinit_req( global.endpt, + dlg->pool, &dlg->auth_sess, + dlg->cred_count, dlg->cred_info, + tsx->last_tx, event->src.rdata ); + if (tdata) { + int rc; + rc = pjsip_dlg_send_msg( dlg, tdata); + report_failure = (rc != 0); + } + } + if (report_failure) { + const pj_str_t *reason; + if (event->src_type == PJSIP_EVENT_RX_MSG) { + reason = &event->src.rdata->msg->line.status.reason; + } else { + reason = pjsip_get_status_text(tsx->status_code); + } + PJ_LOG(2,(THIS_FILE, "Dialog %s: outgoing request failed: %d (%.*s)", + dlg->obj_name, tsx->status_code, + reason->slen, reason->ptr)); + } + } +} + +/* Initialize sockets and optionally get the public address via STUN. */ +static pj_status_t init_sockets() +{ + enum { + RTP_START_PORT = 4000, + RTP_RANDOM_START = 2, + RTP_RETRY = 10 + }; + enum { + SIP_SOCK, + RTP_SOCK, + RTCP_SOCK, + }; + int i; + int rtp_port; + pj_sock_t sock[3]; + pj_sockaddr_in mapped_addr[3]; + + for (i=0; i<3; ++i) + sock[i] = PJ_INVALID_SOCKET; + + /* Create and bind SIP UDP socket. */ + sock[SIP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0); + if (sock[SIP_SOCK] == PJ_INVALID_SOCKET) { + PJ_LOG(2,(THIS_FILE, "Unable to create socket")); + goto on_error; + } + if (pj_sock_bind_in(sock[SIP_SOCK], 0, (pj_uint16_t)global.sip_port) != 0) { + PJ_LOG(2,(THIS_FILE, "Unable to bind to SIP port")); + goto on_error; + } + + /* Initialize start of RTP port to try. */ + rtp_port = RTP_START_PORT + (pj_rand() % RTP_RANDOM_START) / 2; + + /* Loop retry to bind RTP and RTCP sockets. */ + for (i=0; i<RTP_RETRY; ++i, rtp_port += 2) { + + /* Create and bind RTP socket. */ + sock[RTP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0); + if (sock[RTP_SOCK] == PJ_INVALID_SOCKET) + goto on_error; + if (pj_sock_bind_in(sock[RTP_SOCK], 0, (pj_uint16_t)rtp_port) != 0) { + pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET; + continue; + } + + /* Create and bind RTCP socket. */ + sock[RTCP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0); + if (sock[RTCP_SOCK] == PJ_INVALID_SOCKET) + goto on_error; + if (pj_sock_bind_in(sock[RTCP_SOCK], 0, (pj_uint16_t)(rtp_port+1)) != 0) { + pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET; + pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET; + continue; + } + + /* + * If we're configured to use STUN, then find out the mapped address, + * and make sure that the mapped RTCP port is adjacent with the RTP. + */ + if (global.stun_port1 == 0) { + pj_str_t hostname; + pj_sockaddr_in addr; + + /* Get local IP address. */ + char hostname_buf[PJ_MAX_HOSTNAME]; + if (gethostname(hostname_buf, sizeof(hostname_buf))) + goto on_error; + hostname = pj_str(hostname_buf); + + pj_memset( &addr, 0, sizeof(addr)); + addr.sin_family = PJ_AF_INET; + if (pj_sockaddr_set_str_addr( &addr, &hostname) != PJ_SUCCESS) + goto on_error; + + for (i=0; i<3; ++i) + pj_memcpy(&mapped_addr[i], &addr, sizeof(addr)); + + mapped_addr[SIP_SOCK].sin_port = pj_htons((pj_uint16_t)global.sip_port); + mapped_addr[RTP_SOCK].sin_port = pj_htons((pj_uint16_t)rtp_port); + mapped_addr[RTCP_SOCK].sin_port = pj_htons((pj_uint16_t)(rtp_port+1)); + break; + } else { + pj_status_t rc; + rc = pj_stun_get_mapped_addr( global.pf, 3, sock, + &global.stun_srv1, global.stun_port1, + &global.stun_srv2, global.stun_port2, + mapped_addr); + if (rc != 0) { + PJ_LOG(3,(THIS_FILE, "Error: %s", pj_stun_get_err_msg(rc))); + goto on_error; + } + + if (pj_ntohs(mapped_addr[2].sin_port) == pj_ntohs(mapped_addr[1].sin_port)+1) + break; + + pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET; + pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET; + } + } + + if (sock[RTP_SOCK] == PJ_INVALID_SOCKET) { + PJ_LOG(2,(THIS_FILE, "Unable to find appropriate RTP/RTCP ports combination")); + goto on_error; + } + + global.sip_sock = sock[SIP_SOCK]; + pj_memcpy(&global.sip_sock_name, &mapped_addr[SIP_SOCK], sizeof(pj_sockaddr_in)); + global.rtp_sock = sock[RTP_SOCK]; + pj_memcpy(&global.rtp_sock_name, &mapped_addr[RTP_SOCK], sizeof(pj_sockaddr_in)); + global.rtcp_sock = sock[RTCP_SOCK]; + pj_memcpy(&global.rtcp_sock_name, &mapped_addr[RTCP_SOCK], sizeof(pj_sockaddr_in)); + + PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d", + pj_inet_ntoa(global.sip_sock_name.sin_addr), + pj_ntohs(global.sip_sock_name.sin_port))); + PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s:%d", + pj_inet_ntoa(global.rtp_sock_name.sin_addr), + pj_ntohs(global.rtp_sock_name.sin_port))); + PJ_LOG(4,(THIS_FILE, "RTCP UDP socket reachable at %s:%d", + pj_inet_ntoa(global.rtcp_sock_name.sin_addr), + pj_ntohs(global.rtcp_sock_name.sin_port))); + return 0; + +on_error: + for (i=0; i<3; ++i) { + if (sock[i] != PJ_INVALID_SOCKET) + pj_sock_close(sock[i]); + } + return -1; +} + +static void log_function(int level, const char *buffer, int len) +{ + /* Write to both stdout and file. */ + if (level <= global.app_log_level) + pj_log_to_stdout(level, buffer, len); + if (global.log_file) { + fwrite(buffer, len, 1, global.log_file); + fflush(global.log_file); + } +} + +/* Initialize stack. */ +static pj_status_t init_stack() +{ + pj_status_t status; + pj_sockaddr_in bind_addr; + pj_sockaddr_in bind_name; + const char *local_addr; + static char local_uri[128]; + + /* Optionally set logging file. */ + if (global.log_filename) { + global.log_file = fopen(global.log_filename, "wt"); + } + + /* Initialize endpoint. This will also call initialization to all the + * modules. + */ + global.endpt = pjsip_endpt_create(global.pf); + if (global.endpt == NULL) { + return -1; + } + + /* Set dialog callback. */ + pjsip_ua_set_dialog_callback(global.user_agent, &dlg_callback); + + /* Init listener's bound address and port. */ + pj_sockaddr_init2(&bind_addr, "0.0.0.0", global.sip_port); + pj_sockaddr_init(&bind_name, pj_gethostname(), global.sip_port); + + /* Add UDP transport listener. */ + status = pjsip_endpt_create_udp_listener( global.endpt, global.sip_sock, + &global.sip_sock_name); + if (status != 0) + return -1; + + local_addr = pj_inet_ntoa(global.sip_sock_name.sin_addr); + +#if PJ_HAS_TCP + /* Add TCP transport listener. */ + status = pjsip_endpt_create_listener( global.endpt, PJSIP_TRANSPORT_TCP, + &bind_addr, &bind_name); + if (status != 0) + return -1; +#endif + + /* Determine user_id to be put in Contact */ + if (global.local_uri.slen) { + pj_pool_t *pool = pj_pool_create(global.pf, "parser", 1024, 0, NULL); + pjsip_uri *uri; + + uri = pjsip_parse_uri(pool, global.local_uri.ptr, global.local_uri.slen, 0); + if (uri) { + if (pj_stricmp2(pjsip_uri_get_scheme(uri), "sip")==0) { + pjsip_url *url = (pjsip_url*)pjsip_uri_get_uri(uri); + if (url->user.slen) + strncpy(global.user_id, url->user.ptr, url->user.slen); + } + } + pj_pool_release(pool); + } + + if (global.user_id[0]=='\0') { + pj_native_strcpy(global.user_id, "user"); + } + + /* build contact */ + global.real_contact.ptr = local_uri; + global.real_contact.slen = + sprintf(local_uri, "<sip:%s@%s:%d>", global.user_id, local_addr, global.sip_port); + + if (global.contact.slen == 0) + global.contact = global.real_contact; + + /* initialize local_uri with contact if it's not specified in cmdline */ + if (global.local_uri.slen == 0) + global.local_uri = global.contact; + + /* Init proxy. */ + if (global.proxy.slen || global.outbound_proxy.slen) { + int count = 0; + pj_str_t proxy_url[2]; + + if (global.outbound_proxy.slen) { + proxy_url[count++] = global.outbound_proxy; + } + if (global.proxy.slen) { + proxy_url[count++] = global.proxy; + } + + if (pjsip_endpt_set_proxies(global.endpt, count, proxy_url) != 0) { + PJ_LOG(2,(THIS_FILE, "Error setting proxy address!")); + return -1; + } + } + + /* initialize SIP registration if registrar is configured */ + if (global.registrar_uri.slen) { + global.regc = pjsip_regc_create( global.endpt, NULL, ®c_cb); + pjsip_regc_init( global.regc, &global.registrar_uri, + &global.local_uri, + &global.local_uri, + 1, &global.contact, + global.reg_timeout); + pjsip_regc_set_credentials( global.regc, global.cred_count, global.cred_info ); + } + + return PJ_SUCCESS; +} + +/* Worker thread function, only used when threading is enabled. */ +static void *PJ_THREAD_FUNC worker_thread(void *unused) +{ + PJ_UNUSED_ARG(unused) + + while (!global.worker_quit_flag) { + pj_time_val timeout = { 0, 10 }; + pjsip_endpt_handle_events (global.endpt, &timeout); + } + return NULL; +} + + +/* Make call to the specified URI. */ +static pjsip_dlg *make_call(pj_str_t *remote_uri) +{ + pjsip_dlg *dlg; + pj_str_t local = global.contact; + pj_str_t remote = *remote_uri; + struct dialog_data *dlg_data; + pjsip_tx_data *tdata; + pj_media_sock_info sock_info; + + /* Create new dialog instance. */ + dlg = pjsip_ua_create_dialog(global.user_agent, PJSIP_ROLE_UAC); + + /* Attach our own user data. */ + dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data)); + dlg->user_data = dlg_data; + + /* Create media session. */ + pj_memset(&sock_info, 0, sizeof(sock_info)); + sock_info.rtp_sock = global.rtp_sock; + sock_info.rtcp_sock = global.rtcp_sock; + pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in)); + + dlg_data->msession = pj_media_session_create (global.mmgr, &sock_info); + dlg_data->x_ms_msg_session = -1; + + if (global.offer_x_ms_msg) { + const pj_media_stream_info *minfo[32]; + unsigned cnt; + + cnt = pj_media_session_enum_streams(dlg_data->msession, 32, minfo); + if (cnt > 0) + dlg_data->x_ms_msg_session = cnt; + } + + /* Initialize dialog with local and remote URI. */ + if (pjsip_dlg_init(dlg, &local, &remote, NULL) != PJ_SUCCESS) { + pjsip_ua_destroy_dialog(dlg); + return NULL; + } + + /* Initialize credentials. */ + pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info); + + /* Send INVITE! */ + tdata = pjsip_dlg_invite(dlg); + tdata->msg->body = create_msg_body (dlg, 0); + + if (pjsip_dlg_send_msg(dlg, tdata) != PJ_SUCCESS) { + pjsip_ua_destroy_dialog(dlg); + return NULL; + } + + return dlg; +} + +/* + * Callback to receive incoming IM message. + */ +static int on_incoming_im_msg(pjsip_rx_data *rdata) +{ + pjsip_msg *msg = rdata->msg; + pjsip_msg_body *body = msg->body; + int len; + char to[128], from[128]; + + + len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR, + rdata->from->uri, from, sizeof(from)); + if (len > 0) from[len] = '\0'; + else pj_native_strcpy(from, "<URL too long..>"); + + len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR, + rdata->to->uri, to, sizeof(to)); + if (len > 0) to[len] = '\0'; + else pj_native_strcpy(to, "<URL too long..>"); + + PJ_LOG(3,(THIS_FILE, "Incoming instant message:")); + + printf("----- BEGIN INSTANT MESSAGE ----->\n"); + printf("From:\t%s\n", from); + printf("To:\t%s\n", to); + printf("Body:\n%.*s\n", (body ? body->len : 0), (body ? (char*)body->data : "")); + printf("<------ END INSTANT MESSAGE ------\n"); + + fflush(stdout); + + /* Must answer with final response. */ + return 200; +} + +/* + * Input URL. + */ +static pj_str_t *ui_input_url(pj_str_t *out, char *buf, int len, int *selection) +{ + int i; + + *selection = -1; + + printf("\nBuddy list:\n"); + printf("---------------------------------------\n"); + for (i=0; i<global.buddy_cnt; ++i) { + printf(" %d\t%s <%s>\n", i+1, global.buddy[i].ptr, + (global.buddy_status[i]?"Online":"Offline")); + } + printf("-------------------------------------\n"); + + printf("Choices\n" + "\t0 For current dialog.\n" + "\t[1-%02d] Select from buddy list\n" + "\tURL An URL\n" + , global.buddy_cnt); + printf("Input: "); + + fflush(stdout); + fgets(buf, len, stdin); + buf[strlen(buf)-1] = '\0'; /* remove trailing newline. */ + + while (isspace(*buf)) ++buf; + + if (!*buf || *buf=='\n' || *buf=='\r') + return NULL; + + i = atoi(buf); + + if (i == 0) { + if (isdigit(*buf)) { + *selection = 0; + *out = pj_str("0"); + return out; + } else { + if (verify_sip_url(buf) != 0) { + puts("Invalid URL specified!"); + return NULL; + } + *out = pj_str(buf); + return out; + } + } else if (i > global.buddy_cnt || i < 0) { + printf("Error: invalid selection!\n"); + return NULL; + } else { + *out = global.buddy[i-1]; + *selection = i; + return out; + } +} + + +static void generic_request_callback( void *token, pjsip_event *event ) +{ + pjsip_transaction *tsx = event->obj.tsx; + + PJ_UNUSED_ARG(token) + + if (tsx->status_code/100 == 2) { + PJ_LOG(3,(THIS_FILE, "Outgoing %.*s %d (%s)", + event->obj.tsx->method.name.slen, + event->obj.tsx->method.name.ptr, + tsx->status_code, + pjsip_get_status_text(tsx->status_code)->ptr)); + } else if (tsx->status_code==401 || tsx->status_code==407) { + pjsip_tx_data *tdata; + tdata = pjsip_auth_reinit_req( global.endpt, + global.pool, NULL, global.cred_count, global.cred_info, + tsx->last_tx, event->src.rdata); + if (tdata) { + int rc; + pjsip_cseq_hdr *cseq; + cseq = (pjsip_cseq_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); + cseq->cseq++; + rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL, + &generic_request_callback); + if (rc == 0) + return; + } + PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%s)", + event->obj.tsx->method.name.slen, + event->obj.tsx->method.name.ptr, + event->obj.tsx->status_code, + pjsip_get_status_text(event->obj.tsx->status_code)->ptr)); + } else { + const pj_str_t *reason; + if (event->src_type == PJSIP_EVENT_RX_MSG) + reason = &event->src.rdata->msg->line.status.reason; + else + reason = pjsip_get_status_text(tsx->status_code); + PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%.*s)", + event->obj.tsx->method.name.slen, + event->obj.tsx->method.name.ptr, + event->obj.tsx->status_code, + reason->slen, reason->ptr)); + } +} + + +static void ui_send_im_message() +{ + char line[100]; + char text_buf[100]; + pj_str_t str; + pj_str_t text_msg; + int selection, rc; + pjsip_tx_data *tdata; + + if (ui_input_url(&str, line, sizeof(line), &selection) == NULL) + return; + + + printf("Enter text to send (empty to cancel): "); fflush(stdout); + fgets(text_buf, sizeof(text_buf), stdin); + text_buf[strlen(text_buf)-1] = '\0'; + if (!*text_buf) + return; + + text_msg = pj_str(text_buf); + + if (selection==0) { + pjsip_method message_method; + pj_str_t str_MESSAGE = { "MESSAGE", 7 }; + + /* Send IM to current dialog. */ + if (global.cur_dlg == NULL || global.cur_dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) { + printf("No current dialog or dialog state is not ESTABLISHED!\n"); + return; + } + + pjsip_method_init( &message_method, global.cur_dlg->pool, &str_MESSAGE); + tdata = pjsip_dlg_create_request( global.cur_dlg, &message_method, -1 ); + + if (tdata) { + /* Create message body for the text. */ + pjsip_msg_body *body = pj_pool_calloc(tdata->pool, 1, sizeof(*body)); + body->content_type.type = pj_str("text"); + body->content_type.subtype = pj_str("plain"); + body->data = pj_pool_alloc(tdata->pool, text_msg.slen); + pj_memcpy(body->data, text_msg.ptr, text_msg.slen); + body->len = text_msg.slen; + body->print_body = &pjsip_print_text_body; + + /* Assign body to message, and send the message! */ + tdata->msg->body = body; + pjsip_dlg_send_msg( global.cur_dlg, tdata ); + } + + } else { + /* Send IM to buddy list. */ + pjsip_method message; + static pj_str_t MESSAGE = { "MESSAGE", 7 }; + pjsip_method_init_np(&message, &MESSAGE); + tdata = pjsip_endpt_create_request(global.endpt, &message, + &str, + &global.real_contact, + &str, &global.real_contact, NULL, -1, + &text_msg); + if (!tdata) { + puts("Error creating request"); + return; + } + rc = pjsip_endpt_send_request(global.endpt, tdata, -1, NULL, &generic_request_callback); + if (rc == 0) { + printf("Sending IM message %d\n", global.im_counter); + ++global.im_counter; + } else { + printf("Error: unable to send IM message!\n"); + } + } +} + +static void ui_send_options() +{ + char line[100]; + pj_str_t str; + int selection, rc; + pjsip_tx_data *tdata; + pjsip_method options; + + if (ui_input_url(&str, line, sizeof(line), &selection) == NULL) + return; + + pjsip_method_set( &options, PJSIP_OPTIONS_METHOD ); + + if (selection == 0) { + /* Send OPTIONS to current dialog. */ + tdata = pjsip_dlg_create_request(global.cur_dlg, &options, -1); + if (tdata) + pjsip_dlg_send_msg( global.cur_dlg, tdata ); + } else { + /* Send OPTIONS to arbitrary party. */ + tdata = pjsip_endpt_create_request( global.endpt, &options, + &str, + &global.local_uri, &str, + &global.real_contact, + NULL, -1, NULL); + if (tdata) { + rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL, + &generic_request_callback); + if (rc != 0) + PJ_LOG(2,(THIS_FILE, "Error sending OPTIONS!")); + } + } +} + +static void init_presence() +{ + const pjsip_presence_cb pres_cb = { + NULL, + &pres_on_received_request, + &pres_on_received_refresh, + &pres_on_received_update, + &pres_on_terminated + }; + + pjsip_presence_init(&pres_cb); +} + +/* Subscribe presence information for all buddies. */ +static void subscribe_buddies_presence() +{ + int i; + for (i=0; i<global.buddy_cnt; ++i) { + pjsip_presentity *pres; + if (global.buddy_pres[i]) + continue; + pres = pjsip_presence_create( global.endpt, &global.local_uri, + &global.buddy[i], PRESENCE_TIMEOUT, (void*)i); + if (pres) { + pjsip_presence_set_credentials( pres, global.cred_count, global.cred_info ); + pjsip_presence_subscribe( pres ); + } + global.buddy_pres[i] = pres; + } +} + +/* Unsubscribe presence information for all buddies. */ +static void unsubscribe_buddies_presence() +{ + int i; + for (i=0; i<global.buddy_cnt; ++i) { + pjsip_presentity *pres = global.buddy_pres[i]; + if (pres) { + pjsip_presence_unsubscribe(pres); + pjsip_presence_destroy(pres); + global.buddy_pres[i] = NULL; + } + } +} + +/* Unsubscribe presence. */ +static void unsubscribe_presence() +{ + int i; + + unsubscribe_buddies_presence(); + for (i=0; i<global.pres_cnt; ++i) { + pjsip_presentity *pres = global.pres[i]; + pjsip_presence_notify( pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 0); + pjsip_presence_destroy( pres ); + } +} + +/* Advertise online status to subscribers. */ +static void update_im_status() +{ + int i; + for (i=0; i<global.pres_cnt; ++i) { + pjsip_presentity *pres = global.pres[i]; + pjsip_presence_notify( pres, PJSIP_EVENT_SUB_STATE_ACTIVE, + !global.hide_status); + } +} + +/* + * Main program. + */ +int main(int argc, char *argv[]) +{ + /* set to WORKER_COUNT+1 to avoid zero size warning + * when threading is disabled. */ + pj_thread_t *thread[WORKER_COUNT+1]; + pj_caching_pool cp; + int i; + + global.sip_port = 5060; + global.auto_answer = -1; + global.auto_hangup = -1; + global.app_log_level = 3; + + pj_log_set_level(4); + pj_log_set_log_func(&log_function); + + /* Init PJLIB */ + if (pj_init() != PJ_SUCCESS) + return 1; + + /* Init caching pool. */ + pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); + global.pf = &cp.factory; + + /* Create memory pool for application. */ + global.pool = pj_pool_create(global.pf, "main", 1024, 0, NULL); + + /* Parse command line arguments. */ + if (parse_args(global.pool, argc, argv) != PJ_SUCCESS) { + pj_caching_pool_destroy(&cp); + return 1; + } + + /* Init sockets */ + if (init_sockets() != 0) { + pj_caching_pool_destroy(&cp); + return 1; + } + + /* Initialize stack. */ + if (init_stack() != PJ_SUCCESS) { + pj_caching_pool_destroy(&cp); + return 1; + } + + /* Set callback to receive incoming IM */ + pjsip_messaging_set_incoming_callback( &on_incoming_im_msg ); + + /* Set default worker count (can be zero) */ + global.worker_cnt = WORKER_COUNT; + + /* Create user worker thread(s), only when threading is enabled. */ + for (i=0; i<global.worker_cnt; ++i) { + thread[i] = pj_thread_create( global.pool, "sip%p", + &worker_thread, + NULL, 0, NULL, 0); + if (thread == NULL) { + global.worker_quit_flag = 1; + for (--i; i>=0; --i) { + pj_thread_join(thread[i]); + pj_thread_destroy(thread[i]); + } + pj_caching_pool_destroy(&cp); + return 1; + } + } + + printf("Worker thread count: %d\n", global.worker_cnt); + + /* Perform registration, if required. */ + if (global.regc) { + update_registration(global.regc, 1); + } + + /* Initialize media manager. */ + global.mmgr = pj_med_mgr_create(global.pf); + + /* Init presence. */ + init_presence(); + + /* Subscribe presence information of all buddies. */ + if (!global.no_presence) + subscribe_buddies_presence(); + + /* Initializatio completes, loop waiting for commands. */ + for (;!global.worker_quit_flag;) { + pj_str_t str; + char line[128]; + +#if WORKER_COUNT==0 + /* If worker thread does not exist, main thread must poll for evetns. + * But this won't work very well since main thread is blocked by + * fgets(). So keep pressing the ENTER key to get the events! + */ + pj_time_val timeout = { 0, 100 }; + pjsip_endpt_handle_events(global.endpt, &timeout); + puts("Keep pressing ENTER key to get the events!"); +#endif + + printf("\nCurrent dialog: "); + print_dialog(global.cur_dlg); + puts(""); + + keystroke_help(); + + fgets(line, sizeof(line), stdin); + + switch (*line) { + case 'm': + puts("Make outgoing call"); + if (ui_input_url(&str, line, sizeof(line), &i) != NULL) { + pjsip_dlg *dlg = make_call(&str); + if (global.cur_dlg == NULL) { + global.cur_dlg = dlg; + } + } + break; + case 'i': + puts("Send Instant Messaging"); + ui_send_im_message(); + break; + case 'o': + puts("Send OPTIONS"); + ui_send_options(); + break; + case 'a': + if (global.cur_dlg) { + unsigned code; + pjsip_tx_data *tdata; + struct dialog_data *dlg_data = global.cur_dlg->user_data; + + printf("Answer with status code (1xx-6xx): "); + fflush(stdout); + fgets(line, sizeof(line), stdin); + str = pj_str(line); + str.slen -= 1; + + code = pj_strtoul(&str); + tdata = pjsip_dlg_answer(global.cur_dlg, code); + if (tdata) { + if (code/100 == 2) { + tdata->msg->body = dlg_data->body; + } + pjsip_dlg_send_msg(global.cur_dlg, tdata); + + } + } else { + puts("No current dialog"); + } + break; + case 'h': + if (global.cur_dlg) { + pjsip_tx_data *tdata; + tdata = pjsip_dlg_disconnect(global.cur_dlg, PJSIP_SC_DECLINE); + if (tdata) { + pjsip_dlg_send_msg(global.cur_dlg, tdata); + } + } else { + puts("No current dialog"); + } + break; + case ']': + if (global.cur_dlg) { + global.cur_dlg = global.cur_dlg->next; + if (global.cur_dlg == (void*)&global.user_agent->dlg_list) { + global.cur_dlg = global.cur_dlg->next; + } + } else { + puts("No current dialog"); + } + break; + case '[': + if (global.cur_dlg) { + global.cur_dlg = global.cur_dlg->prev; + if (global.cur_dlg == (void*)&global.user_agent->dlg_list) { + global.cur_dlg = global.cur_dlg->prev; + } + } else { + puts("No current dialog"); + } + break; + case 'd': + pjsip_endpt_dump(global.endpt, *(line+1)=='1'); + pjsip_ua_dump(global.user_agent); + break; + case 's': + if (*(line+1) == 'u') + subscribe_buddies_presence(); + break; + case 'u': + if (*(line+1) == 's') + unsubscribe_presence(); + break; + case 't': + global.hide_status = !global.hide_status; + update_im_status(); + break; + case 'q': + goto on_exit; + case 'l': + print_all_dialogs(); + break; + } + } + +on_exit: + /* Unregister, if required. */ + if (global.regc) { + update_registration(global.regc, 0); + } + + /* Unsubscribe presence. */ + unsubscribe_presence(); + + /* Allow one second to get all events. */ + if (1) { + pj_time_val end_time; + + pj_gettimeofday(&end_time); + end_time.sec++; + + PJ_LOG(3,(THIS_FILE, "Shutting down..")); + for (;;) { + pj_time_val timeout = { 0, 20 }, now; + pjsip_endpt_handle_events (global.endpt, &timeout); + pj_gettimeofday(&now); + PJ_TIME_VAL_SUB(now, end_time); + if (now.sec >= 1) + break; + } + } + + global.worker_quit_flag = 1; + + pj_med_mgr_destroy(global.mmgr); + + /* Wait all threads to quit. */ + for (i=0; i<global.worker_cnt; ++i) { + pj_thread_join(thread[i]); + pj_thread_destroy(thread[i]); + } + + /* Destroy endpoint. */ + pjsip_endpt_destroy(global.endpt); + + /* Destroy caching pool. */ + pj_caching_pool_destroy(&cp); + + /* Close log file, if any. */ + if (global.log_file) + fclose(global.log_file); + + return 0; +} + +/* + * Register static modules to the endpoint. + */ +pj_status_t register_static_modules( pj_size_t *count, + pjsip_module **modules ) +{ + /* Reset count. */ + *count = 0; + + /* Register user agent module. */ + modules[(*count)++] = pjsip_ua_get_module(); + global.user_agent = modules[0]->mod_data; + modules[(*count)++] = pjsip_messaging_get_module(); + modules[(*count)++] = pjsip_event_sub_get_module(); + + return PJ_SUCCESS; +} diff --git a/pjsip/src/pjsua/misc.c b/pjsip/src/pjsua/misc.c index a9b3510b..5c063fc7 100644 --- a/pjsip/src/pjsua/misc.c +++ b/pjsip/src/pjsua/misc.c @@ -1,485 +1,485 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-/*
- * THIS FILE IS INCLUDED BY main.c.
- * IT WON'T COMPILE BY ITSELF.
- */
-
-#include "getopt.h"
-#include <stdio.h>
-
-
-/*
- * Display program usage
- */
-static void usage()
-{
- puts("Usage:");
- puts(" pjsua [options] [sip-url]");
- puts("");
- puts(" [sip-url] Default URL to invite.");
- puts("");
- puts("General options:");
- puts(" --config-file=file Read the config/arguments from file.");
- puts(" --log-file=fname Log to filename (default stderr)");
- puts(" --log-level=N Set log max level to N (0(none) to 6(trace))");
- puts(" --app-log-level=N Set log max level for stdout display to N");
- puts(" --help Display this help screen");
- puts(" --version Display version info");
- puts("");
- puts("Media options:");
- puts(" --null-audio Use NULL audio device");
- puts("");
- puts("User Agent options:");
- puts(" --auto-answer=sec Auto-answer all incoming calls after sec seconds.");
- puts(" --auto-hangup=sec Auto-hangup all calls after sec seconds.");
- puts("");
- puts("SIP options:");
- puts(" --local-port=port Set TCP/UDP port");
- puts(" --id=url Set the URL of local ID (used in From header)");
- puts(" --contact=url Override the Contact information");
- puts(" --proxy=url Set the URL of proxy server");
- puts(" --outbound=url Set the URL of outbound proxy server");
- puts(" --registrar=url Set the URL of registrar server");
- puts(" --reg-timeout=secs Set registration interval to secs (default 3600)");
- puts("");
- puts("Authentication options:");
- puts(" --realm=string Set realm");
- puts(" --username=string Set authentication username");
- puts(" --password=string Set authentication password");
- puts("");
- puts("STUN options (all must be specified):");
- puts(" --use-stun1=host[:port]");
- puts(" --use-stun2=host[:port] Use STUN and set host name and port of STUN servers");
- puts("");
- puts("SIMPLE options (may be specified more than once):");
- puts(" --add-buddy url Add the specified URL to the buddy list.");
- puts(" --offer-x-ms-msg Offer \"x-ms-message\" in outgoing INVITE");
- puts(" --no-presence Do not subscribe presence of buddies");
- puts("");
- fflush(stdout);
-}
-
-/* Display keystroke help. */
-static void keystroke_help()
-{
- int i;
-
- printf("Advertise status as: %s\n", (global.hide_status ? "Offline" : "Online"));
- puts("");
- puts("Buddy list:");
- puts("-------------------------------------------------------------------------------");
- for (i=0; i<global.buddy_cnt; ++i) {
- printf(" %d\t%s <%s>\n", i+1, global.buddy[i].ptr,
- (global.buddy_status[i]?"Online":"Offline"));
- }
- //printf("-------------------------------------\n");
- puts("");
- //puts("Commands:");
- puts("+=============================================================================+");
- puts("| Call Commands: | IM & Presence: | Misc: |");
- puts("| | | |");
- puts("| m Make new call | i Send IM | o Send OPTIONS |");
- puts("| a Answer call | su Subscribe presence | d Dump status |");
- puts("| h Hangup call | us Unsubscribe presence | d1 Dump detailed |");
- puts("| ] Select next dialog | t Toggle Online status | |");
- puts("| [ Select previous dialog | | |");
- puts("+-----------------------------------------------------------------------------+");
- puts("| q QUIT |");
- puts("+=============================================================================+");
- puts("");
-
-
- fflush(stdout);
-}
-
-/*
- * Verify that valid SIP url is given.
- */
-static pj_status_t verify_sip_url(char *url)
-{
- pjsip_uri *p;
- pj_pool_t *pool;
- int len = (url ? strlen(url) : 0);
-
- if (!len) return -1;
-
- pool = pj_pool_create(global.pf, "check%p", 1024, 0, NULL);
- if (!pool) return -1;
-
- p = pjsip_parse_uri(pool, url, len, 0);
- if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0)
- p = NULL;
-
- pj_pool_release(pool);
- return p ? 0 : -1;
-}
-
-/*
- * Read command arguments from config file.
- */
-static int read_config_file(pj_pool_t *pool, const char *filename,
- int *app_argc, char ***app_argv)
-{
- int i;
- FILE *fhnd;
- char line[200];
- int argc = 0;
- char **argv;
- enum { MAX_ARGS = 64 };
-
- /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */
- argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
- argv[argc++] = *app_argv[0];
-
- /* Open config file. */
- fhnd = fopen(filename, "rt");
- if (!fhnd) {
- printf("Unable to open config file %s\n", filename);
- return -1;
- }
-
- /* Scan tokens in the file. */
- while (argc < MAX_ARGS && !feof(fhnd)) {
- char *token, *p = line;
-
- if (fgets(line, sizeof(line), fhnd) == NULL) break;
-
- for (token = strtok(p, " \t\r\n"); argc < MAX_ARGS;
- token = strtok(NULL, " \t\r\n"))
- {
- int token_len;
-
- if (!token) break;
- if (*token == '#') break;
-
- token_len = strlen(token);
- if (!token_len)
- continue;
- argv[argc] = pj_pool_alloc(pool, token_len+1);
- pj_memcpy(argv[argc], token, token_len+1);
- ++argc;
- }
- }
-
- /* Copy arguments from command line */
- for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
- argv[argc++] = (*app_argv)[i];
-
- if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
- printf("Too many arguments specified in cmd line/config file\n");
- fclose(fhnd);
- return -1;
- }
-
- fclose(fhnd);
-
- /* Assign the new command line back to the original command line. */
- *app_argc = argc;
- *app_argv = argv;
- return 0;
-
-}
-
-/*
- * Parse program arguments
- */
-static int parse_args(pj_pool_t *pool, int argc, char *argv[])
-{
- int c;
- int option_index;
- enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,
- OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO,
- OPT_LOCAL_PORT, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR,
- OPT_REG_TIMEOUT, OPT_ID, OPT_CONTACT,
- OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
- OPT_USE_STUN1, OPT_USE_STUN2,
- OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
- OPT_AUTO_ANSWER, OPT_AUTO_HANGUP};
- struct option long_options[] = {
- { "config-file",1, 0, OPT_CONFIG_FILE},
- { "log-file", 1, 0, OPT_LOG_FILE},
- { "log-level", 1, 0, OPT_LOG_LEVEL},
- { "app-log-level",1,0,OPT_APP_LOG_LEVEL},
- { "help", 0, 0, OPT_HELP},
- { "version", 0, 0, OPT_VERSION},
- { "null-audio", 0, 0, OPT_NULL_AUDIO},
- { "local-port", 1, 0, OPT_LOCAL_PORT},
- { "proxy", 1, 0, OPT_PROXY},
- { "outbound", 1, 0, OPT_OUTBOUND_PROXY},
- { "registrar", 1, 0, OPT_REGISTRAR},
- { "reg-timeout",1, 0, OPT_REG_TIMEOUT},
- { "id", 1, 0, OPT_ID},
- { "contact", 1, 0, OPT_CONTACT},
- { "realm", 1, 0, OPT_REALM},
- { "username", 1, 0, OPT_USERNAME},
- { "password", 1, 0, OPT_PASSWORD},
- { "use-stun1", 1, 0, OPT_USE_STUN1},
- { "use-stun2", 1, 0, OPT_USE_STUN2},
- { "add-buddy", 1, 0, OPT_ADD_BUDDY},
- { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
- { "no-presence", 0, 0, OPT_NO_PRESENCE},
- { "auto-answer",1, 0, OPT_AUTO_ANSWER},
- { "auto-hangup",1, 0, OPT_AUTO_HANGUP},
- { NULL, 0, 0, 0}
- };
- char *config_file = NULL;
-
- /* Run getopt once to see if user specifies config file to read. */
- while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
- switch (c) {
- case 0:
- config_file = optarg;
- break;
- }
- if (config_file)
- break;
- }
-
- if (config_file) {
- if (read_config_file(pool, config_file, &argc, &argv) != 0)
- return -1;
- }
-
- /* Reinitialize and re-run getopt again, possibly with new arguments
- * read from config file.
- */
- optind = 0;
- while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
- char *err, *p;
-
- switch (c) {
- case OPT_LOG_FILE:
- global.log_filename = optarg;
- break;
- case OPT_LOG_LEVEL:
- c = strtoul(optarg, &err, 10);
- if (*err) {
- printf("Error: expecting integer value 0-6 for --log-level\n");
- return -1;
- }
- pj_log_set_level( c );
- break;
- case OPT_APP_LOG_LEVEL:
- global.app_log_level = strtoul(optarg, &err, 10);
- if (*err) {
- printf("Error: expecting integer value 0-6 for --app-log-level\n");
- return -1;
- }
- break;
- case OPT_HELP:
- usage();
- return -1;
- case OPT_VERSION: /* version */
- pj_dump_config();
- return -1;
- case OPT_NULL_AUDIO:
- global.null_audio = 1;
- break;
- case OPT_LOCAL_PORT: /* local-port */
- global.sip_port = strtoul(optarg, &err, 10);
- if (*err) {
- printf("Error: expecting integer value for --local-port\n");
- return -1;
- }
- break;
- case OPT_PROXY: /* proxy */
- if (verify_sip_url(optarg) != 0) {
- printf("Error: invalid SIP URL '%s' in proxy argument\n", optarg);
- return -1;
- }
- global.proxy = pj_str(optarg);
- break;
- case OPT_OUTBOUND_PROXY: /* outbound proxy */
- if (verify_sip_url(optarg) != 0) {
- printf("Error: invalid SIP URL '%s' in outbound proxy argument\n", optarg);
- return -1;
- }
- global.outbound_proxy = pj_str(optarg);
- break;
- case OPT_REGISTRAR: /* registrar */
- if (verify_sip_url(optarg) != 0) {
- printf("Error: invalid SIP URL '%s' in registrar argument\n", optarg);
- return -1;
- }
- global.registrar_uri = pj_str(optarg);
- break;
- case OPT_REG_TIMEOUT: /* reg-timeout */
- global.reg_timeout = strtoul(optarg, &err, 10);
- if (*err) {
- printf("Error: expecting integer value for --reg-timeout\n");
- return -1;
- }
- break;
- case OPT_ID: /* id */
- if (verify_sip_url(optarg) != 0) {
- printf("Error: invalid SIP URL '%s' in local id argument\n", optarg);
- return -1;
- }
- global.local_uri = pj_str(optarg);
- break;
- case OPT_CONTACT: /* contact */
- if (verify_sip_url(optarg) != 0) {
- printf("Error: invalid SIP URL '%s' in contact argument\n", optarg);
- return -1;
- }
- global.contact = pj_str(optarg);
- break;
- case OPT_USERNAME: /* Default authentication user */
- if (!global.cred_count) global.cred_count = 1;
- global.cred_info[0].username = pj_str(optarg);
- break;
- case OPT_REALM: /* Default authentication realm. */
- if (!global.cred_count) global.cred_count = 1;
- global.cred_info[0].realm = pj_str(optarg);
- break;
- case OPT_PASSWORD: /* authentication password */
- if (!global.cred_count) global.cred_count = 1;
- global.cred_info[0].data_type = 0;
- global.cred_info[0].data = pj_str(optarg);
- break;
- case OPT_USE_STUN1: /* STUN server 1 */
- p = pj_native_strchr(optarg, ':');
- if (p) {
- *p = '\0';
- global.stun_srv1 = pj_str(optarg);
- global.stun_port1 = strtoul(p+1, &err, 10);
- if (*err || global.stun_port1==0) {
- printf("Error: expecting port number with option --use-stun1\n");
- return -1;
- }
- } else {
- global.stun_port1 = 3478;
- global.stun_srv1 = pj_str(optarg);
- }
- break;
- case OPT_USE_STUN2: /* STUN server 2 */
- p = pj_native_strchr(optarg, ':');
- if (p) {
- *p = '\0';
- global.stun_srv2 = pj_str(optarg);
- global.stun_port2 = strtoul(p+1, &err, 10);
- if (*err || global.stun_port2==0) {
- printf("Error: expecting port number with option --use-stun2\n");
- return -1;
- }
- } else {
- global.stun_port2 = 3478;
- global.stun_srv2 = pj_str(optarg);
- }
- break;
- case OPT_ADD_BUDDY: /* Add to buddy list. */
- if (verify_sip_url(optarg) != 0) {
- printf("Error: invalid URL '%s' in --add-buddy option\n", optarg);
- return -1;
- }
- if (global.buddy_cnt == MAX_BUDDIES) {
- printf("Error: too many buddies in buddy list.\n");
- return -1;
- }
- global.buddy[global.buddy_cnt++] = pj_str(optarg);
- break;
- case OPT_OFFER_X_MS_MSG:
- global.offer_x_ms_msg = 1;
- break;
- case OPT_NO_PRESENCE:
- global.no_presence = 1;
- break;
- case OPT_AUTO_ANSWER:
- global.auto_answer = strtoul(optarg, &err, 10);
- if (*err) {
- printf("Error: expecting integer value for --auto-answer option\n");
- return -1;
- }
- break;
- case OPT_AUTO_HANGUP:
- global.auto_hangup = strtoul(optarg, &err, 10);
- if (*err) {
- printf("Error: expecting integer value for --auto-hangup option\n");
- return -1;
- }
- break;
- }
- }
-
- if (optind != argc) {
- printf("Error: unknown options %s\n", argv[optind]);
- return -1;
- }
-
- if (global.reg_timeout == 0)
- global.reg_timeout = 3600;
-
- return 0;
-}
-
-/* Print dialog. */
-static void print_dialog(pjsip_dlg *dlg)
-{
- if (!dlg) {
- puts("none");
- return;
- }
-
- printf("%s: call-id=%.*s", dlg->obj_name,
- (int)dlg->call_id->id.slen,
- dlg->call_id->id.ptr);
-
- printf(" (%s, %s)\n", pjsip_role_name(dlg->role),
- pjsip_dlg_state_str(dlg->state));
-}
-
-/* Dump media statistic */
-void dump_media_statistic(pjsip_dlg *dlg)
-{
- struct dialog_data *dlg_data = dlg->user_data;
- pj_media_stream_stat stat[2];
- const char *statname[2] = { "TX", "RX" };
- int i;
-
- pj_media_session_get_stat (dlg_data->msession, 0, &stat[0], &stat[1]);
-
- printf("Media statistic:\n");
- for (i=0; i<2; ++i) {
- printf(" %s statistics:\n", statname[i]);
- printf(" Pkt TX=%d RX=%d\n", stat[i].pkt_tx, stat[i].pkt_rx);
- printf(" Octets TX=%d RX=%d\n", stat[i].oct_tx, stat[i].oct_rx);
- printf(" Jitter %d ms\n", stat[i].jitter);
- printf(" Pkt lost %d\n", stat[i].pkt_lost);
- }
- printf("\n");
-}
-
-/* Print all dialogs. */
-static void print_all_dialogs()
-{
- pjsip_dlg *dlg = (pjsip_dlg *)global.user_agent->dlg_list.next;
-
- puts("List all dialogs:");
-
- while (dlg != (pjsip_dlg *) &global.user_agent->dlg_list) {
- printf("%c", (dlg==global.cur_dlg ? '*' : ' '));
- print_dialog(dlg);
- dlg = dlg->next;
- }
-
- puts("");
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * THIS FILE IS INCLUDED BY main.c. + * IT WON'T COMPILE BY ITSELF. + */ + +#include "getopt.h" +#include <stdio.h> + + +/* + * Display program usage + */ +static void usage() +{ + puts("Usage:"); + puts(" pjsua [options] [sip-url]"); + puts(""); + puts(" [sip-url] Default URL to invite."); + puts(""); + puts("General options:"); + puts(" --config-file=file Read the config/arguments from file."); + puts(" --log-file=fname Log to filename (default stderr)"); + puts(" --log-level=N Set log max level to N (0(none) to 6(trace))"); + puts(" --app-log-level=N Set log max level for stdout display to N"); + puts(" --help Display this help screen"); + puts(" --version Display version info"); + puts(""); + puts("Media options:"); + puts(" --null-audio Use NULL audio device"); + puts(""); + puts("User Agent options:"); + puts(" --auto-answer=sec Auto-answer all incoming calls after sec seconds."); + puts(" --auto-hangup=sec Auto-hangup all calls after sec seconds."); + puts(""); + puts("SIP options:"); + puts(" --local-port=port Set TCP/UDP port"); + puts(" --id=url Set the URL of local ID (used in From header)"); + puts(" --contact=url Override the Contact information"); + puts(" --proxy=url Set the URL of proxy server"); + puts(" --outbound=url Set the URL of outbound proxy server"); + puts(" --registrar=url Set the URL of registrar server"); + puts(" --reg-timeout=secs Set registration interval to secs (default 3600)"); + puts(""); + puts("Authentication options:"); + puts(" --realm=string Set realm"); + puts(" --username=string Set authentication username"); + puts(" --password=string Set authentication password"); + puts(""); + puts("STUN options (all must be specified):"); + puts(" --use-stun1=host[:port]"); + puts(" --use-stun2=host[:port] Use STUN and set host name and port of STUN servers"); + puts(""); + puts("SIMPLE options (may be specified more than once):"); + puts(" --add-buddy url Add the specified URL to the buddy list."); + puts(" --offer-x-ms-msg Offer \"x-ms-message\" in outgoing INVITE"); + puts(" --no-presence Do not subscribe presence of buddies"); + puts(""); + fflush(stdout); +} + +/* Display keystroke help. */ +static void keystroke_help() +{ + int i; + + printf("Advertise status as: %s\n", (global.hide_status ? "Offline" : "Online")); + puts(""); + puts("Buddy list:"); + puts("-------------------------------------------------------------------------------"); + for (i=0; i<global.buddy_cnt; ++i) { + printf(" %d\t%s <%s>\n", i+1, global.buddy[i].ptr, + (global.buddy_status[i]?"Online":"Offline")); + } + //printf("-------------------------------------\n"); + puts(""); + //puts("Commands:"); + puts("+=============================================================================+"); + puts("| Call Commands: | IM & Presence: | Misc: |"); + puts("| | | |"); + puts("| m Make new call | i Send IM | o Send OPTIONS |"); + puts("| a Answer call | su Subscribe presence | d Dump status |"); + puts("| h Hangup call | us Unsubscribe presence | d1 Dump detailed |"); + puts("| ] Select next dialog | t Toggle Online status | |"); + puts("| [ Select previous dialog | | |"); + puts("+-----------------------------------------------------------------------------+"); + puts("| q QUIT |"); + puts("+=============================================================================+"); + puts(""); + + + fflush(stdout); +} + +/* + * Verify that valid SIP url is given. + */ +static pj_status_t verify_sip_url(char *url) +{ + pjsip_uri *p; + pj_pool_t *pool; + int len = (url ? strlen(url) : 0); + + if (!len) return -1; + + pool = pj_pool_create(global.pf, "check%p", 1024, 0, NULL); + if (!pool) return -1; + + p = pjsip_parse_uri(pool, url, len, 0); + if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0) + p = NULL; + + pj_pool_release(pool); + return p ? 0 : -1; +} + +/* + * Read command arguments from config file. + */ +static int read_config_file(pj_pool_t *pool, const char *filename, + int *app_argc, char ***app_argv) +{ + int i; + FILE *fhnd; + char line[200]; + int argc = 0; + char **argv; + enum { MAX_ARGS = 64 }; + + /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */ + argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*)); + argv[argc++] = *app_argv[0]; + + /* Open config file. */ + fhnd = fopen(filename, "rt"); + if (!fhnd) { + printf("Unable to open config file %s\n", filename); + return -1; + } + + /* Scan tokens in the file. */ + while (argc < MAX_ARGS && !feof(fhnd)) { + char *token, *p = line; + + if (fgets(line, sizeof(line), fhnd) == NULL) break; + + for (token = strtok(p, " \t\r\n"); argc < MAX_ARGS; + token = strtok(NULL, " \t\r\n")) + { + int token_len; + + if (!token) break; + if (*token == '#') break; + + token_len = strlen(token); + if (!token_len) + continue; + argv[argc] = pj_pool_alloc(pool, token_len+1); + pj_memcpy(argv[argc], token, token_len+1); + ++argc; + } + } + + /* Copy arguments from command line */ + for (i=1; i<*app_argc && argc < MAX_ARGS; ++i) + argv[argc++] = (*app_argv)[i]; + + if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) { + printf("Too many arguments specified in cmd line/config file\n"); + fclose(fhnd); + return -1; + } + + fclose(fhnd); + + /* Assign the new command line back to the original command line. */ + *app_argc = argc; + *app_argv = argv; + return 0; + +} + +/* + * Parse program arguments + */ +static int parse_args(pj_pool_t *pool, int argc, char *argv[]) +{ + int c; + int option_index; + enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL, + OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO, + OPT_LOCAL_PORT, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR, + OPT_REG_TIMEOUT, OPT_ID, OPT_CONTACT, + OPT_REALM, OPT_USERNAME, OPT_PASSWORD, + OPT_USE_STUN1, OPT_USE_STUN2, + OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE, + OPT_AUTO_ANSWER, OPT_AUTO_HANGUP}; + struct option long_options[] = { + { "config-file",1, 0, OPT_CONFIG_FILE}, + { "log-file", 1, 0, OPT_LOG_FILE}, + { "log-level", 1, 0, OPT_LOG_LEVEL}, + { "app-log-level",1,0,OPT_APP_LOG_LEVEL}, + { "help", 0, 0, OPT_HELP}, + { "version", 0, 0, OPT_VERSION}, + { "null-audio", 0, 0, OPT_NULL_AUDIO}, + { "local-port", 1, 0, OPT_LOCAL_PORT}, + { "proxy", 1, 0, OPT_PROXY}, + { "outbound", 1, 0, OPT_OUTBOUND_PROXY}, + { "registrar", 1, 0, OPT_REGISTRAR}, + { "reg-timeout",1, 0, OPT_REG_TIMEOUT}, + { "id", 1, 0, OPT_ID}, + { "contact", 1, 0, OPT_CONTACT}, + { "realm", 1, 0, OPT_REALM}, + { "username", 1, 0, OPT_USERNAME}, + { "password", 1, 0, OPT_PASSWORD}, + { "use-stun1", 1, 0, OPT_USE_STUN1}, + { "use-stun2", 1, 0, OPT_USE_STUN2}, + { "add-buddy", 1, 0, OPT_ADD_BUDDY}, + { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG}, + { "no-presence", 0, 0, OPT_NO_PRESENCE}, + { "auto-answer",1, 0, OPT_AUTO_ANSWER}, + { "auto-hangup",1, 0, OPT_AUTO_HANGUP}, + { NULL, 0, 0, 0} + }; + char *config_file = NULL; + + /* Run getopt once to see if user specifies config file to read. */ + while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) { + switch (c) { + case 0: + config_file = optarg; + break; + } + if (config_file) + break; + } + + if (config_file) { + if (read_config_file(pool, config_file, &argc, &argv) != 0) + return -1; + } + + /* Reinitialize and re-run getopt again, possibly with new arguments + * read from config file. + */ + optind = 0; + while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) { + char *err, *p; + + switch (c) { + case OPT_LOG_FILE: + global.log_filename = optarg; + break; + case OPT_LOG_LEVEL: + c = strtoul(optarg, &err, 10); + if (*err) { + printf("Error: expecting integer value 0-6 for --log-level\n"); + return -1; + } + pj_log_set_level( c ); + break; + case OPT_APP_LOG_LEVEL: + global.app_log_level = strtoul(optarg, &err, 10); + if (*err) { + printf("Error: expecting integer value 0-6 for --app-log-level\n"); + return -1; + } + break; + case OPT_HELP: + usage(); + return -1; + case OPT_VERSION: /* version */ + pj_dump_config(); + return -1; + case OPT_NULL_AUDIO: + global.null_audio = 1; + break; + case OPT_LOCAL_PORT: /* local-port */ + global.sip_port = strtoul(optarg, &err, 10); + if (*err) { + printf("Error: expecting integer value for --local-port\n"); + return -1; + } + break; + case OPT_PROXY: /* proxy */ + if (verify_sip_url(optarg) != 0) { + printf("Error: invalid SIP URL '%s' in proxy argument\n", optarg); + return -1; + } + global.proxy = pj_str(optarg); + break; + case OPT_OUTBOUND_PROXY: /* outbound proxy */ + if (verify_sip_url(optarg) != 0) { + printf("Error: invalid SIP URL '%s' in outbound proxy argument\n", optarg); + return -1; + } + global.outbound_proxy = pj_str(optarg); + break; + case OPT_REGISTRAR: /* registrar */ + if (verify_sip_url(optarg) != 0) { + printf("Error: invalid SIP URL '%s' in registrar argument\n", optarg); + return -1; + } + global.registrar_uri = pj_str(optarg); + break; + case OPT_REG_TIMEOUT: /* reg-timeout */ + global.reg_timeout = strtoul(optarg, &err, 10); + if (*err) { + printf("Error: expecting integer value for --reg-timeout\n"); + return -1; + } + break; + case OPT_ID: /* id */ + if (verify_sip_url(optarg) != 0) { + printf("Error: invalid SIP URL '%s' in local id argument\n", optarg); + return -1; + } + global.local_uri = pj_str(optarg); + break; + case OPT_CONTACT: /* contact */ + if (verify_sip_url(optarg) != 0) { + printf("Error: invalid SIP URL '%s' in contact argument\n", optarg); + return -1; + } + global.contact = pj_str(optarg); + break; + case OPT_USERNAME: /* Default authentication user */ + if (!global.cred_count) global.cred_count = 1; + global.cred_info[0].username = pj_str(optarg); + break; + case OPT_REALM: /* Default authentication realm. */ + if (!global.cred_count) global.cred_count = 1; + global.cred_info[0].realm = pj_str(optarg); + break; + case OPT_PASSWORD: /* authentication password */ + if (!global.cred_count) global.cred_count = 1; + global.cred_info[0].data_type = 0; + global.cred_info[0].data = pj_str(optarg); + break; + case OPT_USE_STUN1: /* STUN server 1 */ + p = pj_native_strchr(optarg, ':'); + if (p) { + *p = '\0'; + global.stun_srv1 = pj_str(optarg); + global.stun_port1 = strtoul(p+1, &err, 10); + if (*err || global.stun_port1==0) { + printf("Error: expecting port number with option --use-stun1\n"); + return -1; + } + } else { + global.stun_port1 = 3478; + global.stun_srv1 = pj_str(optarg); + } + break; + case OPT_USE_STUN2: /* STUN server 2 */ + p = pj_native_strchr(optarg, ':'); + if (p) { + *p = '\0'; + global.stun_srv2 = pj_str(optarg); + global.stun_port2 = strtoul(p+1, &err, 10); + if (*err || global.stun_port2==0) { + printf("Error: expecting port number with option --use-stun2\n"); + return -1; + } + } else { + global.stun_port2 = 3478; + global.stun_srv2 = pj_str(optarg); + } + break; + case OPT_ADD_BUDDY: /* Add to buddy list. */ + if (verify_sip_url(optarg) != 0) { + printf("Error: invalid URL '%s' in --add-buddy option\n", optarg); + return -1; + } + if (global.buddy_cnt == MAX_BUDDIES) { + printf("Error: too many buddies in buddy list.\n"); + return -1; + } + global.buddy[global.buddy_cnt++] = pj_str(optarg); + break; + case OPT_OFFER_X_MS_MSG: + global.offer_x_ms_msg = 1; + break; + case OPT_NO_PRESENCE: + global.no_presence = 1; + break; + case OPT_AUTO_ANSWER: + global.auto_answer = strtoul(optarg, &err, 10); + if (*err) { + printf("Error: expecting integer value for --auto-answer option\n"); + return -1; + } + break; + case OPT_AUTO_HANGUP: + global.auto_hangup = strtoul(optarg, &err, 10); + if (*err) { + printf("Error: expecting integer value for --auto-hangup option\n"); + return -1; + } + break; + } + } + + if (optind != argc) { + printf("Error: unknown options %s\n", argv[optind]); + return -1; + } + + if (global.reg_timeout == 0) + global.reg_timeout = 3600; + + return 0; +} + +/* Print dialog. */ +static void print_dialog(pjsip_dlg *dlg) +{ + if (!dlg) { + puts("none"); + return; + } + + printf("%s: call-id=%.*s", dlg->obj_name, + (int)dlg->call_id->id.slen, + dlg->call_id->id.ptr); + + printf(" (%s, %s)\n", pjsip_role_name(dlg->role), + pjsip_dlg_state_str(dlg->state)); +} + +/* Dump media statistic */ +void dump_media_statistic(pjsip_dlg *dlg) +{ + struct dialog_data *dlg_data = dlg->user_data; + pj_media_stream_stat stat[2]; + const char *statname[2] = { "TX", "RX" }; + int i; + + pj_media_session_get_stat (dlg_data->msession, 0, &stat[0], &stat[1]); + + printf("Media statistic:\n"); + for (i=0; i<2; ++i) { + printf(" %s statistics:\n", statname[i]); + printf(" Pkt TX=%d RX=%d\n", stat[i].pkt_tx, stat[i].pkt_rx); + printf(" Octets TX=%d RX=%d\n", stat[i].oct_tx, stat[i].oct_rx); + printf(" Jitter %d ms\n", stat[i].jitter); + printf(" Pkt lost %d\n", stat[i].pkt_lost); + } + printf("\n"); +} + +/* Print all dialogs. */ +static void print_all_dialogs() +{ + pjsip_dlg *dlg = (pjsip_dlg *)global.user_agent->dlg_list.next; + + puts("List all dialogs:"); + + while (dlg != (pjsip_dlg *) &global.user_agent->dlg_list) { + printf("%c", (dlg==global.cur_dlg ? '*' : ' ')); + print_dialog(dlg); + dlg = dlg->next; + } + + puts(""); +} + diff --git a/pjsip/src/test-pjsip/main.c b/pjsip/src/test-pjsip/main.c index 3c819be8..3e5270b1 100644 --- a/pjsip/src/test-pjsip/main.c +++ b/pjsip/src/test-pjsip/main.c @@ -1,24 +1,24 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-
-int main(void)
-{
- return test_main();
-}
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "test.h" + +int main(void) +{ + return test_main(); +} diff --git a/pjsip/src/test-pjsip/msg.c b/pjsip/src/test-pjsip/msg.c index b546a756..18e5af16 100644 --- a/pjsip/src/test-pjsip/msg.c +++ b/pjsip/src/test-pjsip/msg.c @@ -1,369 +1,369 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-#include <pjsip_core.h>
-#include <pjlib.h>
-
-static pjsip_msg *create_msg0(pj_pool_t *pool);
-
-struct test_msg
-{
- char msg[1024];
- pjsip_msg *(*creator)(pj_pool_t *pool);
- pj_size_t len;
-} test_array[] =
-{
- {
- /* 'Normal' message with all headers. */
- "INVITE sip:user@foo SIP/2.0\n"
- "From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=1234578901234567890\r"
- "To: Fellow User <sip:user@foo.bar.domain.com>\r\n"
- "Call-ID: 12345678901234567890@bar\r\n"
- "Content-Length: 0\r\n"
- "CSeq: 123456 INVITE\n"
- "Contact: <sip:joe@bar> ; q=0.5;expires=3600,sip:user@host;q=0.500\r"
- " ,sip:user2@host2\n"
- "Content-Type: text/html ; charset=ISO-8859-4\r"
- "Route: <sip:bigbox3.site3.atlanta.com;lr>,\r\n"
- " <sip:server10.biloxi.com;lr>\r"
- "Record-Route: <sip:server10.biloxi.com>,\r\n"
- " <sip:bigbox3.site3.atlanta.com;lr>\n"
- "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n"
- "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n"
- " ;received=192.0.2.1\r\n"
- "Via: SIP/2.0/UDP 10.2.1.1, SIP/2.0/TCP 192.168.1.1\n"
- "Organization: \r"
- "Max-Forwards: 70\n"
- "X-Header: \r\n"
- "\r",
- &create_msg0
- }
-};
-
-static pj_uint32_t parse_len, parse_time, print_time;
-
-static pj_status_t test_entry( pj_pool_t *pool, struct test_msg *entry )
-{
- pjsip_msg *parsed_msg, *ref_msg;
- pj_status_t status = PJ_SUCCESS;
- int len;
- pj_str_t str1, str2;
- pjsip_hdr *hdr1, *hdr2;
- pj_timestamp t1, t2;
- char *msgbuf;
-
- enum { BUFLEN = 512 };
-
- /* Parse message. */
- parse_len += entry->len;
- pj_get_timestamp(&t1);
- parsed_msg = pjsip_parse_msg(pool, entry->msg, entry->len, NULL);
- if (parsed_msg == NULL) {
- status = -10;
- goto on_return;
- }
- pj_get_timestamp(&t2);
- parse_time += t2.u32.lo - t1.u32.lo;
-
- /* Create reference message. */
- ref_msg = entry->creator(pool);
-
- /* Create buffer for comparison. */
- str1.ptr = pj_pool_alloc(pool, BUFLEN);
- str2.ptr = pj_pool_alloc(pool, BUFLEN);
-
- /* Compare message type. */
- if (parsed_msg->type != ref_msg->type) {
- status = -20;
- goto on_return;
- }
-
- /* Compare request or status line. */
- if (parsed_msg->type == PJSIP_REQUEST_MSG) {
- pjsip_method *m1 = &parsed_msg->line.req.method;
- pjsip_method *m2 = &ref_msg->line.req.method;
-
- if (m1->id != m2->id || pj_strcmp(&m1->name, &m2->name)) {
- status = -30;
- goto on_return;
- }
- } else {
-
- }
-
- /* Compare headers. */
- hdr1 = parsed_msg->hdr.next;
- hdr2 = ref_msg->hdr.next;
-
- while (hdr1 != &parsed_msg->hdr && hdr2 != &ref_msg->hdr) {
- len = hdr1->vptr->print_on(hdr1, str1.ptr, BUFLEN);
- if (len < 1) {
- status = -40;
- goto on_return;
- }
- str1.slen = len;
-
- len = hdr2->vptr->print_on(hdr2, str2.ptr, BUFLEN);
- if (len < 1) {
- status = -50;
- goto on_return;
- }
- str2.slen = len;
-
- if (pj_strcmp(&str1, &str2) != 0) {
- status = -60;
- goto on_return;
- }
-
- hdr1 = hdr1->next;
- hdr2 = hdr2->next;
- }
-
- if (hdr1 != &parsed_msg->hdr || hdr2 != &ref_msg->hdr) {
- status = -70;
- goto on_return;
- }
-
- /* Print message. */
- msgbuf = pj_pool_alloc(pool, PJSIP_MAX_PKT_LEN);
- if (msgbuf == NULL) {
- status = -80;
- goto on_return;
- }
- pj_get_timestamp(&t1);
- len = pjsip_msg_print(parsed_msg, msgbuf, PJSIP_MAX_PKT_LEN);
- if (len < 1) {
- status = -90;
- goto on_return;
- }
- pj_get_timestamp(&t2);
- print_time += t2.u32.lo - t1.u32.lo;
- status = PJ_SUCCESS;
-
-on_return:
- return status;
-}
-
-
-pj_status_t msg_test(void)
-{
- pj_status_t status;
- pj_pool_t *pool;
-
- pool = pjsip_endpt_create_pool(endpt, NULL, 4000, 4000);
-
- status = test_entry( pool, &test_array[0] );
-
- pjsip_endpt_destroy(endpt);
- return status;
-}
-
-/*****************************************************************************/
-
-static pjsip_msg *create_msg0(pj_pool_t *pool)
-{
-
- pjsip_msg *msg;
- pjsip_name_addr *name_addr;
- pjsip_url *url;
- pjsip_fromto_hdr *fromto;
- pjsip_cid_hdr *cid;
- pjsip_clen_hdr *clen;
- pjsip_cseq_hdr *cseq;
- pjsip_contact_hdr *contact;
- pjsip_ctype_hdr *ctype;
- pjsip_routing_hdr *routing;
- pjsip_via_hdr *via;
- pjsip_generic_string_hdr *generic;
- pj_str_t str;
-
- msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG);
-
- /* "INVITE sip:user@foo SIP/2.0\n" */
- pjsip_method_set(&msg->line.req.method, PJSIP_INVITE_METHOD);
- url = pjsip_url_create(pool, 0);
- msg->line.req.uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->user, "user");
- pj_strdup2(pool, &url->host, "foo");
-
- /* "From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=1234578901234567890\r" */
- fromto = pjsip_from_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto);
- pj_strdup2(pool, &fromto->tag, "1234578901234567890");
- name_addr = pjsip_name_addr_create(pool);
- fromto->uri = (pjsip_uri*)name_addr;
- pj_strdup2(pool, &name_addr->display, "Hi I'm Joe");
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->user, "joe.user");
- pj_strdup2(pool, &url->host, "bar.otherdomain.com");
-
- /* "To: Fellow User <sip:user@foo.bar.domain.com>\r\n" */
- fromto = pjsip_to_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto);
- name_addr = pjsip_name_addr_create(pool);
- fromto->uri = (pjsip_uri*)name_addr;
- pj_strdup2(pool, &name_addr->display, "Fellow User");
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->user, "user");
- pj_strdup2(pool, &url->host, "foo.bar.domain.com");
-
- /* "Call-ID: 12345678901234567890@bar\r\n" */
- cid = pjsip_cid_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)cid);
- pj_strdup2(pool, &cid->id, "12345678901234567890@bar");
-
- /* "Content-Length: 0\r\n" */
- clen = pjsip_clen_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)clen);
- clen->len = 0;
-
- /* "CSeq: 123456 INVITE\n" */
- cseq = pjsip_cseq_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)cseq);
- cseq->cseq = 123456;
- pjsip_method_set(&cseq->method, PJSIP_INVITE_METHOD);
-
- /* "Contact: <sip:joe@bar>;q=0.5;expires=3600*/
- contact = pjsip_contact_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact);
- contact->q1000 = 500;
- contact->expires = 3600;
- name_addr = pjsip_name_addr_create(pool);
- contact->uri = (pjsip_uri*)name_addr;
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->user, "joe");
- pj_strdup2(pool, &url->host, "bar");
-
- /*, sip:user@host;q=0.500\r" */
- contact = pjsip_contact_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact);
- contact->q1000 = 500;
- url = pjsip_url_create(pool, 0);
- contact->uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->user, "user");
- pj_strdup2(pool, &url->host, "host");
-
- /* " ,sip:user2@host2\n" */
- contact = pjsip_contact_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact);
- url = pjsip_url_create(pool, 0);
- contact->uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->user, "user2");
- pj_strdup2(pool, &url->host, "host2");
-
- /* "Content-Type: text/html; charset=ISO-8859-4\r" */
- ctype = pjsip_ctype_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)ctype);
- pj_strdup2(pool, &ctype->media.type, "text");
- pj_strdup2(pool, &ctype->media.subtype, "html");
- pj_strdup2(pool, &ctype->media.param, ";charset=ISO-8859-4");
-
- /* "Route: <sip:bigbox3.site3.atlanta.com;lr>,\r\n" */
- routing = pjsip_route_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
- url = pjsip_url_create(pool, 0);
- routing->name_addr.uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com");
- url->lr_param = 1;
-
- /* " <sip:server10.biloxi.com;lr>\r" */
- routing = pjsip_route_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
- url = pjsip_url_create(pool, 0);
- routing->name_addr.uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->host, "server10.biloxi.com");
- url->lr_param = 1;
-
- /* "Record-Route: <sip:server10.biloxi.com>,\r\n" */
- routing = pjsip_rr_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
- url = pjsip_url_create(pool, 0);
- routing->name_addr.uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->host, "server10.biloxi.com");
- url->lr_param = 0;
-
- /* " <sip:bigbox3.site3.atlanta.com;lr>\n" */
- routing = pjsip_rr_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
- url = pjsip_url_create(pool, 0);
- routing->name_addr.uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com");
- url->lr_param = 1;
-
- /* "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n" */
- via = pjsip_via_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
- pj_strdup2(pool, &via->transport, "SCTP");
- pj_strdup2(pool, &via->sent_by.host, "bigbox3.site3.atlanta.com");
- pj_strdup2(pool, &via->branch_param, "z9hG4bK77ef4c2312983.1");
-
- /* "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n"
- " ;received=192.0.2.1\r\n" */
- via = pjsip_via_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
- pj_strdup2(pool, &via->transport, "UDP");
- pj_strdup2(pool, &via->sent_by.host, "pc33.atlanta.com");
- pj_strdup2(pool, &via->branch_param, "z9hG4bKnashds8");
- pj_strdup2(pool, &via->recvd_param, "192.0.2.1");
-
-
- /* "Via: SIP/2.0/UDP 10.2.1.1, */
- via = pjsip_via_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
- pj_strdup2(pool, &via->transport, "UDP");
- pj_strdup2(pool, &via->sent_by.host, "10.2.1.1");
-
-
- /*SIP/2.0/TCP 192.168.1.1\n" */
- via = pjsip_via_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
- pj_strdup2(pool, &via->transport, "TCP");
- pj_strdup2(pool, &via->sent_by.host, "192.168.1.1");
-
- /* "Organization: \r" */
- str.ptr = "Organization";
- str.slen = 12;
- generic = pjsip_generic_string_hdr_create(pool, &str);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic);
- generic->hvalue.ptr = NULL;
- generic->hvalue.slen = 0;
-
- /* "Max-Forwards: 70\n" */
- str.ptr = "Max-Forwards";
- str.slen = 12;
- generic = pjsip_generic_string_hdr_create(pool, &str);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic);
- str.ptr = "70";
- str.slen = 2;
- generic->hvalue = str;
-
- /* "X-Header: \r\n" */
- str.ptr = "X-Header";
- str.slen = 8;
- generic = pjsip_generic_string_hdr_create(pool, &str);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic);
- str.ptr = NULL;
- str.slen = 0;
- generic->hvalue = str;
-
- return msg;
-}
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "test.h" +#include <pjsip_core.h> +#include <pjlib.h> + +static pjsip_msg *create_msg0(pj_pool_t *pool); + +struct test_msg +{ + char msg[1024]; + pjsip_msg *(*creator)(pj_pool_t *pool); + pj_size_t len; +} test_array[] = +{ + { + /* 'Normal' message with all headers. */ + "INVITE sip:user@foo SIP/2.0\n" + "From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=1234578901234567890\r" + "To: Fellow User <sip:user@foo.bar.domain.com>\r\n" + "Call-ID: 12345678901234567890@bar\r\n" + "Content-Length: 0\r\n" + "CSeq: 123456 INVITE\n" + "Contact: <sip:joe@bar> ; q=0.5;expires=3600,sip:user@host;q=0.500\r" + " ,sip:user2@host2\n" + "Content-Type: text/html ; charset=ISO-8859-4\r" + "Route: <sip:bigbox3.site3.atlanta.com;lr>,\r\n" + " <sip:server10.biloxi.com;lr>\r" + "Record-Route: <sip:server10.biloxi.com>,\r\n" + " <sip:bigbox3.site3.atlanta.com;lr>\n" + "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n" + "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n" + " ;received=192.0.2.1\r\n" + "Via: SIP/2.0/UDP 10.2.1.1, SIP/2.0/TCP 192.168.1.1\n" + "Organization: \r" + "Max-Forwards: 70\n" + "X-Header: \r\n" + "\r", + &create_msg0 + } +}; + +static pj_uint32_t parse_len, parse_time, print_time; + +static pj_status_t test_entry( pj_pool_t *pool, struct test_msg *entry ) +{ + pjsip_msg *parsed_msg, *ref_msg; + pj_status_t status = PJ_SUCCESS; + int len; + pj_str_t str1, str2; + pjsip_hdr *hdr1, *hdr2; + pj_timestamp t1, t2; + char *msgbuf; + + enum { BUFLEN = 512 }; + + /* Parse message. */ + parse_len += entry->len; + pj_get_timestamp(&t1); + parsed_msg = pjsip_parse_msg(pool, entry->msg, entry->len, NULL); + if (parsed_msg == NULL) { + status = -10; + goto on_return; + } + pj_get_timestamp(&t2); + parse_time += t2.u32.lo - t1.u32.lo; + + /* Create reference message. */ + ref_msg = entry->creator(pool); + + /* Create buffer for comparison. */ + str1.ptr = pj_pool_alloc(pool, BUFLEN); + str2.ptr = pj_pool_alloc(pool, BUFLEN); + + /* Compare message type. */ + if (parsed_msg->type != ref_msg->type) { + status = -20; + goto on_return; + } + + /* Compare request or status line. */ + if (parsed_msg->type == PJSIP_REQUEST_MSG) { + pjsip_method *m1 = &parsed_msg->line.req.method; + pjsip_method *m2 = &ref_msg->line.req.method; + + if (m1->id != m2->id || pj_strcmp(&m1->name, &m2->name)) { + status = -30; + goto on_return; + } + } else { + + } + + /* Compare headers. */ + hdr1 = parsed_msg->hdr.next; + hdr2 = ref_msg->hdr.next; + + while (hdr1 != &parsed_msg->hdr && hdr2 != &ref_msg->hdr) { + len = hdr1->vptr->print_on(hdr1, str1.ptr, BUFLEN); + if (len < 1) { + status = -40; + goto on_return; + } + str1.slen = len; + + len = hdr2->vptr->print_on(hdr2, str2.ptr, BUFLEN); + if (len < 1) { + status = -50; + goto on_return; + } + str2.slen = len; + + if (pj_strcmp(&str1, &str2) != 0) { + status = -60; + goto on_return; + } + + hdr1 = hdr1->next; + hdr2 = hdr2->next; + } + + if (hdr1 != &parsed_msg->hdr || hdr2 != &ref_msg->hdr) { + status = -70; + goto on_return; + } + + /* Print message. */ + msgbuf = pj_pool_alloc(pool, PJSIP_MAX_PKT_LEN); + if (msgbuf == NULL) { + status = -80; + goto on_return; + } + pj_get_timestamp(&t1); + len = pjsip_msg_print(parsed_msg, msgbuf, PJSIP_MAX_PKT_LEN); + if (len < 1) { + status = -90; + goto on_return; + } + pj_get_timestamp(&t2); + print_time += t2.u32.lo - t1.u32.lo; + status = PJ_SUCCESS; + +on_return: + return status; +} + + +pj_status_t msg_test(void) +{ + pj_status_t status; + pj_pool_t *pool; + + pool = pjsip_endpt_create_pool(endpt, NULL, 4000, 4000); + + status = test_entry( pool, &test_array[0] ); + + pjsip_endpt_destroy(endpt); + return status; +} + +/*****************************************************************************/ + +static pjsip_msg *create_msg0(pj_pool_t *pool) +{ + + pjsip_msg *msg; + pjsip_name_addr *name_addr; + pjsip_url *url; + pjsip_fromto_hdr *fromto; + pjsip_cid_hdr *cid; + pjsip_clen_hdr *clen; + pjsip_cseq_hdr *cseq; + pjsip_contact_hdr *contact; + pjsip_ctype_hdr *ctype; + pjsip_routing_hdr *routing; + pjsip_via_hdr *via; + pjsip_generic_string_hdr *generic; + pj_str_t str; + + msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG); + + /* "INVITE sip:user@foo SIP/2.0\n" */ + pjsip_method_set(&msg->line.req.method, PJSIP_INVITE_METHOD); + url = pjsip_url_create(pool, 0); + msg->line.req.uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->user, "user"); + pj_strdup2(pool, &url->host, "foo"); + + /* "From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=1234578901234567890\r" */ + fromto = pjsip_from_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto); + pj_strdup2(pool, &fromto->tag, "1234578901234567890"); + name_addr = pjsip_name_addr_create(pool); + fromto->uri = (pjsip_uri*)name_addr; + pj_strdup2(pool, &name_addr->display, "Hi I'm Joe"); + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->user, "joe.user"); + pj_strdup2(pool, &url->host, "bar.otherdomain.com"); + + /* "To: Fellow User <sip:user@foo.bar.domain.com>\r\n" */ + fromto = pjsip_to_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto); + name_addr = pjsip_name_addr_create(pool); + fromto->uri = (pjsip_uri*)name_addr; + pj_strdup2(pool, &name_addr->display, "Fellow User"); + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->user, "user"); + pj_strdup2(pool, &url->host, "foo.bar.domain.com"); + + /* "Call-ID: 12345678901234567890@bar\r\n" */ + cid = pjsip_cid_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)cid); + pj_strdup2(pool, &cid->id, "12345678901234567890@bar"); + + /* "Content-Length: 0\r\n" */ + clen = pjsip_clen_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)clen); + clen->len = 0; + + /* "CSeq: 123456 INVITE\n" */ + cseq = pjsip_cseq_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)cseq); + cseq->cseq = 123456; + pjsip_method_set(&cseq->method, PJSIP_INVITE_METHOD); + + /* "Contact: <sip:joe@bar>;q=0.5;expires=3600*/ + contact = pjsip_contact_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact); + contact->q1000 = 500; + contact->expires = 3600; + name_addr = pjsip_name_addr_create(pool); + contact->uri = (pjsip_uri*)name_addr; + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->user, "joe"); + pj_strdup2(pool, &url->host, "bar"); + + /*, sip:user@host;q=0.500\r" */ + contact = pjsip_contact_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact); + contact->q1000 = 500; + url = pjsip_url_create(pool, 0); + contact->uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->user, "user"); + pj_strdup2(pool, &url->host, "host"); + + /* " ,sip:user2@host2\n" */ + contact = pjsip_contact_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact); + url = pjsip_url_create(pool, 0); + contact->uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->user, "user2"); + pj_strdup2(pool, &url->host, "host2"); + + /* "Content-Type: text/html; charset=ISO-8859-4\r" */ + ctype = pjsip_ctype_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)ctype); + pj_strdup2(pool, &ctype->media.type, "text"); + pj_strdup2(pool, &ctype->media.subtype, "html"); + pj_strdup2(pool, &ctype->media.param, ";charset=ISO-8859-4"); + + /* "Route: <sip:bigbox3.site3.atlanta.com;lr>,\r\n" */ + routing = pjsip_route_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); + url = pjsip_url_create(pool, 0); + routing->name_addr.uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com"); + url->lr_param = 1; + + /* " <sip:server10.biloxi.com;lr>\r" */ + routing = pjsip_route_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); + url = pjsip_url_create(pool, 0); + routing->name_addr.uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->host, "server10.biloxi.com"); + url->lr_param = 1; + + /* "Record-Route: <sip:server10.biloxi.com>,\r\n" */ + routing = pjsip_rr_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); + url = pjsip_url_create(pool, 0); + routing->name_addr.uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->host, "server10.biloxi.com"); + url->lr_param = 0; + + /* " <sip:bigbox3.site3.atlanta.com;lr>\n" */ + routing = pjsip_rr_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); + url = pjsip_url_create(pool, 0); + routing->name_addr.uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com"); + url->lr_param = 1; + + /* "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n" */ + via = pjsip_via_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)via); + pj_strdup2(pool, &via->transport, "SCTP"); + pj_strdup2(pool, &via->sent_by.host, "bigbox3.site3.atlanta.com"); + pj_strdup2(pool, &via->branch_param, "z9hG4bK77ef4c2312983.1"); + + /* "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n" + " ;received=192.0.2.1\r\n" */ + via = pjsip_via_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)via); + pj_strdup2(pool, &via->transport, "UDP"); + pj_strdup2(pool, &via->sent_by.host, "pc33.atlanta.com"); + pj_strdup2(pool, &via->branch_param, "z9hG4bKnashds8"); + pj_strdup2(pool, &via->recvd_param, "192.0.2.1"); + + + /* "Via: SIP/2.0/UDP 10.2.1.1, */ + via = pjsip_via_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)via); + pj_strdup2(pool, &via->transport, "UDP"); + pj_strdup2(pool, &via->sent_by.host, "10.2.1.1"); + + + /*SIP/2.0/TCP 192.168.1.1\n" */ + via = pjsip_via_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)via); + pj_strdup2(pool, &via->transport, "TCP"); + pj_strdup2(pool, &via->sent_by.host, "192.168.1.1"); + + /* "Organization: \r" */ + str.ptr = "Organization"; + str.slen = 12; + generic = pjsip_generic_string_hdr_create(pool, &str); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic); + generic->hvalue.ptr = NULL; + generic->hvalue.slen = 0; + + /* "Max-Forwards: 70\n" */ + str.ptr = "Max-Forwards"; + str.slen = 12; + generic = pjsip_generic_string_hdr_create(pool, &str); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic); + str.ptr = "70"; + str.slen = 2; + generic->hvalue = str; + + /* "X-Header: \r\n" */ + str.ptr = "X-Header"; + str.slen = 8; + generic = pjsip_generic_string_hdr_create(pool, &str); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic); + str.ptr = NULL; + str.slen = 0; + generic->hvalue = str; + + return msg; +} diff --git a/pjsip/src/test-pjsip/test.c b/pjsip/src/test-pjsip/test.c index 80d08881..49aa2aa9 100644 --- a/pjsip/src/test-pjsip/test.c +++ b/pjsip/src/test-pjsip/test.c @@ -1,104 +1,104 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-
-#include "test.h"
-#include <pjlib.h>
-#include <pjsip_core.h>
-
-#define DO_TEST(test) do { \
- PJ_LOG(3, ("test", "Running %s...", #test)); \
- rc = test; \
- PJ_LOG(3, ("test", \
- "%s(%d)", \
- (rc ? "..ERROR" : "..success"), rc)); \
- if (rc!=0) goto on_return; \
- } while (0)
-
-
-
-pjsip_endpoint *endpt;
-
-void app_perror(const char *msg, pj_status_t rc)
-{
- char errbuf[256];
-
- PJ_CHECK_STACK();
-
- pjsip_strerror(rc, errbuf, sizeof(errbuf));
- PJ_LOG(1,("test", "%s: [pj_status_t=%d] %s", msg, rc, errbuf));
-
-}
-
-pj_status_t register_static_modules(pj_size_t *count, pjsip_module **modules)
-{
- *count = 0;
- return PJ_SUCCESS;
-}
-
-int test_main(void)
-{
- pj_status_t rc;
- pj_caching_pool caching_pool;
- const char *filename;
- int line;
-
- pj_log_set_level(3);
- pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME |
- PJ_LOG_HAS_MICRO_SEC);
-
- if ((rc=pj_init()) != PJ_SUCCESS) {
- app_perror("pj_init", rc);
- return rc;
- }
-
- pj_dump_config();
-
- pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 );
-
- rc = pjsip_endpt_create(&caching_pool.factory, "endpt", &endpt);
- if (rc != PJ_SUCCESS) {
- app_perror("pjsip_endpt_create", rc);
- pj_caching_pool_destroy(&caching_pool);
- return rc;
- }
-
- PJ_LOG(3,("",""));
-
- DO_TEST(uri_test());
-
-on_return:
-
- pjsip_endpt_destroy(endpt);
- pj_caching_pool_destroy(&caching_pool);
-
- PJ_LOG(3,("test", ""));
-
- pj_thread_get_stack_info(pj_thread_this(), &filename, &line);
- PJ_LOG(3,("test", "Stack max usage: %u, deepest: %s:%u",
- pj_thread_get_stack_max_usage(pj_thread_this()),
- filename, line));
- if (rc == 0)
- PJ_LOG(3,("test", "Looks like everything is okay!.."));
- else
- PJ_LOG(3,("test", "Test completed with error(s)"));
-
- return 0;
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "test.h" +#include <pjlib.h> +#include <pjsip_core.h> + +#define DO_TEST(test) do { \ + PJ_LOG(3, ("test", "Running %s...", #test)); \ + rc = test; \ + PJ_LOG(3, ("test", \ + "%s(%d)", \ + (rc ? "..ERROR" : "..success"), rc)); \ + if (rc!=0) goto on_return; \ + } while (0) + + + +pjsip_endpoint *endpt; + +void app_perror(const char *msg, pj_status_t rc) +{ + char errbuf[256]; + + PJ_CHECK_STACK(); + + pjsip_strerror(rc, errbuf, sizeof(errbuf)); + PJ_LOG(1,("test", "%s: [pj_status_t=%d] %s", msg, rc, errbuf)); + +} + +pj_status_t register_static_modules(pj_size_t *count, pjsip_module **modules) +{ + *count = 0; + return PJ_SUCCESS; +} + +int test_main(void) +{ + pj_status_t rc; + pj_caching_pool caching_pool; + const char *filename; + int line; + + pj_log_set_level(3); + pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | + PJ_LOG_HAS_MICRO_SEC); + + if ((rc=pj_init()) != PJ_SUCCESS) { + app_perror("pj_init", rc); + return rc; + } + + pj_dump_config(); + + pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 ); + + rc = pjsip_endpt_create(&caching_pool.factory, "endpt", &endpt); + if (rc != PJ_SUCCESS) { + app_perror("pjsip_endpt_create", rc); + pj_caching_pool_destroy(&caching_pool); + return rc; + } + + PJ_LOG(3,("","")); + + DO_TEST(uri_test()); + +on_return: + + pjsip_endpt_destroy(endpt); + pj_caching_pool_destroy(&caching_pool); + + PJ_LOG(3,("test", "")); + + pj_thread_get_stack_info(pj_thread_this(), &filename, &line); + PJ_LOG(3,("test", "Stack max usage: %u, deepest: %s:%u", + pj_thread_get_stack_max_usage(pj_thread_this()), + filename, line)); + if (rc == 0) + PJ_LOG(3,("test", "Looks like everything is okay!..")); + else + PJ_LOG(3,("test", "Test completed with error(s)")); + + return 0; +} + diff --git a/pjsip/src/test-pjsip/test.h b/pjsip/src/test-pjsip/test.h index 60b67c5e..aebf6022 100644 --- a/pjsip/src/test-pjsip/test.h +++ b/pjsip/src/test-pjsip/test.h @@ -1,33 +1,33 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __TEST_H__
-#define __TEST_H__
-
-#include <pjsip/sip_types.h>
-
-extern pjsip_endpoint *endpt;
-
-pj_status_t uri_test(void);
-pj_status_t msg_test(void);
-
-int test_main(void);
-void app_perror(const char *msg, pj_status_t status);
-
-
-#endif /* __TEST_H__ */
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __TEST_H__ +#define __TEST_H__ + +#include <pjsip/sip_types.h> + +extern pjsip_endpoint *endpt; + +pj_status_t uri_test(void); +pj_status_t msg_test(void); + +int test_main(void); +void app_perror(const char *msg, pj_status_t status); + + +#endif /* __TEST_H__ */ diff --git a/pjsip/src/test-pjsip/uri.c b/pjsip/src/test-pjsip/uri.c index a61382f0..4435d1a4 100644 --- a/pjsip/src/test-pjsip/uri.c +++ b/pjsip/src/test-pjsip/uri.c @@ -1,560 +1,636 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-#include <pjsip_core.h>
-#include <pjlib.h>
-
-
-#define ALPHANUM "abcdefghijklmnopqrstuvwxyz" \
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
- "0123456789"
-#define MARK "-_.!~*'()"
-#define USER "&=+$,;?/%"
-#define PASS "&=+$,%"
-#define PARAM_CHAR "[]/:&+$" MARK "%"
-
-#define POOL_SIZE 4096
-
-static pj_uint32_t parse_len, parse_time, print_time;
-
-
-/* URI creator functions. */
-static pjsip_uri *create_uri1( pj_pool_t *pool );
-static pjsip_uri *create_uri2( pj_pool_t *pool );
-static pjsip_uri *create_uri3( pj_pool_t *pool );
-static pjsip_uri *create_uri4( pj_pool_t *pool );
-static pjsip_uri *create_uri5( pj_pool_t *pool );
-static pjsip_uri *create_uri6( pj_pool_t *pool );
-static pjsip_uri *create_uri7( pj_pool_t *pool );
-static pjsip_uri *create_uri8( pj_pool_t *pool );
-static pjsip_uri *create_uri9( pj_pool_t *pool );
-static pjsip_uri *create_uri10( pj_pool_t *pool );
-static pjsip_uri *create_uri11( pj_pool_t *pool );
-static pjsip_uri *create_uri12( pj_pool_t *pool );
-static pjsip_uri *create_uri13( pj_pool_t *pool );
-static pjsip_uri *create_uri14( pj_pool_t *pool );
-static pjsip_uri *create_uri15( pj_pool_t *pool );
-static pjsip_uri *create_uri16( pj_pool_t *pool );
-static pjsip_uri *create_uri17( pj_pool_t *pool );
-static pjsip_uri *create_uri18( pj_pool_t *pool );
-static pjsip_uri *create_uri19( pj_pool_t *pool );
-static pjsip_uri *create_dummy( pj_pool_t *pool );
-
-#define ERR_NOT_EQUAL -1001
-#define ERR_SYNTAX_ERR -1002
-
-struct uri_test
-{
- pj_status_t status;
- char str[PJSIP_MAX_URL_SIZE];
- pjsip_uri *(*creator)(pj_pool_t *pool);
- pj_size_t len;
-} uri_test_array[] =
-{
- {
- PJ_SUCCESS,
- "sip:localhost",
- &create_uri1
- },
- {
- PJ_SUCCESS,
- "sip:user@localhost",
- &create_uri2
- },
- {
- PJ_SUCCESS,
- "sip:user:password@localhost:5060",
- &create_uri3,
- },
- {
- /* Port is specified should not match unspecified port. */
- ERR_NOT_EQUAL,
- "sip:localhost:5060",
- &create_uri4
- },
- {
- /* All recognized parameters. */
- PJ_SUCCESS,
- "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK",
- &create_uri5
- },
- {
- /* Params mixed with other params and header params. */
- PJ_SUCCESS,
- "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry"
- "?Subject=Hello%20There&Server=SIP%20Server",
- &create_uri6
- },
- {
- /* SIPS. */
- PJ_SUCCESS,
- "sips:localhost",
- &create_uri7,
- },
- {
- /* Name address */
- PJ_SUCCESS,
- "<sip:localhost>",
- &create_uri8
- },
- {
- /* Name address with display name and SIPS scheme with some redundant
- * whitespaced.
- */
- PJ_SUCCESS,
- " Power Administrator <sips:localhost>",
- &create_uri9
- },
- {
- /* Name address. */
- PJ_SUCCESS,
- " \"User\" <sip:user@localhost:5071>",
- &create_uri10
- },
- {
- /* Escaped sequence in display name (display=Strange User\"\\\"). */
- PJ_SUCCESS,
- " \"Strange User\\\"\\\\\\\"\" <sip:localhost>",
- &create_uri11,
- },
- {
- /* Errorneous escaping in display name. */
- ERR_SYNTAX_ERR,
- " \"Rogue User\\\" <sip:localhost>",
- &create_uri12,
- },
- {
- /* Dangling quote in display name, but that should be OK. */
- PJ_SUCCESS,
- "Strange User\" <sip:localhost>",
- &create_uri13,
- },
- {
- /* Special characters in parameter value must be quoted. */
- PJ_SUCCESS,
- "sip:localhost;pvalue=\"hello world\"",
- &create_uri14,
- },
- {
- /* Excercise strange character sets allowed in display, user, password,
- * host, and port.
- */
- PJ_SUCCESS,
- "This is -. !% *_+`'~ me <sip:a19A&=+$,;?/%2c:%09a&Zz=+$,@"
- "my_proxy09.MY-domain.com:9801>",
- &create_uri15,
- },
- {
- /* Another excercise to the allowed character sets to the hostname. */
- PJ_SUCCESS,
- "sip:" ALPHANUM "-_.com",
- &create_uri16,
- },
- {
- /* Another excercise to the allowed character sets to the username
- * and password.
- */
- PJ_SUCCESS,
- "sip:" ALPHANUM USER ":" ALPHANUM PASS "@host",
- &create_uri17,
- },
- {
- /* Excercise to the pname and pvalue, and mixup of other-param
- * between 'recognized' params.
- */
- PJ_SUCCESS,
- "sip:host;user=ip;" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR
- ";lr;other=1;transport=sctp;other2",
- &create_uri18,
- },
- {
- /* This should trigger syntax error. */
- ERR_SYNTAX_ERR,
- "sip:",
- &create_dummy,
- },
- {
- /* Syntax error: whitespace after scheme. */
- ERR_SYNTAX_ERR,
- "sip :host",
- &create_dummy,
- },
- {
- /* Syntax error: whitespace before hostname. */
- ERR_SYNTAX_ERR,
- "sip: host",
- &create_dummy,
- },
- {
- /* Syntax error: invalid port. */
- ERR_SYNTAX_ERR,
- "sip:user:password",
- &create_dummy,
- },
- {
- /* Syntax error: no host. */
- ERR_SYNTAX_ERR,
- "sip:user@",
- &create_dummy,
- },
- {
- /* Syntax error: no user/host. */
- ERR_SYNTAX_ERR,
- "sip:@",
- &create_dummy,
- },
- {
- /* Syntax error: empty string. */
- ERR_SYNTAX_ERR,
- "",
- &create_dummy,
- }
-};
-
-static pjsip_uri *create_uri1(pj_pool_t *pool)
-{
- /* "sip:localhost" */
- pjsip_url *url = pjsip_url_create(pool, 0);
-
- pj_strdup2(pool, &url->host, "localhost");
- return (pjsip_uri*)url;
-}
-
-static pjsip_uri *create_uri2(pj_pool_t *pool)
-{
- /* "sip:user@localhost" */
- pjsip_url *url = pjsip_url_create(pool, 0);
-
- pj_strdup2( pool, &url->user, "user");
- pj_strdup2( pool, &url->host, "localhost");
-
- return (pjsip_uri*) url;
-}
-
-static pjsip_uri *create_uri3(pj_pool_t *pool)
-{
- /* "sip:user:password@localhost:5060" */
- pjsip_url *url = pjsip_url_create(pool, 0);
-
- pj_strdup2( pool, &url->user, "user");
- pj_strdup2( pool, &url->passwd, "password");
- pj_strdup2( pool, &url->host, "localhost");
- url->port = 5060;
-
- return (pjsip_uri*) url;
-}
-
-static pjsip_uri *create_uri4(pj_pool_t *pool)
-{
- /* Like: "sip:localhost:5060", but without the port. */
- pjsip_url *url = pjsip_url_create(pool, 0);
-
- pj_strdup2(pool, &url->host, "localhost");
- return (pjsip_uri*)url;
-}
-
-static pjsip_uri *create_uri5(pj_pool_t *pool)
-{
- /* "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK" */
- pjsip_url *url = pjsip_url_create(pool, 0);
-
- pj_strdup2(pool, &url->host, "localhost");
- pj_strdup2(pool, &url->transport_param, "tcp");
- pj_strdup2(pool, &url->user_param, "ip");
- url->ttl_param = 255;
- url->lr_param = 1;
- pj_strdup2(pool, &url->maddr_param, "127.0.0.1");
- pj_strdup2(pool, &url->method_param, "ACK");
-
- return (pjsip_uri*)url;
-}
-
-static pjsip_uri *create_uri6(pj_pool_t *pool)
-{
- /* "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry"
- "?Subject=Hello%20There&Server=SIP%20Server"
- */
- pjsip_url *url = pjsip_url_create(pool, 0);
-
- pj_strdup2(pool, &url->host, "localhost");
- pj_strdup2(pool, &url->user_param, "phone");
- pj_strdup2(pool, &url->other_param, ";pickup=hurry;message=I%20am%20sorry");
- pj_strdup2(pool, &url->header_param, "?Subject=Hello%20There&Server=SIP%20Server");
- return (pjsip_uri*)url;
-
-}
-
-static pjsip_uri *create_uri7(pj_pool_t *pool)
-{
- /* "sips:localhost" */
- pjsip_url *url = pjsip_url_create(pool, 1);
-
- pj_strdup2(pool, &url->host, "localhost");
- return (pjsip_uri*)url;
-}
-
-static pjsip_uri *create_uri8(pj_pool_t *pool)
-{
- /* "<sip:localhost>" */
- pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
- pjsip_url *url;
-
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*) url;
-
- pj_strdup2(pool, &url->host, "localhost");
- return (pjsip_uri*)name_addr;
-}
-
-static pjsip_uri *create_uri9(pj_pool_t *pool)
-{
- /* " Power Administrator <sips:localhost>" */
- pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
- pjsip_url *url;
-
- url = pjsip_url_create(pool, 1);
- name_addr->uri = (pjsip_uri*) url;
-
- pj_strdup2(pool, &name_addr->display, "Power Administrator");
- pj_strdup2(pool, &url->host, "localhost");
- return (pjsip_uri*)name_addr;
-}
-
-static pjsip_uri *create_uri10(pj_pool_t *pool)
-{
- /* " \"User\" <sip:user@localhost:5071>" */
- pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
- pjsip_url *url;
-
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*) url;
-
- pj_strdup2(pool, &name_addr->display, "\"User\"");
- pj_strdup2(pool, &url->user, "user");
- pj_strdup2(pool, &url->host, "localhost");
- url->port = 5071;
- return (pjsip_uri*)name_addr;
-}
-
-static pjsip_uri *create_uri11(pj_pool_t *pool)
-{
- /* " \"Strange User\\\"\\\\\\\"\" <sip:localhost>" */
- pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
- pjsip_url *url;
-
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*) url;
-
- pj_strdup2(pool, &name_addr->display, "\"Strange User\\\"\\\\\\\"\"");
- pj_strdup2(pool, &url->host, "localhost");
- return (pjsip_uri*)name_addr;
-}
-
-static pjsip_uri *create_uri12(pj_pool_t *pool)
-{
- /* " \"Rogue User\\\" <sip:localhost>" */
- pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
- pjsip_url *url;
-
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*) url;
-
- pj_strdup2(pool, &name_addr->display, "\"Rogue User\\\"");
- pj_strdup2(pool, &url->host, "localhost");
- return (pjsip_uri*)name_addr;
-}
-
-static pjsip_uri *create_uri13(pj_pool_t *pool)
-{
- /* "Strange User\" <sip:localhost>" */
- pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
- pjsip_url *url;
-
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*) url;
-
- pj_strdup2(pool, &name_addr->display, "Strange User\"");
- pj_strdup2(pool, &url->host, "localhost");
- return (pjsip_uri*)name_addr;
-}
-
-static pjsip_uri *create_uri14(pj_pool_t *pool)
-{
- /* "sip:localhost;pvalue=\"hello world\"" */
- pjsip_url *url;
- url = pjsip_url_create(pool, 0);
- pj_strdup2(pool, &url->host, "localhost");
- pj_strdup2(pool, &url->other_param, ";pvalue=\"hello world\"");
- return (pjsip_uri*)url;
-}
-
-static pjsip_uri *create_uri15(pj_pool_t *pool)
-{
- /* "This is -. !% *_+`'~ me <sip:a19A&=+$,;?/%2c:%09a&Zz=+$,@my_proxy09.my-domain.com:9801>" */
- pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
- pjsip_url *url;
-
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*) url;
-
- pj_strdup2(pool, &name_addr->display, "This is -. !% *_+`'~ me");
- pj_strdup2(pool, &url->user, "a19A&=+$,;?/%2c");
- pj_strdup2(pool, &url->passwd, "%09a&Zz=+$,");
- pj_strdup2(pool, &url->host, "my_proxy09.MY-domain.com");
- url->port = 9801;
- return (pjsip_uri*)name_addr;
-}
-
-static pjsip_uri *create_uri16(pj_pool_t *pool)
-{
- /* "sip:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.com" */
- pjsip_url *url;
- url = pjsip_url_create(pool, 0);
- pj_strdup2(pool, &url->host, ALPHANUM "-_.com");
- return (pjsip_uri*)url;
-}
-
-static pjsip_uri *create_uri17(pj_pool_t *pool)
-{
- /* "sip:" ALPHANUM USER ":" ALPHANUM PASS "@host" */
- pjsip_url *url;
- url = pjsip_url_create(pool, 0);
- pj_strdup2(pool, &url->user, ALPHANUM USER);
- pj_strdup2(pool, &url->passwd, ALPHANUM PASS);
- pj_strdup2(pool, &url->host, "host");
- return (pjsip_uri*)url;
-}
-
-static pjsip_uri *create_uri18(pj_pool_t *pool)
-{
- /* "sip:host;user=ip;" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR ";lr;other=1;transport=sctp;other2" */
- pjsip_url *url;
- url = pjsip_url_create(pool, 0);
- pj_strdup2(pool, &url->host, "host");
- pj_strdup2(pool, &url->user_param, "ip");
- pj_strdup2(pool, &url->transport_param, "sctp");
- pj_strdup2(pool, &url->other_param, ";" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR ";other=1;other2");
- url->lr_param = 1;
- return (pjsip_uri*)url;
-}
-
-static pjsip_uri *create_dummy(pj_pool_t *pool)
-{
- PJ_UNUSED_ARG(pool);
- return NULL;
-}
-
-/*****************************************************************************/
-
-/*
- * Test one test entry.
- */
-static pj_status_t do_uri_test(pj_pool_t *pool, struct uri_test *entry)
-{
- pj_status_t status;
- int len;
- pjsip_uri *parsed_uri, *ref_uri;
- pj_str_t s1 = {NULL, 0}, s2 = {NULL, 0};
- pj_timestamp t1, t2;
-
- entry->len = pj_native_strlen(entry->str);
-
- /* Parse URI text. */
- pj_get_timestamp(&t1);
- parse_len += entry->len;
- parsed_uri = pjsip_parse_uri(pool, entry->str, entry->len, 0);
- if (!parsed_uri) {
- /* Parsing failed. If the entry says that this is expected, then
- * return OK.
- */
- status = entry->status==ERR_SYNTAX_ERR ? PJ_SUCCESS : -10;
- goto on_return;
- }
- pj_get_timestamp(&t2);
- parse_time += t2.u32.lo - t1.u32.lo;
-
- /* Create the reference URI. */
- ref_uri = entry->creator(pool);
-
- /* Print both URI. */
- s1.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
- s2.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
-
- pj_get_timestamp(&t1);
- len = pjsip_uri_print( PJSIP_URI_IN_OTHER, parsed_uri, s1.ptr, PJSIP_MAX_URL_SIZE);
- if (len < 1) {
- status = -20;
- goto on_return;
- }
- s1.slen = len;
-
- len = pjsip_uri_print( PJSIP_URI_IN_OTHER, ref_uri, s2.ptr, PJSIP_MAX_URL_SIZE);
- if (len < 1) {
- status = -30;
- goto on_return;
- }
- s2.slen = len;
- pj_get_timestamp(&t2);
- print_time += t2.u32.lo - t1.u32.lo;
-
- /* Full comparison of parsed URI with reference URI. */
- if (pjsip_uri_cmp(PJSIP_URI_IN_OTHER, parsed_uri, ref_uri) != 0) {
- /* Not equal. See if this is the expected status. */
- status = entry->status==ERR_NOT_EQUAL ? PJ_SUCCESS : -40;
- goto on_return;
-
- } else {
- /* Equal. See if this is the expected status. */
- status = entry->status==PJ_SUCCESS ? PJ_SUCCESS : -50;
- if (status != PJ_SUCCESS) {
- goto on_return;
- }
- }
-
- /* Compare text. */
- if (pj_strcmp(&s1, &s2) != 0) {
- /* Not equal. */
- status = -60;
- }
-
-on_return:
- return status;
-}
-
-pj_status_t uri_test()
-{
- unsigned i;
- pj_pool_t *pool;
- pj_status_t status;
-
- pool = pjsip_endpt_create_pool(endpt, "", 4000, 4000);
-
- for (i=0; i<PJ_ARRAY_SIZE(uri_test_array); ++i) {
- status = do_uri_test(pool, &uri_test_array[i]);
- if (status != PJ_SUCCESS) {
- PJ_LOG(3,("uri_test", " error %d when testing entry %d",
- status, i));
- break;
- }
- }
-
- pjsip_endpt_destroy_pool(endpt, pool);
- return status;
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "test.h" +#include <pjsip_core.h> +#include <pjlib.h> + + +#define ALPHANUM "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "0123456789" +#define MARK "-_.!~*'()" +#define USER_CHAR ALPHANUM MARK "&=+$,;?/" +#define PASS_CHAR ALPHANUM MARK "&=+$," +#define PARAM_CHAR ALPHANUM MARK "[]/:&+$" + +#define POOL_SIZE 4000 +#define LOOP_COUNT 1000 +#define AVERAGE_URL_LEN 80 +#define THREAD_COUNT 4 + +static pj_uint32_t parse_len; +static pj_timestamp parse_time, print_time; + + +/* URI creator functions. */ +static pjsip_uri *create_uri0( pj_pool_t *pool ); +static pjsip_uri *create_uri1( pj_pool_t *pool ); +static pjsip_uri *create_uri2( pj_pool_t *pool ); +static pjsip_uri *create_uri3( pj_pool_t *pool ); +static pjsip_uri *create_uri4( pj_pool_t *pool ); +static pjsip_uri *create_uri5( pj_pool_t *pool ); +static pjsip_uri *create_uri6( pj_pool_t *pool ); +static pjsip_uri *create_uri7( pj_pool_t *pool ); +static pjsip_uri *create_uri8( pj_pool_t *pool ); +static pjsip_uri *create_uri9( pj_pool_t *pool ); +static pjsip_uri *create_uri10( pj_pool_t *pool ); +static pjsip_uri *create_uri11( pj_pool_t *pool ); +static pjsip_uri *create_uri12( pj_pool_t *pool ); +static pjsip_uri *create_uri13( pj_pool_t *pool ); +static pjsip_uri *create_uri14( pj_pool_t *pool ); +static pjsip_uri *create_uri15( pj_pool_t *pool ); +static pjsip_uri *create_uri16( pj_pool_t *pool ); +static pjsip_uri *create_uri17( pj_pool_t *pool ); +static pjsip_uri *create_dummy( pj_pool_t *pool ); + +#define ERR_NOT_EQUAL -1001 +#define ERR_SYNTAX_ERR -1002 + +struct uri_test +{ + pj_status_t status; + char str[PJSIP_MAX_URL_SIZE]; + pjsip_uri *(*creator)(pj_pool_t *pool); + pj_size_t len; +} uri_test_array[] = +{ + { + PJ_SUCCESS, + "sip:localhost", + &create_uri0 + }, + { + PJ_SUCCESS, + "sip:user@localhost", + &create_uri1 + }, + { + PJ_SUCCESS, + "sip:user:password@localhost:5060", + &create_uri2, + }, + { + /* Port is specified should not match unspecified port. */ + ERR_NOT_EQUAL, + "sip:localhost:5060", + &create_uri3 + }, + { + /* All recognized parameters. */ + PJ_SUCCESS, + "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK", + &create_uri4 + }, + { + /* Params mixed with other params and header params. */ + PJ_SUCCESS, + "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry" + "?Subject=Hello%20There&Server=SIP%20Server", + &create_uri5 + }, + { + /* SIPS. */ + PJ_SUCCESS, + "sips:localhost", + &create_uri6, + }, + { + /* Name address */ + PJ_SUCCESS, + "<sip:localhost>", + &create_uri7 + }, + { + /* Name address with display name and SIPS scheme with some redundant + * whitespaced. + */ + PJ_SUCCESS, + " Power Administrator <sips:localhost>", + &create_uri8 + }, + { + /* Name address. */ + PJ_SUCCESS, + " \"User\" <sip:user@localhost:5071>", + &create_uri9 + }, + { + /* Escaped sequence in display name (display=Strange User\"\\\"). */ + PJ_SUCCESS, + " \"Strange User\\\"\\\\\\\"\" <sip:localhost>", + &create_uri10, + }, + { + /* Errorneous escaping in display name. */ + ERR_SYNTAX_ERR, + " \"Rogue User\\\" <sip:localhost>", + &create_uri11, + }, + { + /* Dangling quote in display name, but that should be OK. */ + PJ_SUCCESS, + "Strange User\" <sip:localhost>", + &create_uri12, + }, + { + /* Special characters in parameter value must be quoted. */ + PJ_SUCCESS, + "sip:localhost;pvalue=\"hello world\"", + &create_uri13, + }, + { + /* Excercise strange character sets allowed in display, user, password, + * host, and port. + */ + PJ_SUCCESS, + "This is -. !% *_+`'~ me <sip:a19A&=+$,;?/%2c:%40a&Zz=+$,@" + "my_proxy09.MY-domain.com:9801>", + &create_uri14, + }, + { + /* Another excercise to the allowed character sets to the hostname. */ + PJ_SUCCESS, + "sip:" ALPHANUM "-_.com", + &create_uri15, + }, + { + /* Another excercise to the allowed character sets to the username + * and password. + */ + PJ_SUCCESS, + "sip:" USER_CHAR ":" PASS_CHAR "@host", + &create_uri16, + }, + { + /* Excercise to the pname and pvalue, and mixup of other-param + * between 'recognized' params. + */ + PJ_SUCCESS, + "sip:host;user=ip;" PARAM_CHAR "%21=" PARAM_CHAR "%21" + ";lr;other=1;transport=sctp;other2", + &create_uri17, + }, + { + /* 18: This should trigger syntax error. */ + ERR_SYNTAX_ERR, + "sip:", + &create_dummy, + }, + { + /* 19: Syntax error: whitespace after scheme. */ + ERR_SYNTAX_ERR, + "sip :host", + &create_dummy, + }, + { + /* 20: Syntax error: whitespace before hostname. */ + ERR_SYNTAX_ERR, + "sip: host", + &create_dummy, + }, + { + /* 21: Syntax error: invalid port. */ + ERR_SYNTAX_ERR, + "sip:user:password", + &create_dummy, + }, + { + /* 22: Syntax error: no host. */ + ERR_SYNTAX_ERR, + "sip:user@", + &create_dummy, + }, + { + /* 23: Syntax error: no user/host. */ + ERR_SYNTAX_ERR, + "sip:@", + &create_dummy, + }, + { + /* 24: Syntax error: empty string. */ + ERR_SYNTAX_ERR, + "", + &create_dummy, + } +}; + +static pjsip_uri *create_uri0(pj_pool_t *pool) +{ + /* "sip:localhost" */ + pjsip_url *url = pjsip_url_create(pool, 0); + + pj_strdup2(pool, &url->host, "localhost"); + return (pjsip_uri*)url; +} + +static pjsip_uri *create_uri1(pj_pool_t *pool) +{ + /* "sip:user@localhost" */ + pjsip_url *url = pjsip_url_create(pool, 0); + + pj_strdup2( pool, &url->user, "user"); + pj_strdup2( pool, &url->host, "localhost"); + + return (pjsip_uri*) url; +} + +static pjsip_uri *create_uri2(pj_pool_t *pool) +{ + /* "sip:user:password@localhost:5060" */ + pjsip_url *url = pjsip_url_create(pool, 0); + + pj_strdup2( pool, &url->user, "user"); + pj_strdup2( pool, &url->passwd, "password"); + pj_strdup2( pool, &url->host, "localhost"); + url->port = 5060; + + return (pjsip_uri*) url; +} + +static pjsip_uri *create_uri3(pj_pool_t *pool) +{ + /* Like: "sip:localhost:5060", but without the port. */ + pjsip_url *url = pjsip_url_create(pool, 0); + + pj_strdup2(pool, &url->host, "localhost"); + return (pjsip_uri*)url; +} + +static pjsip_uri *create_uri4(pj_pool_t *pool) +{ + /* "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK" */ + pjsip_url *url = pjsip_url_create(pool, 0); + + pj_strdup2(pool, &url->host, "localhost"); + pj_strdup2(pool, &url->transport_param, "tcp"); + pj_strdup2(pool, &url->user_param, "ip"); + url->ttl_param = 255; + url->lr_param = 1; + pj_strdup2(pool, &url->maddr_param, "127.0.0.1"); + pj_strdup2(pool, &url->method_param, "ACK"); + + return (pjsip_uri*)url; +} + +#define param_add(list,pname,pvalue) \ + do { \ + pjsip_param *param; \ + param=pj_pool_alloc(pool, sizeof(pjsip_param)); \ + param->name = pj_str(pname); \ + param->value = pj_str(pvalue); \ + pj_list_insert_before(&list, param); \ + } while (0) + +static pjsip_uri *create_uri5(pj_pool_t *pool) +{ + /* "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry" + "?Subject=Hello%20There&Server=SIP%20Server" + */ + pjsip_url *url = pjsip_url_create(pool, 0); + + pj_strdup2(pool, &url->host, "localhost"); + pj_strdup2(pool, &url->user_param, "phone"); + + //pj_strdup2(pool, &url->other_param, ";pickup=hurry;message=I%20am%20sorry"); + param_add(url->other_param, "pickup", "hurry"); + param_add(url->other_param, "message", "I am sorry"); + + //pj_strdup2(pool, &url->header_param, "?Subject=Hello%20There&Server=SIP%20Server"); + param_add(url->header_param, "Subject", "Hello There"); + param_add(url->header_param, "Server", "SIP Server"); + return (pjsip_uri*)url; + +} + +static pjsip_uri *create_uri6(pj_pool_t *pool) +{ + /* "sips:localhost" */ + pjsip_url *url = pjsip_url_create(pool, 1); + + pj_strdup2(pool, &url->host, "localhost"); + return (pjsip_uri*)url; +} + +static pjsip_uri *create_uri7(pj_pool_t *pool) +{ + /* "<sip:localhost>" */ + pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); + pjsip_url *url; + + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*) url; + + pj_strdup2(pool, &url->host, "localhost"); + return (pjsip_uri*)name_addr; +} + +static pjsip_uri *create_uri8(pj_pool_t *pool) +{ + /* " Power Administrator <sips:localhost>" */ + pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); + pjsip_url *url; + + url = pjsip_url_create(pool, 1); + name_addr->uri = (pjsip_uri*) url; + + pj_strdup2(pool, &name_addr->display, "Power Administrator"); + pj_strdup2(pool, &url->host, "localhost"); + return (pjsip_uri*)name_addr; +} + +static pjsip_uri *create_uri9(pj_pool_t *pool) +{ + /* " \"User\" <sip:user@localhost:5071>" */ + pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); + pjsip_url *url; + + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*) url; + + pj_strdup2(pool, &name_addr->display, "User"); + pj_strdup2(pool, &url->user, "user"); + pj_strdup2(pool, &url->host, "localhost"); + url->port = 5071; + return (pjsip_uri*)name_addr; +} + +static pjsip_uri *create_uri10(pj_pool_t *pool) +{ + /* " \"Strange User\\\"\\\\\\\"\" <sip:localhost>" */ + pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); + pjsip_url *url; + + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*) url; + + pj_strdup2(pool, &name_addr->display, "Strange User\\\"\\\\\\\""); + pj_strdup2(pool, &url->host, "localhost"); + return (pjsip_uri*)name_addr; +} + +static pjsip_uri *create_uri11(pj_pool_t *pool) +{ + /* " \"Rogue User\\\" <sip:localhost>" */ + pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); + pjsip_url *url; + + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*) url; + + pj_strdup2(pool, &name_addr->display, "Rogue User\\"); + pj_strdup2(pool, &url->host, "localhost"); + return (pjsip_uri*)name_addr; +} + +static pjsip_uri *create_uri12(pj_pool_t *pool) +{ + /* "Strange User\" <sip:localhost>" */ + pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); + pjsip_url *url; + + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*) url; + + pj_strdup2(pool, &name_addr->display, "Strange User\""); + pj_strdup2(pool, &url->host, "localhost"); + return (pjsip_uri*)name_addr; +} + +static pjsip_uri *create_uri13(pj_pool_t *pool) +{ + /* "sip:localhost;pvalue=\"hello world\"" */ + pjsip_url *url; + url = pjsip_url_create(pool, 0); + pj_strdup2(pool, &url->host, "localhost"); + //pj_strdup2(pool, &url->other_param, ";pvalue=\"hello world\""); + param_add(url->other_param, "pvalue", "hello world"); + return (pjsip_uri*)url; +} + +static pjsip_uri *create_uri14(pj_pool_t *pool) +{ + /* "This is -. !% *_+`'~ me <sip:a19A&=+$,;?/%2c:%40a&Zz=+$,@my_proxy09.my-domain.com:9801>" */ + pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); + pjsip_url *url; + + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*) url; + + pj_strdup2(pool, &name_addr->display, "This is -. !% *_+`'~ me"); + pj_strdup2(pool, &url->user, "a19A&=+$,;?/,"); + pj_strdup2(pool, &url->passwd, "@a&Zz=+$,"); + pj_strdup2(pool, &url->host, "my_proxy09.MY-domain.com"); + url->port = 9801; + return (pjsip_uri*)name_addr; +} + +static pjsip_uri *create_uri15(pj_pool_t *pool) +{ + /* "sip:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.com" */ + pjsip_url *url; + url = pjsip_url_create(pool, 0); + pj_strdup2(pool, &url->host, ALPHANUM "-_.com"); + return (pjsip_uri*)url; +} + +static pjsip_uri *create_uri16(pj_pool_t *pool) +{ + /* "sip:" USER_CHAR ":" PASS_CHAR "@host" */ + pjsip_url *url; + url = pjsip_url_create(pool, 0); + pj_strdup2(pool, &url->user, USER_CHAR); + pj_strdup2(pool, &url->passwd, PASS_CHAR); + pj_strdup2(pool, &url->host, "host"); + return (pjsip_uri*)url; +} + +static pjsip_uri *create_uri17(pj_pool_t *pool) +{ + /* "sip:host;user=ip;" PARAM_CHAR "%21=" PARAM_CHAR "%21;lr;other=1;transport=sctp;other2" */ + pjsip_url *url; + url = pjsip_url_create(pool, 0); + pj_strdup2(pool, &url->host, "host"); + pj_strdup2(pool, &url->user_param, "ip"); + pj_strdup2(pool, &url->transport_param, "sctp"); + param_add(url->other_param, PARAM_CHAR "!", PARAM_CHAR "!"); + param_add(url->other_param, "other", "1"); + param_add(url->other_param, "other2", ""); + url->lr_param = 1; + return (pjsip_uri*)url; +} + +static pjsip_uri *create_dummy(pj_pool_t *pool) +{ + PJ_UNUSED_ARG(pool); + return NULL; +} + +/*****************************************************************************/ + +/* + * Test one test entry. + */ +static pj_status_t do_uri_test(pj_pool_t *pool, struct uri_test *entry) +{ + pj_status_t status; + int len; + pjsip_uri *parsed_uri, *ref_uri; + pj_str_t s1 = {NULL, 0}, s2 = {NULL, 0}; + pj_timestamp t1, t2; + + entry->len = pj_native_strlen(entry->str); + + /* Parse URI text. */ + pj_get_timestamp(&t1); + parse_len += entry->len; + parsed_uri = pjsip_parse_uri(pool, entry->str, entry->len, 0); + if (!parsed_uri) { + /* Parsing failed. If the entry says that this is expected, then + * return OK. + */ + status = entry->status==ERR_SYNTAX_ERR ? PJ_SUCCESS : -10; + if (status != 0) { + PJ_LOG(3,("", " uri parse error!\n" + " uri='%s'\n", + entry->str)); + } + goto on_return; + } + pj_get_timestamp(&t2); + pj_sub_timestamp(&t2, &t1); + pj_add_timestamp(&parse_time, &t2); + + /* Create the reference URI. */ + ref_uri = entry->creator(pool); + + /* Print both URI. */ + s1.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); + s2.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); + + pj_get_timestamp(&t1); + len = pjsip_uri_print( PJSIP_URI_IN_OTHER, parsed_uri, s1.ptr, PJSIP_MAX_URL_SIZE); + if (len < 1) { + status = -20; + goto on_return; + } + s1.ptr[len] = '\0'; + s1.slen = len; + + pj_get_timestamp(&t2); + pj_sub_timestamp(&t2, &t1); + pj_add_timestamp(&print_time, &t2); + + len = pjsip_uri_print( PJSIP_URI_IN_OTHER, ref_uri, s2.ptr, PJSIP_MAX_URL_SIZE); + if (len < 1) { + status = -30; + goto on_return; + } + s2.ptr[len] = '\0'; + s2.slen = len; + + /* Full comparison of parsed URI with reference URI. */ + status = pjsip_uri_cmp(PJSIP_URI_IN_OTHER, parsed_uri, ref_uri); + if (status != 0) { + /* Not equal. See if this is the expected status. */ + status = entry->status==ERR_NOT_EQUAL ? PJ_SUCCESS : -40; + if (status != 0) { + PJ_LOG(3,("", " uri comparison mismatch, status=%d:\n" + " uri1='%s'\n" + " uri2='%s'", + status, s1.ptr, s2.ptr)); + } + goto on_return; + + } else { + /* Equal. See if this is the expected status. */ + status = entry->status==PJ_SUCCESS ? PJ_SUCCESS : -50; + if (status != PJ_SUCCESS) { + goto on_return; + } + } + + /* Compare text. */ + if (pj_strcmp(&s1, &s2) != 0) { + /* Not equal. */ + status = -60; + } + +on_return: + return status; +} + +pj_status_t uri_test() +{ + unsigned i, loop; + pj_pool_t *pool; + pj_status_t status; + pj_timestamp zero; + pj_highprec_t avg_parse, avg_print; + + zero.u32.hi = zero.u32.lo = 0; + + PJ_LOG(3,("", " simple test")); + pool = pjsip_endpt_create_pool(endpt, "", POOL_SIZE, POOL_SIZE); + for (i=0; i<PJ_ARRAY_SIZE(uri_test_array); ++i) { + status = do_uri_test(pool, &uri_test_array[i]); + if (status != PJ_SUCCESS) { + PJ_LOG(3,("uri_test", " error %d when testing entry %d", + status, i)); + goto on_return; + } + } + pjsip_endpt_destroy_pool(endpt, pool); + + PJ_LOG(3,("", " benchmarking...")); + parse_len = 0; + parse_time.u32.hi = parse_time.u32.lo = 0; + print_time.u32.hi = print_time.u32.lo = 0; + pool = pjsip_endpt_create_pool(endpt, "", POOL_SIZE, POOL_SIZE); + for (loop=0; loop<LOOP_COUNT; ++loop) { + for (i=0; i<PJ_ARRAY_SIZE(uri_test_array); ++i) { + status = do_uri_test(pool, &uri_test_array[i]); + if (status != PJ_SUCCESS) { + PJ_LOG(3,("uri_test", " error %d when testing entry %d", + status, i)); + goto on_return; + } + } + } + + avg_parse = pj_elapsed_usec(&zero, &parse_time); + pj_highprec_mul(avg_parse, AVERAGE_URL_LEN); + pj_highprec_div(avg_parse, parse_len); + avg_parse = 1000000 / avg_parse; + + avg_print = pj_elapsed_usec(&zero, &print_time); + pj_highprec_mul(avg_print, AVERAGE_URL_LEN); + pj_highprec_div(avg_print, parse_len); + avg_print = 1000000 / avg_print; + + PJ_LOG(3,("", " done. Average parse=%d url/sec, print=%d url/sec", + (unsigned)avg_parse, (unsigned)avg_print)); + + PJ_LOG(3,("", " multithreaded test")); + + +on_return: + pjsip_endpt_destroy_pool(endpt, pool); + return status; +} + |