Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:01:52 2007

Asterisk developer's documentation :: Codename Pineapple


sip3_refer.c File Reference


Detailed Description

Various SIP transfer/refer functions Version 3 of chan_sip.

Author:
Mark Spencer <markster@digium.com>

Olle E. Johansson <oej@edvina.net> (all the chan_sip3 changes)

See Also:

Definition in file sip3_refer.c.

#include "asterisk.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <regex.h>
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/logger.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
#include "asterisk/sched.h"
#include "asterisk/io.h"
#include "asterisk/rtp.h"
#include "asterisk/udptl.h"
#include "asterisk/acl.h"
#include "asterisk/manager.h"
#include "asterisk/callerid.h"
#include "asterisk/cli.h"
#include "asterisk/app.h"
#include "asterisk/musiconhold.h"
#include "asterisk/dsp.h"
#include "asterisk/features.h"
#include "asterisk/srv.h"
#include "asterisk/astdb.h"
#include "asterisk/causes.h"
#include "asterisk/utils.h"
#include "asterisk/file.h"
#include "asterisk/astobj.h"
#include "asterisk/dnsmgr.h"
#include "asterisk/devicestate.h"
#include "asterisk/linkedlists.h"
#include "asterisk/stringfields.h"
#include "asterisk/monitor.h"
#include "asterisk/localtime.h"
#include "asterisk/abstract_jb.h"
#include "asterisk/compiler.h"
#include "sip3.h"
#include "sip3funcs.h"

Include dependency graph for sip3_refer.c:

Go to the source code of this file.

Data Structures

struct  c_referstatusstring
 Table to convert from REFER status variable to string. More...

Functions

static int attempt_transfer (struct sip_dual *transferer, struct sip_dual *target)
 Attempt transfer of SIP call This fix for attended transfers on a local PBX.
static int get_refer_info (struct sip_dialog *transferer, struct sip_request *outgoing_req)
 Call transfer support (the REFER method) Extracts Refer headers into pvt dialog structure.
int handle_request_refer (struct sip_dialog *p, struct sip_request *req, int debug, int seqno, int *nounlock)
static int local_attended_transfer (struct sip_dialog *transferer, struct sip_dual *current, struct sip_request *req, int seqno)
 Find all call legs and bridge transferee with target called from handle_request_refer.
const char * referstatus2str (enum referstatus rstatus)
 Convert transfer status to string.
static int sip_park (struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno)
 Park a call using the subsystem in res_features.c This is executed in a separate thread.
static void * sip_park_thread (void *stuff)
 Park SIP call support function Starts in a new thread, then parks the call XXX Should we add a wait period after streaming audio and before hangup?? Sometimes the audio can't be heard before hangup.
int sip_refer_allocate (struct sip_dialog *p)
 Allocate SIP refer structure.
GNURK int transmit_notify_with_sipfrag (struct sip_dialog *p, int cseq, char *message, int terminate)
 Notify a transferring party of the status of transfer.

Variables

static const struct c_referstatusstring referstatusstrings []
 Table to convert from REFER status variable to string.


Function Documentation

static int attempt_transfer struct sip_dual transferer,
struct sip_dual target
[static]
 

Attempt transfer of SIP call This fix for attended transfers on a local PBX.

Definition at line 451 of file sip3_refer.c.

References ast_channel::_state, ast_cdr_append(), ast_channel_masquerade(), ast_log(), ast_quiet_chan(), AST_SOFTHANGUP_DEV, ast_softhangup_nolock(), ast_state2str(), sip_dual::chan1, sip_dual::chan2, LOG_DEBUG, LOG_NOTICE, LOG_WARNING, and option_debug.

00452 {
00453    int res = 0;
00454    struct ast_channel *peera = NULL,   
00455       *peerb = NULL,
00456       *peerc = NULL,
00457       *peerd = NULL;
00458 
00459 
00460    /* We will try to connect the transferee with the target and hangup
00461       all channels to the transferer */   
00462    if (option_debug > 3) {
00463       ast_log(LOG_DEBUG, "Sip transfer:--------------------\n");
00464       if (transferer->chan1)
00465          ast_log(LOG_DEBUG, "-- Transferer to PBX channel: %s State %s\n", transferer->chan1->name, ast_state2str(transferer->chan1->_state));
00466       else
00467          ast_log(LOG_DEBUG, "-- No transferer first channel - odd??? \n");
00468       if (target->chan1)
00469          ast_log(LOG_DEBUG, "-- Transferer to PBX second channel (target): %s State %s\n", target->chan1->name, ast_state2str(target->chan1->_state));
00470       else
00471          ast_log(LOG_DEBUG, "-- No target first channel ---\n");
00472       if (transferer->chan2)
00473          ast_log(LOG_DEBUG, "-- Bridged call to transferee: %s State %s\n", transferer->chan2->name, ast_state2str(transferer->chan2->_state));
00474       else
00475          ast_log(LOG_DEBUG, "-- No bridged call to transferee\n");
00476       if (target->chan2)
00477          ast_log(LOG_DEBUG, "-- Bridged call to transfer target: %s State %s\n", target->chan2 ? target->chan2->name : "<none>", target->chan2 ? ast_state2str(target->chan2->_state) : "(none)");
00478       else
00479          ast_log(LOG_DEBUG, "-- No target second channel ---\n");
00480       ast_log(LOG_DEBUG, "-- END Sip transfer:--------------------\n");
00481    }
00482    if (transferer->chan2) {         /* We have a bridge on the transferer's channel */
00483       peera = transferer->chan1; /* Transferer - PBX -> transferee channel * the one we hangup */
00484       peerb = target->chan1;     /* Transferer - PBX -> target channel - This will get lost in masq */
00485       peerc = transferer->chan2; /* Asterisk to Transferee */
00486       peerd = target->chan2;     /* Asterisk to Target */
00487       if (option_debug > 2)
00488          ast_log(LOG_DEBUG, "SIP transfer: Four channels to handle\n");
00489    } else if (target->chan2) {   /* Transferer has no bridge (IVR), but transferee */
00490       peera = target->chan1;     /* Transferer to PBX -> target channel */
00491       peerb = transferer->chan1; /* Transferer to IVR*/
00492       peerc = target->chan2;     /* Asterisk to Target */
00493       peerd = transferer->chan2; /* Nothing */
00494       if (option_debug > 2)
00495          ast_log(LOG_DEBUG, "SIP transfer: Three channels to handle\n");
00496    }
00497 
00498    if (peera && peerb && peerc && (peerb != peerc)) {
00499       ast_quiet_chan(peera);     /* Stop generators */
00500       ast_quiet_chan(peerb);  
00501       ast_quiet_chan(peerc);
00502       if (peerd)
00503          ast_quiet_chan(peerd);
00504 
00505       /* Fix CDRs so they're attached to the remaining channel */
00506       if (peera->cdr && peerb->cdr)
00507          peerb->cdr = ast_cdr_append(peerb->cdr, peera->cdr);
00508       else if (peera->cdr) 
00509          peerb->cdr = peera->cdr;
00510       peera->cdr = NULL;
00511 
00512       if (peerb->cdr && peerc->cdr) 
00513          peerb->cdr = ast_cdr_append(peerb->cdr, peerc->cdr);
00514       else if (peerc->cdr)
00515          peerb->cdr = peerc->cdr;
00516       peerc->cdr = NULL;
00517    
00518       if (option_debug > 3)
00519          ast_log(LOG_DEBUG, "SIP transfer: trying to masquerade %s into %s\n", peerc->name, peerb->name);
00520       if (ast_channel_masquerade(peerb, peerc)) {
00521          ast_log(LOG_WARNING, "Failed to masquerade %s into %s\n", peerb->name, peerc->name);
00522          res = -1;
00523       } else
00524          ast_log(LOG_DEBUG, "SIP transfer: Succeeded to masquerade channels.\n");
00525       return res;
00526    } else {
00527       ast_log(LOG_NOTICE, "SIP Transfer attempted with no appropriate bridged calls to transfer\n");
00528       if (transferer->chan1)
00529          ast_softhangup_nolock(transferer->chan1, AST_SOFTHANGUP_DEV);
00530       if (target->chan1)
00531          ast_softhangup_nolock(target->chan1, AST_SOFTHANGUP_DEV);
00532       return -1;
00533    }
00534    return 0;
00535 }

static int get_refer_info struct sip_dialog transferer,
struct sip_request outgoing_req
[static]
 

Call transfer support (the REFER method) Extracts Refer headers into pvt dialog structure.

Definition at line 203 of file sip3_refer.c.

References ast_exists_extension(), ast_log(), ast_strdupa, ast_strlen_zero(), ast_uri_decode(), ast_verbose(), sip_refer::attendedtransfer, sip_globals::default_context, get_header(), get_in_brackets(), global, sip_dialog::initreq, LOG_DEBUG, LOG_WARNING, ast_channel::macrocontext, option_debug, sip_dialog::owner, pbx_builtin_getvar_helper(), sip_dialog::refer, sip_refer::refer_to, sip_refer::refer_to_context, sip_refer::refer_to_domain, sip_refer::refer_to_urioption, sip_refer::referred_by, sip_refer::referred_by_name, sip_refer::replaces_callid, sip_refer::replaces_callid_fromtag, sip_refer::replaces_callid_totag, S_OR, sip_debug_test_pvt(), and strcasestr().

00204 {
00205 
00206    const char *p_referred_by = NULL;
00207    char *h_refer_to = NULL; 
00208    char *h_referred_by = NULL;
00209    char *refer_to;
00210    const char *p_refer_to;
00211    char *referred_by_uri = NULL;
00212    char *ptr;
00213    struct sip_request *req = NULL;
00214    const char *transfer_context = NULL;
00215    struct sip_refer *referdata;
00216 
00217 
00218    req = outgoing_req;
00219    referdata = transferer->refer;
00220 
00221    if (!req)
00222       req = transferer->initreq;
00223 
00224    p_refer_to = get_header(req, "Refer-To");
00225    if (ast_strlen_zero(p_refer_to)) {
00226       ast_log(LOG_WARNING, "Refer-To Header missing. Skipping transfer.\n");
00227       return -2;  /* Syntax error */
00228    }
00229    h_refer_to = ast_strdupa(p_refer_to);
00230    refer_to = get_in_brackets(h_refer_to);
00231    ast_uri_decode(refer_to);
00232 
00233    if (strncasecmp(refer_to, "sip:", 4)) {
00234       ast_log(LOG_WARNING, "Can't transfer to non-sip: URI.  (Refer-to: %s)?\n", refer_to);
00235       return -3;
00236    }
00237    refer_to += 4;       /* Skip sip: */
00238 
00239    /* Get referred by header if it exists */
00240    p_referred_by = get_header(req, "Referred-By");
00241    if (!ast_strlen_zero(p_referred_by)) {
00242       char *lessthan;
00243       h_referred_by = ast_strdupa(p_referred_by);
00244       ast_uri_decode(h_referred_by);
00245 
00246       /* Store referrer's caller ID name */
00247       ast_copy_string(referdata->referred_by_name, h_referred_by, sizeof(referdata->referred_by_name));
00248       if ((lessthan = strchr(referdata->referred_by_name, '<'))) {
00249          *(lessthan - 1) = '\0'; /* Space */
00250       }
00251 
00252       referred_by_uri = get_in_brackets(h_referred_by);
00253       if(strncasecmp(referred_by_uri, "sip:", 4)) {
00254          ast_log(LOG_WARNING, "Huh?  Not a sip: header (Referred-by: %s). Skipping.\n", referred_by_uri);
00255          referred_by_uri = (char *) NULL;
00256       } else {
00257          referred_by_uri += 4;      /* Skip sip: */
00258       }
00259    }
00260 
00261    /* Check for arguments in the refer_to header */
00262    if ((ptr = strchr(refer_to, '?'))) { /* Search for arguments */
00263       *ptr++ = '\0';
00264       if (!strncasecmp(ptr, "REPLACES=", 9)) {
00265          char *to = NULL, *from = NULL;
00266 
00267          /* This is an attended transfer */
00268          referdata->attendedtransfer = 1;
00269          strncpy(referdata->replaces_callid, ptr+9, sizeof(referdata->replaces_callid));
00270          ast_uri_decode(referdata->replaces_callid);
00271          if ((ptr = strchr(referdata->replaces_callid, ';')))  /* Find options */ {
00272             *ptr++ = '\0';
00273          }
00274 
00275          if (ptr) {
00276             /* Find the different tags before we destroy the string */
00277             to = strcasestr(ptr, "to-tag=");
00278             from = strcasestr(ptr, "from-tag=");
00279          }
00280 
00281          /* Grab the to header */
00282          if (to) {
00283             ptr = to + 7;
00284             if ((to = strchr(ptr, '&')))
00285                *to = '\0';
00286             if ((to = strchr(ptr, ';')))
00287                *to = '\0';
00288             ast_copy_string(referdata->replaces_callid_totag, ptr, sizeof(referdata->replaces_callid_totag));
00289          }
00290 
00291          if (from) {
00292             ptr = from + 9;
00293             if ((to = strchr(ptr, '&')))
00294                *to = '\0';
00295             if ((to = strchr(ptr, ';')))
00296                *to = '\0';
00297             ast_copy_string(referdata->replaces_callid_fromtag, ptr, sizeof(referdata->replaces_callid_fromtag));
00298          }
00299 
00300          if (option_debug > 1)
00301             ast_log(LOG_DEBUG,"Attended transfer: Will use Replace-Call-ID : %s F-tag: %s T-tag: %s\n", referdata->replaces_callid, referdata->replaces_callid_fromtag ? referdata->replaces_callid_fromtag : "<none>", referdata->replaces_callid_totag ? referdata->replaces_callid_totag : "<none>" );
00302       }
00303    }
00304    
00305    if ((ptr = strchr(refer_to, '@'))) {   /* Separate domain */
00306       char *urioption;
00307 
00308       *ptr++ = '\0';
00309       if ((urioption = strchr(ptr, ';')))
00310          *urioption++ = '\0';
00311       /* Save the domain for the dial plan */
00312       strncpy(referdata->refer_to_domain, ptr, sizeof(referdata->refer_to_domain));
00313       if (urioption)
00314          strncpy(referdata->refer_to_urioption, urioption, sizeof(referdata->refer_to_urioption));
00315    }
00316 
00317    if ((ptr = strchr(refer_to, ';')))  /* Remove options */
00318       *ptr = '\0';
00319    ast_copy_string(referdata->refer_to, refer_to, sizeof(referdata->refer_to));
00320    
00321    if (referred_by_uri) {
00322       if ((ptr = strchr(referred_by_uri, ';')))    /* Remove options */
00323          *ptr = '\0';
00324       ast_copy_string(referdata->referred_by, referred_by_uri, sizeof(referdata->referred_by));
00325    } else {
00326       referdata->referred_by[0] = '\0';
00327    }
00328 
00329    /* Determine transfer context */
00330    if (transferer->owner)  /* Mimic behaviour in res_features.c */
00331       transfer_context = pbx_builtin_getvar_helper(transferer->owner, "TRANSFER_CONTEXT");
00332 
00333    /* By default, use the context in the channel sending the REFER */
00334    if (ast_strlen_zero(transfer_context)) {
00335       transfer_context = S_OR(transferer->owner->macrocontext,
00336                S_OR(transferer->context, global.default_context));
00337    }
00338 
00339    strncpy(referdata->refer_to_context, transfer_context, sizeof(referdata->refer_to_context));
00340    
00341    /* Either an existing extension or the parking extension */
00342    if (ast_exists_extension(NULL, transfer_context, refer_to, 1, NULL) ) {
00343       if (sip_debug_test_pvt(transferer)) {
00344          ast_verbose("SIP transfer to extension %s@%s by %s\n", refer_to, transfer_context, referred_by_uri);
00345       }
00346       /* We are ready to transfer to the extension */
00347       return 0;
00348    } 
00349    if (sip_debug_test_pvt(transferer))
00350       ast_verbose("Failed SIP Transfer to non-existing extension %s in context %s\n n", refer_to, transfer_context);
00351 
00352    /* Failure, we can't find this extension */
00353    return -1;
00354 }

int handle_request_refer struct sip_dialog p,
struct sip_request req,
int  debug,
int  seqno,
int *  nounlock
 

Definition at line 708 of file sip3_refer.c.

References sip_globals::allow_external_domains, sip_dialog::allowtransfer, append_history, ast_async_goto(), ast_bridged_channel(), AST_CAUSE_NORMAL_CLEARING, ast_channel_unlock, ast_clear_flag, AST_CONTROL_UNHOLD, ast_log(), ast_parking_ext(), ast_queue_control(), ast_set_flag, ast_string_field_set, ast_strlen_zero(), ast_test_flag, ast_uri_encode(), ast_verbose(), sip_refer::attendedtransfer, sip_dual::chan1, sip_dual::chan2, check_sip_domain(), context, sip_globals::default_context, domains_configured(), FALSE, sip_dialog::flags, get_refer_info(), global, ast_channel::hangupcause, local_attended_transfer(), sip_refer::localtransfer, LOG_DEBUG, option_debug, sip_dialog::owner, pbx_builtin_setvar_helper(), sip_dialog::refer, REFER_200OK, REFER_FAILED, REFER_SENT, sip_refer::refer_to, sip_refer::refer_to_context, sip_refer::refer_to_domain, sip_refer::referred_by, sip_refer::replaces_callid, sip_refer::replaces_callid_fromtag, sip_refer::replaces_callid_totag, sip_dual::req, SIP_ALREADYGONE, SIP_DEFER_BYE_ON_TRANSFER, SIP_GOTREFER, SIP_NEEDDESTROY, SIP_OUTGOING, sip_park(), SIP_PKT_DEBUG, SIP_PKT_IGNORE, sip_refer_allocate(), sipdebug, sip_refer::status, TRANSFER_CLOSED, transmit_notify_with_sipfrag(), transmit_response(), and TRUE.

00709 {
00710    struct sip_dual current;   /* Chan1: Call between asterisk and transferer */
00711                /* Chan2: Call between asterisk and transferee */
00712 
00713    int res = 0;
00714 
00715    if (ast_test_flag(req, SIP_PKT_DEBUG))
00716       ast_verbose("Call %s got a SIP call transfer from %s: (REFER)!\n", p->callid, ast_test_flag(&p->flags[0], SIP_OUTGOING) ? "callee" : "caller");
00717 
00718    if (!p->owner) {
00719       /* This is a REFER outside of an existing SIP dialog */
00720       /* We can't handle that, so decline it */
00721       if (option_debug > 2)
00722          ast_log(LOG_DEBUG, "Call %s: Declined REFER, outside of dialog...\n", p->callid);
00723       transmit_response(p, "603 Declined (No dialog)", req);
00724       if (!ast_test_flag(req, SIP_PKT_IGNORE)) {
00725          append_history(p, "Xfer", "Refer failed. Outside of dialog.");
00726          ast_set_flag(&p->flags[0], SIP_ALREADYGONE); 
00727          ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); 
00728       }
00729       return 0;
00730    }  
00731 
00732 
00733    /* Check if transfer is allowed from this device */
00734    if (p->allowtransfer == TRANSFER_CLOSED ) {
00735       /* Transfer not allowed, decline */
00736       transmit_response(p, "603 Declined (policy)", req);
00737       append_history(p, "Xfer", "Refer failed. Allowtransfer == closed.");
00738       /* Do not destroy SIP session */
00739       return 0;
00740    }
00741 
00742    if(!ast_test_flag(req, SIP_PKT_IGNORE) && ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
00743       /* Already have a pending REFER */  
00744       transmit_response(p, "491 Request pending", req);
00745       append_history(p, "Xfer", "Refer failed. Request pending.");
00746       return 0;
00747    }
00748 
00749    /* Allocate memory for call transfer data */
00750    if (!p->refer && !sip_refer_allocate(p)) {
00751       transmit_response(p, "500 Internal Server Error", req);
00752       append_history(p, "Xfer", "Refer failed. Memory allocation error.");
00753       return -3;
00754    }
00755 
00756    res = get_refer_info(p, req); /* Extract headers */
00757 
00758    p->refer->status = REFER_SENT;
00759 
00760    if (res != 0) {
00761       switch (res) {
00762       case -2: /* Syntax error */
00763          transmit_response(p, "400 Bad Request (Refer-to missing)", req);
00764          append_history(p, "Xfer", "Refer failed. Refer-to missing.");
00765          if (ast_test_flag(req, SIP_PKT_DEBUG) && option_debug)
00766             ast_log(LOG_DEBUG, "SIP transfer to black hole can't be handled (no refer-to: )\n");
00767          break;
00768       case -3:
00769          transmit_response(p, "603 Declined (Non sip: uri)", req);
00770          append_history(p, "Xfer", "Refer failed. Non SIP uri");
00771          if (ast_test_flag(req, SIP_PKT_DEBUG) && option_debug)
00772             ast_log(LOG_DEBUG, "SIP transfer to non-SIP uri denied\n");
00773          break;
00774       default:
00775          /* Refer-to extension not found, fake a failed transfer */
00776          transmit_response(p, "202 Accepted", req);
00777          append_history(p, "Xfer", "Refer failed. Bad extension.");
00778          transmit_notify_with_sipfrag(p, seqno, "404 Not found", TRUE);
00779          ast_clear_flag(&p->flags[0], SIP_GOTREFER);  
00780          if (ast_test_flag(req, SIP_PKT_DEBUG) && option_debug)
00781             ast_log(LOG_DEBUG, "SIP transfer to bad extension: %s\n", p->refer->refer_to);
00782          break;
00783       } 
00784       return 0;
00785    }
00786    if (ast_strlen_zero(p->context))
00787       ast_string_field_set(p, context, global.default_context);
00788 
00789    /* If we do not support SIP domains, all transfers are local */
00790    if (global.allow_external_domains && check_sip_domain(p->refer->refer_to_domain, NULL, 0)) {
00791       p->refer->localtransfer = 1;
00792       if (sipdebug && option_debug > 2)
00793          ast_log(LOG_DEBUG, "This SIP transfer is local : %s\n", p->refer->refer_to_domain);
00794    } else if (domains_configured()) {
00795       /* This PBX don't bother with SIP domains, so all transfers are local */
00796       p->refer->localtransfer = 1;
00797    } else
00798       if (sipdebug && option_debug > 2)
00799          ast_log(LOG_DEBUG, "This SIP transfer is to a remote SIP extension (remote domain %s)\n", p->refer->refer_to_domain);
00800    
00801    /* Is this a repeat of a current request? Ignore it */
00802    /* Don't know what else to do right now. */
00803    if (ast_test_flag(req, SIP_PKT_IGNORE)) 
00804       return res;
00805 
00806    /* If this is a blind transfer, we have the following
00807       channels to work with:
00808       - chan1, chan2: The current call between transferer and transferee (2 channels)
00809       - target_channel: A new call from the transferee to the target (1 channel)
00810       We need to stay tuned to what happens in order to be able
00811       to bring back the call to the transferer */
00812 
00813    /* If this is a attended transfer, we should have all call legs within reach:
00814       - chan1, chan2: The call between the transferer and transferee (2 channels)
00815       - target_channel, targetcall_pvt: The call between the transferer and the target (2 channels)
00816    We want to bridge chan2 with targetcall_pvt!
00817    
00818       The replaces call id in the refer message points
00819       to the call leg between Asterisk and the transferer.
00820       So we need to connect the target and the transferee channel
00821       and hangup the two other channels silently 
00822    
00823       If the target is non-local, the call ID could be on a remote
00824       machine and we need to send an INVITE with replaces to the
00825       target. We basically handle this as a blind transfer
00826       and let the sip_call function catch that we need replaces
00827       header in the INVITE.
00828    */
00829 
00830 
00831    /* Get the transferer's channel */
00832    current.chan1 = p->owner;
00833 
00834    /* Find the other part of the bridge (2) - transferee */
00835    current.chan2 = ast_bridged_channel(current.chan1);
00836    
00837    if (sipdebug && option_debug > 2)
00838       ast_log(LOG_DEBUG, "SIP %s transfer: Transferer channel %s, transferee channel %s\n", p->refer->attendedtransfer ? "attended" : "blind", current.chan1->name, current.chan2 ? current.chan2->name : "<none>");
00839 
00840    if (!current.chan2 && !p->refer->attendedtransfer) {
00841       /* No bridged channel, propably IVR or echo or similar... */
00842       /* Guess we should masquerade or something here */
00843       /* Until we figure it out, refuse transfer of such calls */
00844       if (sipdebug && option_debug > 2)
00845          ast_log(LOG_DEBUG,"Refused SIP transfer on non-bridged channel.\n");
00846       p->refer->status = REFER_FAILED;
00847       append_history(p, "Xfer", "Refer failed. Non-bridged channel.");
00848       transmit_response(p, "603 Declined", req);
00849       return -1;
00850    }
00851 
00852    if (current.chan2) {
00853       if (sipdebug && option_debug > 3)
00854          ast_log(LOG_DEBUG, "Got SIP transfer, applying to bridged peer '%s'\n", current.chan2->name);
00855 
00856       ast_queue_control(current.chan1, AST_CONTROL_UNHOLD);
00857    }
00858 
00859    ast_set_flag(&p->flags[0], SIP_GOTREFER); 
00860 
00861    /* Attended transfer: Find all call legs and bridge transferee with target*/
00862    if (p->refer->attendedtransfer) {
00863       if ((res = local_attended_transfer(p, &current, req, seqno)))
00864          return res; /* We're done with the transfer */
00865       /* Fall through for remote transfers that we did not find locally */
00866       if (sipdebug && option_debug > 3)
00867          ast_log(LOG_DEBUG, "SIP attended transfer: Still not our call - generating INVITE with replaces\n");
00868       /* Fallthrough if we can't find the call leg internally */
00869    }
00870 
00871 
00872    /* Parking a call */
00873    if (p->refer->localtransfer && !strcmp(p->refer->refer_to, ast_parking_ext())) {
00874       /* Must release c's lock now, because it will not longer be accessible after the transfer! */
00875       *nounlock = 1;
00876       ast_channel_unlock(current.chan1);
00877       current.req = req;
00878       ast_clear_flag(&p->flags[0], SIP_GOTREFER);  
00879       p->refer->status = REFER_200OK;
00880       append_history(p, "Xfer", "REFER to call parking.");
00881       if (sipdebug && option_debug > 3)
00882          ast_log(LOG_DEBUG, "SIP transfer to parking: trying to park %s. Parked by %s\n", current.chan2->name, current.chan1->name);
00883       sip_park(current.chan2, current.chan1, req, seqno);
00884       return res;
00885    } 
00886 
00887    /* Blind transfers and remote attended xfers */
00888    transmit_response(p, "202 Accepted", req);
00889 
00890    if (current.chan1 && current.chan2) {
00891       if (option_debug > 2)
00892          ast_log(LOG_DEBUG, "chan1->name: %s\n", current.chan1->name);
00893       pbx_builtin_setvar_helper(current.chan1, "BLINDTRANSFER", current.chan2->name);
00894    }
00895    if (current.chan2) {
00896       pbx_builtin_setvar_helper(current.chan2, "BLINDTRANSFER", current.chan1->name);
00897       pbx_builtin_setvar_helper(current.chan2, "SIPDOMAIN", p->refer->refer_to_domain);
00898       pbx_builtin_setvar_helper(current.chan2, "SIPTRANSFER", "yes");
00899       /* One for the new channel */
00900       pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER", "yes");
00901       if (p->refer->referred_by)
00902          pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REFERER", p->refer->referred_by);
00903       if (p->refer->referred_by)
00904       /* Attended transfer to remote host, prepare headers for the INVITE */
00905       pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REFERER", p->refer->referred_by);
00906    }
00907    /* Generate an URI-encoded string */
00908    if (p->refer->replaces_callid && !ast_strlen_zero(p->refer->replaces_callid)) {
00909       char tempheader[BUFSIZ];
00910       char tempheader2[BUFSIZ];
00911       snprintf(tempheader, sizeof(tempheader), "%s%s%s%s%s", p->refer->replaces_callid, 
00912             p->refer->replaces_callid_totag ? ";to-tag=" : "", 
00913             p->refer->replaces_callid_totag, 
00914             p->refer->replaces_callid_fromtag ? ";from-tag=" : "",
00915             p->refer->replaces_callid_fromtag);
00916 
00917       /* Convert it to URL encoding, also convert reserved strings */
00918       ast_uri_encode(tempheader, tempheader2, sizeof(tempheader2), 1);
00919 
00920       if (current.chan2)
00921          pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REPLACES", tempheader2);
00922    }
00923    /* Must release lock now, because it will not longer
00924          be accessible after the transfer! */
00925    *nounlock = 1;
00926    ast_channel_unlock(current.chan1);
00927    ast_channel_unlock(current.chan2);
00928 
00929    /* Connect the call */
00930 
00931    /* FAKE ringing if not attended transfer */
00932    if (!p->refer->attendedtransfer)
00933       transmit_notify_with_sipfrag(p, seqno, "183 Ringing", FALSE);
00934       
00935    /* For blind transfer, this will lead to a new call */
00936    /* For attended transfer to remote host, this will lead to
00937          a new SIP call with a replaces header, if the dial plan allows it 
00938    */
00939    if (!current.chan2) {
00940       /* We have no bridge, so we're talking with Asterisk somehow */
00941       /* We need to masquerade this call */
00942       /* What to do to fix this situation:
00943          * Set up the new call in a new channel 
00944          * Let the new channel masq into this channel
00945          Please add that code here :-)
00946       */
00947       transmit_response(p, "202 Accepted", req);
00948       p->refer->status = REFER_FAILED;
00949       transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable (can't handle one-legged xfers)", TRUE);
00950       ast_clear_flag(&p->flags[0], SIP_GOTREFER);  
00951       append_history(p, "Xfer", "Refer failed (only bridged calls).");
00952       return -1;
00953    }
00954    ast_set_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);   /* Delay hangup */
00955 
00956    /* For blind transfers, move the call to the new extensions. For attended transfers on multiple
00957       servers - generate an INVITE with Replaces. Either way, let the dial plan decided  */
00958    res = ast_async_goto(current.chan2, p->refer->refer_to_context, p->refer->refer_to, 1);
00959 
00960    if (!res) {
00961       /* Success  - we have a new channel */
00962       if (option_debug > 2)
00963          ast_log(LOG_DEBUG, "%s transfer succeeded. Telling transferer.\n", p->refer->attendedtransfer? "Attended" : "Blind");
00964       transmit_notify_with_sipfrag(p, seqno, "200 Ok", TRUE);
00965       if (p->refer->localtransfer)
00966          p->refer->status = REFER_200OK;
00967       if (p->owner)
00968          p->owner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
00969       append_history(p, "Xfer", "Refer succeeded.");
00970       ast_clear_flag(&p->flags[0], SIP_GOTREFER);  
00971       /* Do not hangup call, the other side do that when we say 200 OK */
00972       /* We could possibly implement a timer here, auto congestion */
00973       res = 0;
00974    } else {
00975       ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Don't delay hangup */
00976       if (option_debug > 2)
00977          ast_log(LOG_DEBUG, "%s transfer failed. Resuming original call.\n", p->refer->attendedtransfer? "Attended" : "Blind");
00978       append_history(p, "Xfer", "Refer failed.");
00979       /* Failure of some kind */
00980       p->refer->status = REFER_FAILED;
00981       transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable", TRUE);
00982       ast_clear_flag(&p->flags[0], SIP_GOTREFER);  
00983       res = -1;
00984    }
00985    return res;
00986 }

static int local_attended_transfer struct sip_dialog transferer,
struct sip_dual current,
struct sip_request req,
int  seqno
[static]
 

Find all call legs and bridge transferee with target called from handle_request_refer.

Definition at line 539 of file sip3_refer.c.

References ast_channel::_state, append_history, ast_bridged_channel(), ast_channel_unlock, ast_clear_flag, ast_hangup(), ast_log(), ast_set_flag, ast_state2str(), AST_STATE_RING, AST_STATE_RINGING, AST_STATE_UP, attempt_transfer(), sip_dual::chan1, sip_dual::chan2, dialog_lock(), error(), FALSE, sip_dialog::flags, get_sip_dialog_byid_locked(), sip_refer::localtransfer, LOG_DEBUG, option_debug, sip_dialog::owner, sip_dialog::refer, REFER_200OK, REFER_FAILED, sip_refer::replaces_callid, sip_refer::replaces_callid_fromtag, sip_refer::replaces_callid_totag, SIP_DEFER_BYE_ON_TRANSFER, SIP_GOTREFER, sipdebug, sip_refer::status, transmit_notify_with_sipfrag(), transmit_response(), and TRUE.

00540 {
00541    struct sip_dual target;    /* Chan 1: Call from tranferer to Asterisk */
00542                /* Chan 2: Call from Asterisk to target */
00543    int res = 0;
00544    struct sip_dialog *targetcall_pvt;
00545    int error = 0;
00546 
00547    /* Check if the call ID of the replaces header does exist locally */
00548    if (!(targetcall_pvt = get_sip_dialog_byid_locked(transferer->refer->replaces_callid, transferer->refer->replaces_callid_totag, 
00549       transferer->refer->replaces_callid_fromtag))) {
00550       if (transferer->refer->localtransfer) {
00551          /* We did not find the refered call. Sorry, can't accept then */
00552          transmit_response(transferer, "202 Accepted", req);
00553          /* Let's fake a response from someone else in order
00554             to follow the standard */
00555          transmit_notify_with_sipfrag(transferer, seqno, "481 Call leg/transaction does not exist", TRUE);
00556          append_history(transferer, "Xfer", "Refer failed");
00557          ast_clear_flag(&transferer->flags[0], SIP_GOTREFER);  
00558          transferer->refer->status = REFER_FAILED;
00559          return -1;
00560       }
00561       /* Fall through for remote transfers that we did not find locally */
00562       if (option_debug > 2)
00563          ast_log(LOG_DEBUG, "SIP attended transfer: Not our call - generating INVITE with replaces\n");
00564       return 0;
00565    }
00566 
00567    /* Ok, we can accept this transfer */
00568    transmit_response(transferer, "202 Accepted", req);
00569    append_history(transferer, "Xfer", "Refer accepted");
00570    if (!targetcall_pvt->owner) { /* No active channel */
00571       if (option_debug > 3)
00572          ast_log(LOG_DEBUG, "SIP attended transfer: Error: No owner of target call\n");
00573       error = 1;
00574    }
00575    /* We have a channel, find the bridge */
00576    target.chan1 = targetcall_pvt->owner;           /* Transferer to Asterisk */
00577 
00578    if (!error) {
00579       target.chan2 = ast_bridged_channel(targetcall_pvt->owner);  /* Asterisk to target */
00580 
00581       if (!target.chan2 || !(target.chan2->_state == AST_STATE_UP || target.chan2->_state == AST_STATE_RINGING) ) {
00582          /* Wrong state of new channel */
00583          if (option_debug > 3) {
00584             if (target.chan2) 
00585                ast_log(LOG_DEBUG, "SIP attended transfer: Error: Wrong state of target call: %s\n", ast_state2str(target.chan2->_state));
00586             else if (target.chan1->_state != AST_STATE_RING)
00587                ast_log(LOG_DEBUG, "SIP attended transfer: Error: No target channel\n");
00588             else
00589                ast_log(LOG_DEBUG, "SIP attended transfer: Attempting transfer in ringing state\n");
00590          }
00591          if (target.chan1->_state != AST_STATE_RING)
00592             error = 1;
00593       }
00594    }
00595    if (error) {   /* Cancel transfer */
00596       transmit_notify_with_sipfrag(transferer, seqno, "503 Service Unavailable", TRUE);
00597       append_history(transferer, "Xfer", "Refer failed");
00598       ast_clear_flag(&transferer->flags[0], SIP_GOTREFER);  
00599       transferer->refer->status = REFER_FAILED;
00600       dialog_lock(targetcall_pvt, FALSE);
00601       ast_channel_unlock(current->chan1);
00602       ast_channel_unlock(target.chan1);
00603       return -1;
00604    }
00605 
00606    /* Transfer */
00607    if (option_debug > 3 && sipdebug) {
00608       if (current->chan2)  /* We have two bridges */
00609          ast_log(LOG_DEBUG, "SIP attended transfer: trying to bridge %s and %s\n", target.chan1->name, current->chan2->name);
00610       else        /* One bridge, propably transfer of IVR/voicemail etc */
00611          ast_log(LOG_DEBUG, "SIP attended transfer: trying to make %s take over (masq) %s\n", target.chan1->name, current->chan1->name);
00612    }
00613 
00614    ast_set_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER);   /* Delay hangup */
00615 
00616    /* Perform the transfer */
00617    res = attempt_transfer(current, &target);
00618    dialog_lock(targetcall_pvt, FALSE);
00619    if (res) {
00620       /* Failed transfer */
00621       /* Could find better message, but they will get the point */
00622       transmit_notify_with_sipfrag(transferer, seqno, "486 Busy", TRUE);
00623       append_history(transferer, "Xfer", "Refer failed");
00624       if (targetcall_pvt->owner)
00625          ast_channel_unlock(targetcall_pvt->owner);
00626       /* Right now, we have to hangup, sorry. Bridge is destroyed */
00627       ast_hangup(transferer->owner);
00628    } else {
00629       /* Transfer succeeded! */
00630 
00631       /* Tell transferer that we're done. */
00632       transmit_notify_with_sipfrag(transferer, seqno, "200 OK", TRUE);
00633       append_history(transferer, "Xfer", "Refer succeeded");
00634       transferer->refer->status = REFER_200OK;
00635       if (targetcall_pvt->owner) {
00636          if (option_debug)
00637             ast_log(LOG_DEBUG, "SIP attended transfer: Unlocking channel %s\n", targetcall_pvt->owner->name);
00638          ast_channel_unlock(targetcall_pvt->owner);
00639       }
00640    }
00641    return 1;
00642 }

const char* referstatus2str enum referstatus  rstatus  ) 
 

Convert transfer status to string.

sip3_refer.c

Definition at line 109 of file sip3_refer.c.

References referstatusstrings.

00110 {
00111    int i = (sizeof(referstatusstrings) / sizeof(referstatusstrings[0]));
00112    int x;
00113 
00114    for (x = 0; x < i; x++) {
00115       if (referstatusstrings[x].status ==  rstatus)
00116          return (char *) referstatusstrings[x].text;
00117    }
00118    return "";
00119 }

static int sip_park struct ast_channel chan1,
struct ast_channel chan2,
struct sip_request req,
int  seqno
[static]
 

Park a call using the subsystem in res_features.c This is executed in a separate thread.

Definition at line 360 of file sip3_refer.c.

References ast_calloc, AST_CAUSE_SWITCH_CONGESTION, ast_channel_alloc(), ast_channel_lock, ast_channel_masquerade(), ast_channel_unlock, ast_do_masquerade(), ast_hangup(), ast_log(), ast_pthread_create_background, AST_STATE_DOWN, ast_channel::context, ast_channel::exten, free, ast_channel::hangupcause, LOG_DEBUG, LOG_WARNING, option_debug, ast_channel::priority, ast_channel::readformat, sip_park_thread(), and ast_channel::writeformat.

00361 {
00362    struct sip_dual *d;
00363    struct ast_channel *transferee, *transferer;
00364       /* Chan2m: The transferer, chan1m: The transferee */
00365    pthread_t th;
00366 
00367    transferee = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "Parking/%s", chan1->name);
00368    transferer = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "SIPPeer/%s", chan2->name);
00369    if ((!transferer) || (!transferee)) {
00370       if (transferee) {
00371          transferee->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
00372          ast_hangup(transferee);
00373       }
00374       if (transferer) {
00375          transferer->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
00376          ast_hangup(transferer);
00377       }
00378       return -1;
00379    }
00380 
00381    /* Make formats okay */
00382    transferee->readformat = chan1->readformat;
00383    transferee->writeformat = chan1->writeformat;
00384 
00385    /* Prepare for taking over the channel */
00386    ast_channel_masquerade(transferee, chan1);
00387 
00388    /* Setup the extensions and such */
00389    ast_copy_string(transferee->context, chan1->context, sizeof(transferee->context));
00390    ast_copy_string(transferee->exten, chan1->exten, sizeof(transferee->exten));
00391    transferee->priority = chan1->priority;
00392       
00393    /* We make a clone of the peer channel too, so we can play
00394       back the announcement */
00395 
00396    /* Make formats okay */
00397    transferer->readformat = chan2->readformat;
00398    transferer->writeformat = chan2->writeformat;
00399 
00400    /* Prepare for taking over the channel */
00401    ast_channel_masquerade(transferer, chan2);
00402 
00403    /* Setup the extensions and such */
00404    ast_copy_string(transferer->context, chan2->context, sizeof(transferer->context));
00405    ast_copy_string(transferer->exten, chan2->exten, sizeof(transferer->exten));
00406    transferer->priority = chan2->priority;
00407 
00408    ast_channel_lock(transferer);
00409    if (ast_do_masquerade(transferer)) {
00410       ast_log(LOG_WARNING, "Masquerade failed :(\n");
00411       ast_channel_unlock(transferer);
00412       transferer->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
00413       ast_hangup(transferer);
00414       return -1;
00415    }
00416    ast_channel_unlock(transferer);
00417    if (!transferer || !transferee) {
00418       if (!transferer) { 
00419          if (option_debug)
00420             ast_log(LOG_DEBUG, "No transferer channel, giving up parking\n");
00421       }
00422       if (!transferee) {
00423          if (option_debug)
00424             ast_log(LOG_DEBUG, "No transferee channel, giving up parking\n");
00425       }
00426       return -1;
00427    }
00428    if ((d = ast_calloc(1, sizeof(*d)))) {
00429       pthread_attr_t attr;
00430       
00431       pthread_attr_init(&attr);
00432       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00433 
00434       /* Save original request for followup */
00435       d->req = req;
00436       d->chan1 = transferee;  /* Transferee */
00437       d->chan2 = transferer;  /* Transferer */
00438       d->seqno = seqno;
00439       if (ast_pthread_create_background(&th, &attr, sip_park_thread, d) < 0) {
00440          /* Could not start thread */
00441          free(d); /* We don't need it anymore. If thread is created, d will be free'd
00442                   by sip_park_thread() */
00443          return 0;
00444       }
00445    } 
00446    return -1;
00447 }

static void* sip_park_thread void *  stuff  )  [static]
 

Park SIP call support function Starts in a new thread, then parks the call XXX Should we add a wait period after streaming audio and before hangup?? Sometimes the audio can't be heard before hangup.

Definition at line 133 of file sip3_refer.c.

References append_history, AST_CAUSE_NORMAL_CLEARING, ast_channel_lock, ast_channel_unlock, ast_do_masquerade(), ast_hangup(), ast_log(), ast_park_call(), sip_dual::chan1, sip_dual::chan2, ext, free, ast_channel::hangupcause, LOG_DEBUG, LOG_ERROR, LOG_WARNING, option_debug, sip_dual::req, sip_dual::seqno, siprequest_free(), ast_channel::tech_pvt, transmit_message_with_text(), transmit_notify_with_sipfrag(), transmit_response(), and TRUE.

00134 {
00135    struct ast_channel *transferee, *transferer; /* Chan1: The transferee, Chan2: The transferer */
00136    struct sip_dual *d;
00137    struct sip_request *req;
00138    int ext;
00139    int res;
00140 
00141    d = stuff;
00142    transferee = d->chan1;
00143    transferer = d->chan2;
00144    
00145    req = d->req;
00146    free(d);
00147 
00148    if (!transferee || !transferer) {
00149       ast_log(LOG_ERROR, "Missing channels for parking! Transferer %s Transferee %s\n", transferer ? "<available>" : "<missing>", transferee ? "<available>" : "<missing>" );
00150       return NULL;
00151    }
00152    if (option_debug > 3) 
00153       ast_log(LOG_DEBUG, "SIP Park: Transferer channel %s, Transferee %s\n", transferer->name, transferee->name);
00154 
00155    ast_channel_lock(transferee);
00156    if (ast_do_masquerade(transferee)) {
00157       ast_log(LOG_WARNING, "Masquerade failed.\n");
00158       transmit_response(transferer->tech_pvt, "503 Internal error", req);
00159       ast_channel_unlock(transferee);
00160       
00161       return NULL;
00162    } 
00163    ast_channel_unlock(transferee);
00164 
00165    res = ast_park_call(transferee, transferer, 0, &ext);
00166    
00167 
00168 #ifdef WHEN_WE_KNOW_THAT_THE_CLIENT_SUPPORTS_MESSAGE
00169    if (!res) {
00170       transmit_message_with_text(transferer->tech_pvt, "Unable to park call.\n");
00171    } else {
00172       /* Then tell the transferer what happened */
00173       sprintf(buf, "Call parked on extension '%d'", ext);
00174       transmit_message_with_text(transferer->tech_pvt, buf);
00175    }
00176 #endif
00177 
00178    /* Any way back to the current call??? */
00179    /* Transmit response to the REFER request */
00180    transmit_response(transferer->tech_pvt, "202 Accepted", req);
00181 
00182    if (!res)   {
00183       /* Transfer succeeded */
00184       append_history(transferer->tech_pvt, "SIPpark","Parked call on %d", ext);
00185       transmit_notify_with_sipfrag(transferer->tech_pvt, d->seqno, "200 OK", TRUE);
00186       transferer->hangupcause = AST_CAUSE_NORMAL_CLEARING;
00187       ast_hangup(transferer); /* This will cause a BYE */
00188       if (option_debug)
00189          ast_log(LOG_DEBUG, "SIP Call parked on extension '%d'\n", ext);
00190    } else {
00191       transmit_notify_with_sipfrag(transferer->tech_pvt, d->seqno, "503 Service Unavailable", TRUE);
00192       append_history(transferer->tech_pvt, "SIPpark","Parking failed\n");
00193       if (option_debug)
00194          ast_log(LOG_DEBUG, "SIP Call parked failed \n");
00195       /* Do not hangup call */
00196    }
00197    siprequest_free(req);
00198    return NULL;
00199 }

int sip_refer_allocate struct sip_dialog p  ) 
 

Allocate SIP refer structure.

Definition at line 122 of file sip3_refer.c.

References ast_calloc, and sip_dialog::refer.

00123 {
00124    p->refer = ast_calloc(1, sizeof(struct sip_refer)); 
00125    return p->refer ? 1 : 0;
00126 }

GNURK int transmit_notify_with_sipfrag struct sip_dialog p,
int  cseq,
char *  message,
int  terminate
 

Notify a transferring party of the status of transfer.

Definition at line 989 of file sip3_refer.c.

References add_header(), add_header_contentLength(), add_line(), ALLOWED_METHODS, initialize_initreq(), sip_dialog::initreq, reqprep(), send_request(), SIP_MAX_PACKET, SIP_NOTIFY, sipnet, siprequest_alloc(), SUPPORTED_EXTENSIONS, TRUE, and XMIT_RELIABLE.

00990 {
00991    struct sip_request *req;
00992    char tmp[BUFSIZ/2];
00993 
00994    req = siprequest_alloc(SIP_MAX_PACKET, &sipnet);
00995 
00996    reqprep(req, p, SIP_NOTIFY, 0, TRUE);
00997    snprintf(tmp, sizeof(tmp), "refer;id=%d", cseq);
00998    add_header(req, "Event", tmp);
00999    add_header(req, "Subscription-state", terminate ? "terminated;reason=noresource" : "active");
01000    add_header(req, "Content-Type", "message/sipfrag;version=2.0");
01001    add_header(req, "Allow", ALLOWED_METHODS);
01002    add_header(req, "Supported", SUPPORTED_EXTENSIONS);
01003 
01004    snprintf(tmp, sizeof(tmp), "SIP/2.0 %s\r\n", message);
01005    add_header_contentLength(req, strlen(tmp));
01006    add_line(req, tmp);
01007 
01008    if (!p->initreq)
01009       initialize_initreq(p, req);
01010 
01011    return send_request(p, req, XMIT_RELIABLE);
01012 }


Variable Documentation

const struct c_referstatusstring referstatusstrings[] [static]
 

Table to convert from REFER status variable to string.


Asterisk is a trademark for Digium, inc.. | Edvina.net | Asterisk.org | This documentation was generated with Doxygen