Index: channels/chan_sip.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v retrieving revision 1.669 diff -u -r1.669 chan_sip.c --- channels/chan_sip.c 26 Feb 2005 16:29:15 -0000 1.669 +++ channels/chan_sip.c 27 Feb 2005 17:43:07 -0000 @@ -124,9 +124,10 @@ static char default_useragent[AST_MAX_EXTENSION] = DEFAULT_USERAGENT; #define DEFAULT_CONTEXT "default" -static char default_context[AST_MAX_EXTENSION] = DEFAULT_CONTEXT; +static char default_context[AST_MAX_EXTENSION]; /* Default context for calls */ +static char default_subscribecontext[AST_MAX_EXTENSION]; /* Default context for subscriptions */ -static char default_language[MAX_LANGUAGE] = ""; +static char default_language[MAX_LANGUAGE] = ""; /* Default language */ #define DEFAULT_CALLERID "asterisk" static char default_callerid[AST_MAX_EXTENSION] = DEFAULT_CALLERID; @@ -356,6 +357,7 @@ char from[256]; /* The From: header */ char useragent[256]; /* User agent in SIP request */ char context[AST_MAX_EXTENSION]; /* Context for this call */ + char subscribecontext[AST_MAX_EXTENSION]; /* SubscribeContext for this call */ char fromdomain[AST_MAX_EXTENSION]; /* Domain to show in the from field */ char fromuser[AST_MAX_EXTENSION]; /* User to show in the user field */ char fromname[AST_MAX_EXTENSION]; /* Name to show in the user field */ @@ -402,7 +404,8 @@ int rtpkeepalive; /* Send RTP packets for keepalive */ int subscribed; /* Is this call a subscription? */ - int stateid; + int stateid; /* State ID */ + int laststate; /* Last Known State */ int dialogver; struct ast_dsp *vad; /* Voice Activation Detection dsp */ @@ -438,7 +441,7 @@ ASTOBJ_COMPONENTS(struct sip_user); char secret[80]; /* Password */ char md5secret[80]; /* Password in md5 */ - char context[80]; /* Default context for incoming calls */ + char context[AST_MAX_EXTENSION]; /* Default context for incoming calls */ char cid_num[80]; /* Caller ID num */ char cid_name[80]; /* Caller ID name */ char accountcode[20]; /* Account code */ @@ -466,7 +469,8 @@ /* peer->name is the unique name of this object */ char secret[80]; /* Password */ char md5secret[80]; /* Password in MD5 */ - char context[80]; /* Default context for incoming calls */ + char context[AST_MAX_EXTENSION]; /* Default context for incoming calls */ + char subscribecontext[AST_MAX_EXTENSION]; /* Context for subscriptions */ char username[80]; /* Temporary username until registration */ char accountcode[20]; /* Account code */ int amaflags; /* AMA Flags (for billing) */ @@ -591,6 +595,7 @@ /* The list of manual NOTIFY types we know how to send */ struct ast_config *notify_types; +static void append_date(struct sip_request *req); static struct ast_frame *sip_read(struct ast_channel *ast); static int transmit_response(struct sip_pvt *p, char *msg, struct sip_request *req); static int transmit_response_with_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans); @@ -3166,7 +3171,8 @@ if (maddr) { maddr += 6; hn = strspn(maddr, "0123456789."); - if (hn > (sizeof(hostname) - 1)) hn = sizeof(hostname) - 1; + if (hn > (sizeof(hostname) - 1)) + hn = sizeof(hostname) - 1; strncpy(hostname, maddr, hn); hostname[hn] = '\0'; /* safe */ } @@ -3251,16 +3257,20 @@ add_header(resp, "Allow", ALLOWED_METHODS); if (p->expiry) { /* For registration responses, we also need expiry and - contact info */ + contact info, but not for subscriptions */ char contact[256]; - char tmp[256]; + snprintf(contact, sizeof(contact), "%s;expires=%d", p->our_contact, p->expiry); - snprintf(tmp, sizeof(tmp), "%d", p->expiry); - add_header(resp, "Expires", tmp); add_header(resp, "Contact", contact); } else { add_header(resp, "Contact", p->our_contact); } + if (p->expiry) { /* Subscriptions and registrations */ + char tmp[256]; + + snprintf(tmp, sizeof(tmp), "%d", p->expiry); + add_header(resp, "Expires", tmp); + } if (p->maxforwards) { char tmp[256]; snprintf(tmp, sizeof(tmp), "%d", p->maxforwards); @@ -3358,7 +3368,13 @@ add_header(req, "From", ot); add_header(req, "To", of); } - add_header(req, "Contact", p->our_contact); + if (!strcasecmp(msg, "MESSAGE")) { + /* Add date header to MESSAGE */ + append_date(req); + } else { + /* Do not add Contact on MESSAGE */ + add_header(req, "Contact", p->our_contact); + } copy_header(req, orig, "Call-ID"); add_header(req, "CSeq", tmp); @@ -4036,10 +4052,12 @@ int maxbytes = 0; int bytes = 0; char from[256], to[256]; - char *t, *c, *a; + char *t = NULL, *c, *a; char *mfrom, *mto; struct sip_request req; char clen[20]; + char hint[AST_MAX_EXTENSION]; + int hintstate = 1; memset(from, 0, sizeof(from)); memset(to, 0, sizeof(to)); @@ -4057,30 +4075,36 @@ reqprep(&req, p, "NOTIFY", 0, 1); - if (p->subscribed == 1) { - strncpy(to, get_header(&p->initreq, "To"), sizeof(to)-1); + /* Check which device/devices we are watching and if they are registred */ + /* If they are not registred, we will override notification and show no availability */ + if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, p->exten)) { + if(ast_device_state(hint) == AST_DEVICE_UNAVAILABLE) + hintstate = 0; /* Not registred */ + } + + strncpy(to, get_header(&p->initreq, "To"), sizeof(to)-1); + c = ditch_braces(to); + if (strncmp(c, "sip:", 4)) { + ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c); + return -1; + } + if ((a = strchr(c, ';'))) { + *a = '\0'; + } + mto = c; - c = ditch_braces(to); - if (strncmp(c, "sip:", 4)) { - ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c); - return -1; - } - if ((a = strchr(c, ';'))) { - *a = '\0'; - } - mto = c; + if ((state==AST_EXTENSION_UNAVAILABLE) || (state==AST_EXTENSION_BUSY) || !hintstate) + state = 2; /* Closed */ + else if (state==AST_EXTENSION_INUSE) + state = 1; /* In use */ + else + state = 0; /* Open */ + if (p->subscribed == 1 || p->subscribed == 3) { add_header(&req, "Event", "presence"); add_header(&req, "Subscription-State", "active"); add_header(&req, "Content-Type", "application/xpidf+xml"); - if ((state==AST_EXTENSION_UNAVAILABLE) || (state==AST_EXTENSION_BUSY)) - state = 2; - else if (state==AST_EXTENSION_INUSE) - state = 1; - else - state = 0; - t = tmp; maxbytes = sizeof(tmp); bytes = snprintf(t, maxbytes, "\n"); @@ -4108,7 +4132,41 @@ t += bytes; maxbytes -= bytes; bytes = snprintf(t, maxbytes, "\n\n\n"); - } else { + } else if (p->subscribed == 4) { /* pidf+xml */ + add_header(&req, "Event", "presence"); + add_header(&req, "Subscription-State", "active"); + add_header(&req, "Content-Type", "application/pidf+xml"); + t = tmp; + maxbytes = sizeof(tmp); + bytes = snprintf(t, maxbytes, "\n"); + t += bytes; + maxbytes -= bytes; + bytes = snprintf(t, maxbytes, "\n", mfrom); + t += bytes; + maxbytes -= bytes; + bytes = snprintf(t, maxbytes, "\n", p->exten); + t += bytes; + maxbytes -= bytes; + bytes = snprintf(t, maxbytes, "%s\n", mto); + t += bytes; + maxbytes -= bytes; + + bytes = snprintf(t, maxbytes, "%s\n", !state ? "open" : "closed"); + t += bytes; + maxbytes -= bytes; + + bytes = snprintf(t, maxbytes, "%s", state != 0?"on-the-phone\n":""); + t += bytes; + maxbytes -= bytes; + + bytes = snprintf(t, maxbytes, "%s", state == 1?"On the phone\n":""); + t += bytes; + maxbytes -= bytes; + + bytes = snprintf(t, maxbytes, "\n\n"); + t += bytes; + maxbytes -= bytes; + } else if (p->subscribed == 2) { add_header(&req, "Event", "dialog"); add_header(&req, "Content-Type", "application/dialog-info+xml"); @@ -4127,6 +4185,8 @@ t += bytes; maxbytes -= bytes; bytes = snprintf(t, maxbytes, "\n\n"); + t += bytes; + maxbytes -= bytes; } if (t > tmp + sizeof(tmp)) ast_log(LOG_WARNING, "Buffer overflow detected!! (Please file a bug report)\n"); @@ -4573,7 +4633,7 @@ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", p->name); register_peer_exten(p, 0); p->expire = -1; - ast_device_state_changed("SIP/%s", p->name); + ast_device_state_changed("SIP/%s", p->name); /* Activate notification */ if (ast_test_flag(p, SIP_SELFDESTRUCT) || ast_test_flag((&p->flags_page2), SIP_PAGE2_RTAUTOCLEAR)) { ASTOBJ_MARK(p); prune_peers(); @@ -5002,7 +5062,7 @@ #endif ) return 0; - if (!strcasecmp(method, "REGISTER")) { + if (!strcasecmp(method, "REGISTER") || !strcasecmp(method, "SUBSCRIBE")) { /* On a REGISTER, we have to use 401 and its family of headers instead of 407 and its family of headers -- GO SIP! Whoo hoo! Two things that do the same thing but are used in different circumstances! What a surprise. */ @@ -5128,21 +5188,25 @@ return res; } -/*--- cb_extensionstate: Part of thte SUBSCRIBE support subsystem ---*/ +/*--- cb_extensionstate: Callback for the SUBSCRIBE (devicestate notification) support subsystem ---*/ +/* If you add an "hint" priority to the dial plan, you will get notifications + */ static int cb_extensionstate(char *context, char* exten, int state, void *data) { - struct sip_pvt *p = data; - if (state == -1) { - sip_scheddestroy(p, 15000); - p->stateid = -1; - return 0; - } - - transmit_state_notify(p, state, 1); + struct sip_pvt *p = data; /* Pointer to the subscription "call" */ + + if (state == -1) { + sip_scheddestroy(p, 15000); + p->stateid = -1; + return 0; + } + + transmit_state_notify(p, state, 1); + p->laststate = state; - if (option_debug) - ast_verbose(VERBOSE_PREFIX_1 "Extension Changed %s new state %d for Notify User %s\n", exten, state, p->username); - return 0; + if (option_debug) + ast_verbose(VERBOSE_PREFIX_1 "Extension Changed %s new state %d for Notify User %s\n", exten, state, p->username); + return 0; } /*--- register_verify: Verify registration of user */ @@ -5154,6 +5218,7 @@ char iabuf[INET_ADDRSTRLEN]; char *name, *c; char *t; + /* Terminate URI */ t = uri; while(*t && (*t > 32) && (*t != ';')) @@ -5162,6 +5227,7 @@ strncpy(tmp, get_header(req, "To"), sizeof(tmp) - 1); c = ditch_braces(tmp); + /* Ditch ;user=phone */ name = strchr(c, ';'); if (name) @@ -5222,7 +5288,7 @@ } } if (!res) { - ast_device_state_changed("SIP/%s", peer->name); + ast_device_state_changed("SIP/%s", peer->name); /* Activate notification */ } if (res < 0) transmit_response(p, "403 Forbidden", &p->initreq); @@ -5265,7 +5331,7 @@ char tmp[256] = "", *c, *a; char tmpf[256]= "", *fr; struct sip_request *req; - + req = oreq; if (!req) req = &p->initreq; @@ -5838,6 +5904,8 @@ strncpy(p->fullcontact, peer->fullcontact, sizeof(p->fullcontact) - 1); if (!ast_strlen_zero(peer->context)) strncpy(p->context, peer->context, sizeof(p->context) - 1); + if (!ast_strlen_zero(peer->subscribecontext)) + strncpy(p->subscribecontext, peer->subscribecontext, sizeof(p->subscribecontext) - 1); strncpy(p->peersecret, peer->secret, sizeof(p->peersecret) - 1); strncpy(p->peermd5secret, peer->md5secret, sizeof(p->peermd5secret) - 1); strncpy(p->language, peer->language, sizeof(p->language) -1); @@ -5906,14 +5974,26 @@ /*--- receive_message: Receive SIP MESSAGE method messages ---*/ -/* we handle messages within current calls currently */ +/* We only handle messages within current calls currently */ +/* Reference: RFC 3428 */ static void receive_message(struct sip_pvt *p, struct sip_request *req) { char buf[1024]; struct ast_frame f; + char *content_type; + + content_type = get_header(req, "Content-Type"); + if (strcmp(content_type, "text/plain")) { + /* No text/plain attachment */ + transmit_response(p, "415 unsupported media type", req); /* Good enough or? */ + ast_set_flag(p, SIP_NEEDDESTROY); + return; + } if (get_msg_text(buf, sizeof(buf), req)) { ast_log(LOG_WARNING, "Unable to retrieve text from %s\n", p->callid); + transmit_response(p, "202 Accepted", req); + ast_set_flag(p, SIP_NEEDDESTROY); return; } if (p->owner) { @@ -5926,7 +6006,15 @@ f.data = buf; f.datalen = strlen(buf); ast_queue_frame(p->owner, &f); + /* Should we respond somehow? */ + transmit_response(p, "202 Accepted", req); + /* We should respond 202 accepted, since we relay the message */ + } else { + ast_log(LOG_WARNING,"Received message to %s from %s, dropped it...\n Content-Type:%s\n Message: %s\n", get_header(req,"To"), get_header(req,"From"), content_type, buf); + transmit_response(p, "405 Method not allowed", req); /* Good enough or? */ } + ast_set_flag(p, SIP_NEEDDESTROY); + return; } /*--- sip_show_inuse: CLI Command to show calls within limits set by @@ -6050,14 +6138,75 @@ #undef FORMAT } +static char mandescr_show_peers[] = +"Description: Lists SIP peers in text format with details on current status.\n" +"Variables: NONE\n"; + +static char mandescr_xshow_peers[] = +"Description: Lists SIP peers in XML format with details on current status.\n" +" The XML format is under development, feedback welcome! /oej\n" +"Variables: NONE\n"; + +static int _sip_show_peers(int type, int fd, int argc, char *argv[]); + +/*--- manager_sip_show_peers: Show SIP peers in the manager API ---*/ +/* Inspired from chan_iax2 */ +static int manager_sip_show_peers( struct mansession *s, struct message *m ) +{ + char *id = astman_get_header(m,"ActionID"); + char *a[] = { "sip", "show", "peers" }; + int ret; + + ast_mutex_lock(&s->lock); + ast_cli(s->fd, "Response: Success\r\n"); + if (id && !ast_strlen_zero(id)) + ast_cli(s->fd, "ActionID: %s\r\n",id); + ret = _sip_show_peers(0, s->fd, 3, a ); + ast_cli( s->fd, "\r\n\r\n" ); + ast_mutex_unlock(&s->lock); + return ret; +} + +/*--- manager_sip_xshow_peers: Show SIP peers in the manager API (XML)---*/ +static int manager_sip_xshow_peers( struct mansession *s, struct message *m ) +{ + char *id = astman_get_header(m,"ActionID"); + char *a[] = { "sip", "xshow", "peers" }; + int ret; + + ast_mutex_lock(&s->lock); + ast_cli(s->fd, "Response: Success\r\n"); + if (id && !ast_strlen_zero(id)) + ast_cli(s->fd, "ActionID: %s\r\n",id); + ast_cli(s->fd, "Content-type: text/xml\r\n"); + ret = _sip_show_peers(1, s->fd, 3, a ); + ast_cli( s->fd, "\r\n\r\n" ); + ast_mutex_unlock(&s->lock); + return ret; +} + + /*--- sip_show_peers: CLI Show Peers command */ static int sip_show_peers(int fd, int argc, char *argv[]) { + return _sip_show_peers(0, fd, argc, argv); +} + +/*--- sip_show_xpeers: CLI Show Peers command */ +static int sip_xshow_peers(int fd, int argc, char *argv[]) +{ + return _sip_show_peers(1, fd, argc, argv); +} + +/*--- _sip_show_peers: Execute sip show peers and xshow commands */ +static int _sip_show_peers(int type, int fd, int argc, char *argv[]) +{ regex_t regexbuf; int havepattern = 0; #define FORMAT2 "%-25.25s %-15.15s %-3.3s %-3.3s %-3.3s %-15.15s %-8s %-10s\n" #define FORMAT "%-25.25s %-15.15s %-3.3s %-3.3s %-3.3s %-15.15s %-8d %-10s\n" +#define FORMATXML "\n" char name[256] = ""; char iabuf[INET_ADDRSTRLEN]; @@ -6076,7 +6225,11 @@ havepattern = 1; } - ast_cli(fd, FORMAT2, "Name/username", "Host", "Dyn", "Nat", "ACL", "Mask", "Port", "Status"); + if (type == 0) { /* Normal list */ + ast_cli(fd, FORMAT2, "Name/username", "Host", "Dyn", "Nat", "ACL", "Mask", "Port", "Status"); + } else { + ast_cli(fd, "\n\n", havepattern?"on":"off"); + } ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do { char nm[20] = ""; @@ -6091,7 +6244,7 @@ } ast_inet_ntoa(nm, sizeof(nm), iterator->mask); - if (!ast_strlen_zero(iterator->username)) + if (!ast_strlen_zero(iterator->username) && type==0) snprintf(name, sizeof(name), "%s/%s", iterator->name, iterator->username); else strncpy(name, iterator->name, sizeof(name) - 1); @@ -6131,20 +6284,36 @@ iterator->ha ? " A " : " ", /* permit/deny */ nm, ntohs(iterator->addr.sin_port), status); - ast_cli(fd, FORMAT, name, + if (type == 0) {/* Normal CLI list */ + ast_cli(fd, FORMAT, name, iterator->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), iterator->addr.sin_addr) : "(Unspecified)", ast_test_flag(iterator, SIP_DYNAMIC) ? " D " : " ", /* Dynamic or not? */ (ast_test_flag(iterator, SIP_NAT) & SIP_NAT_ROUTE) ? " N " : " ", /* NAT=yes? */ iterator->ha ? " A " : " ", /* permit/deny */ nm, ntohs(iterator->addr.sin_port), status); + } else { /* XML format */ + ast_cli(fd, FORMATXML, iterator->name, + iterator->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), iterator->addr.sin_addr) : "(Unspecified)", + ntohs(iterator->addr.sin_port), + ast_test_flag(iterator, SIP_DYNAMIC) ? "yes" : "no", /* Dynamic or not? */ + (ast_test_flag(iterator, SIP_NAT) & SIP_NAT_ROUTE) ? "yes" : "no", /* NAT=yes? */ + iterator->ha ? "yes" : "no", /* permit/deny */ + nm, + status); + + } ASTOBJ_UNLOCK(iterator); total_peers++; } while(0) ); - ast_cli(fd,"%d sip peers [%d online , %d offline]\n",total_peers,peers_online,peers_offline); + if (type==0) { + ast_cli(fd,"%d sip peers [%d online , %d offline]\n",total_peers,peers_online,peers_offline); + } else { + ast_cli(fd,"\n"); + } if (havepattern) regfree(®exbuf); @@ -6228,9 +6397,59 @@ return RESULT_SUCCESS; } +static char mandescr_xshow_peer[] = +"Description: Show one SIP peer in XML format with details on current status.\n" +" The XML format is under development, feedback welcome! /oej\n" +"Variables: \n" +" Peer: \n"; + +static int _sip_show_peer(int type, int fd, int argc, char *argv[]); + +/*--- manager_sip_xshow_peer: Show SIP peers in the manager API (xml format) ---*/ +static int manager_sip_xshow_peer( struct mansession *s, struct message *m ) +{ + char *id = astman_get_header(m,"ActionID"); + char *a[4]; + char *peer; + int ret; + + peer = astman_get_header(m,"Peer"); + if (!peer || ast_strlen_zero(peer)) { + astman_send_error(s, m, "Peer: missing.\n"); + return 0; + } + ast_mutex_lock(&s->lock); + a[0] = "sip"; + a[1] = "show"; + a[2] = "peer"; + a[3] = peer; + + /* Should respond with error if peer not found, really */ + ast_cli(s->fd, "Response: Success\r\n"); + if (id && !ast_strlen_zero(id)) + ast_cli(s->fd, "ActionID: %s\r\n",id); + ret = _sip_show_peer(1, s->fd, 4, a ); + ast_cli( s->fd, "\r\n\r\n" ); + ast_mutex_unlock(&s->lock); + return ret; +} + + + +/*--- sip_xshow_peer: Show one peer in detail ---*/ +static int sip_xshow_peer(int fd, int argc, char *argv[]) +{ + return _sip_show_peer(1, fd, argc, argv); +} + /*--- sip_show_peer: Show one peer in detail ---*/ static int sip_show_peer(int fd, int argc, char *argv[]) { + return _sip_show_peer(0, fd, argc, argv); +} + +static int _sip_show_peer(int type, int fd, int argc, char *argv[]) +{ char status[30] = ""; char cbuf[256]; char iabuf[INET_ADDRSTRLEN]; @@ -6247,12 +6466,13 @@ load_realtime = (argc == 5 && !strcmp(argv[4], "load")) ? 1 : 0; peer = find_peer(argv[3], NULL, load_realtime); - if (peer) { + if (peer && type==0 ) { /* Normal listing */ ast_cli(fd,"\n\n"); ast_cli(fd, " * Name : %s\n", peer->name); ast_cli(fd, " Secret : %s\n", ast_strlen_zero(peer->secret)?"":""); ast_cli(fd, " MD5Secret : %s\n", ast_strlen_zero(peer->md5secret)?"":""); ast_cli(fd, " Context : %s\n", peer->context); + ast_cli(fd, " SubContext : %s\n", peer->subscribecontext); ast_cli(fd, " Language : %s\n", peer->language); if (!ast_strlen_zero(peer->accountcode)) ast_cli(fd, " Accountcode : %s\n", peer->accountcode); @@ -6317,7 +6537,7 @@ strncpy(status, "UNKNOWN", sizeof(status) - 1); ast_cli(fd, "%s\n",status); ast_cli(fd, " Useragent : %s\n", peer->useragent); - ast_cli(fd, " Full Contact : %s\n", peer->fullcontact); + ast_cli(fd, " Reg. Contact : %s\n", peer->fullcontact); if (peer->chanvars) { ast_cli(fd, " Variables :\n"); for (v = peer->chanvars ; v ; v = v->next) @@ -6325,6 +6545,83 @@ } ast_cli(fd,"\n"); ASTOBJ_UNREF(peer,sip_destroy_peer); + } else if (peer && type == 1) { /* XML listing */ + ast_cli(fd,"\n"); + ast_cli(fd,"\n"); + ast_cli(fd,"\n"); + ast_cli(fd, "\n%s\n", peer->name); + ast_cli(fd, "%smd5\n", ast_strlen_zero(peer->secret)?"no":"yes"); + ast_cli(fd, "%s\n", ast_strlen_zero(peer->md5secret)?"no":"yes"); + ast_cli(fd, "%s\n", peer->context); + ast_cli(fd, "%s\n", peer->context); + ast_cli(fd, "%s\n", peer->language); + ast_cli(fd, "%s\n", peer->accountcode); + ast_cli(fd, "%s\n", ast_cdr_flags2str(peer->amaflags)); + ast_cli(fd, "%s\n", peer->fromuser); + ast_cli(fd, "%s\n", peer->fromdomain); + ast_cli(fd, ""); + print_group(fd, peer->callgroup); + ast_cli(fd, "\n"); + ast_cli(fd, ""); + print_group(fd, peer->pickupgroup); + ast_cli(fd, ""); + ast_cli(fd, "%s\n", peer->mailbox); + ast_cli(fd, "%d\n", peer->lastmsgssent); + ast_cli(fd, "%d\n", peer->lastmsg); + ast_cli(fd, "%d\n", peer->incominglimit); + ast_cli(fd, "%d\n", peer->outgoinglimit); + ast_cli(fd, "%s\n", (ast_test_flag(peer, SIP_DYNAMIC)?"yes":"no")); + ast_cli(fd, "%s%s%d\n", peer->cid_name, peer->cid_num, peer->callingpres); + ast_cli(fd, "%d\n", peer->expire); + ast_cli(fd, "%d\n", peer->expiry); + ast_cli(fd, "%s\n", insecure2str(ast_test_flag(peer, SIP_INSECURE))); + ast_cli(fd, "%s\n", nat2str(ast_test_flag(peer, SIP_NAT))); + ast_cli(fd, "%s\n", (peer->ha?"yes":"no")); + ast_cli(fd, "%s\n", (ast_test_flag(peer, SIP_CAN_REINVITE)?"yes":"no")); + ast_cli(fd, "%s\n", (ast_test_flag(peer, SIP_PROMISCREDIR)?"yes":"no")); + ast_cli(fd, "%s\n", (ast_test_flag(peer, SIP_USEREQPHONE)?"yes":"no")); + /* - is enumerated */ + ast_cli(fd, "%s\n", dtmfmode2str(ast_test_flag(peer, SIP_DTMF))); + ast_cli(fd, "%s\n", peer->tohost); + ast_cli(fd, "%s%d\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "", ntohs(peer->addr.sin_port)); + ast_cli(fd, "%s%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), peer->defaddr.sin_addr), ntohs(peer->defaddr.sin_port)); + ast_cli(fd, "%s\n", peer->username); + ast_cli(fd, ""); + ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, peer->capability); + ast_cli(fd, "%s\n", codec_buf); + ast_cli(fd, ""); + pref = &peer->prefs; + for(x = 0; x < 32 ; x++) { + codec = ast_codec_pref_index(pref,x); + if(!codec) + break; + ast_cli(fd, "%s", ast_getformatname(codec)); + if(x < 31 && ast_codec_pref_index(pref,x+1)) + ast_cli(fd, ","); + } + ast_cli(fd, ""); + + ast_cli(fd, ""); + if (peer->lastms < 0) + strncpy(status, "UNREACHABLE", sizeof(status) - 1); + else if (peer->lastms > peer->maxms) + snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms); + else if (peer->lastms) + snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms); + else + strncpy(status, "UNKNOWN", sizeof(status) - 1); + ast_cli(fd, "%s\n",status); + ast_cli(fd, "%s\n", peer->useragent); + ast_cli(fd, "%s\n", peer->fullcontact); + if (peer->chanvars) { + ast_cli(fd, "\n"); + for (v = peer->chanvars ; v ; v = v->next) + ast_cli(fd, " %s%s\n", v->name, v->value); + ast_cli(fd, "\n"); + } + ast_cli(fd,"\n\n"); + ASTOBJ_UNREF(peer,sip_destroy_peer); + } else { ast_cli(fd,"Peer %s not found.\n", argv[3]); ast_cli(fd,"\n"); @@ -6436,7 +6733,8 @@ static int __sip_show_channels(int fd, int argc, char *argv[], int subscriptions) { -#define FORMAT3 "%-15.15s %-10.10s %-21.21s %-15.15s\n" +#define FORMAT4 "%-15.15s %-10.10s %-21.21s %-15.15s %10s\n" +#define FORMAT3 "%-15.15s %-10.10s %-21.21s %-15.15s %2d (%d)\n" #define FORMAT2 "%-15.15s %-10.10s %-11.11s %-11.11s %s %s\n" #define FORMAT "%-15.15s %-10.10s %-11.11s %5.5d/%5.5d %-6.6s%s %s\n" struct sip_pvt *cur; @@ -6449,7 +6747,7 @@ if (!subscriptions) ast_cli(fd, FORMAT2, "Peer", "User/ANR", "Call ID", "Seq (Tx/Rx)", "Format", "Last Msg"); else - ast_cli(fd, FORMAT3, "Peer", "User", "Call ID", "URI"); + ast_cli(fd, FORMAT4, "Peer", "User", "Call ID", "Extension", "Last State"); while (cur) { if (!cur->subscribed && !subscriptions) { ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), cur->sa.sin_addr), @@ -6464,7 +6762,8 @@ if (cur->subscribed && subscriptions) { ast_cli(fd, FORMAT3, ast_inet_ntoa(iabuf, sizeof(iabuf), cur->sa.sin_addr), ast_strlen_zero(cur->username) ? ( ast_strlen_zero(cur->cid_num) ? "(None)" : cur->cid_num ) : cur->username, - cur->callid, cur->uri); + cur->callid, cur->exten, cur->laststate, cur->subscribed); + numchans++; } cur = cur->next; @@ -7164,6 +7463,16 @@ " Lists all known SIP peers.\n" " Optional regular expression pattern is used to filter the peer list.\n"; +static char xshow_peers_usage[] = +"Usage: sip xshow peers [pattern]\n" +" Lists all known SIP peers in XML format.\n" +" Optional regular expression pattern is used to filter the peer list.\n"; + +static char xshow_peer_usage[] = +"Usage: sip xshow peer [load]\n" +" Lists all details on one SIP peer and the current status in XML format.\n" +" Option \"load\" forces lookup of peer in realtime storage.\n"; + static char show_peer_usage[] = "Usage: sip show peer [load]\n" " Lists all details on one SIP peer and the current status.\n" @@ -7234,8 +7543,12 @@ { { "sip", "debug", "ip", NULL }, sip_do_debug, "Enable SIP debugging on IP", debug_usage }; static struct ast_cli_entry cli_debug_peer = { { "sip", "debug", "peer", NULL }, sip_do_debug, "Enable SIP debugging on Peername", debug_usage, complete_sip_debug_peer }; +static struct ast_cli_entry cli_xshow_peer = + { { "sip", "xshow", "peer", NULL }, sip_xshow_peer, "Show details on specific SIP peer (XML format)", xshow_peer_usage, complete_sip_show_peer }; static struct ast_cli_entry cli_show_peer = { { "sip", "show", "peer", NULL }, sip_show_peer, "Show details on specific SIP peer", show_peer_usage, complete_sip_show_peer }; +static struct ast_cli_entry cli_xshow_peers = + { { "sip", "xshow", "peers", NULL }, sip_xshow_peers, "List defined SIP peers in XML format", xshow_peers_usage }; static struct ast_cli_entry cli_show_peers = { { "sip", "show", "peers", NULL }, sip_show_peers, "Show defined SIP peers", show_peers_usage }; static struct ast_cli_entry cli_prune_realtime = @@ -7370,7 +7683,7 @@ peer->lastms = pingtime; peer->call = NULL; if (statechanged) { - ast_device_state_changed("SIP/%s", peer->name); + ast_device_state_changed("SIP/%s", peer->name); /* Activate notification */ if (newstate == 2) { manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Lagged\r\nTime: %d\r\n", peer->name, pingtime); } else { @@ -8279,16 +8592,12 @@ ast_set_flag(p, SIP_NEEDDESTROY); transmit_response(p, "200 OK", req); } else if (!strcasecmp(cmd, "MESSAGE")) { - if (p->lastinvite) { - if (!ignore) { - if (debug) - ast_verbose("Receiving message!\n"); - receive_message(p, req); - } - transmit_response(p, "200 OK", req); + if (!ignore) { + if (debug) + ast_verbose("Receiving message!\n"); + receive_message(p, req); } else { - transmit_response(p, "405 Method Not Allowed", req); - ast_set_flag(p, SIP_NEEDDESTROY); + transmit_response(p, "202 ACCEPTED", req); } } else if (!strcasecmp(cmd, "SUBSCRIBE")) { if (!ignore) { @@ -8316,8 +8625,11 @@ return 0; } /* Initialize the context if it hasn't been already */ - if (ast_strlen_zero(p->context)) + if (p->subscribecontext && !ast_strlen_zero(p->subscribecontext)) { + strncpy(p->context, p->subscribecontext, sizeof(p->context) - 1); + } else if (ast_strlen_zero(p->context)) strncpy(p->context, default_context, sizeof(p->context) - 1); + /* Get destination right away */ gotdest = get_destination(p, NULL); build_contact(p); @@ -8328,12 +8640,34 @@ transmit_response(p, "484 Address Incomplete", req); ast_set_flag(p, SIP_NEEDDESTROY); } else { + char *event; + event = get_header(req, "Event"); /* Get the even pacīkage name */ + /* Initialize tag */ p->tag = rand(); - if (!strcmp(get_header(req, "Accept"), "application/dialog-info+xml")) - p->subscribed = 2; - else if (!strcmp(get_header(req, "Accept"), "application/simple-message-summary")) { - /* Looks like they actually want a mailbox */ + if (!strcmp(event,"presence")) { /* Presence, RFC 3842 */ + char *accept = get_header(req, "Accept"); + /* Header from Xten Eye-beam + Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml + */ + if (strstr(accept, "application/dialog-info+xml")) + p->subscribed = 2; + /* IETF draft: draft-ietf-sipping-dialog-package-05.txt */ + /* Should not be used for SUBSCRIBE, but anyway */ + else if (!p->subscribed && strstr(accept, "application/pidf+xml")) + p->subscribed = 4; /* RFC 3863 format */ + else if (!p->subscribed && strstr(accept, "application/xpidf+xml")) + p->subscribed = 1; /* Early pre-RFC 3863 format with MSN additions (Microsoft Messenger) */ + else if (!p->subscribed && strstr(accept, "application/cpim-pidf+xml")) + p->subscribed = 3; /* RFC 3863 format */ + else { + /* Can't find a format for events that we know about */ + transmit_response(p, "489 Bad Event", req); + ast_set_flag(p, SIP_NEEDDESTROY); + return 0; + } + } else if (!strcmp(event, "message-summary") && !strcmp(get_header(req, "Accept"), "application/simple-message-summary")) { + /* Looks like they actually want a mailbox (RFC 3842) */ /* At this point, we should check if they subscribe to a mailbox that has the same extension as the peer or the mailbox id. If we configure @@ -8359,10 +8693,14 @@ ast_set_flag(p, SIP_NEEDDESTROY); } return 0; - } else - p->subscribed = 1; - if (p->subscribed) + } else { + transmit_response(p, "489 Bad Event", req); + ast_set_flag(p, SIP_NEEDDESTROY); + return 0; + } + if (p->subscribed) { p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, p); + } } } else @@ -8371,16 +8709,15 @@ if (!ignore && p) p->lastinvite = seqno; if (p && !ast_test_flag(p, SIP_NEEDDESTROY)) { - if (!(p->expiry = atoi(get_header(req, "Expires")))) { + if (!(p->expiry = atoi(get_header(req, "Expires")))) { transmit_response(p, "200 OK", req); ast_set_flag(p, SIP_NEEDDESTROY); return 0; - } - /* The next line can be removed if the SNOM200 Expires bug is fixed */ - if (p->subscribed == 1) { - if (p->expiry>max_expiry) + } + if (p->subscribed != 0) { + if (p->expiry > max_expiry) p->expiry = max_expiry; - } + } /* Go ahead and free RTP port */ if (p->rtp) { ast_rtp_destroy(p->rtp); @@ -8390,9 +8727,14 @@ ast_rtp_destroy(p->vrtp); p->vrtp = NULL; } - transmit_response(p, "200 OK", req); - sip_scheddestroy(p, (p->expiry+10)*1000); - transmit_state_notify(p, ast_extension_state(NULL, p->context, p->exten),1); + if (sipdebug || option_debug > 1) + ast_log(LOG_DEBUG, "Adding subscription for extension %s context %s for peer %s\n", p->exten, p->context, p->username); + + transmit_response(p, "200 OK", req); + /* Set timer for destruction of call at expiration */ + sip_scheddestroy(p, (p->expiry+10)*1000); + /* Send first notification */ + transmit_state_notify(p, ast_extension_state(NULL, p->context, p->exten),1); } } else if (!strcasecmp(cmd, "INFO")) { if (!ignore) { @@ -8750,7 +9092,7 @@ sip_destroy(peer->call); peer->call = NULL; peer->lastms = -1; - ast_device_state_changed("SIP/%s", peer->name); + ast_device_state_changed("SIP/%s", peer->name); /* Activate notification */ /* Try again quickly */ peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_NOTOK, sip_poke_peer_s, peer); return 0; @@ -8845,13 +9187,35 @@ if (p) { found++; res = AST_DEVICE_UNAVAILABLE; + if (option_debug > 2) + ast_log(LOG_DEBUG, "Checking device state for peer %s\n", dest); if ((p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) && (!p->maxms || ((p->lastms > -1) && (p->lastms <= p->maxms)))) { - /* peer found and valid */ - res = AST_DEVICE_UNKNOWN; + /* Peer is registred, or have default IP address + and a valid registration */ + + + /* Now check if we know anything about the state. The only way is by implementing + call control with incominglimit=X in sip.conf where X > 0 + Check if the device has incominglimit, and if qualify=on, if the device + is reachable + */ + if (p->incominglimit && (p->lastms == 0 || p->lastms <= p->maxms)) { + /* Free for a call */ + res = AST_DEVICE_NOT_INUSE; + + if (p->inUse) { /* On a call */ + res = AST_DEVICE_BUSY; + } + } else { + /* peer found and valid, state unknown */ + res = AST_DEVICE_UNKNOWN; + } } } if (!p && !found) { + if (option_debug > 2) + ast_log(LOG_DEBUG, "Checking device state for DNS host %s\n", dest); hp = ast_gethostbyname(host, &ahp); if (hp) res = AST_DEVICE_UNKNOWN; @@ -9216,6 +9580,7 @@ peer->chanvars = NULL; } strncpy(peer->context, default_context, sizeof(peer->context)-1); + strncpy(peer->subscribecontext, default_subscribecontext, sizeof(peer->subscribecontext)-1); strncpy(peer->language, default_language, sizeof(peer->language)-1); strncpy(peer->musicclass, global_musicclass, sizeof(peer->musicclass)-1); ast_copy_flags(peer, &global_flags, SIP_USEREQPHONE); @@ -9258,9 +9623,11 @@ strncpy(peer->md5secret, v->value, sizeof(peer->md5secret)-1); else if (!strcasecmp(v->name, "callerid")) { ast_callerid_split(v->value, peer->cid_name, sizeof(peer->cid_name), peer->cid_num, sizeof(peer->cid_num)); - } else if (!strcasecmp(v->name, "context")) + } else if (!strcasecmp(v->name, "context")) { strncpy(peer->context, v->value, sizeof(peer->context)-1); - else if (!strcasecmp(v->name, "fromdomain")) + } else if (!strcasecmp(v->name, "subscribecontext")) { + strncpy(peer->subscribecontext, v->value, sizeof(peer->subscribecontext)-1); + } else if (!strcasecmp(v->name, "fromdomain")) strncpy(peer->fromdomain, v->value, sizeof(peer->fromdomain)-1); else if (!strcasecmp(v->name, "usereqphone")) ast_set2_flag(peer, ast_true(v->value), SIP_USEREQPHONE); @@ -9456,6 +9823,7 @@ /* Initialize some reasonable defaults at SIP reload */ strncpy(default_context, DEFAULT_CONTEXT, sizeof(default_context) - 1); + default_subscribecontext[0] = '\0'; default_language[0] = '\0'; default_fromdomain[0] = '\0'; default_qualify = 0; @@ -10174,7 +10542,9 @@ ast_cli_register(&cli_show_history); ast_cli_register(&cli_prune_realtime); ast_cli_register(&cli_show_peer); + ast_cli_register(&cli_xshow_peer); ast_cli_register(&cli_show_peers); + ast_cli_register(&cli_xshow_peers); ast_cli_register(&cli_show_registry); ast_cli_register(&cli_debug); ast_cli_register(&cli_debug_ip); @@ -10184,6 +10554,9 @@ ast_cli_register(&cli_no_history); ast_cli_register(&cli_sip_reload); ast_cli_register(&cli_inuse_show); + ast_manager_register2( "SIPpeers", 0, manager_sip_show_peers, "List SIP Peers", mandescr_show_peers ); + ast_manager_register2( "SIPpeers-xml", 0, manager_sip_xshow_peers, "List SIP Peers in XML format" , mandescr_xshow_peers); + ast_manager_register2( "SIPpeer-xml", 0, manager_sip_xshow_peer, "Show SIP Peer in XML format" , mandescr_xshow_peer); sip_rtp.type = channeltype; ast_rtp_proto_register(&sip_rtp); ast_register_application(app_dtmfmode, sip_dtmfmode, synopsis_dtmfmode, descrip_dtmfmode); @@ -10216,6 +10589,7 @@ ast_cli_unregister(&cli_prune_realtime); ast_cli_unregister(&cli_show_peer); ast_cli_unregister(&cli_show_peers); + ast_cli_unregister(&cli_xshow_peers); ast_cli_unregister(&cli_show_registry); ast_cli_unregister(&cli_show_subscriptions); ast_cli_unregister(&cli_debug); @@ -10228,6 +10602,9 @@ ast_cli_unregister(&cli_inuse_show); ast_rtp_proto_unregister(&sip_rtp); ast_channel_unregister(channeltype); + ast_manager_unregister("SIPpeers"); + ast_manager_unregister("SIPpeers-xml"); + ast_manager_unregister("SIPpeer-xml"); if (!ast_mutex_lock(&iflock)) { /* Hangup all interfaces if they have an owner */ p = iflist;