/* * pua_reginfo module - Presence-User-Agent Handling of reg events * * Copyright (C) 2011 Carsten Bock, carsten@ng-voice.com * http://www.ng-voice.com * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "usrloc_cb.h" #include "pua_reginfo.h" #include <libxml/parser.h> #include "../pua/pua.h" #include "../pua/send_publish.h" /* Contact: <sip:carsten@10.157.87.36:44733;transport=udp>;expires=600000;+g.oma.sip-im;language="en,fr";+g.3gpp.smsip;+g.oma.sip-im.large-message;audio;+g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-application.ims.iari.gsma-vs";+g.3gpp.cs-voice. Call-ID: 9ad9f89f-164d-bb86-1072-52e7e9eb5025. */ /*<?xml version="1.0"?> <reginfo xmlns="urn:ietf:params:xml:ns:reginfo" version="0" state="full"> .<registration aor="sip:carsten@ng-voice.com" id="0xb33fa860" state="active"> ..<contact id="0xb33fa994" state="active" event="registered" expires="3600"> ...<uri>sip:carsten@10.157.87.36:43582;transport=udp</uri> ...<unknown-param name="+g.3gpp.cs-voice"></unknown-param> ...<unknown-param name="+g.3gpp.icsi-ref">urn0X0.0041FB74E7B54P-1022urn-70X0P+03gpp-application.ims.iari.gsma-vs</unknown-param> ...<unknown-param name="audio"></unknown-param> ...<unknown-param name="+g.oma.sip-im.large-message"></unknown-param> ...<unknown-param name="+g.3gpp.smsip"></unknown-param> ...<unknown-param name="language">en,fr</unknown-param> ...<unknown-param name="+g.oma.sip-im"></unknown-param> ...<unknown-param name="expires">600000</unknown-param> ..</contact> .</registration> </reginfo> */ static int _pua_reginfo_self_op = 0; void pua_reginfo_update_self_op(int v) { _pua_reginfo_self_op = v; } str* build_reginfo_full(urecord_t * record, str uri, ucontact_t* c, int type) { xmlDocPtr doc = NULL; xmlNodePtr root_node = NULL; xmlNodePtr registration_node = NULL; xmlNodePtr contact_node = NULL; xmlNodePtr uri_node = NULL; str * body= NULL; ucontact_t * ptr; char buf[512]; int buf_len; int reg_active = 0; time_t cur_time = time(0); /* create the XML-Body */ doc = xmlNewDoc(BAD_CAST "1.0"); if(doc==0) { LM_ERR("Unable to create XML-Doc\n"); return NULL; } root_node = xmlNewNode(NULL, BAD_CAST "reginfo"); if(root_node==0) { LM_ERR("Unable to create reginfo-XML-Element\n"); return NULL; } /* This is our Root-Element: */ xmlDocSetRootElement(doc, root_node); xmlNewProp(root_node, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:reginfo"); /* we set the version to 0 but it should be set to the correct value in the pua module */ xmlNewProp(root_node, BAD_CAST "version", BAD_CAST "0"); xmlNewProp(root_node, BAD_CAST "state", BAD_CAST "full" ); /* Registration Node */ registration_node =xmlNewChild(root_node, NULL, BAD_CAST "registration", NULL) ; if( registration_node ==NULL) { LM_ERR("while adding child\n"); goto error; } /* Add the properties to this Node for AOR and ID: */ xmlNewProp(registration_node, BAD_CAST "aor", BAD_CAST uri.s); buf_len = snprintf(buf, sizeof(buf), "%p", record); xmlNewProp(registration_node, BAD_CAST "id", BAD_CAST buf); LM_DBG("Updated Contact %.*s[%.*s]\n", c->c.len, c->c.s, c->ruid.len, c->ruid.s); ptr = record->contacts; while (ptr) { if (VALID_CONTACT(ptr, cur_time)) { LM_DBG("Contact %.*s[%.*s]\n", ptr->c.len, ptr->c.s, ptr->ruid.len, ptr->ruid.s); /* Contact-Node */ contact_node =xmlNewChild(registration_node, NULL, BAD_CAST "contact", NULL) ; if( contact_node ==NULL) { LM_ERR("while adding child\n"); goto error; } memset(buf, 0, sizeof(buf)); buf_len = snprintf(buf, sizeof(buf), "%p", ptr); xmlNewProp(contact_node, BAD_CAST "id", BAD_CAST buf); /* Check, if this is the modified contact: */ if ((c->ruid.len == ptr->ruid.len) && !memcmp(c->ruid.s, ptr->ruid.s, c->ruid.len)) { if ((type & UL_CONTACT_INSERT) || (type & UL_CONTACT_UPDATE)) { reg_active = 1; xmlNewProp(contact_node, BAD_CAST "state", BAD_CAST "active"); } else xmlNewProp(contact_node, BAD_CAST "state", BAD_CAST "terminated"); if (type & UL_CONTACT_INSERT) xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "created"); else if (type & UL_CONTACT_UPDATE) xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "refreshed"); else if (type & UL_CONTACT_EXPIRE) xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "expired"); else if (type & UL_CONTACT_DELETE) xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "unregistered"); else xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "unknown"); memset(buf, 0, sizeof(buf)); buf_len = snprintf(buf, sizeof(buf), "%i", (int)(ptr->expires-cur_time)); xmlNewProp(contact_node, BAD_CAST "expires", BAD_CAST buf); } else { reg_active = 1; xmlNewProp(contact_node, BAD_CAST "state", BAD_CAST "active"); xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "registered"); memset(buf, 0, sizeof(buf)); buf_len = snprintf(buf, sizeof(buf), "%i", (int)(ptr->expires-cur_time)); xmlNewProp(contact_node, BAD_CAST "expires", BAD_CAST buf); } if (ptr->q != Q_UNSPECIFIED) { float q = (float)ptr->q/1000; memset(buf, 0, sizeof(buf)); buf_len = snprintf(buf, sizeof(buf), "%.3f", q); xmlNewProp(contact_node, BAD_CAST "q", BAD_CAST buf); } /* CallID Attribute */ memset(buf, 0, sizeof(buf)); buf_len = snprintf(buf, sizeof(buf), "%.*s", ptr->callid.len, ptr->callid.s); xmlNewProp(contact_node, BAD_CAST "callid", BAD_CAST buf); /* CSeq Attribute */ memset(buf, 0, sizeof(buf)); buf_len = snprintf(buf, sizeof(buf), "%d", ptr->cseq); xmlNewProp(contact_node, BAD_CAST "cseq", BAD_CAST buf); /* received Attribute */ memset(buf, 0, sizeof(buf)); buf_len = snprintf(buf, sizeof(buf), "%.*s", ptr->received.len, ptr->received.s); xmlNewProp(contact_node, BAD_CAST "received", BAD_CAST buf); /* path Attribute */ memset(buf, 0, sizeof(buf)); buf_len = snprintf(buf, sizeof(buf), "%.*s", ptr->path.len, ptr->path.s); xmlNewProp(contact_node, BAD_CAST "path", BAD_CAST buf); /* user_agent Attribute */ memset(buf, 0, sizeof(buf)); buf_len = snprintf(buf, sizeof(buf), "%.*s", ptr->user_agent.len, ptr->user_agent.s); xmlNewProp(contact_node, BAD_CAST "user_agent", BAD_CAST buf); /* URI-Node */ memset(buf, 0, sizeof(buf)); buf_len = snprintf(buf, sizeof(buf), "%.*s", ptr->c.len, ptr->c.s); uri_node = xmlNewChild(contact_node, NULL, BAD_CAST "uri", BAD_CAST buf) ; if(uri_node == NULL) { LM_ERR("while adding child\n"); goto error; } } ptr = ptr->next; } /* add registration state (at least one active contact): */ if (reg_active==0) xmlNewProp(registration_node, BAD_CAST "state", BAD_CAST "terminated"); else xmlNewProp(registration_node, BAD_CAST "state", BAD_CAST "active"); /* create the body */ body = (str*)pkg_malloc(sizeof(str)); if(body == NULL) { LM_ERR("while allocating memory\n"); return NULL; } memset(body, 0, sizeof(str)); /* Write the XML into the body */ xmlDocDumpFormatMemory(doc,(unsigned char**)(void*)&body->s,&body->len,1); /*free the document */ xmlFreeDoc(doc); xmlCleanupParser(); return body; error: if(body) { if(body->s) xmlFree(body->s); pkg_free(body); } if(doc) xmlFreeDoc(doc); return NULL; } void reginfo_usrloc_cb(ucontact_t* c, int type, void* param) { str* body= NULL; publ_info_t publ; str content_type; udomain_t * domain; urecord_t * record; ucontact_t* _c = NULL; int res; str uri = {NULL, 0}; char* at = NULL; char id_buf[512]; int id_buf_len; if(_pua_reginfo_self_op == 1) { LM_DBG("operation triggered by own action for aor: %.*s (%d)\n", c->aor->len, c->aor->s, type); return; } content_type.s = "application/reginfo+xml"; content_type.len = 23; /* Debug Output: */ LM_DBG("AOR: %.*s (%.*s)\n", c->aor->len, c->aor->s, c->domain->len, c->domain->s); if(type & UL_CONTACT_INSERT) LM_DBG("type= UL_CONTACT_INSERT\n"); else if(type & UL_CONTACT_UPDATE) LM_DBG("type= UL_CONTACT_UPDATE\n"); else if(type & UL_CONTACT_EXPIRE) LM_DBG("type= UL_CONTACT_EXPIRE\n"); else if(type & UL_CONTACT_DELETE) LM_DBG("type= UL_CONTACT_DELETE\n"); else { LM_ERR("Unknown Type %i\n", type); return; } /* Get the UDomain for this account */ res = ul.get_udomain(c->domain->s, &domain); if(res < 0) { LM_ERR("no domain found\n"); return; } /* Get the URecord for this ruid */ res = ul.get_urecord_by_ruid(domain, ul.get_aorhash(c->aor), &(c->ruid), &record, &_c); if (res < 0) { LM_ERR("'%.*s (%.*s)' Not found in usrloc\n", c->aor->len, c->aor->s, c->domain->len, c->domain->s); return; } /* Create AOR to be published */ /* Search for @ in the AOR. In case no domain was provided, we will add the "default domain" */ at = memchr(record->aor.s, '@', record->aor.len); if (!at) { uri.len = record->aor.len + default_domain.len + 6; uri.s = (char*)pkg_malloc(sizeof(char) * uri.len); if(uri.s == NULL) { LM_ERR("Error allocating memory for URI!\n"); goto error; } if (record->aor.len > 0) uri.len = snprintf(uri.s, uri.len, "sip:%.*s@%.*s", record->aor.len, record->aor.s, default_domain.len, default_domain.s); else uri.len = snprintf(uri.s, uri.len, "sip:%.*s", default_domain.len, default_domain.s); } else { uri.len = record->aor.len + 6; uri.s = (char*)pkg_malloc(sizeof(char) * uri.len); if(uri.s == NULL) { LM_ERR("Error allocating memory for URI!\n"); goto error; } uri.len = snprintf(uri.s, uri.len, "sip:%.*s", record->aor.len, record->aor.s); } /* Build the XML-Body: */ body = build_reginfo_full(record, uri, c, type); if(body == NULL || body->s == NULL) { LM_ERR("Error on creating XML-Body for publish\n"); goto error; } LM_DBG("XML-Body:\n%.*s\n", body->len, body->s); LM_DBG("Contact %.*s, %p\n", c->c.len, c->c.s, c); memset(&publ, 0, sizeof(publ_info_t)); publ.pres_uri = &uri; publ.body = body; id_buf_len = snprintf(id_buf, sizeof(id_buf), "REGINFO_PUBLISH.%.*s@%.*s", c->aor->len, c->aor->s, c->domain->len, c->domain->s); publ.id.s = id_buf; publ.id.len = id_buf_len; publ.content_type = content_type; publ.expires = 3600; /* make UPDATE_TYPE, as if this "publish dialog" is not found by pua it will fallback to INSERT_TYPE anyway */ publ.flag|= UPDATE_TYPE; publ.source_flag |= REGINFO_PUBLISH; publ.event |= REGINFO_EVENT; publ.extra_headers= NULL; if(pua.send_publish(&publ) < 0) { LM_ERR("Error while sending publish\n"); } error: if (uri.s) pkg_free(uri.s); if(body) { if(body->s) xmlFree(body->s); pkg_free(body); } return; }