Index: channels/chan_sip.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v retrieving revision 1.868 diff -u -r1.868 chan_sip.c --- channels/chan_sip.c 27 Sep 2005 02:12:36 -0000 1.868 +++ channels/chan_sip.c 27 Sep 2005 07:38:01 -0000 @@ -141,6 +141,8 @@ #define RTP 1 #define NO_RTP 0 +#define SIP_RETVAL_IGNORE 1 + /* Do _NOT_ make any changes to this enum, or the array following it; if you think you are doing the right thing, you are probably not doing the right thing. If you think there are changes @@ -898,6 +900,7 @@ static void sip_dump_history(struct sip_pvt *dialog); /* Dump history to LOG_DEBUG at end of dialog, before destroying data */ static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype); static int transmit_state_notify(struct sip_pvt *p, int state, int full, int substate); +static int sip_addheader(struct ast_channel *chan, void *data); /* Definition of this channel for channel registration */ static const struct ast_channel_tech sip_tech = { @@ -938,6 +941,21 @@ return res; } +/* --- sip_extract_tag: extract from and to tags from a callid ---*/ +static int sip_extract_tag(char **in) +{ + char *tag; + + if ((tag = strcasestr(*in, "tag="))) { + char *ptr; + tag += 4; + if ((ptr = strchr(tag, ';'))) + *ptr = '\0'; + return 0; + } + return -1; +} + /*--- parse_sip_options: Parse supported header in incoming packet */ unsigned int parse_sip_options(struct sip_pvt *pvt, char *supported) { @@ -1870,9 +1888,9 @@ struct ast_hostent ahp; struct sip_peer *p; int found=0; - char *port; + char *port, *ptr, *hostp, *hostn; int portno; - char host[MAXHOSTNAMELEN], *hostn; + char host[MAXHOSTNAMELEN]; char peer[256]; ast_copy_string(peer, opeer, sizeof(peer)); @@ -1910,7 +1928,13 @@ portno = tportno; } } - hp = ast_gethostbyname(hostn, &ahp); + if ((hostp = ast_strdupa(hostn))) { + if ((ptr = strchr(hostp, '?'))) + *ptr = '\0'; + } else + hostp = peer; + + hp = ast_gethostbyname(hostp, &ahp); if (hp) { ast_copy_string(dialog->tohost, peer, sizeof(dialog->tohost)); memcpy(&dialog->sa.sin_addr, hp->h_addr, sizeof(dialog->sa.sin_addr)); @@ -4021,6 +4045,9 @@ add_header(req, "From", ot); add_header(req, "To", of); } +// if (sipmethod == SIP_MESSAGE) /* Add date header to MESSAGE */ +// append_date(req); +// else add_header(req, "Contact", p->our_contact); copy_header(req, orig, "Call-ID"); add_header(req, "CSeq", tmp); @@ -4832,6 +4859,7 @@ enum state { NOTIFY_OPEN, NOTIFY_INUSE, NOTIFY_CLOSED } local_state = NOTIFY_OPEN; char *pidfstate = "--"; char *pidfnote= "Ready"; + struct sip_pvt *np = NULL; switch (state) { case (AST_EXTENSION_RINGING | AST_EXTENSION_INUSE): @@ -4963,11 +4991,36 @@ case DIALOG_INFO_XML: /* SNOM subscribes in this format */ ast_build_string(&t, &maxbytes, "\n"); ast_build_string(&t, &maxbytes, "\n", p->dialogver++, full ? "full":"partial", mto); - if ((state & AST_EXTENSION_RINGING) && global_notifyringing) - ast_build_string(&t, &maxbytes, "\n", p->exten); - else + + if (!ast_strlen_zero(hint)) { + /* check if the device is ringing and if so get the callid to enable pickup functionality (e.g. for snom phones) */ + if ((state & AST_EXTENSION_RINGING)) { + struct ast_channel *chan = NULL; + int hintlen = strlen(hint); + + while ((chan = ast_channel_walk_locked(chan)) != NULL) { + if (chan->_state == AST_STATE_RINGING && !strncasecmp(chan->name, "SIP", 3) && chan->tech_pvt && !strncasecmp(chan->name, hint, hintlen)) { + np = chan->tech_pvt; + ast_build_string(&t, &maxbytes, "\n", p->exten, np->callid, np->tag, np->theirtag); + ast_build_string(&t, &maxbytes, "%s\n", statestring); + ast_build_string(&t, &maxbytes, "%s\n", p->exten, mfrom, mfrom); + ast_build_string(&t, &maxbytes, "%s\n", np->fromname, mto, mto); + + if (option_debug > 1) + ast_log(LOG_NOTICE, "Transmitting CallID in NOTIFY message - DialogID: %s CallID: %s\n", p->exten, np->callid); + + ast_mutex_unlock(&chan->lock); + break; + } + ast_mutex_unlock(&chan->lock); + } + } + } + + if (!np) { ast_build_string(&t, &maxbytes, "\n", p->exten); - ast_build_string(&t, &maxbytes, "%s\n", statestring); + ast_build_string(&t, &maxbytes, "%s\n", statestring); + } ast_build_string(&t, &maxbytes, "\n\n"); break; case NONE: @@ -6459,19 +6512,44 @@ } /*--- get_sip_pvt_byid_locked: Lock interface lock and find matching pvt lock ---*/ -static struct sip_pvt *get_sip_pvt_byid_locked(char *callid) +static struct sip_pvt *get_sip_pvt_byid_locked(char *callid, struct sip_request *req, char *totag, char *fromtag) { struct sip_pvt *sip_pvt_ptr = NULL; + char *real_totag = NULL, *real_fromtag = NULL; + int match = 1; /* Search interfaces and find the match */ ast_mutex_lock(&iflock); sip_pvt_ptr = iflist; - while(sip_pvt_ptr) { + while (sip_pvt_ptr) { if (!strcmp(sip_pvt_ptr->callid, callid)) { /* Go ahead and lock it (and its owner) before returning */ ast_mutex_lock(&sip_pvt_ptr->lock); + + if (req && pedanticsipchecking) { + if (totag) { + real_totag = ast_strdupa(get_header(req, "To")); + if (sip_extract_tag(&real_totag)) + real_totag = NULL; + if (strcmp(real_totag, totag)) + match = 0; + } + if (match && fromtag) { + real_fromtag = ast_strdupa(get_header(req, "From")); + if (sip_extract_tag(&real_fromtag)) + real_fromtag = NULL; + if (strcmp(real_fromtag, fromtag)) + match = 0; + } + } + + if (!match) { + ast_mutex_unlock(&sip_pvt_ptr->lock); + break; + } + if (sip_pvt_ptr->owner) { - while(ast_mutex_trylock(&sip_pvt_ptr->owner->lock)) { + while (ast_mutex_trylock(&sip_pvt_ptr->owner->lock)) { ast_mutex_unlock(&sip_pvt_ptr->lock); usleep(1); ast_mutex_lock(&sip_pvt_ptr->lock); @@ -6492,7 +6570,7 @@ { char *p_refer_to = NULL, *p_referred_by = NULL, *h_refer_to = NULL, *h_referred_by = NULL, *h_contact = NULL; - char *replace_callid = "", *refer_to = NULL, *referred_by = NULL, *ptr = NULL; + char *replace_callid = "", *refer_to = NULL, *referred_by = NULL, *ptr = NULL, *replaces_header = NULL, *refer_uri; struct sip_request *req = NULL; struct sip_pvt *sip_pvt_ptr = NULL; struct ast_channel *chan = NULL, *peer = NULL; @@ -6537,6 +6615,8 @@ if (referred_by) referred_by += 4; + refer_uri = ast_strdupa(refer_to); + if ((ptr = strchr(refer_to, '?'))) { /* Search for arguments */ *ptr = '\0'; @@ -6544,10 +6624,7 @@ if (!strncasecmp(ptr, "REPLACES=", 9)) { char *p; replace_callid = ast_strdupa(ptr + 9); - /* someday soon to support invite/replaces properly! - replaces_header = ast_strdupa(replace_callid); - -anthm - */ + replaces_header = ast_strdupa(replace_callid); ast_uri_decode(replace_callid); if ((ptr = strchr(replace_callid, '%'))) *ptr = '\0'; @@ -6585,18 +6662,47 @@ ast_copy_string(sip_pvt->referred_by, "", sizeof(sip_pvt->referred_by)); ast_copy_string(sip_pvt->refer_contact, "", sizeof(sip_pvt->refer_contact)); sip_pvt->refer_call = NULL; - if ((sip_pvt_ptr = get_sip_pvt_byid_locked(replace_callid))) { + if ((sip_pvt_ptr = get_sip_pvt_byid_locked(replace_callid, req, NULL, NULL))) { sip_pvt->refer_call = sip_pvt_ptr; if (sip_pvt->refer_call == sip_pvt) { ast_log(LOG_NOTICE, "Supervised transfer attempted to transfer into same call id (%s == %s)!\n", replace_callid, sip_pvt->callid); sip_pvt->refer_call = NULL; - } else - return 0; + } + return 0; } else { - ast_log(LOG_NOTICE, "Supervised transfer requested, but unable to find callid '%s'. Both legs must reside on Asterisk box to transfer at this time.\n", replace_callid); - /* XXX The refer_to could contain a call on an entirely different machine, requiring an - INVITE with a replaces header -anthm XXX */ - /* The only way to find out is to use the dialplan - oej */ + /* Don't ask me =0 ?, SIP made do it! */ + int cause = 0, res = -1; + struct ast_channel *ichan = NULL; + transmit_notify_with_sipfrag(sip_pvt, sip_pvt->ocseq); + if ((ptr = strchr(refer_uri, ';'))) + *ptr = '\0'; + + if ((ichan = sip_request_call("SIP", sip_pvt->owner ? sip_pvt->owner->readformat : AST_FORMAT_ULAW, refer_uri, &cause))) { + struct ast_frame *f; + char *rbuf; + if (sip_debug_test_pvt(sip_pvt)) + ast_log(LOG_DEBUG, "Going hunting for a remote INVITE/Replaces at [%s] Wish me luck!\n", refer_uri); + if ((rbuf = alloca(strlen(replaces_header) + 10))) { + sprintf(rbuf, "Replaces: %s", replaces_header); + sip_addheader(ichan, rbuf); + sip_call(ichan, refer_uri, 20000); + ast_channel_masquerade(sip_pvt->owner, ichan); + if ((f = ast_read(ichan))) { + ast_log(LOG_NOTICE, "INVITE/Replaces successful for URI %s\n", refer_uri); + ast_frfree(f); + transmit_response(sip_pvt, "202 Accepted", req); + res = SIP_RETVAL_IGNORE; /* means do nothing more */ + } else + res = -1; + } else { + ast_log(LOG_ERROR,"Memory Error!\n"); + res = -1; + } + + ast_hangup(ichan); + } else + res = -1; + return res; } } else if (ast_exists_extension(NULL, sip_pvt->context, refer_to, 1, NULL) || !strcmp(refer_to, ast_parking_ext())) { /* This is an unsupervised transfer (blind transfer) */ @@ -6867,10 +6973,12 @@ } if (p->rtp) { + if (option_debug > 1) ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); ast_rtp_setnat(p->rtp, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); } if (p->vrtp) { + if (option_debug > 1) ast_log(LOG_DEBUG, "Setting NAT on VRTP to %d\n", (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); ast_rtp_setnat(p->vrtp, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); } @@ -8441,7 +8549,10 @@ unsigned int event; char resp = 0; struct ast_frame f; - char *c; + char *c = NULL; + + if (sip_debug_test_pvt(p)) + ast_log(LOG_NOTICE, "Receiving INFO from %s\n", p->callid); /* Need to check the media/type */ if (!strcasecmp(get_header(req, "Content-Type"), "application/dtmf-relay") || @@ -8489,10 +8600,47 @@ transmit_response(p, "200 OK", req); return; } else { - transmit_response(p, "481 Call leg/transaction does not exist", req); + transmit_response(p, "481 Call/Transaction Does Not Exist", req); ast_set_flag(p, SIP_NEEDDESTROY); } return; + } else if ((c = get_header(req, "Record"))) { + /* Record button on SNOM */ + if (!strcasecmp(c, "on")) { + if (sip_debug_test_pvt(p)) + ast_log(LOG_NOTICE, "Record State 'On' from %s\n", p->exten); + if (!p->owner->monitor) { + time_t t; + struct tm tm; + char date[20]; + + time(&t); + localtime_r(&t, &tm); + strftime(date, sizeof(date), "%Y%m%d_%H%M%S", &tm); + snprintf(buf, sizeof(buf), "/var/spool/asterisk/monitor/%s/%s", p->exten, date); + + if (!(ast_monitor_start(p->owner, NULL, buf, 0))) { + ast_monitor_setjoinfiles(p->owner, 1); /* join the two audio files */ + transmit_response(p, "200 OK", req); /* i don't really know how to answer to this, so the phone toggles recording on/off */ + ast_log(LOG_NOTICE, "Recording extension: %s to file: %s\n", p->exten, buf); + } + } else { + if (!(ast_monitor_stop(p->owner, 0))) { + transmit_response(p, "200 OK", req); /* i don't really know how to answer to this, so the phone toggles recording on/off */ + ast_log(LOG_NOTICE, "Stopped Recording extension %s\n", p->exten); + } + } + } else if (!strcasecmp(c, "off")) { +// if (sip_debug_test_pvt(p)) + ast_log(LOG_NOTICE, "Record State 'Off' from %s\n", p->callid); + if (!(ast_monitor_stop(p->owner, 0))) { + transmit_response(p, "200 OK", req); /* i don't really know how to answer to this, so the phone toggles recording on/off */ + ast_log(LOG_NOTICE, "Stopped Recording extension %s\n", p->exten); + } + } else { + transmit_response(p, "405 Method Not Allowed", req); + } + return; } else if (!strcasecmp(get_header(req, "Content-Type"), "application/media_control+xml")) { /* Eh, we'll just assume it's a fast picture update for now */ if (p->owner) @@ -8516,7 +8664,7 @@ /* if (get_msg_text(buf, sizeof(buf), req)) { */ ast_log(LOG_WARNING, "Unable to parse INFO message from %s. Content %s\n", p->callid, buf); - transmit_response(p, "415 Unsupported media type", req); + transmit_response(p, "501 Not Implemented", req); return; } @@ -10008,6 +10156,7 @@ } peerc->cdr = NULL; + ast_log(LOG_DEBUG, "Trying to masquerade %s and %s\n", peerb->name, peerc->name); if (ast_channel_masquerade(peerb, peerc)) { ast_log(LOG_WARNING, "Failed to masquerade %s into %s\n", peerb->name, peerc->name); res = -1; @@ -10051,13 +10200,13 @@ /*--- handle_request_invite: Handle incoming INVITE request */ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int ignore, int seqno, struct sockaddr_in *sin, int *recount, char *e) { - int res = 1; - struct ast_channel *c=NULL; - int gotdest; - struct ast_frame af = { AST_FRAME_NULL, }; - char *supported; - char *required; + int res = 1, gotdest = 0; + struct ast_channel *c = NULL; + struct ast_frame af = { AST_FRAME_NULL, }, *f = NULL; + char *supported = NULL, *required = NULL; unsigned int required_profile = 0; + char *ptr, *p_replaces = NULL, *replace_id = NULL; + struct sip_pvt *refer_pvt = NULL; /* Find out what they support */ if (!p->sipoptions) { @@ -10073,7 +10222,20 @@ transmit_response_with_unsupported(p, "420 Bad extension", req, required); ast_set_flag(p, SIP_NEEDDESTROY); return -1; + } + } + if ((p_replaces = get_header(req, "Replaces"))) { + if (ast_strlen_zero(p_replaces)) { + p_replaces = NULL; + } else { + if (debug) + ast_log(LOG_DEBUG, "Found a Replaces header %s\n", get_header(req, "Replaces")); + replace_id = ast_strdupa(p_replaces); + if (strchr(replace_id, '%')) + ast_uri_decode(replace_id); + if ((ptr = strchr(replace_id, ';'))) + *ptr = '\0'; } } @@ -10166,7 +10328,7 @@ extract_uri(p, req); build_contact(p); - if (gotdest) { + if (!replace_id && gotdest) { if (gotdest < 0) { if (ignore) transmit_response(p, "404 Not Found", req); @@ -10193,6 +10355,38 @@ /* Save Record-Route for any later requests we make on this dialogue */ build_route(p, req, 0); if (c) { + if (replace_id) { + if ((refer_pvt = get_sip_pvt_byid_locked(replace_id, req, NULL, p->theirtag))) { + if (refer_pvt->owner->_state == AST_STATE_RINGING) { + transmit_response(p, "100 Trying", req); + ast_mutex_unlock(&refer_pvt->owner->lock); + ast_mutex_unlock(&refer_pvt->lock); + ast_channel_masquerade(refer_pvt->owner, c ); + ast_hangup(c); + c = refer_pvt->owner; + if ((f = ast_read(c))) { + if (option_debug > 1) + ast_log(LOG_DEBUG, "SIP Call Replacement (pickup) successful for CallID: %s\n", p_replaces); + ast_frfree(f); + ast_setstate(c, AST_STATE_UP); + } else { + ast_log(LOG_WARNING, "SIP Call Replacement (pickup) not successful for CallID: %s\n", p_replaces); + transmit_response_with_allow(p, "403 Call Can Not Be Read From", req, 0); /* Don't know if this is the correct answer, but seems to work */ + return 0; + } + } else { + ast_mutex_unlock(&refer_pvt->owner->lock); + ast_mutex_unlock(&refer_pvt->lock); + ast_hangup(c); + ast_log(LOG_WARNING, "SIP Call Replacement (pickup) not possible. Call already answered\n"); + transmit_response_with_allow(p, "403 Call Already Answered Or Hung Up", req, 0); /* Don't know if this is the correct answer, but seems to work */ + return 0; + } + } else { + transmit_response_with_allow(p, "481 Call/Transaction Does Not Exist", req, 0); + return 0; + } + } /* Pre-lock the call */ ast_mutex_lock(&c->lock); } @@ -10785,6 +10979,10 @@ if (!p->lastinvite && ast_strlen_zero(p->randdata)) ast_set_flag(p, SIP_NEEDDESTROY); break; + case SIP_PUBLISH: + if (debug) + ast_log(LOG_DEBUG, "PUBLISH: from %s\n", e); + break; default: transmit_response_with_allow(p, "501 Method Not Implemented", req, 0); ast_log(LOG_NOTICE, "Unknown SIP command '%s' from '%s'\n", @@ -11185,16 +11383,16 @@ /* we have an address for the peer */ /* if qualify is turned on, check the status */ if (p->maxms && (p->lastms > p->maxms)) { - res = AST_DEVICE_UNAVAILABLE; + res = AST_DEVICE_UNAVAILABLE; } else { /* qualify is not on, or the peer is responding properly */ /* check call limit */ if (p->call_limit && (p->inUse >= p->call_limit)) res = AST_DEVICE_BUSY; else if (p->call_limit) - res = AST_DEVICE_NOT_INUSE; + res = AST_DEVICE_NOT_INUSE; else - res = AST_DEVICE_UNKNOWN; + res = AST_DEVICE_UNKNOWN; } } else { /* there is no address, it's unavailable */ Index: configs/sip.conf.sample =================================================================== RCS file: /usr/cvsroot/asterisk/configs/sip.conf.sample,v retrieving revision 1.71 diff -u -r1.71 sip.conf.sample --- configs/sip.conf.sample 27 Sep 2005 01:54:17 -0000 1.71 +++ configs/sip.conf.sample 27 Sep 2005 07:38:04 -0000 @@ -48,6 +48,9 @@ ;maxexpiry=3600 ; Max length of incoming registration we allow ;defaultexpiry=120 ; Default length of incoming/outoing registration ;notifymimetype=text/plain ; Allow overriding of mime type in MWI NOTIFY +;notifyringing=no ; Sent ringing (yes) or inuse (no) as state in + ; NOTIFY messages when observed extension is + ; in state RINGING_AND_INUSE (defaults to "yes") ;checkmwi=10 ; Default time between mailbox checks for peers ;vmexten=voicemail ; dialplan extension to reach mailbox sets the ; Message-Account in the MWI notify message @@ -232,6 +235,7 @@ ; User config options: Peer configuration: ; -------------------- ------------------- ; context context +; subscribecontext subscribecontext ; permit permit ; deny deny ; secret secret @@ -251,10 +255,10 @@ ; useclientcode useclientcode ; accountcode accountcode ; setvar setvar -; callerid callerid -; amaflags amaflags -; call-limit call-limit -; restrictcid restrictcid +; callerid callerid +; amaflags amaflags +; call-limit call-limit +; restrictcid restrictcid ; mailbox ; username ; template @@ -343,6 +347,7 @@ ;[snom] ;type=friend ; Friends place calls and receive calls ;context=from-sip ; Context for incoming calls from this user +;subscribecontext=mygroup ; context for subscriptions (hints) ;secret=blah ;language=de ; Use German prompts for this user ;host=dynamic ; This peer register with us Index: include/asterisk/pbx.h =================================================================== RCS file: /usr/cvsroot/asterisk/include/asterisk/pbx.h,v retrieving revision 1.53 diff -u -r1.53 pbx.h --- include/asterisk/pbx.h 13 Sep 2005 21:59:45 -0000 1.53 +++ include/asterisk/pbx.h 27 Sep 2005 07:38:09 -0000 @@ -45,23 +45,15 @@ /*! Extension states */ enum ast_extension_states { - /*! Extension removed */ - AST_EXTENSION_REMOVED = -2, - /*! Extension hint removed */ - AST_EXTENSION_DEACTIVATED = -1, - /*! No device INUSE or BUSY */ - AST_EXTENSION_NOT_INUSE = 0, - /*! One or more devices INUSE */ - AST_EXTENSION_INUSE = 1 << 0, - /*! All devices BUSY */ - AST_EXTENSION_BUSY = 1 << 1, - /*! All devices UNAVAILABLE/UNREGISTERED */ - AST_EXTENSION_UNAVAILABLE = 1 << 2, - /*! All devices RINGING */ - AST_EXTENSION_RINGING = 1 << 3, + AST_EXTENSION_REMOVED = -2, /*! Extension removed */ + AST_EXTENSION_DEACTIVATED = -1, /*! Extension hint removed */ + AST_EXTENSION_NOT_INUSE = 0, /*! No device INUSE or BUSY */ + AST_EXTENSION_INUSE = 1 << 0, /*! One or more devices INUSE */ + AST_EXTENSION_BUSY = 1 << 1, /*! All devices BUSY */ + AST_EXTENSION_UNAVAILABLE = 1 << 2, /*! All devices UNAVAILABLE/UNREGISTERED */ + AST_EXTENSION_RINGING = 1 << 3, /*! All devices RINGING */ }; - static const struct cfextension_states { int extension_state; const char * const text; @@ -658,4 +650,5 @@ } #endif -#endif /* _ASTERISK_PBX_H */ + +#endif