Pjsip Dev Guide
Pjsip Dev Guide
Pjsip Dev Guide
Developers Guide
Version 0.5.4
ABOUT PJSIP PJSIP is small-footprint and high-performance SIP stack written in C. PJSIP is distributed under GNU General Public License (GPL). Alternative licensing is available. Please visit http://www.pjproject.net for more details.
ABOUT THIS DOCUMENT Copyright 2005-2006 Benny Prijono This is a free document distributed under GNU Free Documentation License version 1.2. Everyone is permitted to copy and distribute verbatim copies of this document, but changing it is not allowed.
Page 2
Date
07 Mar 2006
By
bennylp
Changes
Added dlg_terminate(), inv_terminate() et all. Review the evsub API, added few more words. Added IM and iscomposing chapter. Added PJSUA abstraction chapter. Added event framework, presence, and refer event package. Application needs to call pjsip_tsx_recv_msg() after creating UAS transaction. added generic capabilities management to endpoint. changed module interface (removed supported methods). no more stateless operations in dialogs. introducing dialog set.
0.5-pre 0.5-pre
bennylp bennylp
Page 3
Table of Contents
TABLE OF CONTENTS....................................................................................................................... 4 TABLE OF FIGURES............................................................................................................................ 8 TABLE OF CODES................................................................................................................................ 8 CHAPTER 1: GENERAL DESIGN.....................................................................................................11 1.1 ARCHITECTURE..................................................................................................................................11 1.1.1 Communication Diagram...................................................................................................... 11 1.1.2 Class Diagram....................................................................................................................... 11 1.2 THE ENDPOINT..................................................................................................................................12 1.2.1 Pool Allocations and Deallocations...................................................................................... 12 1.2.2 Timer Management................................................................................................................ 12 1.2.3 Polling the Stack.................................................................................................................... 13 1.3 THREAD SAFETY AND THREAD COMPLICATIONS..................................................................................... 13 1.3.1 Thread Safety......................................................................................................................... 13 1.3.2 The Complications................................................................................................................. 13 1.3.3 The Relief............................................................................................................................... 14 CHAPTER 2: MODULE....................................................................................................................... 15 2.1.1 Module Declaration............................................................................................................... 15 2.1.2 Module Priorities................................................................................................................... 16 2.1.3 Incoming Message Processing by Modules........................................................................... 17 2.1.4 Outgoing Message Processing by Modules........................................................................... 17 2.1.5 Transaction User and State Callback.................................................................................... 18 2.1.6 Module Specific Data............................................................................................................ 18 2.1.7 Callback Summary................................................................................................................. 19 2.1.8 Sample Callback Diagrams................................................................................................... 20 2.2 MODULE MANAGEMENT..................................................................................................................... 21 2.2.1 Module Management API...................................................................................................... 21 2.2.2 Module Capabilities.............................................................................................................. 21 CHAPTER 3: MESSAGE ELEMENTS.............................................................................................. 23 3.1 UNIFORM RESOURCE INDICATOR (URI)............................................................................................... 23 3.1.1 URI Class Diagram........................................................................................................... 23 3.1.2 URI Context........................................................................................................................... 23 3.1.3 Base URI................................................................................................................................ 24 3.1.4 SIP and SIPS URI.................................................................................................................. 25 3.1.5 Tel URI.................................................................................................................................. 25 3.1.6 Name Address........................................................................................................................ 26 3.1.7 Sample URI Manipulation Program......................................................................................26 3.2 SIP METHODS.................................................................................................................................. 27 3.2.1 SIP Method Representation (pjsip_method).......................................................................... 27 3.2.2 SIP Method API..................................................................................................................... 28 3.3 HEADER FIELDS................................................................................................................................ 29 3.3.1 Header Class Diagram..................................................................................................... 29 3.3.2 Header Structure................................................................................................................... 29 3.3.3 Common Header Functions................................................................................................... 30 3.3.4 Supported Header Fields....................................................................................................... 31 3.3.5 Header Array Elements......................................................................................................... 31 3.4 MESSAGE BODY (PJSIP_MSG_BODY).....................................................................................................32 3.5 MESSAGE (PJSIP_MSG)....................................................................................................................... 33 3.6 SIP STATUS CODES...........................................................................................................................34 3.7 NON-STANDARD PARAMETER ELEMENTS.............................................................................................. 35 3.7.1 Data Structure Representation (pjsip_param)...................................................................... 36 3.7.2 Non-Standard Parameter Manipulation................................................................................ 36 3.8 ESCAPEMENT RULES.......................................................................................................................... 36 Page 4
PJSIP Developers Guide CHAPTER 4: PARSER......................................................................................................................... 38 4.1 FEATURES.........................................................................................................................................38 4.2 FUNCTIONS....................................................................................................................................... 39 4.2.1 Message Parsing....................................................................................................................39 4.2.2 URI Parsing........................................................................................................................... 39 4.2.3 Header Parsing......................................................................................................................39 4.3 EXTENDING PARSER........................................................................................................................... 40 CHAPTER 5: MESSAGE BUFFERS.................................................................................................. 41 5.1 RECEIVE DATA BUFFER..................................................................................................................... 41 5.1.1 Receive Data Buffer Structure............................................................................................... 41 5.2 TRANSMIT DATA BUFFER (PJSIP_TX_DATA).......................................................................................... 42 CHAPTER 6: TRANSPORT LAYER................................................................................................. 43 6.1 TRANSPORT LAYER DESIGN................................................................................................................ 43 6.1.1 Class Diagram.................................................................................................................. 43 6.1.2 Transport Manager................................................................................................................43 6.1.3 Transport Factory..................................................................................................................44 6.1.4 Transport .............................................................................................................................. 44 6.2 USING TRANSPORTS........................................................................................................................... 46 6.2.1 Function Reference................................................................................................................ 46 6.3 EXTENDING TRANSPORTS.................................................................................................................... 46 6.4 INITIALIZING TRANSPORTS...................................................................................................................46 6.4.1 UDP Transport Initialization................................................................................................ 47 6.4.2 TCP Transport Initialization................................................................................................. 47 6.4.3 TLS Transport Initialization.................................................................................................. 47 6.4.4 SCTP Transport Initialization............................................................................................... 47 CHAPTER 7: SENDING MESSAGES................................................................................................ 48 7.1 SENDING MESSAGES OVERVIEW.......................................................................................................... 48 7.1.1 Creating Messages................................................................................................................ 48 7.1.2 Sending Messages.................................................................................................................. 48 7.2 FUNCTION REFERENCE........................................................................................................................49 7.2.1 Sending Response.................................................................................................................. 49 7.2.2 Sending Request..................................................................................................................... 50 7.2.3 Stateless Proxy Forwarding.................................................................................................. 52 7.3 EXAMPLES........................................................................................................................................ 53 7.3.1 Sending Responses................................................................................................................. 53 7.3.2 Sending Requests................................................................................................................... 54 7.3.3 Stateless Forwarding............................................................................................................. 55 CHAPTER 8: TRANSACTIONS......................................................................................................... 56 8.1 DESIGN............................................................................................................................................ 56 8.1.1 Introduction........................................................................................................................... 56 8.1.2 Timers and Retransmissions.................................................................................................. 56 8.1.3 INVITE Final Response and ACK Request............................................................................56 8.1.4 Incoming ACK Request.......................................................................................................... 57 8.1.5 Server Resolution and Transports......................................................................................... 57 8.1.6 Via Header............................................................................................................................. 58 8.2 REFERENCE.......................................................................................................................................58 8.2.1 Base Functions...................................................................................................................... 58 8.2.2 Composite Functions............................................................................................................. 59 8.3 SENDING STATEFULL RESPONSES......................................................................................................... 60 8.3.1 Usage Examples.................................................................................................................... 60 8.4 SENDING STATEFULL REQUEST............................................................................................................ 60 8.4.1 Usage Examples.................................................................................................................... 61 8.5 STATEFULL PROXY FORWARDING......................................................................................................... 61 8.5.1 Usage Examples.................................................................................................................... 61 CHAPTER 9: AUTHENTICATION FRAMEWORK.......................................................................63 Page 5
PJSIP Developers Guide 9.1 CLIENT AUTHENTICATION FRAMEWORK................................................................................................ 63 9.1.1 Client Authentication Framework Reference........................................................................ 63 9.1.2 Examples................................................................................................................................ 64 9.2 SERVER AUTHORIZATION FRAMEWORK................................................................................................. 65 9.2.1 Server Authorization Reference............................................................................................. 65 9.3 EXTENDING AUTHENTICATION FRAMEWORK.......................................................................................... 67 CHAPTER 10: BASIC USER AGENT LAYER (UA)....................................................................... 68 10.1 BASIC DIALOG CONCEPT.................................................................................................................. 68 10.1.1 Dialog Sessions....................................................................................................................68 10.1.2 Dialog Usages..................................................................................................................... 68 10.1.3 Dialog Set............................................................................................................................ 69 10.1.4 Client Authentication........................................................................................................... 69 10.1.5 Class Diagram..................................................................................................................... 69 10.1.6 Forking................................................................................................................................ 70 10.1.7 CSeq Sequencing................................................................................................................. 72 10.1.8 Transactions........................................................................................................................ 72 10.2 BASIC UA API REFERENCE............................................................................................................. 73 10.2.1 User Agent Module API....................................................................................................... 73 10.2.2 Dialog Structure.................................................................................................................. 73 10.2.3 Dialog Creation API............................................................................................................ 74 10.2.4 Dialog Termination............................................................................................................. 74 10.2.5 Dialog Session Management API........................................................................................ 75 10.2.6 Dialog Usages API.............................................................................................................. 75 10.2.7 Dialog Request and Response API...................................................................................... 75 10.2.8 Dialog Auxiliary API........................................................................................................... 76 10.3 EXAMPLES...................................................................................................................................... 78 10.3.1 Invite UAS Dialog................................................................................................................ 78 10.3.2 Outgoing Invite Dialog........................................................................................................ 80 10.3.3 Terminating Dialog............................................................................................................. 82 CHAPTER 11: SDP OFFER/ANSWER FRAMEWORK................................................................. 83 11.1 SDP NEGOTIATOR STRUCTURE......................................................................................................... 83 11.2 SDP NEGOTIATOR SESSION.............................................................................................................. 84 11.3 SDP NEGOTIATION FUNCTION.......................................................................................................... 85 CHAPTER 12: DIALOG INVITE SESSION AND USAGE............................................................. 86 12.1 INTRODUCTION................................................................................................................................ 86 12.1.1 Terms................................................................................................................................... 86 12.1.2 Features............................................................................................................................... 86 12.1.3 Invite Session State.............................................................................................................. 86 12.1.4 Invite Session Creation........................................................................................................ 87 12.1.5 Messages Handling..............................................................................................................88 12.1.6 Extending the Dialog........................................................................................................... 88 12.1.7 Extending the Invite Session................................................................................................ 88 12.2 REFERENCE.....................................................................................................................................89 12.2.1 Data Structure..................................................................................................................... 89 12.2.2 Invite Usage Module............................................................................................................ 89 12.2.3 Session Callback.................................................................................................................. 90 12.2.4 Session Creation and Termination...................................................................................... 91 12.2.5 Session Operations.............................................................................................................. 92 12.2.6 Auxiliary API....................................................................................................................... 93 CHAPTER 13: SIP-SPECIFIC EVENT NOTIFICATION.............................................................. 95 13.1 INTRODUCTION................................................................................................................................ 95 13.1.1 Basic Concept...................................................................................................................... 95 13.1.2 Event Package..................................................................................................................... 95 13.1.3 Header Fields...................................................................................................................... 96 13.2 BASIC OPERATION........................................................................................................................... 96 13.2.1 Client Initiating Subscription.............................................................................................. 96 13.2.2 Server Receiving Incoming Subscription............................................................................. 97 Page 6
PJSIP Developers Guide 13.2.3 Server Activating Subscription (Sending NOTIFY)............................................................. 98 13.2.4 Client Receiving NOTIFY Requests..................................................................................... 98 13.2.5 Server Terminating Subscription......................................................................................... 99 13.2.6 Client Receiving Subscription Termination....................................................................... 100 13.2.7 Client Refreshing Subscription.......................................................................................... 100 13.2.8 Server Detecting Refresh Timeout..................................................................................... 100 13.3 REFERENCE...................................................................................................................................101 13.3.1 Module Management......................................................................................................... 101 13.3.2 Event Package Management............................................................................................. 101 13.3.3 Event Subscription State.................................................................................................... 101 13.3.4 Event Subscription Session................................................................................................ 102 13.3.5 Generic Event Subscription Callback................................................................................102 13.3.6 Event Subscription API...................................................................................................... 104 13.3.7 Auxiliary API..................................................................................................................... 106 CHAPTER 14: PRESENCE EVENT PACKAGE............................................................................107 14.1 INTRODUCTION.............................................................................................................................. 107 14.2 REFERENCE...................................................................................................................................107 CHAPTER 15: REFER EVENT PACKAGE................................................................................... 108 CHAPTER 16: INSTANT MESSAGING......................................................................................... 109 16.1 INSTANT MESSAGING..................................................................................................................... 109 16.1.1 Sending MESSAGE............................................................................................................ 109 16.1.2 Receiving MESSAGE......................................................................................................... 110 16.2 MESSAGE COMPOSITION INDICATION................................................................................................ 110 CHAPTER 17: PJSUA ABSTRACTION.......................................................................................... 112
Page 7
Table of Figures
FIGURE 1 COLLABORATION DIAGRAM..................................................................................... 11 FIGURE 2 CLASS DIAGRAM............................................................................................................ 11 FIGURE 3 MODULE STATE DIAGRAM......................................................................................... 15 FIGURE 4 CASCADE MODULE CALLBACK................................................................................ 17 FIGURE 5 CALLBACK SUMMARY................................................................................................. 19 FIGURE 6 PROCESSING OF INCOMING MESSAGE OUTSIDE TRANSACTION/DIALOG ..................................................................................................................................................................20 FIGURE 7 PROCESSING OF INCOMING MESSAGE INSIDE TRANSACTION.....................20 FIGURE 8 PROCESSING OF INCOMING MESSAGE INSIDE DIALOG BUT OUTSIDE TRANSACTION.................................................................................................................................... 21 FIGURE 9 URI CLASS DIAGRAM................................................................................................23 FIGURE 10 HEADER CLASS DIAGRAM.................................................................................... 29 FIGURE 11 TRANSPORT LAYER "CLASS DIAGRAM"............................................................. 43 FIGURE 12 AUTHENTICATION FRAMEWORK.......................................................................... 63 FIGURE 13 CLIENT AUTHENTICATION DATA STRUCTURE................................................ 64 FIGURE 14 BASIC USER AGENT CLASS DIAGRAM.................................................................. 70 FIGURE 15 SDP NEGOTIATOR "CLASS DIAGRAM".................................................................83 FIGURE 16 SDP OFFER/ANSWER SESSION STATE DIAGRAM.............................................. 84 FIGURE 17 INVITE SESSION STATE DIAGRAM.........................................................................87 FIGURE 18 INVITE SESSION STATE DESCRIPTION................................................................. 87 FIGURE 19 CLIENT INITIATING SUBSCRIPTION..................................................................... 96 FIGURE 20 SERVER CREATING SUBSCRIPTION...................................................................... 97 FIGURE 21 SERVER ACTIVATING SUBSCRIPTION..................................................................98 FIGURE 22 CLIENT RECEIVING NOTIFY.................................................................................... 99 FIGURE 23 SERVER TERMINATING SUBSCRIPTION.............................................................. 99 FIGURE 24 CLIENT RECEIVING SUBSCRIPTION TERMINATION..................................... 100 FIGURE 25 CLIENT REFRESHING SUBSCRIPTION................................................................ 100 FIGURE 26 SERVER DETECTING SUBSCRIPTION TIMEOUT............................................. 101
Table of Codes
CODE 1 LOCKING DIALOG PROBLEM........................................................................................ 14 CODE 2 CORRECT WAY TO LOCK A DIALOG...........................................................................14 CODE 3 MODULE DECLARATION................................................................................................. 15 CODE 4 MODULE PRIORITIES....................................................................................................... 16 CODE 5 MODULE SPECIFIC DATA................................................................................................ 18 CODE 6 ACCESSING MODULE SPECIFIC DATA........................................................................19 CODE 7 URI CONTEXT...................................................................................................................... 23 Page 8
PJSIP Developers Guide CODE 8 GENERIC URI DECLARATION........................................................................................ 24 CODE 9 URI VIRTUAL FUNCTION TABLE.................................................................................. 24 CODE 10 SIP URI DECLARATION.................................................................................................. 25 CODE 11 TEL URI DECLARATION.................................................................................................26 CODE 12 NAME ADDRESS DECLARATION................................................................................. 26 CODE 13 SAMPLE URI MANIPULATION PROGRAM................................................................27 CODE 14 SIP METHOD DECLARATION........................................................................................28 CODE 15 SIP METHOD ID................................................................................................................. 28 CODE 16 GENERIC HEADER DECLARATION............................................................................ 30 CODE 17 GENERIC HEADER DECLARATION............................................................................ 30 CODE 18 HEADER VIRTUAL FUNCTION TABLE.......................................................................30 CODE 19 MESSAGE BODY DECLARATION................................................................................. 32 CODE 20 SIP MESSAGE DECLARATION...................................................................................... 33 CODE 21 SIP STATUS CODE CONSTANTS................................................................................... 35 CODE 22 NON-STANDARD PARAMETER DECLARATION......................................................36 CODE 23 RECEIVE DATA BUFFER DECLARATION................................................................. 41 CODE 24 TRANSMIT DATA BUFFER DECLARATION.............................................................. 42 CODE 25 TRANSPORT OBJECT DECLARATION....................................................................... 45 CODE 26 SAMPLE: STATELESS RESPONSE................................................................................ 53 CODE 27 SAMPLE: STATELESS RESPONSE................................................................................ 53 CODE 28 STATELESS REDIRECTION........................................................................................... 54 CODE 29 SENDING STATELESS REQUEST..................................................................................54 CODE 30 STATELESS FORWARDING........................................................................................... 55 CODE 31 SENDING STATEFULL RESPONSE............................................................................... 60 CODE 32 SENDING STATEFULL RESPONSE............................................................................... 60 CODE 33 SENDING REQUEST STATEFULLY.............................................................................. 61 CODE 34 STATEFULL FORWARDING...........................................................................................62 CODE 35 CLIENT ATHORIZATION EXAMPLE...........................................................................65 CODE 36 DIALOG STRUCTURE...................................................................................................... 73 CODE 37 CREATING DIALOG FOR INCOMING INVITE..........................................................78 CODE 38 ANSWERING DIALOG......................................................................................................79 CODE 39 PROCESSING CANCEL REQUEST................................................................................ 80 CODE 40 PROCESSING ACK REQUEST........................................................................................ 80 CODE 41 CREATING OUTGOING DIALOG..................................................................................81 CODE 42 RECEIVING RESPONSE IN DIALOG............................................................................ 81 CODE 43 SENDING ACK REQUEST................................................................................................82 CODE 44 INVITE SESSION DATA STRUCTURE.......................................................................... 89 CODE 45 INVITE SESSION OPTIONS............................................................................................. 89 CODE 46 EVENT SUBSCRIPTION STATE................................................................................... 102 CODE 47 EVENT SUBSCRIPTION CALLBACK..........................................................................103 Page 9
PJSIP Developers Guide CODE 48 SENDING IM OUTSIDE DIALOG................................................................................. 109 CODE 49 SENDING IM INSIDE DIALOG..................................................................................... 110
Page 10
ENDPOINT
sip_endoint.c Global Timer PARSER sip_parser.c TRANSPORT MANAGER sip_transport.c Global I/O Queue
Parser Plugin
Parser Plugin
TRANSPORT (pjsip_transport)
TRANSPORT (pjsip_transport)
MODULE n
ENDPOINT
sip_endoint.c 1 TRANSPORT MANAGER sip_transport.c n TRANSPORT sip_transport.c
1 PARSER sip_parser.c
n Header Parser
...
Page 11
Some of the basic functionalities will be described in the following sections, and the other will be described in next chapters.
When the endpoint is created (pjsip_endpt_create()), application MUST specify the pool factory that will be used by the endpoint. Endpoint keeps this pool factory pointer throughout its lifetime, and will use this to create and release memory pools.
The endpoint checks for timers expiration when the endpoint polling function is called.
Page 12
These data structures are not thread safe; the thread safety to these data structures will be guaranteed by the object that contains them. If data structures were made thread safe, it will seriously affect the performance of the stack and drains the operating systems resources. In contrast, SIP objects MUST be thread safe. Examples of what we call objects are: PJLIB objects, such as ioqueue. PJSIP objects, such as endpoint, transactions, dialogs, dialog usages, etc.
PJSIP Developers Guide between the two approaches are, the dialog inc/dec lock guarantees that the dialog will not be destroyed in the function call, causing pj_mutex_unlock() to crash because the dialog has been destroyed. Consider the following example.
1 2 3
In the previous (imaginary) example, the application MAY crash in line 3, because pjsip_dlg_end_session() may destroy the dialog in certain cases, e.g. when outgoing initial INVITE transaction has not been answered with any responses, thus the transaction can be destroyed immediately, causing the dialog to be destroyed immediately. The dialogs inc/dec lock prevents this problem by temporarily increase dialogs session counter, so that the dialog wont get destroyed on end_session() function. The dialog MAY be destroyed in the dec_lock() function. So the sequence to properly lock a dialog should be like this:
1 2 3
And finally, to make matters REALLY worse, the sequence of locking must be done in correct order, or otherwise a deadlock will occur. For example, if application wants to lock both a dialog and a transaction inside the dialog, application MUST acquire the dialog mutex first before transaction mutex, or otherwise deadlock will occur when other thread is currently acquiring the same dialog and transaction in the reverse order!
Page 14
Chapter 2:Module
Module framework is the main means for distributing SIP messages among software components in PJSIP application. All software components in PJSIP, including the transaction layer and dialog layer, are implemented as module. Without modules, the core stack (pjsip_endpoint and transport) simply wouldnt know how to handle SIP messages. The module framework is based on a simple but yet powerfull interface abstraction. For incoming messages, the endpoint (pjsip_endpoint) distributes the message to all modules starting from module with highest priority, until one of them says that it has processed the message. For outgoing messages, the endpoint distributes the outgoing messages before they are transmitted to the wire, to allow modules to put last modification on the message if they wish.
// // // // // // // // // // // // // //
For internal list mgmt. Module name. Module ID, set by endpt Priority Called to load the mod. Called to start. Called top stop. Called before unload Called on rx request Called on rx response Called on tx request Called on tx request Called on transaction state changed
All function pointers are optional; if theyre not specified, theyll be treated as if they have returned successfully. The four function pointers load, start, stop, and unload are called by endpoint to control the module state. The following diagram shows the modules state lifetime.
load() NULL STATE unload() LOADED stop() start() STARTED
Page 15
PJSIP Developers Guide The on_rx_request() and on_rx_response() function pointers are the primary means for the module to receive SIP messages from endpoint (pjsip_endpt) or from other modules. The return value of these callbacks is important. If a callback has returned non-zero (i.e. true condition), it semantically means that the module has taken care the message; in this case, the endpoint will stop distributing the message to other modules. Section 2.1.3 Incoming Message Processing by Modules will describe this in more detail. The on_tx_request() and on_tx_response() function pointers are called by transport manager before a message is transmitted. This gives an opportunity for some types of modules (e.g. sigcomp, message signing) chance to make last modification to the message. All modules MUST return PJ_SUCCESS (i.e. zero status), or otherwise the transmission will be cancelled. Section 2.1.4 Outgoing Message Processing by Modules will describe this in more detail. The on_tsx_state() function pointer is used to receive notification every time a transaction state has changed, which can be caused by receipt of message, transmission of message, timer events, or transport error event. More information about this callback will be described in next section 2.1.5 Transaction User and State Callback.
= = = = =
// // // // //
Transport Transaction layer. UA or proxy layer Invite usage, event subscr. framework. Application has lowest priority.
Note: remember that lower priority number means higher priority! The priority PJSIP_MOD_PRIORITY_TRANSPORT_LAYER is the priority used by transport manager. This priority currently is only used to control message transmission, i.e. module with lower priority than this (that means higher priority number!) will have the on_tx_request()/on_tx_response() called before the message is processed by transport layer (e.g. destination is calculated, message is printed to contiguous buffer), while module with higher priority than this will have the callback called after the message has been processed by transport layer. Please see 2.1.4Outgoing Message Processing by Modules for more information. PJSIP_MOD_PRIORITY_TSX_LAYER is the priority used by transaction layer module. The transaction layer absorbs all incoming messages that belong to a transaction. PJSIP_MOD_PRIORITY_UA_PROXY_LAYER is the priority used by UA layer (i.e. dialog framework) or proxy layer. The UA layer absorbs all incoming messages that belong to a dialog set (this means forked responses as well). Page 16
PJSIP Developers Guide PJSIP_MOD_PRIORITY_DIALOG_USAGE is for dialog usages. Currently PJSIP implements two types of dialog usages: invite sesssion and event subscription session (including REFER subscription). The dialog usage absorbs messages inside a dialog that belong to particular session. PJSIP_MOD_PRIORITY_APPLICATION is the appropriate value for typical application modules, when they want to utilize transactions, dialogs, and dialog usages.
MODULE
MODULE APPLICATION
ENDPOINT
rdata
TRANSACTION
Modules with priority lower than PJSIP_MOD_PRIORITY_TRANSPORT_LAYER (i.e. has higher priority number) will receive the message before these information Page 17
PJSIP Developers Guide are obtained. That means the destination address has not been calculated, and message has not been printed to contiguous buffer. If modules want to modify the message structure before it is printed to buffer, then it must set its priority number higher than transport layer priority. If modules want to see the actual packet bytes as they are transmitted to the wire (e.g. for logging purpose), then it should set its priority number to lower than transport layer.
A practical case where a module wants to set its priority higher than transport layer (i.e. has lower priority number) is the logging module, where it wants to print outgoing message after it has been printed to contiguous buffer and destination address has been calculated.
In all cases, modules MUST return PJ_SUCCESS for the return value of these callbacks. If a module returns other error codes, the transmission will be cancelled and the error code is returned back to pjsip_transport_send() caller.
The mod_data array is indexed by module ID. The module ID is determined when the module is registered to endpoint. Page 18
PJSIP Developers Guide When an incoming packet buffer (pjsip_rx_data) is passed around to modules, a module can put module specific data in the appropriate index in mod_data, so that the value can be picked up later by that module or by application. For example, the transaction layer will put the matching transaction instance in the mod_data, and user agent layer will put the matching dialog instance in the mod_data too. Application can retrieve the value calling pjsip_rdata_get_tsx() or pjsip_rdata_get_dlg(), which is a simple array lookup function as follows:
// This code can be found in sip_transaction.c static pjsip_module mod_tsx_layer; pjsip_transaction *pjsip_rdata_get_tsx(pjsip_rx_data *rdata) { return rdata->endpt_info.mod_data[mod_tsx_layer.id]; } Code 6 Accessing Module Specific Data
Event
Receipt of new requests or responses Receipt retransmissions of requests or responses. Transmission of new requests or responses. Retransmissions of requests or responses. Transaction timeout Other transaction failure events (e.g. DNS query failure, transport failure)
on_tsx_state()
Called Not Called Called Not Called Called Called
This is because the matching transaction prevents the message from being distributed further (by returning PJ_TRUE) and it also does NOT call TU callback upon receiving retransmissions.
1
Page 19
1)
Transport manager (pjsip_tpmgr) passes all received messages to endpoint (after parsing the message). Endpoint (pjsip_endpt) distributes the message to all registered callbacks. First in the callback list is transaction layer. Transaction layer looks up the message in transaction table, and couldnt find a matching transaction. Endpoint distributes the message to next callback in the list, which is user agent. User agent looks up the message in dialogs hash table and couldnt find matching dialog set. Endpoint continues distributing the message to next registered callbacks until it reaches application. Application processes the message (e.g. respond statelessly, create UAS transaction, or proxy the request, or create dialog, etc.)
DIALOG
2)
USER AGENT
on_rx_xxx()
(5) rdata TRANSACTION Transaction Hash Table (2) lookup / not found (3) rdata ENDPOINT
3) 4)
5)
endpt_transport_callback()
(1) rdata TRANSPORT MANAGER Figure 6 Processing of Incoming Message Outside Transaction/Dialog
Transport manager (pjsip_tpmgr) passes all received messages to endpoint (after parsing the message). Endpoint (pjsip_endpt) distributes the message to all registered callbacks. First in the callback list is transaction layer. Transaction layer looks up the message in transaction table, and found a matching transaction. Because transactions callback returns PJ_TRUE, endpoint does not distribute the message further. The transaction processes the response (e.g. updates the FSM). If the message is a retransmission, the processing stops here. Otherwise transaction then passes the message to its transaction user (TU), which can be a dialog or application. If the TU is a dialog, the dialog processes the response then pass the response to its dialog user (DU, e.g. application). If the arrival of the message has changed transactions state, transaction will notify its TU about the new state. If TU is a dialog, it may further notify application about dialogs state changed.
2)
USER AGENT (6) notify state (4) rdata +tsx TRANSACTION Transaction Hash Table (2) lookup / found !
on_rx_xxx()
3)
4)
endpt_transport_callback()
7)
Page 20
PJSIP Developers Guide Incoming Message Inside Dialog but Outside Transaction The processing is as follows:
APPLICATION
on_rx_xxx()
1)
Transport manager (pjsip_tpmgr) passes all received messages to endpoint (after parsing the message). Endpoint (pjsip_endpt) distributes the message to all registered callbacks. First in the callback list is transaction layer. Transaction layer looks up the message in transaction table, and couldnt find a matching transaction. Endpoint distributes the message to next modules in the list, until it reaches user agent module. The user agent module looks-up the owning of the message in dialogs hash table and found a matching dialog. The user agent module passes the message to the appropriate dialog. The dialog always creates transaction for incoming request, then distribute the request by calling on_rx_request() AND on_tsx_state() of its dialog usages.
2)
3)
TRANSACTION Transaction Hash Table (2) lookup / not found (3) msg ENDPOINT
4)
5) 6)
endpt_transport_callback()
Register a module to the endpoint. The endpoint will then call the load and start function in the module to properly initialize the module, and assign a unique module ID for the module.
pj_status_t pjsip_endpt_unregister_module( pjsip_endpoint *endpt, pjsip_module *module );
Unregister a module from the endpoint. The endpoint will then call the stop and unload function in the module to properly shutdown the module.
PJSIP Developers Guide allowed SIP methods (Allow header field), supported SIP extensions (Supported header field). supported content type (Accept header field).
These header fields will be added to outgoing requests or responses automatically, where appropriate. A module declares new capability by calling pjsip_endpt_add_capability() function.
pj_status_t pjsip_endpt_add_capability( pjsip_endpoint *endpt, pjsip_module *mod, int htype, const pj_str_t *hname, unsigned count, const pj_str_t tags[]);
Register new capabilities to the endpoint. The htype argument specifies which header to add the capabilities to, and such as PJSIP_H_ACCEPT, PJSIP_H_ALLOW, and PJSIP_H_SUPPORTED. The hname argument is optional; it is used only to specify capabilities in header fields that are not recognized by the core stack. The count and tags arguments specifies array of string tags to be added to the header field.
pjsip_endpoint *endpt, int htype, const pj_str_t *hname);
Get a capability header field, which contains all capabilities that have been registered to the endpoint for the specified header field.
Page 22
pjsip_sip_uri
Attributes: user, passwd, host, port, user_param, method_param, transport_param, ttl_param, lr_param, maddr_param, other_param, header_param Operations: pjsip_sip_uri *pjsip_sip_uri_create(pool,secure); void pjsip_sip_uri_init(uri, secure); void pjsip_sip_uri_assign(pool, dst_uri, src_uri);
pjsip_tel_uri
Attributes: number, context, ext_param, isub_param, other_param Operations: pjsip_tel_uri *pjsip_tel_uri_create(pool);
pjsip_name_addr
Attributes: uri, display Operations: pjsip_name_addr * pjsip_name_addr_create(pool);
// // // // //
The URI is in The URI is in The URI is in The URI is in Other context
Request URI. From/To header. Contact header. Route/Record-Route header. (web page, business card, etc.)
Page 23
The pjsip_uri_vptr specifies virtual function table, which members will be defined by each type of URI. Application is discouraged from calling these function pointers directly; instead it is recommended to use the URI API because they are more readable (and it saves some typings too).
struct pjsip_uri_vptr { const pj_str_t* (*p_get_scheme) pjsip_uri* (*p_get_uri) int (*p_print)
pj_status_t pjsip_uri * };
(*p_compare) (*p_clone)
( const pjsip_uri *uri); ( pjsip_uri *uri); ( pjsip_uri_context_e context, const pjsip_uri *uri, char *buf, pj_size_t size); ( pjsip_uri_context_e context, const pjsip_uri *uri1, const pjsip_uri *uri2); ( pj_pool_t *pool, const pjsip_uri *uri);
The URI functions below can be applied for all types of URI objects. These functions normally are implemented as inline functions which call the corresponding function pointer in virtual function table of the URI.
const pj_str_t* pjsip_uri_get_scheme( const pjsip_uri *uri );
Get the URI scheme string (e.g. sip, sips, tel, etc.).
Get the URI object. Normally all URI objects will return itself except name address which will return the URI inside the name address object.
pj_status_t pjsip_uri_cmp( pjsip_uri_context_e context, const pjsip_uri *uri1, const pjsip_uri *uri2);
Compare uri1 and uri2 according to the specified context. Parameters which are not allowed to appear in the specified context will be ignored in the comparison. It will return PJ_SUCCESS is both URIs are equal.
int pjsip_uri_print( pjsip_uri_context_e context, const pjsip_uri *uri, char *buffer, pj_size_t max_size);
Print uri to the specified buffer according to the specified context. Parameters which are not allowed to appear in the specified context will not be included in the printing. Page 24
// // // // // // // // // // // // //
Pointer to virtual function table. Optional user part. Optional password part. Host part, always exists. Optional port number, or zero. Optional user parameter Optional method parameter. Optional transport parameter. Optional TTL param, or -1. Optional loose routing param, or 0 Optional maddr param Other parameters as list. Optional header parameters as list.
The following functions are specific to SIP/SIPS URI objects. In addition to these functions, application can also use the base URI functions described in previous section to manipulate SIP and SIPS URI too.
pjsip_sip_uri* pjsip_sip_uri_create( pj_pool_t *pool, pj_bool_t secure );
Create a new SIP URL using the specified pool. If the secure flag is set to non-zero, then SIPS URL will be created. This function will set vptr member of the URL to SIP or SIPS vptr and set all other members to blank value.
Page 25
The functions below are specific to TEL URI. In addition to these functions, application can also use the base URI functions described in previous section for TEL URI too.
pjsip_tel_uri* pjsip_tel_uri_create( pj_pool_t *pool );
This utility function compares two telephone numbers for equality, according to rules specified in RFC 3966 (about tel: URI). It recognizes global and local numbers, and it ignores visual separators during the comparison.
The following functions are specific to name address URI object. In addition to these functions, application can also use the base URI functions described before for name address object too.
pjsip_name_addr* pjsip_name_addr_create( pj_pool_t *pool );
Create a new name address. This will set initialize the virtual function table pointer, set blank display name and set the uri member to NULL.
pj_pool_t *pool, pjsip_name_addr *name_addr, const pjsip_name_addr *rhs );
void pjsip_name_addr_assign(
Page 26
// Method ID, from pjsip_method_e. // Method name, which will always contain the method string.
PJSIP core library declares only methods that are specified in core SIP standard (RFC 3261). For these core methods, the id field of pjsip_method will contain the appropriate value from the following enumeration:
enum pjsip_method_e { PJSIP_INVITE_METHOD, PJSIP_CANCEL_METHOD, PJSIP_ACK_METHOD, PJSIP_BYE_METHOD, PJSIP_REGISTER_METHOD, PJSIP_OPTIONS_METHOD, PJSIP_OTHER_METHOD, }; Code 15 SIP Method ID
For methods not specified in the enumeration, the id field of pjsip_method will contain PJSIP_OTHER_METHOD value. In this case, application must inspect the name field of pjsip_method to know the actual method.
Initialize method from method_name string without duplicating the string (np stands for no pool). The id field will be initialize accordingly.
void pjsip_method_set( pjsip_method *method, pjsip_method_id_e method_id );
Initialize method from the method ID enumeration. The name field will be initialized accordingly.
void pjsip_method_copy( pj_pool_t *pool, pjsip_method *method, const pjsip_method *rhs ); const pjsip_method *method1, const pjsip_method *method2 );
Compare method1 to method2 for equality. This function returns zero if both methods are equal, and (-1) or (+1) if method1 is less or greater than method2 respectively. Page 28
pjsip_hdr
Attributes: pjsip_hdr *next, *prev, pjsip_hdr_e type; pj_str_t name, sname Operations: pjsip_hdr *pjsip_hdr_clone(pool, hdr); pjsip_hdr *pjsip_hdr_shallow_clone(pool, hdr); int pjsip_hdr_print_on(hdr, buf, maxlen);
pjsip_via_hdr
Attributes: transport, sent_by, ttl_param, rport_param, maddr_param, recvd_param, branch_param, other_param, comment Operations: pjsip_via_hdr * pjsip_via_hdr_create(pool);
pjsip_fromto_hdr
Attributes: uri, tag, other_param Operations:
pjsip_generic_int_hdr
Attributes: ivalue Operations:
pjsip_from_hdr
Attributes: Operations: pjsip_from_hdr * pjsip_from_hdr_create(pool);
pjsip_max_forwards_hdr
Attributes: Operations: pjsip_max_fowards_hdr * pjsip_max_forwards_hdr_create(pool);
pjsip_generic_string_hdr
Attributes: hvalue Operations:
pjsip_to_hdr pjsip_generic_array_hdr
Attributes: hvalue Operations: Attributes: Operations: pjsip_to_hdr * pjsip_to_hdr_create(pool); pjsip_accept_encoding_hdr, pjsip_accept_lang_hdr, pjsip_alert_info_hdr, pjsip_auth_info_hdr, pjsip_call_info_hdr, pjsip_content_disposition_hdr, pjsip_content_encoding_hdr, ...
pjsip_expires_hdr
Attributes: Operations: pjsip_expires_hdr * pjsip_expires_hdr_create(pool); pjsip_min_expires_hdr, pjsip_retry_after_hdr,
As seen in the class diagram, each of the specific header normaly only provide one function that is specific for that particular header, i.e. function to create the instance of the header.
Page 29
PJSIP defines pjsip_hdr structure, which contains common properties shared by all header fields. Because of this, all header fields can be typecasted to pjsip_hdr so that they can be manipulated uniformly.
struct pjsip_hdr { PJSIP_DECL_HDR_MEMBER(struct pjsip_hdr); }; Code 17 Generic Header Declaration
Although application can freely call the function pointers in the pjsip_hdr_vptr directly, it is recommended that it uses the following header APIs instead, because they will make the program more readable.
pjsip_hdr *pjsip_hdr_clone( pj_pool_t *pool, const pjsip_hdr *hdr ); pj_pool_t *pool, const pjsip_hdr *hdr );
Perform shallow clone of hdr header. A shallow cloning creates a new exact copy of the specified header field, however most of its value will still point to the values in the original header. Normally shallow clone is just a simple memcpy() from the original header to a new header, therefore its expected that this operation is faster than deep cloning. However, care must be taken when shallow cloning headers. It must be understood that the new header still shares common pointers to the values in the old header. Therefore, when the pool containing the original header is destroyed, the new header will be rendered invalid too although the new header was shallow-cloned using different memory pool. Or if some values in the original header was modified, then the corresponding values in the shallow-cloned header will be modified too. Despite of this, shallow cloning is used widely in the library. For example, a dialog has some headers which values are more or less persistent during Page 30
PJSIP Developers Guide the session (e.g. From, To, Call-Id, Route, and Contact). When creating a request, the dialog can just shallow-clone these headers (instead of performing full cloning) and put them in the request message.
int pjsip_hdr_print_on( pjsip_hdr *hdr, char *buf, pj_size_t max_size);
Print the specified header to a buffer (e.g. before transmission). This function returns the number of bytes printed to the buffer, or 1 when the buffer is overflow.
When the parser encounters such arrays in headers, it will split the array into individual headers while maintaining their order of appearance. So for the example above, the parser will modify the message to:
Contact: <sip:[email protected]>;q=1.0 Contact: <tel:+442081234567>;q=0.5 Via: SIP/2.0/UDP proxy1.example.com;branch=z9hG4bK87asdks7 Via: SIP/2.0/UDP proxy2.example.com;branch=z9hG4bK77asjd
The SIP standard specifies that there should NOT be any difference in the processing of message containing either kind of header representations. So we believe that the removal of header array support will not limit the functionality of PJSIP at all. The reason why we impose this limitation is because based on our experience, the removal of header array support greatly simplifies processing of headers. If header array were supported, then application not only must inspect all headers, it also has to inspect some headers to see if they contain arrays. With the Page 31
PJSIP Developers Guide removal of array support, application only has to inspect the main header list in the message.
(*print_body)
/** Pointer to function to clone the data in this message body. */ void* (*clone_data) ( pj_pool_t *pool, const void *data, unsigned len ); }; Code 19 Message Body Declaration
The following are APIs that are provided for manipulating SIP message objects. Page 32
Clone the message body in src_body to the dst_body. This will duplicate the contents of the message body using the clone_data member of the source message body.
// Method for this request line. // URI for this request line.
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 } 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; }; Code 20 SIP Message Declaration
status;
The following are APIs that are provided for manipulating SIP message objects.
pjsip_msg* pjsip_msg_create( pj_pool_t *pool,
Page 33
Find header in the msg which has the specified type, searching from (and including) the specified start position in the header list. If start is NULL, then the function searches from the first header in the message. Returns NULL when no more header at and after the specified position can be found.
pjsip_hdr* pjsip_msg_find_hdr_by_name( pjsip_msg *msg, const pj_str_t *name, pjsip_hdr *start);
Find header in the msg which has the specified name, searching both long and short name version of the header from the specified start position in the header list. If start is NULL, then the function searches from the first header in the message. Returns NULL when no more headers at and after the specified position can be found.
void pjsip_msg_add_hdr( pjsip_msg *msg, pjsip_hdr *hdr);
Print the whole contents of msg to the specified buffer. The function returns the number of bytes written, or 1 if buffer is overflow.
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,
Page 34
PJSIP also defines new status class (i.e. 7xx) for fatal error status during message processing (e.g. transport error, DNS error, etc). This class however is only used internally; it will not go out on the wire.
Page 35
For example of its usage, please see other_param and header_param fields in the declaration of pjsip_sip_uri (see previous section 3.1.4 SIP and SIPS URI) or other_param field in the declaration of pjsip_tel_uri (see previous section 3.1.5 Tel URI).
This function will perform case-insensitive search for the specified parameter name.
void pjsip_param_clone( pj_pool_t *pool, pjsip_param *dst_list, const pjsip_param *src_list);
pj_ssize_t pjsip_param_print_on( const pjsip_param *param_list, char *buf, pj_size_t max_size, const pj_cis_t *pname_unres, const pj_cis_t *pvalue_unres, int sep);
Print the parameter list to the specified buffer. The pname_unres and pvalue_unres is the specification of which characters are allowed to appear unescaped in pname and pvalue respectively; any characters outside these specifications will be escaped by the function. The argument sep specifies separator character to be used between parameters (normally it is semicolon (;) character for normal parameter or comma (,) when the parameter list is a header parameter).
PJSIP Developers Guide o o all types of URI and their elements are automatically escaped and unescaped according to their individual escapement rule. parameters appearing in all message elements (e.g. in URL, in header fields, etc.) are automatically escaped and un-escaped.
Page 37
Chapter 4:Parser
4.1 Features
Some features of the PJSIP parser: o Its a top-down, handwritten parser. It uses PJLIBs scanner, which is pretty fast and reduces the complexity of the parser, which make the parser readable. As said above, its pretty fast. On a single P4/2.6GHz machine, its able to parse more than 68K of typical 800 bytes SIP message or 860K of 80 bytes URLs in one second. Note that your mileage may vary, and different PJSIP versions may have different performance. Its reentrant, which will make it scalable on machine with multiprocessors. Its extensible. Modules can plug-in new types of header or URI to the parser.
o o
The parser features almost a lot of tricks thinkable to achieve the highest performance, such as: o it uses zero-copy for all message elements; i.e., when an element, e.g. a pvalue, is parsed, the parser does not copy the pvalue contents to the appropriate field in the message; instead it will just put the pointer and length to the appropriate field in the message. This is only possible because PJSIP uses pj_str_t all the way throughout the library, which does not require strings to be NULL terminated. it uses PJLIBs memory pool (pj_pool_t) for memory allocation for the message structures, which provides multiple times speed-up over traditional malloc() function. it uses zero synchronization. The parser is completely reentrant so that no synchronization function is required. it uses PJLIBs try/catch exception framework, which not only greatly simplifies the parser and make it readable, but also saves tedious error checking in the parsers. With an exception framework, only one exception handler needs to be installed at the top-most function of the parser.
o o
One feature that PJSIP parser doesnt implement is lazy parsing, which a lot of people probably brag about its usability. In early stage of the design, we decided not to implement lazy parsing, because of the following reasons: o it complicates things, especially error handling. With lazy parsing, basically all parts of the program must be prepared to handle error condition when parsing failed at later stage when application needs to access a particular message element. at the end of the day, we believe that PJSIP parser is very fast anyway that it doesnt need lazy parsing. Although having said that, there will be some switches that can be turned-on in PJSIP parser to ignore parsing of some headers for some type of applications (e.g. proxies, which only needs to inspect few header types).
Page 38
4.2 Functions
The main PJSIP parser is declared in <pjsip/sip_parser.h> and defined in <pjsip/sip_parser.c>. Other parts of the library may provide other parsing functionalities and extend the parser (e.g. <pjsip/sip_tel_uri.c> provides function to parse TEL URI and registers this function to the main parser).
Checks that an incoming packet in buf contains a valid SIP message. When a valid SIP message is detected, the size of the message will be indicated in msg_size. If is_datagram is specified, this function will always return PJ_SUCCESS. Note that the function expects the buffer in buf to be NULL terminated.
pjsip_msg* pjsip_parse_msg( pj_pool_t *pool, char *buf, pj_size_t size, pjsip_parser_err_report *err_list);
Parse a buffer in buf into SIP message. The parser will return the message if at least SIP request/status line has been successfully parsed. Any error encountered during parsing will be reported in err_list if this parameter is not NULL. Note that the function expects the buffer in buf to be NULL terminated.
pjsip_msg* pjsip_parse_rdata( char *buf, pj_size_t size, pjsip_rx_data *rdata );
Parse a buffer in buf into SIP message. The parser will return the message if at least SIP request/status line has been successfully parsed. In addition, this function updates various pointer to headers in msg_info portion of the rdata. Note that the function expects the buffer in buf to be NULL terminated.
Parse a buffer in buf into SIP URI. If PJSIP_PARSE_URI_AS_NAMEADDR is specified in the option, the function will always wrap the URI as name address. If PJSIP_PARSE_URI_IN_FROM_TO_HDR is specified in the option, the function will not parse the parameters after the URI if the URI is not enclosed in brackets (because they will be treated as header parameters, not URI parameters). This function is able to parse any types of URI that are recognized by the library, and return the correct instance of the URI depending on the scheme. Note that the function expects the buffer in buf to be NULL terminated.
Page 39
PJSIP Developers Guide Parse the content of a header in line (i.e. part of header after the colon character) according to the header type hname. It returns the appropriate instance of the header. Note that the function expects the buffer in buf to be NULL terminated.
pj_status_t pjsip_parse_headers( pj_pool_t *pool, char *input, pj_size_t size, pj_list *hdr_list );
Parse multiple headers found in input buffer and put the results in hdr_list. The function expects the header to be separated either by a newline (as in SIP message) or ampersand character (as in URI). The separator is optional for the last header. Note that the function expects the buffer in buf to be NULL terminated.
Page 40
// // // // // //
Packet arrival time The packet buffer Zero padding. Packet length Source address Address length.
// This part describes the message and message elements after parsing. struct { char *msg_buf; // Pointer to start of msg in the buf. int len; // Message length. pjsip_msg *msg; // The parsed message. // Shortcut to important headers: pj_str_t pjsip_from_hdr pjsip_to_hdr pjsip_via_hdr pjsip_cseq_hdr pjsip_max_forwards_hdr pjsip_route_hdr pjsip_rr_hdr pjsip_ctype_hdr pjsip_clen_hdr pjsip_require_hdr pjsip_parser_err_report } msg_info; call_id; *from; *to; *via; *cseq; *max_fwd; *route; *record_route; *ctype; *clen; *require; parse_err; // // // // // // // // // // // Call-ID string. From header. To header. First Via header. CSeq header. Max-Forwards header. First Route header. First Record-Route header. Content-Type header. Content-Length header. The first Require header.
// This part is updated after the rx_data reaches endpoint. struct { pj_str_t key; // Transaction key. void *mod_data[PJSIP_MAX_MODULE]; // Module specific data. } endpt_info; }; Code 23 Receive Data Buffer Declaration
Page 41
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]; /** Time of the rx request; set by 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; /** Contigous buffer containing the packet. */ 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); /** Transport info, only valid during on_tx_request() and on_tx_response() */ struct { pjsip_transport *transport; /**< Transport being used. */ pj_sockaddr dst_addr; /**< Destination address. */ int dst_addr_len; /**< Length of address. */ char dst_name[16]; /**< Destination address. */ int dst_port; /**< Destination port. */ } tp_info; }; Code 24 Transmit Data Buffer Declaration
Page 42
pjsip_tpmgr Attributes: Operations: pj_status_t pjsip_tpmgr_create(...); pj_status_t pjsip_tpmgr_destroy(...); pj_status_t pjsip_tpmgr_register_tpfactory(...); pj_status_t pjsip_tpmgr_unregister_tpfactory(...); pj_status_t pjsip_transport_register(...); pj_status_t pjsip_transport_unregister(...); pj_status_t pjsip_tpmgr_receive_packet(...); pj_status_t pjsip_tpmgr_acquire_transport();
pjsip_transport Attributes: pool, ref_cnt, lock, type, local_addr, remote_addr, public_addr Operations: pj_status_t pjsip_transport_send(...); pj_status_t pjsip_transport_add_ref(...); pj_status_t pjsip_transport_dec_ref(...); Attributes:
pjsip_tpfactory
PJSIP Developers Guide Find matching transport to send SIP message to particular destination based on the transport type and remote address. Create new transports dynamically when no existing transport is available to send SIP message to a new destination.
There is only one transport manager per endpoint. Transport manager is normally not visible to applications; applications should use the functions provided by endpoint.
6.1.4 Transport
Transport object is represented with pjsip_transport structure. Each instance of this structure normally represents one socket handle (e.g. UDP, TCP), although the transport layer supports non-socket transport as well. General Transport Operations From the frameworks point of view, transport object is an active object. The framework doesnt have mechanism to poll the transport objects; instead, the transport objects must find their own way to receive packets from network and deliver the packets to transport manager for further processing. The recommended way to achieve this is to register the transports socket handle to endpoints I/O queue (pj_ioqueue_t), so that when the endpoint polls the I/O queue, packets from the network will be received by the transport object. Once a packet has been received by the transport object, it must deliver the packet to transport manager by calling pjsip_tpmgr_receive_packet() function, so that it can be parsed and distributed to the rest of the stack. The transport object must initialize both tp_info and pkt_info member of receive data buffer (pjsip_rx_data). Each transport object has a pointer to function to send messages to the network (i.e. send_msg() attribute of the transport object). Application (or the stack) sends messages to the network by calling pjsip_transport_send() function, which eventually will reach the transport object, and send_msg() will be called. The sending of packet may complete asynchronously; if so, transport must return PJ_EPENDING status in send_msg() and call the callback that is specified in argument when the message has been sent to destination. Transport Object Declaration The following code shows the declaration of a transport object.
Page 44
// Name.
type; // Transport type. type_name[8]; // Type name. flag; // See #pjsip_transport_flags_e local_addr; addr_name; rem_addr; *endpt; *tpmgr; idle_timer; // Bound address. // Published name (e.g. STUN address). // Remote addr (zero for UDP) // Endpoint instance. // Transport manager. // Timer when ref cnt is zero.
/* Function to be called by transport manager to send SIP messages. */ pj_status_t (*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 sent)); /* Called to destroy this transport. */ pj_status_t (*destroy)( pjsip_transport *transport ); /* Application may extend this structure. */ }; Code 25 Transport Object Declaration
Transport Management Transports are registered to transport manager by pjsip_transport_register(). Before this function is called, all members of the transport structure must be initialized. Transports life-time is managed automatically by transport manager. Each time reference counter of the transport reaches zero, an idle timer will start. When the idle timer expires and the reference counter is still zero, transport manager will destroy the transport by calling pjsip_transport_unregister(). This function unregisters the transport from transport managers hash table and eventually destroy the transport. Some transports need to exist forever even when nobody is using the transport (for example, UDP transport, which is a singleton instance). To prevent that transport from being deleted, it must set the reference counter to one initially, so that reference counter will never reach zero. Transport Error Handling Any errors in the transport (such as failure to send packet or connection reset) are handled by transport user. Transport object doesnt need to handle such errors, other than reporting the error in the functions return value. In particular, it must not try to reconnect a failed/closed connection.
Page 45
Acquire transport of type t_type to be used to send message to destination remote_addr. Note that if transport is successfully acquired, the transports reference counter will be incremented.
pj_status_t pjsip_transport_add_ref( pjsip_transport *transport );
Add reference counter of the transport. This function will prevent the transport from being destoyed, and it also cancels idle timer if such timer is active. Decrement reference counter of the transport. When transports reference counter reaches zero, an idle timer will be started and transport will be destroyed by transport manager when the timer has elapsed and reference counter is still zero.
pj_status_t pjsip_transport_send( pjsip_transport *transport, pjsip_tx_data *tdata, const pj_sockaddr_t *remote_addr, int addrlen, void *token, void (*cb)(void *token, pjsip_tx_data *tdata, pj_ssize_t bytes_sent));
Send the message in tdata to remote_addr using transport transport. If the function completes immediately and data has been sent, the function returns PJ_SUCCESS. If the function completes immediately with error, a non-zero error code will be returned. In both cases, the callback will not be called. If the function can not complete immediately (e.g. when the underlying socket buffer is full), the function will return PJ_EPENDING, and caller will be notified about the completion via the callback cb. If the pending send operation completes with error, the error code will be indicated as negative value of the error code, in the bytes_sent argument of the callback (to get the error code, use pj_status_t status = -bytes_sent). This function sends the message as is; it doesnt perform any validation to the message. The Via header is also not modified by this function either.
PJSIP Developers Guide Below are the initialization functions for the built-in UDP and TCP transports.
Create, initialize, register, and start a new UDP transport. The UDP socket will be bound to local_addr. If the endpoint is located behind firewall/NAT or other port-forwarding devices, then pub_addr can be used as the address that is advertised for this transport; otherwise pub_addr should be the same as local_addr. The argument async_cnt specifies how many simultaneous operations are allowed for this transport, and for maximum performance, the value should be equal to the number of processors in the node. If transport is successfully started, the function returns PJ_SUCCESS and the transport is returned in p_transport argument, should the application want to use the transport immediately. Application doesnt need to register the transport to transport manager; this function has done that when the function returns successfully. Upon error, the function returns a non-zero error code.
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);
Use this function to create, initialize, register, and start a new UDP transport when the UDP socket is already available. This is useful for example when application has just resolved the public address of the socket with STUN, and instead of closing the socket and re-create it, the application can just reuse the same socket for the SIP transport.
Page 47
proxies can create request or response messages based on incoming message to be forwarded by calling pjsip_endpt_create_request_fwd() and pjsip_endpt_create_response_fwd(). alternatively you may create request or response messages manually by creating the transmit buffer with pjsip_endpt_create_tdata(), creating the message with pjsip_msg_create(), adding header fields to the message with pjsip_msg_add_hdr() or pjsip_msg_insert_first_hdr(), set the message body, etc. higher layer module may provide more specific way to create message (e.g. dialog layer). This will be described in the individual modules documentation.
All message creating API (except the low-level pjsip_endpt_create_tdata()) sets the reference counter of the transmit buffer (pjsip_tx_data) to one, which means that at some point application (or stack) must decrement the reference counter to destroy the transmit buffer. All message sending API will decrement transmit buffers reference counter. Which means that as long as application doesnt do anything with the transmit buffers reference counter, the buffer will be destroyed after it is sent.
PJSIP Developers Guide The core API to send messages are pjsip_endpt_send_request_stateless() and pjsip_endpt_send_response() functions. These two are very powerfull functions in the sense that it handles transport layer automatically, and are the basic building-blocks used by upper layer modules (e.g. transactions). The pjsip_endpt_send_request_stateless() function are for sending request messages, and it performs the following procedures: Determine which destination to contact based on the Request-URI and parameters in Route headers, Resolve the destination server using procedures in RFC 3263 (Locating SIP Servers), Select and establish transport to be used to contact the server, Modify sent-by in Via header to reflect current transport being used, Send the message using current transport, Fail-over to next server/transport if server can not be contacted using current transport
The pjsip_endpt_send_response() function are for sending response messages, and it performs the following procedures: Follow the procedures in Section 18.2.2 of RFC 3261 to select which transport to use and which address to send response to, Additionally conform to RFC 3581 about rport parameter, Send the response using the selected transport, Fail-over to next address when response failed to be sent using the selected transport, resolving the server according to RFC 3263 when necessary.
Since messages may be sent asynchronously (e.g. after TCP has been connected), both functions provides callback to notify application about the status of the transmission. This callback also inform the application that fail-over will happen (or not), and application has the chance to override the behavior.
Create a standard response message for the request in rdata with status code st_code and status text st_text. If st_text is NULL, default status text will be used.
pj_status_t pjsip_get_response_addr( pj_pool_t *pool, pjsip_rx_data *rdata, pjsip_response_addr *res_addr);
Determine which address (and transport) to use to send response message based on the received request in rdata. This function follows the specification in section 18.2.2 of RFC 3261 and RFC 3581 for calculating the destination address and transport. The address and transport information about destination to send the response will be returned in res_addr argument. Page 49
Send response in response statelessly, using the destination address and transport in res_addr. The response address information (res_addr) is normally initialized by calling pjsip_get_response_addr(). The definite status of the transmission will be reported when callback cb is called, along with other information (including the original token) which will be stored in pjsip_send_state. If message was successfully sent, the sent argument of the callback will be a non-zero positive number. If there is failure, the sent argument will be negative value, and the error code is the positive part of the value (i.e. status=-sent). If cont argument value is non-zero, it means the function will try other addresses to send the message (i.e. fail-over). Application can choose not to try other addresses by setting this argument to zero upon exiting the callback. If application doesnt specify callback cb, then the function will not failover to next address in case the selected transport fails to deliver the message. The function returns PJ_SUCCESS if the message is valid, or a non-zero error code. However, even when it returns PJ_SUCCESS, there is no guarantee that the response has been successfully sent. Note that callback MAY be called before the function returns. Composite Functions
pj_status_t pjsip_endpt_respond_stateless( pjsip_endpoint *endpt, pjsip_rx_data *rdata, int st_code, const char *st_text, const pjsip_hdr *hdr_list, const pjsip_msg_body *body);
This function creates and sends a response to an incoming request. In addition, caller may specify message body and additional headers to be put in the response message in the hdr_list and body argument. If there is no additional header or body, to be sent, the arguments should be NULL. The function returns PJ_SUCCESS if response has been successfully created and send to transport layer, or a non-zero error code. However, even when it returns PJ_SUCCESS, there is no guarantee that the response has been successfully sent.
Page 50
Create a new request message of the specified method for the specified target URI, from, to, and contact. The call_id and cseq are optional. If text is specified, then a text/plain body is added. The request message has initial reference counter set to 1, and is then returned to sender in p_tdata.
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 *ch, const pjsip_cid_hdr *call_id, int cseq, const pj_str_t *text, pjsip_tx_data **p_tdata);
Create a new request header by shallow-cloning the headers from the specified arguments.
pjsip_endpoint *endpt, const pjsip_tx_data *tdata, const pjsip_rx_data *rdata, pjsip_tx_data **ack );
pj_status_t pjsip_endpt_create_ack(
Create ACK request message from the original request in tdata based on the received response in rdata. This function is normally used by transaction when it receives non-successful response to INVITE. An ACK request for successful INVITE response is normally generated by dialogs create request function.
pj_status_t pjsip_endpt_create_cancel( pjsip_endpoint *endpt, const pjsip_tx_data *tdata, pjsip_tx_data **p_tdata);
Create CANCEL request based on the previously sent request in tdata. This will create a new transmit data buffer in p_tdata.
pj_status_t pjsip_endpt_send_request_stateless(pjsip_endpoint *endpt, pjsip_tx_data *tdata, void *token, void (*cb)(pjsip_send_state*, pj_ssize_t sent, pj_bool_t *cont));
Send request in tdata statelessly. The function will take care of which destination and transport to use based on the information in the message, taking care of URI in the request line and Route header. There are several steps will be performed by this function: determine which host to contact based on Request-URI and Route headers (pjsip_get_request_addr()), resolve the destination host (pjsip_endpt_resolve()), acquire transport to be used (pjsip_endpt_acquire_transport()). send the message (pjsip_transport_send()). fail-over to next address/transport if necessary.
The definite status of the transmission will be reported when callback cb is called, along with other information (including the original token) which will be stored in pjsip_send_state. If message was successfully sent, the sent argument of the callback will be a non-zero positive number. If there is failure, the sent argument will be negative value, and the error code is the positive part of the value (i.e. status=-sent). If cont argument value is non-zero, it means the function will try other addresses to send the message (i.e. fail-over). Application can choose not to try other addresses by setting this argument to zero upon exiting the callback. Page 51
PJSIP Developers Guide If application doesnt specify callback cb, then the function will not failover to next address in case the selected transport fails to deliver the message. The function returns PJ_SUCCESS if the message is valid, or a non-zero error code. However, even when it returns PJ_SUCCESS, there is no guarantee that the request has been successfully sent. Note that callback MAY be called before the function returns.
Create new request message to be forwarded upstream to new destination URI uri. The new request is a full/deep clone of the request received in rdata, unless if other copy mechanism is specified in the options. The branch parameter, if not NULL, will be used as the branch-param in the Via header. If it is NULL, then a unique branch parameter will be used.
pjsip_endpoint *endpt, pjsip_rx_data *rdata, unsigned options, pjsip_tx_data **tdata);
pj_status_t pjsip_endpt_create_response_fwd(
Create new response message to be forwarded downstream by the proxy from the response message found in rdata. Note that this function practically will clone the response as is, i.e. without checking the validity of the response or removing top most Via header. This function will perform full/deep clone of the response, unless other copy mechanism is used in the options.
pj_str_t pjsip_calculate_branch_id( pjsip_rx_data *rdata );
Create a globally unique branch parameter based on the information in the incoming request message. This function guarantees that subsequent retransmissions of the same request will generate the same branch id. This function can also be used in the loop detection process. If the same request arrives back in the proxy with the same URL, it will calculate into the same branch id. Note that the returned string was allocated from rdatas pool.
Page 52
7.3 Examples
7.3.1 Sending Responses
Sending Account Not Found Response Statelessly
static pj_bool_t on_rx_request(pjsip_rx_data *rdata ) { pjsip_account *acc; pj_status_t status; // Find account referred to in the request. acc = ... // Respond statelessly if account can not be found. if (!acc) { status = pjsip_endpt_respond_stateless( endpt, rdata, 404, NULL /*Not Found*/, NULL, NULL, NULL); return PJ_TRUE; } // Process the account ... return PJ_TRUE; } Code 26 Sample: Stateless Response
Handling Authentication Failures Statelessly Another (longer) way to send stateless response:
static pj_bool_t on_rx_request( pjsip_rx_data *rdata ) { pjsip_account *acc; // Lookup acc. acc = ...; // Check authorization and handle failure statelessly if (!pjsip_auth_authorize( acc, rdata->msg )) { pjsip_proxy_authenticate_hdr *auth_hdr; status = pjsip_endpt_create_response( endpt, rdata, 407, NULL /* Proxy Auth Required */, &tdata);
// Add Proxy-Authenticate header. status = pjsip_auth_create_challenge( tdata->pool, ..., &auth_hdr); pjsip_msg_add_hdr( &tdata->msg, auth_hdr ); // Send response statelessly status = pjsip_endpt_send_response( endpt, tdata, NULL); return PJ_TRUE; } // Authorization success. Proceed to next stage.. ... return PJ_TRUE; } Code 27 Sample: Stateless Response
Page 53
Page 54
Page 55
Chapter 8:Transactions
8.1 Design
8.1.1 Introduction
Transaction in PJSIP is represented with pjsip_transaction structure in header file <pjsip/sip_transaction.h>. Transactions lifetime normally follows these steps: o o Created by pjsip_tsx_endpt_create_uac() / pjsip_tsx_create_uas(). After initializing UAS transaction, application needs to call pjsip_tsx_recv_msg() to pass in the initial request message so that transaction state can move from NULL to TRYING. Subsequent request retransmissions will be absorbed by the transaction. When application wants to send request or response message using the transaction, it will call pjsip_tsx_send_msg(). Transaction state automatically changes as messages are passed to it (either by endpoint for incoming message or by transaction user for outgoing message) or timer elapses, and transaction user is notified via on_tsx_state() callback. Transaction will be automatically destroyed once it the state has reached PJSIP_TSX_STATE_TERMINATED. Application can also forcely terminate the transaction by calling pjsip_tsx_terminate().
o o
Client transaction: when a client INVITE transaction receives 300-699 final response to INVITE, it will automatically emit ACK request to the response. The transaction then wait for timer D interval before it is terminated, during which any incoming 300-699 response retransmissions will be automatically answered with ACK request. Page 56
PJSIP Developers Guide Server transaction: when a server INVITE transaction is asked to transmit 300699 final response, it will transmit the response and keep retransmitting the response until an ACK request is received or timer H interval has elapsed. During this interval, when ACK request is received, transaction will move to Confirmed state and will be destroyed after timer I interval has elapsed. When timer H elapsed without receving a valid ACK request, transaction will be destroyed. Successfull INVITE Request Client transaction: when a client INVITE transaction receives 2xx final response to INVITE, it will destroy itself automatically after it passes the response to its transaction user (can be a dialog or application). Subsequent incoming 2xx response retransmission will be passed directly to dialog or application. In any case, application MUST send ACK request manually upon receiving 2xx final response to INVITE. Server transaction: when a server INVITE transaction is asked to transmit 2xx final response, it will transmit the response and keep retransmitting the response until ACK is received or transaction is terminated by application with pjsip_tsx_terminate(). For simplicity in the implementation, a typical UAS dialog normally will let the transaction handle the retransmission of the 2xx INVITE response. But proxy application MUST destroy the UAS transaction as soon as it receives and sends the 2xx response, to allow the 2xx retransmission to be handled by end-to-end user agents.
This behavior of INVITE server transaction is different than RFC 3261 for successfull INVITE request, which says that INVITE server transaction MUST be destroyed once 2xx response is sent. The PJSIP transaction behavior allows more simplicity in the dialog implementation, while maintaining the flexibility to be compliant with RFC 3261 for proxy applications.
The default behavior of the INVITE server transaction can be overridden by setting transaction->handle_200resp to zero (default is non-zero) after transaction is created. In this case, UAS INVITE transaction will be destroyed as soon as 2xx response to INVITE is sent.
TCP Connection Closure A TCP connection closure will not automatically cause the transaction to fail. In fact, the transaction will not even detect the failure until it tries to send a message. When it does, it follows the normal procedure to send the message using alternative transport.
8.2 Reference
8.2.1 Base Functions
pj_status_t pjsip_tsx_layer_init_module( pjsip_endpoint *endpt );
Initialize and register the transaction layer module to the specified endpoint. Get the instance of transaction layer module. Shutdown the transaction layer module and unregister it from the endpoint where it currently registered.
pj_status_t pjsip_tsx_create_uac (
Create a new UAC transaction for the outgoing request in tdata with the transaction user set to tsx_user. The transaction is automatically initialized and registered to the transaction table. Note that after calling this function, applications normally would call pjsip_tsx_send_msg() to actually send the request.
pj_status_t pjsip_tsx_create_uas ( pjsip_module *tsx_user, pjsip_rx_data *rdata, pjsip_transaction **p_tsx );
Create a new UAS transaction for the incoming request in rdata with the transaction user set to tsx_user. The transaction is automatically initialized and registered to endpoints transaction table.
void pjsip_tsx_recv_msg( pjsip_transaction *tsx, pjsip_rx_data *rdata );
Application MUST call this function after UAS transaction is created, passing the initial request message, so that transaction state can move from NULL to TRYING. The transaction users on_tsx_state() is called.
pj_status_t pjsip_tsx_send_msg( pjsip_transaction *tsx, pjsip_tx_data *tdata );
Page 58
PJSIP Developers Guide Send message through the transaction. If tdata is NULL, the last message or the message that was specified during creation will be retransmitted. When the function returns PJ_SUCCESS, the tdata reference counter will be decremented.
pj_status_t pjsip_tsx_create_key( pj_pool_t *pool, pj_str_t *out_key, pjsip_role_e role, const pjsip_method *method, const pjsip_rx_data *rdata);
Create a transaction key from an incoming request or response message, taking into consideration whether the message is compliant with RFC 3261 or RFC 2543. The key can be used to find the transaction in endpoints transaction table. The function returns the key in out_key parameter. The role parameter is used to find either UAC or UAS transaction, and the method parameter contains the method of the message.
pjsip_transaction* pjsip_tsx_layer_find_tsx( const pj_str_t *key, pj_bool_t lock );
Find transaction with the specified key in transaction table. If lock parameter is non-zero, this function will also lock the transaction before returning the transaction, so that other threads are not able to delete the transaction. Caller then is responsible to unlock the transaction when its finished using the transaction, using pj_mutex_unlock().
pj_status_t pjsip_tsx_terminate( pjsip_transaction *tsx, int st_code );
Forcefully terminate the transaction tsx with the specified status code st_code. Normally application doesnt need to call this function, since transactions will terminate and destroy themselves according to their state machine. This function is used for example when 200/OK response to INVITE is sent/received and the UA layer wants to handle retransmission of 200/OK response manually. The transaction will emit transaction state changed event (state changed to PJSIP_TSX_STATE_TERMINATED), then it will be unregistered and destroyed immediately by this function.
pjsip_transaction* pjsip_rdata_get_tsx ( pjsip_rx_data *rdata );
Send respond by creating a new UAS transaction for the incoming request.
pj_status_t pjsip_endpt_send_request(pjsip_endpoint *endpt, pjsip_tx_data *tdata, int timeout, void *token, void (*cb)(void*, pjsip_event*))
Page 59
PJSIP Developers Guide Send the request by using an UAC transaction, and optionally request callback to be called when the transaction completes.
Page 60
// You may modify the message before sending it. ... // Create transaction. status = pjsip_endpt_create_uac_tsx( endpt, &app_module, tdata, &tsx ); // Send the request. status = pjsip_tsx_send_msg( tsx, tdata /*or NULL*/); } static void on_tsx_state( pjsip_transaction *tsx, pjsip_event *event ) { pj_assert(event->type == PJSIP_EVENT_TSX_STATE); PJ_LOG(3,(app, Transaction %s: state changed to %s, tsx->obj_name, pjsip_tsx_state_str(tsx->state))); } Code 33 Sending Request Statefully
Page 61
Page 62
pjsip_cached_auth Attributes: realm, is_proxy, qop_value, nc, cnonce, last_chal, cached_hdr Operations:
pjsip_cred_info
This structure keeps the last challenge received from a particular server. It is needed so that client can initialize next request with the last challenge. This structure describes the client authentication session. Client would normally keep this structure for Page 63
Function Reference
pj_status_t pjsip_auth_clt_init( pjsip_auth_clt_sess *sess, pj_pool_t *pool, unsigned options);
Initialize client authentication session data structure, and set the session to use pool for its subsequent memory allocation. The argument options should be set to zero for this PJSIP version.
pj_status_t pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *s, int cred_cnt, const pjsip_cred_info cred[]);
Set the credentials to be used during the session. This will duplicate the specified credentials using client authentications pool.
pj_status_t pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess, pjsip_tx_data *tdata );
This function add all relevant authorization headers to a new outgoing request tdata according to the cached information in the session. The request line in the request message must be valid before calling this function.
pjsip_auth_clt_session *sess, pjsip_endpoint *endpt, const pjsip_rx_data *rdata, pjsip_tx_data *old_request, pjsip_tx_data **new_request );
pj_status_t pjsip_auth_clt_reinit_req(
Call this function to re-initialize a request upon receiving failed authentication status (401/407 response). This function will recreate new_request according to old_request, and add appropriate Authorization and Proxy-Authorization headers according to the challenges found in rdata response. In addition, this function also put the relevant information in the session. This function will return failure if there is a missing credential for the challenge. Note that this function may reuse the old request instead of creating a fresh one.
9.1.2 Examples
Client Transaction Authentication The following example illustrates how to initialize outgoing request with authorization information and how to handle challenge received from the server. For brevity, error handling is not shown in the example. A real application should be prepared to handle error situation in all stages.
pjsip_auth_client_session auth_sess; // Initialize client authentication session with some credentials. void init_auth(pj_pool_t *session_pool) { pjsip_cred_info cred; pj_status_t status; cred.realm = pj_str(sip.example.com); cred.scheme = pj_str(digest); cred.username = pj_str(alice); cred.data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; cred.data = pj_str(secretpassword);
Page 64
The server authorization session currently is not implemented. Only global, session-less server authorization framework is available.
Page 65
Type of function to be registered to authorization server to lookup for credential information for the specified acc_name in the specified realm. When credential information is successfully retrieved, the function must fill in the cred_info with the credentials and return PJ_SUCCESS. Otherwise it should return one of the following error code: o o PJSIP_EAUTHACCNOTFOUND: account not found for the specified realm, PJSIP_EAUTHACCDISABLED: account was found but disabled,
Functions Reference
pj_status_t pjsip_auth_srv_init( pj_pool_t *pool, pjsip_auth_srv *auth_srv, const pj_str_t *realm, pjsip_auth_lookup_cred *lookup_func, unsigned options );
Initialize server authorization session data structure to serve the specified realm and to use lookup_func function to look for the credential info. The argument options is bitmask combination of the following values: o PJSIP_AUTH_SRV_IS_PROXY: to specify that the server will authorize clients as a proxy server (instead of as UAS), which means that Proxy-Authenticate will be used instead of WWWAuthenticate.
pjsip_auth_srv *auth_srv, pjsip_rx_data *rdata, int *status_code );
pj_status_t pjsip_auth_srv_verify(
Request the authorization server framework to verify the authorization information in the specified request in rdata. If status_code is not NULL, it will be filled with suitable status for the response (401/407/etc.). This function will return PJ_SUCCESS if the authorization information found in the request can be accepted, or the following error when authorization failed: o o o o o PJSIP_EAUTHNOAUTH: no authorization header is specified in the request. PJSIP_EINVALIDAUTHSCHEME: invalid/unsupported authorization scheme (only digest is supported at present). PJSIP_EAUTHACCNOTFOUND or PJSIP_EAUTHACCDISABLED are the error codes returned by the lookup function. PJSIP_EAUTHINVALIDDIGEST: invalid digest, other non-zero values may be returned to indicate system error.
pjsip_auth_srv *auth_srv, const pj_str_t *qop, const pj_str_t *nonce, const pj_str_t *opaque, pj_bool_t stale, pjsip_tx_data *tdata);
pj_status_t pjsip_auth_srv_challenge(
Add authentication challenge headers to the outgoing response in tdata. If qop is specified, then it will be put in the challenge. Application may also specify its customized nonce and opaque for the challenge, or can leave the value to NULL to make the function fills them in with random characters.
Page 66
Page 67
PJSIP dialog does not know the state of its sessions. It doesnt know whether the INVITE session has been established or disconnected. In fact, PJSIP dialog does not even know what kind of sessions are there in the dialog. All it cares is how many active sessions are there in the dialog. The dialog is started with one active session, and when the session counter reaches zero and the last transaction is terminated, the dialog will be destroyed. It will be the responsibility of each dialog usages to increment and decrement the dialogs session counter.
PJSIP Developers Guide increment dialogs session counter), and decrement the session counter when the subscribe session has terminated. The processing of dialog usages by a dialog is similar to the processing of modules by endpoint; on each on_rx_request() and on_rx_response() event, the dialog passes the event to each dialog usages starting from the higher priority module (i.e. the one with lower priority number) until one of the module returns true (i.e. non-zero), which in this case the dialog will stop the distribution of the event further. The on_tsx_state() notification will be distributed to all dialog usages. Each dialog usage should filter out the transaction events that dont belong to it. In its most basic (i.e. low-level) use, the application manages the dialog directly, and it is the only usage (or user) of the dialog. In this case, the application is responsible for managing the sessions inside the dialog, which means handling ALL requests and responses and establishing/tearing down sessions manually. In later chapters, we will learn about high-level APIs that can be used to manage sessions. These high-level APIs are PJSIP modules that are registered to the dialog as dialog usages, and they will handle/react to different types of SIP messages that are specific to each type of sessions (e.g. an invite usage module will handle INVITE, PRACK, CANCEL, ACK, BYE, UPDATE and INFO, a subscribe usage module will handle REFER, SUBSCRIBE, and NOTIFY, etc.). These high level APIs provide high-level callbacks according to the sessions specification. In this chapter however, well only lean about basic, low-level dialog usage.
Page 69
pjsip_dialog Attributes: state, session_counter, initial_cseq, local_cseq, remote_cseq, route_set, local_info+tag, local_contact, remote_info+tag, remote_contact, next_set Operations: pj_status_t pjsip_dlg_create_uac(ua, local_uri, contact, ...); pj_status_t pjsip_dlg_create_uas(ua, rdata, contact, &dlg); pj_status_t pjsip_dlg_fork(old_dlg,rdata,&dlg); pj_status_t pjsip_dlg_set_route_set(dlg, route_set); pj_status_t pjsip_dlg_inc_session(dlg); pj_status_t pjsip_dlg_dec_session(dlg); pj_status_t pjsip_dlg_add_usage(dlg, mod); pj_status_t pjsip_dlg_create_request(dlg,method,cseq,&tdata); pj_status_t pjsip_dlg_send_request(dlg,tdata,&tsx); pj_status_t pjsip_dlg_create_response(dlg,rdata,code,txt,&tdata); pj_status_t pjsip_dlg_modify_response(dlg,tdata,code,txt); pj_status_t pjsip_dlg_send_response(dlg,tsx,tdata); pj_status_t pjsip_dlg_send_msg(dlg,tdata); pjsip_dialog* pjsip_tsx_get_dlg(tsx); pjsip_dialog* pjsip_rdata_get_dlg(rdata);
dialog usages Basic UA Application pjsip_module Attributes: name, id, priority, ... INVITE Dialog Usage Callbacks: pj_bool_t on_rx_request(rdata); pj_bool_t on_rx_response(rdata); void on_tsx_state(tsx,event);
Subscription Usage
The diagram shows the relationship between dialog and its usages. In the most basic/low-level scenario, the application module is the only usage of the dialog. In more high-level scenario, some high-level modules (e.g. pjsip_invite_usage and pjsip_subscribe_usage) can be registered to a dialog as dialogs usages, and the application will receive events from these usages instead instead of directly from the dialog. The diagram also shows PJSIP user agent module (pjsip_user_agent). The user agent module is the owner of all dialogs; the user agent module maintains a hash table of all dialog sets currently active.
10.1.6 Forking
Handling Forking Condition The user agent module provides a callback that can be registered by application when the user agent detects forked response from the downstream proxy. A forked response in defined as a response (can be provisional or 2xx response) within a dialog that has To tag that is different from any of existing dialogs. When such responses are received, the user agent will call on_dlg_forked() callback, passing the received response and the original dialog (the dialog that application created originally) as the arguments.
PJSIP Developers Guide o o ignore the provisional response (perhaps waiting until a final, forked 2xx response is received); or create a new dialog (by calling pjsip_dlg_fork()). In this case, subsequent responses received from this particular call leg will go to this new dialog. decide to terminate this particular call leg. In this case, the application would construct ACK request from the response, send the ACK, then construct a BYE transaction and send it to the call-leg. Application MUST construct Route headers manually for both ACK and BYE requests according to the Record-Route headers found in the response before sending them to the transaction/transport layer. create a dialog for this particular call leg (by calling pjsip_dlg_fork()). Application then constructs and sends ACK request to the call leg to establish the dialog. After dialog is established, application may terminate the dialog by sending BYE request.
Application MUST NOT ignore a forked 2xx responses. Creating Forked Dialog Application creates a forked dialog by calling pjsip_dlg_fork() function. This function creates a dialog and performs the following: o o o o Copy all attributes of the original dialog (including authorization client session) to the new dialog. Assign different remote tag value, according to the tag found in the To header in the response. Register the new dialog to user agents dialog set. If the original dialog has an application timer, it will copy the timer and update the timer of the new dialog.
Note that the function WILL NOT copy the dialog usages (i.e. modules) from the original dialog.
The reason why the function pjsip_dlg_fork() doesnt copy the dialog usages from the original dialog is because each usage will normally have dialog specific data that can not be copied without knowing the semantic of the data.
After the new dialog has been created, the application then MUST re-register each dialog usages with the new dialog, by calling pjsip_dlg_add_usage(). The new dialog then MUST be returned as return value of the callback function. This will cause the user agent to dispatch the message to the new dialog, causing dialog usages (e.g. application) to receive on_rx_response() notification on the behalf of the new dialog. Using Timer to Handle Failed Forked Dialog Application can schedule application specific timer with the dialog by calling pjsip_dlg_start_app_timer() function. For timer associated with a dialog, this timer is preferable than general purpose timer because this timer will be automatically deleted when the dialog is destroyed. Page 71
PJSIP Developers Guide Timer is important to handle failed forked dialog. A forked early dialog may not complete with a final response at all, because forking proxy will not forward 300699 if it receives 2xx response. So the only way to terminate these dangling early dialogs is by setting a timer on these dialogs. The best way to use dialogs application timer to handle failed forked early dialog, is to start the timer on the other forked dialogs the first time when it receives 2xx response on one of the dialog in the dialog set. When the timer expires and no 2xx response is received, the dialog should be terminated.
10.1.8 Transactions
Dialog always acts statefully. It automatically creates UAS transaction when incoming request arrives, and it creates UAC transaction when it is asked to send outgoing request. The only time when dialog acts statelessly is when it receives incoming request with CSeq lower then current CSeq, which in this case it would answer the request with 500 (Internal Server Error) response. When a transaction is created on behalf of a dialog (via dialog API, for both UAS and UAC transactions), the transaction user (TU) of the transaction is set to user agent instance, and the dialog instance will be put in the transactions mod_data in the appropriate index. The index is the user agents module ID. When events or message arrives, the transaction reports the events to user agent module, which will lookup the dialog and pass the event to the dialog.
Page 72
Get the instance of the user agent. Destroy the user agent module.
pj_status_t pjsip_ua_destroy(void);
// // // // //
Log identification Dialogs memory pool. Dialog's mutex. User agent instance. The dialog set.
// Dialog session properties. pjsip_uri *target; pjsip_dlg_party local; pjsip_dlg_party remote; pjsip_role_e role; pj_bool_t secure; pjsip_cid_hdr *call_id; pjsip_route_hdr route_set; pjsip_auth_clt_sess auth_sess; // Session Management int int // Dialog usages unsigned pjsip_module // Module specific data. void };
// // // // // // // //
Current target. Local party info. Remote party info. Initial role. Use secure transport? Call-ID header. Route set list. Client authentication session.
sess_count; tsx_count;
*mod_data[PJSIP_MAX_MODULE];
Page 73
Create a new dialog and return the instance in p_dlg parameter. After creating the dialog, application can add modules as dialog usages by calling pjsip_dlg_add_usage(). Note that initially, the session count in the dialog will be initialized to zero.
pj_status_t pjsip_dlg_create_uas( pjsip_user_agent *ua, pjsip_rx_data *rdata, const pj_str_t *contact, pjsip_dialog **p_dlg);
Initialize UAS dialog from the information found in the incoming request that creates a dialog (such as INVITE, REFER, or SUBSCRIBE), and set the local Contact to contact. If contact is not specified, the local contact is initialized from the URI in the To header in the request. If the request has To tag parameter, dialogs local tag will be initialized from this value. Otherwise a globally unique id generator will be invoked to create dialogs local tag. This function also initializes the dialogs route set based on the RecordRoute header in the request, if present. Note that initially, the session count in the dialog will be initialized to zero.
pj_status_t pjsip_dlg_fork( pjsip_dialog *original_dlg, pjsip_rx_data *rdata, pjsip_dialog **new_dlg );
Create a new (forked) dialog on receipt on forked response in rdata. This function clones a new dialog from original_dlg (including authentication session), but the new dialog will have new remote tag as copied from the To header in the response. Upon return, the new_dlg will have been registered to the user agent. Applications just need to add modules as dialogs usages. Note that initially, the session count in the dialog will be initialized to zero.
Destroy the dialog and unregister from UA modules hash table. This function can only be called when the session counter is zero.
Page 74
Increment the number of sessions in the dialog. Note that initially (after created) the dialog already has the session counter set to one.
pj_status_t pjsip_dlg_dec_session( pjsip_dialog *dlg );
Decrement the number of sessions in the dialog. Once the session counter reach zero and there is no pending transaction, the dialog will be destroyed. Note that this function may destroy the dialog immediately if there is no pending transaction when this function is called.
Add a module as dialog usage, and optionally set the module specific data.
pjsip_dialog *dlg, int module_id, void *data );
Get module specific data previously attached to the dialog. Application can also get value directly by accessing dlg->mod_data[module_id].
Create a basic/generic request with the specified method and optionally specify the cseq. Use value 1 for cseq to have the dialog automatically put next cseq number for the request. Otherwise for some requests, e.q. CANCEL and ACK, application must put the CSeq in the original INVITE request as the parameter. This function will also put Contact header where appropriate.
pj_status_t pjsip_dlg_send_request ( pjsip_dialog *dlg, pjsip_tx_data *tdata, pjsip_transaction **p_tsx );
Send request message to remote peer. If the request is not an ACK request, the dialog will send the request statefully, by creating an UAC transaction and send the request with the transaction. Also when the request is not ACK or CANCEL, the dialog will increment its local cseq number and update the cseq in the request according to dialogs cseq. Note that on_tsx_state callback of the dialog usages may be called before this function returns. If p_tsx is not null, this argument will be set with the transaction instance that was used to send the request. Page 75
PJSIP Developers Guide This function decrements the transmit data's reference counter regardless the status of the operation.
pj_status_t pjsip_dlg_create_response( pjsip_dialog *dlg, pjsip_rx_data *rdata, int st_code, const pj_str_t *st_text, pjsip_tx_data **tdata);
Create a response message for the incoming request in rdata with status code st_code and optional status text st_text. This function is different than endpoints API pjsip_endpt_create_response() in that the dialog function adds Contact header and Record-Route headers in the response where appropriate.
pj_status_t pjsip_dlg_modify_response( pjsip_dialog *dlg, pjsip_tx_data *tdata, int st_code, const pj_str_t *st_text);
Modify previously sent response with other status code. Contact header will be added when appropriate.
pj_status_t pjsip_dlg_send_response( pjsip_dialog *dlg, pjsip_transaction *tsx, pjsip_tx_data *tdata);
Send response message statefully. The transaction instance MUST be the transaction that was reported on on_rx_request() callback. This function decrements the transmit data's reference counter regardless the status of the operation.
Set dialog's initial route set to route_set list. This can only be called for UAC dialog, before any request is sent. After dialog has been established, the route set can not be changed. For UAS dialog,the route set will be initialized in pjsip_dlg_create_uas() from the Record-Route headers in the incoming request. The route_set argument is standard list of Route headers (i.e. with sentinel).
pj_status_t pjsip_dlg_start_app_timer( pjsip_dialog *dlg, int app_id, const pj_time_val *interval, void (*cb)(pjsip_dialog*,int));
Start application timer with this dialog with application specific id in app_id and callback to be called in cb. Application can only set one application timer per dialog. This timer is more usefull for dialog specific timer, because it will be automatically destroyed once the dialog is destroyed. Note that timer will also be copied to the forked dialog.
pj_status_t pjsip_dlg_stop_app_timer( pjsip_dialog* pjsip_rdata_get_dlg( pjsip_dialog *dlg );
Stop application specific timer if exists. Get the dialog instance in the incoming rdata. If an incoming message matches an existing dialog, the user agent must have put the matching dialog instance in the rdata, or otherwise this function will return NULL if the message didnt match any existing dialog. Page 76
pjsip_rx_data *rdata );
Page 77
10.3 Examples
10.3.1 Invite UAS Dialog
The following examples uses basic/low-level dialog API to process an incoming dialog. The examples show how to: o o o o create and initialize incoming dialog, create UAS transaction to process the incoming INVITE request and transmit 1xx responses, transmit 2xx response to INVITE reliably, process the incoming ACK.
As usual, most error handlings are omited for brevity. Real-world application should be prepare to handle error conditions in all stages of the processing. Creating Initial Invite Dialog In this example well learn how to create a dialog for an incoming INVITE request and respond the dialog with 180/Ringing provisional response.
pj_bool_t on_rx_request(pjsip_rx_data *rdata) { if (rdata->msg->line.request.method.id == PJSIP_INVITE_METHOD && pjsip_rdata_get_dlg(rdata) == NULL) { // Process incoming INVITE! pjsip_dialog *dlg; pjsip_transaction *tsx; pjsip_tx_data *tdata; struct app_dialog *app_dlg; // Create, initialize, and register new dialog for incoming INVITE. // This also implicitly create UAS transaction for rdata. status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, NULL, &dlg); // Register application as the only dialog usage status = pjsip_dlg_add_usage( dlg, &app_module, NULL ); // Increment session. pjsip_dlg_inc_session(dlg); // Create 180/Ringing response status = pjsip_dlg_create_response( dlg, rdata, 180, NULL /*Ringing*/, &tdata); // Send 180 response statefully. A transaction will be created in &tsx. status = pjsip_dlg_send_response( dlg, pjsip_rdata_get_tsx(rdata), tdata); // As in real application, normally we will send 200/OK later, // when the user press the Answer button. In this example, well send // 200/OK in answer_dlg() function which will be explained later. In order // to do so, we must save the INVITE transaction. We do this by putting // the transaction instance in dialogs module data at index application // modules ID. // dlg->mod_data[app_module.id] = pjsip_rdata_get_tsx(rdata); // Done processing INVITE request return PJ_TRUE; } // Process other requests ... } Code 37 Creating Dialog for Incoming Invite
Page 78
PJSIP Developers Guide Answering Dialog In this example we will learn how to send 200/OK response to establish the dialog.
static void answer_dlg(pjsip_dlg *dlg) { pjsip_transaction *invite_tsx; pjsip_tx_data *tdata; invite_tsx = dlg->mod_data[app_module.id]; // Modify previously sent (provisional) response to 200/OK response. // The previously sent message is found in tsx->last_tx. tdata = invite_tsx->last_tx; status = pjsip_dlg_modify_response( dlg, tdata, 200, NULL /*OK*/ ); // You may modify the response before its sent // (e.g. add msg body etc). ... // Send the 200 response using previous transaction. // Transaction will take care of the retransmission. status = pjsip_dlg_send_response( dlg, invite_tsx, tdata); // We dont need to keep pending invite tsx anymore. dlg->mod_data[app_module.id] = NULL; } Code 38 Answering Dialog
Processing CANCEL Request In this example we will learn how to handle incoming CANCEL request.
pj_bool_t on_rx_request(pjsip_rx_data *rdata) { ... if (rdata->msg->line.request.method.id == PJSIP_CANCEL_METHOD) { // See if we have pending INVITE transaction. pjsip_dialog *dlg; pjsip_transaction *invite_tsx; // All requests within a dialog will have the dialog instance // recorded in rdata. dlg = pjsip_rdata_get_dlg(rdata); if (!dlg) { // Not associated with any dialog. Respond statelessly with 481. status = pjsip_endpt_respond_stateless( endpt, rdata, 481, NULL, NULL, NULL, NULL); return PJ_TRUE; } invite_tsx = dlg->mod_data[app_module.id]; if (invite_tsx) { pjsip_tx_data *tdata; // Transaction found. Respond CANCEL (statefully!) with 200 regardless // whether the INVITE transaction has completed or not. status = pjsip_dlg_respond( dlg, rdata, 200, NULL /*OK*/); // Respond the INVITE transaction with 487/Request Terminated // only when INVITE transaction has not send final response. if (invite_tsx->status_code < 200) { tdata = invite_tsx->last_tx;
Page 79
Processing ACK Request In this example we will learn how to handle incoming ACK request.
pj_bool_t on_rx_request(pjsip_rx_data *rdata) { ... if (rdata->msg->line.request.method.id == PJSIP_ACK_METHOD && pjsip_rdata_get_dlg(rdata) != NULL) { // Process the ACK request pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata); ... return PJ_TRUE; } ... } Code 40 Processing ACK Request
Page 80
// Add session. pjsip_dlg_inc_session(dlg); // Send initial INVITE. status = pjsip_dlg_create_request( dlg, &pjsip_invite_method, -1, &tdata); // Modify the INVITE (e.g. add message body etc.. ) ... // Send the INVITE request. status = pjsip_dlg_send_request( dlg, tdata, NULL); // Done. // Further responses will be received in on_rx_response. return status; } Code 41 Creating Outgoing Dialog
Receiving Response
static pj_bool_t on_rx_response( pjsip_rx_data *rdata ) { pjsip_dialog *dlg; dlg = pjsip_rdata_get_dlg( rdata ); if (dlg != NULL ) { pjsip_transaction *tsx = pjsip_rdata_get_tsx( rdata ); if ( tsx != NULL && tsx->method.id == PJSIP_INVITE_METHOD) { if (tsx->status_code < 200) { PJ_LOG(3,(app, Received provisional response %d, tsx->status_code)); } else if (tsx->status_code >= 300) { PJ_LOG(3,(app, Dialog failed with status %d, tsx->status_code)); pjsip_dlg_dec_session(dlg); // ACK for non-2xx final response is sent by transaction. } else { PJ_LOG(3,(app, Received OK response %d!, tsx->status_code)); send_ack( dlg, rdata ); } } else if (tsx == NULL && rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD && rdata->msg_info.msg->line.status.code/100 == 2) { // Process 200/OK response retransmission. send_ack( dlg, rdata ); } return PJ_TRUE; } else // Process other responses not belonging to any dialog ... } Code 42 Receiving Response in Dialog
Sending ACK
static void send_ack( pjsip_dialog *dlg, pjsip_rx_data *rdata ) { pjsip_tx_data *tdata; // Create ACK request status = pjsip_dlg_create_request( dlg, &pjsip_ack_method, rdata->msg_info.cseq->cseq, &tdata ); // Add message body
Page 81
// Create BYE request status = pjsip_dlg_create_request( dlg, &pjsip_bye_method, -1, &tdata ); // Send the request. status = pjsip_dlg_send_request ( dlg, tdata, NULL ); // Decrement session. // Dialog will be destroyed once the BYE transaction terminates. pjsip_dlg_dec_session(dlg); }
Page 82
pjmedia_sdp_session Attributes: origin, name, time, conn, attr, media Operations: pj_status_t pjmedia_sdp_parse(...); int pjmedia_sdp_print(...); Attributes: state
pjmedia_sdp_neg
Operations: status pjmedia_sdp_neg_init_w_local_offer(pool,neg,local); status pjmedia_sdp_neg_init_w_remote_offer(neg,loc,rmt); status pjmedia_sdp_neg_modify_local_offer(neg,local); status pjmedia_sdp_neg_negotiate(neg); sdp* pjmedia_sdp_neg_tx_local_offer(neg); sdp* pjmedia_sdp_neg_rx_remote_offer(neg,remote); sdp* pjmedia_sdp_neg_rx_remote_answer(neg,remote); sdp* pjmedia_sdp_neg_get_local(neg); sdp* pjmedia_sdp_neg_get_remote(neg);
passed to the negotiator during creation, and the contents generally will not be changed throughout the session (even after negotiation). The negotiator uses this SDP in the negotiation when it receives new offer from remote (as opposed to receiving updated SDP from remote).
active_local_sdp: contains local SDP after it has been negotiated with
remote. The dialog MUST use this to start its local media instead of the initial SDP. o
active_remote_sdp: contains the SDP currently used by peer/remote.
Page 83
PJSIP Developers Guide The negotiator also has two other SDP variables which are only used internally during negotiation process, namely neg_local_sdp and neg_remote_sdp. These are temporary SDP description, and application MUST NOT refer to these variables.
create_w_local_offer()
LOCAL OFFER
NULL
WAIT NEGO
NEGOTIATING
DONE
rx_remote_offer()
The negotiation session starts with PJMEDIA_SDP_NEG_STATE_NULL. If the dialog has a local media description ready and want to offer the media to remote (normally this is the case when the dialog is acting as UAC), it creates the SDP negotiator by passing the local SDP to the function pjmedia_sdp_neg_create_w_local_offer(). This function will set the initial capability of local endpoint, and set the negotiation session state to PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER. The initial SDP then can be sent to remote party in the outgoing INVITE request. Once dialog has received remotes SDP, it must call pjmedia_sdp_neg_rx_remote_answer() with providing the remotes SDP. The negotiation function can then be called. If the dialog already has remote media description in hand (normally this is the case when dialog is acting as UAS), it can create the SDP negotiator session by passing both local and remote SDP to pjmedia_sdp_neg_create_w_remote_offer(). After this, the negotiation function can be called. After the session has been established, both local and remote party may modify the session. The negotiator can handle one of these two situations: o The dialog has received SDP from remote. In this case, the dialog will call pjmedia_sdp_neg_rx_remote_offer() and passing the remotes SDP to this function. After this the negotiation function can be called. The negotiation functions return value determines whether there is modification needed in the local media. The local party wants to send SDP to remote. Dialog can further choose one of the following actions: If it just wants to send currently active local SDP without modification, it should call pjmedia_sdp_neg_tx_local_offer() to get the active local SDP, send the SDP, then wait for the remotes answer. If it wants to modify currently active local media (e.g. changing stream direction, change active codec, etc), it should get the active local media with pjmedia_sdp_neg_get_local(), modify it, call pjmedia_sdp_neg_modify_local_offer() to update the offer, send the local SDP, then wait for the remotes answer. Page 84
PJSIP Developers Guide The dialog may want to completely change the local media (e.g. changing IP address, changing codec set, adding new media line). This is different than updating current media described above because it will change initial_sdp, so that future negotiation will be based on this new SDP. If the dialog wants to do this, it calls pjmedia_sdp_neg_reinit_local_offer() with the new local SDP, send the SDP, then wait for remotes answer.
After the dialog has sent offer to remote party, it should receive answer back from the remote party. The dialog must provide the remotes SDP to the negotiator so that the negotiation function can be called. The dialog provides the remotes answer by calling pjsip_sdp_neg_rx_remote_answer(). If remote has rejected locals offer (e.g. returning 488/Not Acceptable Here response), dialog MUST still call pjsip_sdp_neg_rx_remote_answer() with providing NULL in remotes SDP argument, and call the negotiation function so that the negotiator session can revert back to previously active session descriptions, if any.
between local and remote SDP. In this case, both locals and remotes active SDP will be stored in the session for future reference, and application can query these active SDPs to start the local media. o
PJMEDIA_ESDPNOCHANGE, if it found out that there is no modification needed
in currently used SDPs (both local and remote). In this case, the previously agreed SDP sessions will not be modified either. o
PJMEDIA_ESDPFAIL, if it couldnt find agreement on local and remote
capabilities. In this case, if the session is keeping a previously agreed SDP, these SDP (local and remote) will not be modified. If dialog is acting as UAS for this session, it should respond the request with 488/Not Acceptable Here response to the offer.
PJMEDIA_ESDPNOOFFER, if negotiator has not sent/received any offer yet. PJMEDIA_ESDPNOANSWER, if negotiator has not received remotes answer yet.
o o o
In all cases, the negotiation function will set the negotiators state to PJMEDIA_SDP_NEG_STATE_DONE.
Page 85
12.1.1 Terms
Dialog invite session is an invite session inside a dialog. If application decides to use the high level invite session management, it needs to create one and only one instance of dialog invite session for each dialog. Dialog invite usage is a PJSIP module, registered to PJSIP endpoint. When a dialog has dialog invite session, this module needs to be registered to the particular dialog as the dialog usage. This will be achieved automatically during invite session creation.
12.1.2 Features
The dialog invite session provides the following features for the application: o o o o o o Session progress reporting (e.g. session progressing, connected, confirmed, disconnected), Automatic authentication handling (e.g. retry the request on receipt of 401/407 response), SDP offer and answer handling, High-level forking handler, Session timeout (i.e. Expires header), Session extensions, such as session timer, and reliable provisional response.
Page 86
CALLING
Rx 300-699, errors Tx INVITE Rx 1xx Tx/Rx 2xx Rx 2xx Tx/Rx ACK Tx/Rx BYE
NULL
Rx INVITE
EARLY
Tx 1xx
CONNECTING
Tx 2xx
CONFIRMED
DISCONNECTED
Timer
Tx 300-699, errors
INCOMING
TERMINATED
This is the state of the session when it was first created. No messages have been sent/received at this point. The session state after the first INVITE message is sent, but before any provisional response is received. The session state after the first INVITE message is received, but before any provisional response is sent. The session state after dialog has sent or received provisional response messages for the INVITE request, only when To tag is present. The session state after a final 2xx response has been sent or received. The session state after ACK request has been sent or received. The session state when the session has been disconnected, either because of non-successful final response to INVITE or BYE request.
PJSIP_INV_STATE_CALLING
PJSIP_INV_STATE_INCOMING
PJSIP_INV_STATE_EARLY
PJSIP Developers Guide invite sesion has been created, or otherwise the invite session will loose some events. When an outgoing dialog forked, and if an invite session exists in the original dialog, the invite usage will module automatically create the invite session for the new (forked) dialog. Application will be notified about the creation of the new session via a callback. The invite session creation functions (i.e. pjsip_inv_create_uac() and pjsip_inv_create_uas() functions) automatically registers the invite session usage to the dialog. Application does not need to call pjsip_dlg_add_usage() to register the invite usage module to the dialog.
Page 88
12.2 Reference
12.2.1 Data Structure
The invite session functionalities are declared in header file <pjsip-ua/sip_inv.h>.
enum pjsip_inv_state { PJSIP_INV_STATE_NULL, PJSIP_INV_STATE_CALLING, PJSIP_INV_STATE_INCOMING, PJSIP_INV_STATE_EARLY, PJSIP_INV_STATE_CONNECTING, PJSIP_INV_STATE_CONFIRMED, PJSIP_INV_STATE_DISCONNECTED, PJSIP_INV_STATE_TERMINATED, }; struct pjsip_inv_session { pjsip_inv_state pjsip_dialog pjmedia_sdp_neg pj_uint32_t };
// // // // // // // //
Before INVITE is sent or received. After INVITE is sent. After INVITE is received After response with To tag is sent/received. After 2xx response is sent/received. After ACK is sent/received. Session is terminated Session will be destroyed.
// // // //
Session state. The base dialog. SDP negotiator. Options in use, see pjsip_inv_option
The following code shows various options that can be applied to a session. The bitmask combination of these options need to be specified when creating a session. After the dialog is established (including early), the options member of pjsip_inv_session shows which capabilities are common in both endpoints.
= 1, // Indicate support for 100rel extension = 2, // Indicate support for session timer extension. = 4, // Indicate support for UPDATE method. = 32, // Require 100rel extension. = 64, // Require session timer extension.
Initialize the invite usage module and register it to the endpoint. The callback argument contains pointer to functions to be called on occurences of events in invite sessions.
pjsip_module* pjsip_inv_usage_instance(void);
Page 89
PJSIP Developers Guide Get the instance of the invite usage module.
This callback is called when the invite sesion state has changed. Application should inspect the session state (inv_sess->state) to get the current state. This callback is mandatory.
void on_new_session( pjsip_inv_session *inv_ses, pjsip_event *e);
This callback is called when the invite usage module has created a new dialog and invite because of forked outgoing request. This callback is mandatory.
void on_tsx_state_changed( pjsip_inv_session *inv_ses, pjsip_transaction *tsx, pjsip_event *e );
This callback is called whenever any transactions within the session has changed their state. Application MAY implement this callback, e.g. to monitor the progress of an outgoing request. This callback is optional.
void on_rx_offer(
This callback is called when the invite session has received new offer from peer. Application set local answer by calling pjsip_inv_set_sdp_answer(). This function will not send outgoing message. It just keeps the answer for SDP negotiation process, and will be included in subsequent response or request sent. This callback is optional. When its not specified, the default behavior is to negotiate remote offer with sessions initial capability.
void on_media_update( pjsip_inv_session *inv_ses, pj_status_t status );
This callback is called after SDP offer/answer session has completed. The status argument specifies the status of the offer/answer, as returned by pjmedia_sdp_neg_negotiate(). This callback is optional (from the point of view of the framework), but all useful applications normally need to implement this callback.
Page 90
Create UAC invite session for the specified dialog in dlg. If application has determined its media capability, it can specify the SDP in local_sdp. Otherwise it can leave this to NULL, to let remote UAS specifies an offer. The options argument is bitmask combination of SIP features in pjsip_inv_options enumeration. On successful return, the invite session will be put in inv_sess argument and the function will return PJ_SUCCESS. Otherwise the appropriate error status will be returned on failure.
pj_status_t pjsip_inv_verify_request(pjsip_rx_data *rdata, unsigned *options, const pjmedia_sdp_session *local_sdp, pjsip_dialog *dlg, pjsip_endpoint *endpt, pjsip_tx_data **tdata);
Application SHOULD call this function upon receiving the initial INVITE request in rdata before creating the invite session (or even dialog), to verify that the invite session can handle the INVITE request. This function verifies that local endpoint is capable to handle required SIP extensions in the request (i.e. Require header field) and also the media, if media description is present in the request. Upon calling this function, the options argument SHOULD contain the desired SIP extensions to be applied to the session. Upon return, this argument will contain the SIP extension that will be applied to the session, after considering the Supported, Require, and Allow headers in the request. If local media capability has been determined, and if application wishes to verify that it can handle the media offer in the incoming INVITE request, it SHOULD specify its local media capability in local_sdp argument. If it is not specified, media verification will not be performed by this function. If everything has been negotiated successfully, the function will return PJ_SUCCESS. Otherwise it will return the reason of the failure. This function is capable to create the appropriate response message when the verification has failed. If tdata is specified, then a non-2xx final response will be created and put in this argument upon return, when the verification has failed. If a dialog has been created prior to calling this function, then it MUST be specified in dlg argument. Otherwise application MUST specify the endpt argument (this is useful e.g. when application wants to send the response statelessly).
pj_status_t pjsip_inv_create_uas(
pjsip_dialog *dlg, pjsip_rx_data *rdata, const pjmedia_sdp_session *local_sdp, unsigned options, pjsip_inv_session **inv_sess);
Create UAS invite session for the specified dialog in dlg. Application MUST specify the received INVITE request in rdata. The invite session needs to inspect the received request to see if the request contains features that it supports. Page 91
PJSIP Developers Guide Application SHOULD call the verification function before calling this function, to ensure that it can create the session successfully. If application has determined its media capability, it can specify this capability in local_sdp. If SDP is received in the initial INVITE, the UAS capability specified in local_sdp doesnt have to match the received offer; the SDP negotiator is able to rearrange the media lines in the answer so that it matches the offer. The options argument is bitmask combination of SIP features in pjsip_inv_options enumeration. On successful return, the invite session will be put in inv_sess argument and the function will return PJ_SUCCESS. Otherwise the appropriate error status will be returned on failure.
pj_status_t pjsip_inv_terminate( pjsip_inv_session *inv, int st_code, pj_bool_t notify );
Terminate the INVITE session prematurely and destroy the underlying dialog (if the dialog has no other usage). This function should only be called when INVITE session initialization has failed. For normal cases, application MUST terminate the INVITE session by calling pjsip_inv_end_session(). The st_code argument specifies the SIP status code to be put as the disconnect cause. If notify is true, the application callback will be called.
Create the initial INVITE request for this session. This function can only be called for UAC session. The initial INVITE request will be put in tdata argument if it can be created successfully. If local media capability is specified when the invite session was created, then this function will put an SDP offer in the outgoing INVITE request. Otherwise the outgoing request will not contain SDP body.
pj_status_t pjsip_inv_answer( pjsip_inv_session *inv, int st_code, const pj_str_t *st_text, const pjmedia_sdp_session *local_sdp, pjsip_tx_data **tdata );
Create a response message to the initial INVITE request. The st_code contains the status code to be sent, which may be a provisional or final response. If custom status text is desired, application can specify the text in st_text; otherwise if this argument is NULL, default status text will be used. If application has specified its media capability during creation of UAS invite session, the local_sdp argument MUST be NULL. This is because application can not perform more than one SDP offer/answer session in a single INVITE transaction. If application has not specified its media capability during creation of UAS invite session, it MAY or MUST specify its capability in local_sdp argument, depending whether st_code indicates a 2xx final response. Page 92
Create a SIP message to initiate invite session termination. Depending on the state of the session, this function may return CANCEL request, a non2xx final response, or a BYE request. If the session has not answered the incoming INVITE, this function creates the non-2xx final response with the specified status code in st_code and optional status text in st_text.
pj_status_t pjsip_inv_reinvite( pjsip_inv_session *inv, const pj_str_t *new_contact, const pjmedia_sdp_session *new_offer, pjsip_tx_data **tdata );
Create a re-INVITE request. If application wants to update its local contact and inform peer to perform target refresh with a new contact, it can specify the new contact in new_contact argument; otherwise this argument must be NULL. Application MAY initiate a new SDP offer/answer session in the request when there is no pending answer to be sent or received. It can detect this condition by observing the state of the SDP negotiator of the invite session. If new offer should be sent to remote, the offer must be specified in new_offer, otherwise this argument must be NULL.
pj_status_t pjsip_inv_update ( pjsip_inv_session *inv, const pj_str_t *new_contact, const pjmedia_sdp_session *new_offer, pjsip_tx_data **tdata );
Create an UPDATE request. If application wants to update its local contact and inform peer to perform target refresh with a new contact, it can specify the new contact in new_contact argument; otherwise this argument must be NULL. Application MAY initiate a new SDP offer/answer session in the request when there is no pending answer to be sent or received. It can detect this condition by observing the state of the SDP negotiator of the invite session. If new offer should be sent to remote, the offer must be specified in new_offer, otherwise this argument must be NULL.
pj_status_t pjsip_inv_send_msg( pjsip_inv_session *inv, pjsip_tx_data *tdata, void *token );
Send request or response message in tdata. The token is an arbitrary application data that will be put in the transactions mod_data array, at application modules index.
Get the invite session instance associated with dialog dlg, or NULL.
pjsip_dialog *dlg );
pjsip_inv_session* pjsip_tsx_get_inv_session(
pjsip_transaction *tsx );
Get the invite session instance associated with transaction tsx, or NULL. Page 93
Page 94
PJSIP Developers Guide The event package is responsible mainly for providing message body to NOTIFY requests. For example, PJSIP presence event package creates message body with content type application/pidf+xml or application/xpidf+xml for all outgoing NOTIFY requests.
1a 1b 2a 2b 3 4
dlg_create_uac() evsub_create_uac()
evsub_initiate() evsub_send_request()
dlg_add_usage(), dlg_inc_session(),
dlg_create_request()
dlg_send_request()
SUBSCRIBE
SUBSCRIBE
200 (OK)
on_evsub_state() (state=ACCEPTED)
on_tsx_state (200)
Page 96
PJSIP Developers Guide Description: 1. Application (or event package) initiates client subscription by first creating an UAC dialog (1a) then creates the client subscription session (1b). Application MAY set dialog credentials and route set between step 1a and 1b. 2. Application sends initial SUBSCRIBE (or other method that establishes subscription, such as REFER) by creating the request (2a) and send the request (2b). 3. The sending of SUBSCRIBE request in step 2 above will trigger on_evsub_state() callback to be called. This will happen even before evsub_send_request() function returns. 4. Application receives any transaction state progress in on_tsx_state() callback in step 4. This callback is OPTIONAL, and only serves for informational purpose only. If the request is challenged, AND credentials have been set in the dialog, the event framework will resubmit the request with proper credential. 5. When 2xx response to initial SUBSCRIBE request (or other request that establishes subscription) is received, on_evsub_state() callback is called. Application can retrieve the subscription state by calling pjsip_evsub_get_state() function, which should return PJSIP_EVSUB_STATE_ACCEPTED when 2xx response is received. If non-2xx final response is received, the subscription state will be set to PJSIP_EVSUB_STATE_TERMINATED.
DIALOG
EVENT MODULE
APPLICATION
1a
endpt_respond(401)
1b
2a 2b 3 4
200 (OK)
Description: 1. Incoming requests outside any dialogs will always come to application, which ultimately decides how to handle the request. For incoming SUBSCRIBE request, if application wants to authenticate the request, it can respond the request with 401/407 response (step 1a), statefully or statelessly. This MUST be done before any dialog or server subscription instance is created. When Page 97
PJSIP Developers Guide application is happy with the request (step 1b), it can then create the server subscription instance (step 2a and 2b). If the request comes within a dialog (e.g. REFER request), application receives the request in on_tsx_state() callback of the dialog. In this case steps 1 to 2a are not required, and application proceeds direcly to step 2b. 2. The server side event subscription needs a dialog instance for it to operate. Application MAY create a new UAS dialog for the subscription (step 2a), or MAY use existing dialog (for example to handle incoming REFER request inside a dialog). Application then creates the server side event subscription (step 2b), passing the dialog instance. 3. Application calls pjsip_evsub_accept() to send response to the subscription request (step 3), passing a status code to be put in the response. The status code MUST be a 2xx class status. 4. The sending of 2xx response in step 3 above will trigger on_evsub_state() callback to be called (step 4). Server then MUST send initial NOTIFY request immediately, which will be described below.
dlg_create_request()
NOTIFY (state=active)
401 (U nautho rized)
5a 5b 6
dlg_send_request()
NOTIFY (state=active)
on_tsx_state(401) dlg_send_request()
on_tsx_state(401)
200 (OK)
on_tsx_state(200)
on_evsub_state() state=ACTIVE
PJSIP Developers Guide The on_rx_notify() callback is OPTIONAL. The default behavior is to respond incoming NOTIFY with 200 response. Note that the event framework only update its state (according to state in the incoming NOTIFY request) if application answer the NOTIFY request with 2xx response.
APPLICATION
EVENT MODULE
DIALOG
REMOTE ENDPOINT
=active) NOTIFY (state
200 (OK)
on_evsub_state() state=active
dlg_create_request()
NOTIFY ) (state=terminated
8a 8b 9
dlg_send_request()
200 (OK)
on_tsx_state(200)
The sending of NOTIFY request with Subscription-State set to terminated will trigger on_evsub_state() callback to be called (step 8b), regardless of the response of the NOTIFY request. However, when the NOTIFY request is challenged, the framework will correctly respond the challenge by resending the request with proper credential, if it has one.
Note: in addition, the receipt of 481 (Call/Transaction Does Not Exist), 408 (Request Timeout), transaction timeout, or transport error events on any outgoing NOTIFY requests will also
Page 99
APPLICATION
EVENT MODULE
DIALOG
REMOTE ENDPOINT
200 (OK)
on_evsub_state() state=terminated
dlg_dec_session()
APPLICATION
EVENT MODULE
DIALOG
REMOTE ENDPOINT
10 11a 11b
on_client_refresh() evsub_initiate()
evsub_send_request()
timer
dlg_create_request() dlg_send_request()
SUBSCRIBE
PJSIP Developers Guide When PJSIP event package implementation such presence or refer is being used, these packages provide default implementation for this callback. The default implementation is to terminate the subscription by sending NOTIFY with state set to terminated, and the last message body. Thus if application is using these packages, it doesnt have to implement this callback.
REMOTE ENDPOINT
DIALOG
EVENT MODULE
timer
APPLICATION
10 11a 11b
NOTIFY d) (state=terminate
dlg_create_request() dlg_send_request()
200 (OK)
on_tsx_state(200)
13.3 Reference
13.3.1 Module Management
pj_status_t pjsip_evsub_init_module( pjsip_endpoint *endpt );
Initialize the event notify module and register the module to the specified endpoint. This function MUST be called before any other event subscription functions.
pjsip_module* pjsip_evsub_instance(void);
Register event package to the event subscription framework. The pkg_mod argument specifies the module that implements the event package being registered.. The event_name specifies event package name, such as presence (RFC 3856). The accept_cnt and accept arguments specifies array of tokens that describes media types that will be acceptable. Examples of these media types for presence event package are application/pidf+xml and application/xpidf+xml.
enum pjsip_evsub_state
Page 101
Notes: When the state has reached PJSIP_EVSUB_STATE_TERMINATED, application MUST release applications resources associated to the subscription, because after this the subscription will be destroyed and there will be no further notification from the event subscription. The PJSIP_EVSUB_STATE_UNKNOWN occurs when server sends unrecognized state in the Subscription-State header.
Return the string representation of the state, or when the state is PJSIP_EVSUB_STATE_UNKNOWN, return the state string sent by server.
void pjsip_evsub_set_mod_data( pjsip_evsub *sub, unsigned mod_id, void *mod_data);
PJSIP Developers Guide The generic event subscription callback is declared as follows.
struct pjsip_evsub_user { void (*on_evsub_state) void (*on_tsx_state) void (*on_rx_refresh)
void (*on_rx_notify)
(pjsip_evsub *sub, pjsip_event *event); (pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event); (pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body); (pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body); (pjsip_evsub *sub); (pjsip_evsub *sub);
This callback is called when subscription state has changed. Application MUST be prepared to receive NULL event and events with type other than PJSIP_EVENT_TSX_STATE. This callback is optional, although normally application will definitely want to implement this callback.
void on_tsx_state(
This callback is called when transaction state has changed, for transactions that belong to this subscription (i.e. the request with method that creates the subscription, and NOTIFY transactions). This callback is OPTIONAL, as it only servers informational purpose only.
void on_rx_refresh( pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body);
This callback is called when incoming SUBSCRIBE (or any method that establishes the subscription in the first place) is received. It allows application to specify what response should be sent to remote, along with additional headers and message body to be put in the response, if any. This callback is OPTIONAL when application is using PJSIPs event package such as presence or call transfer; the packages default implementation will send 200 (OK) and NOTIFY containing current subscription state. However, if application implements this callback (i.e. the value of the callback is not NULL), it MUST send NOTIFY request upon receiving this Page 103
PJSIP Developers Guide callback. The suggested behavior is to call pjsip_evsub_last_notify() to create the NOTIFY request, since this function takes care about unsubscription request and calculates the appropriate expiration interval.
void on_rx_notify( pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body);
This callback is called when client/subscriber received incoming NOTIFY request. It allows the application to specify what response should be sent to remote, along with additional headers and message body to be put in the response, if any. This callback is OPTIONAL. When it is not implemented, the default behavior is to respond incoming NOTIFY request with 200 (OK) response.
void on_client_refresh( pjsip_evsub *sub );
This callback is called when it is time for the client to refresh the subscription. This callback is OPTIONAL when PJSIP package such as presence or refer is used; the event package will refresh subscription by sending SUBSCRIBE with the interval set to current/last interval. However, if application implements this callback (i.e. the value of the callback is not NULL), it MUST send the refresh subscription itself.
This callback is called when server doesn't receive subscription refresh after the specified subscription interval. This callback is OPTIONAL when PJSIP package such as presence or refer is used; the event package send NOTIFY to terminate the subscription. However, if application implements this callback (i.e. the value of the callback is not NULL), it MUST handle the timeout itself, and the suggested behaviour is to send NOTIFY with state set to terminated.
Create client subscription session, using dlg as the underlying dialog. The event argument specifies the event package to be used, and this must have been registered previously to the event framework. The option Page 104
PJSIP Developers Guide argument currently is only used for refer subscription, and it should be zero for other type of packages.
pj_status_t pjsip_evsub_create_uas( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, unsigned option, pjsip_evsub **p_evsub);
Create server subscription session, using dlg as the underlying dialog. The rdata argument specifies the incoming request. The option argument currently is only used for refer subscription, and it should be zero for other type of packages.
pj_status_t pjsip_evsub_terminate( pjsip_evsub *sub, pj_bool_t notify );
Forcefully destroy the event subscription. This function should only be be called on special condition when initialization has failed. For normal situation, the subscription will be destroyed automatically when subscription is terminated. This function MAY destroy the underlying dialog when the dialog has no other usages.
pj_status_) pjsip_evsub_initiate( pjsip_evsub *sub, const pjsip_method *method, pj_int32_t expires, pjsip_tx_data **p_tdata);
Call this function to create request to initiate subscription, to refresh subcription, or to request subscription termination. The method argument must be the method that establishes the subscription, such as SUBSCRIBE or REFER. If this argument is NULL, then SUBSCRIBE will be used. The expires argument will be put as Expires header in the request. If the value is set to zero, this will request unsubscription. If the value is negative, default expiration as defined by the package will be used. Application then MUST call pjsip_evsub_send_request() to send the subscription request.
pj_status_t pjsip_evsub_accept(
Accept the incoming subscription request by sending 2xx response to incoming SUBSCRIBE or REFER request. The st_code argument MUST specify 2xx code. The hdr_list is optional list of headers to be put in the response.
pj_status_t pjsip_evsub_notify( pjsip_evsub *sub, pjsip_evsub_state state, const pj_str_t *state_str, const pj_str_t *reason, pjsip_tx_data **p_tdata);
For notifier, set the state of the subscription and create NOTIFY request to subscriber. The state_str argument is optional, it is only used when the state is set to PJSIP_EVSUB_STATE_UNKNOWN. The reason argument MUST be set when subscription state is set to PJSIP_EVSUB_STATE_TERMINATED. Application SHOULD use the values Page 105
PJSIP Developers Guide defined in RFC 3265, such as noresource, timeout, giveup, rejected, probation, and deactivated. PJSIP does not interpret the value of the reason string, it will just put the string in the outgoing NOTIFY request. Note that the state of the subscription will actually be set when the NOTIFY request is sent.
pj_status_t pjsip_evsub_current_notify( pjsip_evsub *sub, pjsip_tx_data **p_tdata );
For notifier, create a NOTIFY request that reflects current subscription status. This function normally is used by package implementors, not directly by the application.
pjsip_event_sub *sub, pjsip_tx_data *tdata);
pj_status_t pjsip_event_sub_send_request(
Get the event subscription instance associated with the specified transaction.
Page 106
14.2 Reference
The presence API is very much identical to the core event API, and the behavior is also identical. Please refer to header file <pjsip-simple/presence.h> for more details. Application MUST call pjsip_pres_init_module() before using any presence functionalities. This function registers the presence module endpoint, and also register event package presence to event framework.
Page 107
Page 108
pj_status_t send_im(const pj_str_t *from, const pj_str_t *to, const pj_str_t *text) { pjsip_method message_method = { PJSIP_OTHER_METHOD, {MESSAGE, 7}}; pjsip_tx_data *tdata; pj_status_t status; status = pjsip_endpt_create_request( endpt, &message_method, to, from, to, NULL /*Contact*/, NULL /*Call-ID */, -1 /*CSeq*/, text /*Text body*/ &tdata); status = pjsip_endpt_send_request( endpt, tdata, -1 /*Timeout*/, NULL /*Callback data*/, NULL /*Callback*/); } Code 48 Sending IM Outside Dialog
Inside Dialog To send MESSAGE request inside dialog, application constructs a new request within dialog as usual, by calling pjsip_dlg_create_request().Application then attached a text/plain message body to the request, and call pjsip_dlg_send_request() to send the request. Page 109
PJSIP Developers Guide The following code snippet shows how to create MESSAGE request inside a dialog.
pj_status_t send_im_in_dlg( pjsip_dialog *dlg, const pj_str_t *text) { pjsip_method message_method = { PJSIP_OTHER_METHOD, {MESSAGE, 7}}; const pj_str_t STR_TEXT = pj_str(text); const pj_str_t STR_PLAIN = pj_str(plain); pjsip_tx_data *tdata; pj_status_t status; /* Must lock dialog. */ pjsip_dlg_inc_lock(dlg); /* Create the MESSAGE request. */ status = pjsip_dlg_create_request( dlg, &message_method, -1 /*CSeq*/, &tdata); /* Attach text/plain body. */ tdata->msg->body = pjsip_msg_body_create( tdata->pool, &STR_TEXT, &STR_PLAIN, text ); /* Send the request. */ status = pjsip_dlg_send_request( dlg, tdata, NULL /*Ptr. to receive tsx*/ ); /* Done */ pjsip_dlg_dec_lock(dlg); } Code 49 Sending IM Inside Dialog
Create XML document conformant to application/im-iscomposing+xml specification. The only required arguments are the pool and is_composing status. Other arguments are optional attributes to be put in the XML document. The last_active attribute indicates the time when the person is last typing. Put NULL to omit this attribute. The content_type argument Page 110
PJSIP Developers Guide specifies the type of message being composed. Put NULL to omit this attribute. The refresh argument indicates when the recepient can expect the sender to refresh the status. Put -1 to omit this attribute.
pjsip_msg_body* pjsip_iscomposing_create_body( pj_pool_t *pool, pj_bool_t is_composing, const pj_time_val *last_active, const pj_str_t *content_type, int refresh);
Create a SIP message body containing the XML document for the message composition indication.
pj_status_t pjsip_iscomposing_parse( pj_pool_t *pool, char *msg, pj_size_t len, pj_bool_t *p_is_composing, pj_str_t **p_last_active, pj_str_t **p_content_type, int *p_refresh);
Parse a buffer containg XML document containing the message composition indication. The values in the document will be returned in p_is_composing, p_last_active, p_content_type, and p_refresh arguments, which are all optional.
Page 111
Currently the PJSUA abstraction is used by pjsua console application. The PJSUA abstraction API is declared in <pjsua-lib/pjsua.h>, and application MUST linked with pjsua-lib static library. Although it has been tested to work properly, the PJSUA abstraction library is still in experimental stage, until enough applications are built on top of it and the API is proven to be sufficiently strong.
Page 112