2 * pua_reginfo module - Presence-User-Agent Handling of reg events
4 * Copyright (C) 2011-2012 Carsten Bock, carsten@ng-voice.com
5 * http://www.ng-voice.com
7 * This file is part of Kamailio, a free SIP server.
9 * Kamailio is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version
14 * Kamailio is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 #include "../../core/parser/parse_from.h"
26 #include "../../core/parser/parse_content.h"
27 #include "../../core/parser/parse_uri.h"
28 #include "../../modules/usrloc/usrloc.h"
29 #include "../../lib/srutils/sruid.h"
30 #include <libxml/parser.h>
31 #include "usrloc_cb.h"
32 #include "pua_reginfo.h"
34 /*<?xml version="1.0"?>
35 <reginfo xmlns="urn:ietf:params:xml:ns:reginfo" version="0" state="full">
36 .<registration aor="sip:carsten@ng-voice.com" id="0xb33fa860" state="active">
37 ..<contact id="0xb33fa994" state="active" event="registered" expires="3600">
38 ...<uri>sip:carsten@10.157.87.36:43582;transport=udp</uri>
39 ...<unknown-param name="+g.3gpp.cs-voice"></unknown-param>
40 ...<unknown-param name="+g.3gpp.icsi-ref">urn0X0.0041FB74E7B54P-1022urn-70X0P+03gpp-application.ims.iari.gsma-vs</unknown-param>
41 ...<unknown-param name="audio"></unknown-param>
42 ...<unknown-param name="+g.oma.sip-im.large-message"></unknown-param>
43 ...<unknown-param name="+g.3gpp.smsip"></unknown-param>
44 ...<unknown-param name="language">en,fr</unknown-param>
45 ...<unknown-param name="+g.oma.sip-im"></unknown-param>
46 ...<unknown-param name="expires">600000</unknown-param>
51 #define STATE_ACTIVE 1
52 #define STATE_TERMINATED 0
53 #define STATE_UNKNOWN -1
55 #define EVENT_UNKNOWN -1
56 #define EVENT_REGISTERED 0
57 #define EVENT_UNREGISTERED 1
58 #define EVENT_TERMINATED 2
59 #define EVENT_CREATED 3
60 #define EVENT_REFRESHED 4
61 #define EVENT_EXPIRED 5
63 #define RESULT_ERROR -1
64 #define RESULT_CONTACTS_FOUND 1
65 #define RESULT_NO_CONTACTS 2
67 extern sruid_t _reginfo_sruid;
69 int process_contact(udomain_t * domain, urecord_t ** ul_record, str aor, str callid,
70 int cseq, int expires, int event, str contact_uri) {
72 static str no_ua = str_init("n/a");
73 static ucontact_info_t ci;
74 ucontact_t * ul_contact;
77 pua_reginfo_update_self_op(1);
78 if (*ul_record == NULL) {
80 case EVENT_REGISTERED:
83 /* In case, no record exists and new one should be created,
84 create a new entry for this user in the usrloc-DB */
85 if (ul.insert_urecord(domain, &aor, ul_record) < 0) {
86 LM_ERR("failed to insert new user-record\n");
92 /* No entry in usrloc and the contact is expired, deleted, unregistered, whatever:
93 We do not need to do anything. */
94 ret = RESULT_NO_CONTACTS;
99 /* Make sure, no crap is in the structure: */
100 memset( &ci, 0, sizeof(ucontact_info_t));
101 /* Get callid of the message */
103 /* Get CSeq number of the message */
106 /* additional info from message */
107 ci.user_agent = &no_ua;
108 ci.last_modified = time(0);
110 /* set expire time */
111 ci.expires = time(0) + expires;
114 if(sruid_next(&_reginfo_sruid) < 0) {
115 LM_ERR("failed to generate ruid");
117 ci.ruid = _reginfo_sruid.uid;
120 /* Now we start looking for the contact: */
121 if (((*ul_record)->contacts == 0)
122 || (ul.get_ucontact(*ul_record, &contact_uri, &callid, &no_str, cseq+1, &ul_contact) != 0)) {
123 if (ul.insert_ucontact(*ul_record, &contact_uri, &ci, &ul_contact) < 0) {
124 LM_ERR("failed to insert new contact\n");
129 if (ul.update_ucontact(*ul_record, ul_contact, &ci) < 0) {
130 LM_ERR("failed to update contact\n");
135 ul_contact = (*ul_record)->contacts;
137 if (VALID_CONTACT(ul_contact, time(0))) return RESULT_CONTACTS_FOUND;
138 ul_contact = ul_contact->next;
141 ret = RESULT_NO_CONTACTS;
143 pua_reginfo_update_self_op(0);
147 xmlNodePtr xmlGetNodeByName(xmlNodePtr parent, const char *name) {
148 xmlNodePtr cur = parent;
149 xmlNodePtr match = NULL;
151 if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0)
153 match = xmlGetNodeByName(cur->children, name);
161 char * xmlGetAttrContentByName(xmlNodePtr node, const char *name) {
162 xmlAttrPtr attr = node->properties;
164 if (xmlStrcasecmp(attr->name, (unsigned char*)name) == 0)
165 return (char*)xmlNodeGetContent(attr->children);
171 int reginfo_parse_state(char * s) {
173 return STATE_UNKNOWN;
177 if (strncmp(s, "active", 6) == 0) return STATE_ACTIVE;
180 if (strncmp(s, "terminated", 10) == 0) return STATE_TERMINATED;
183 LM_ERR("Unknown State %s\n", s);
184 return STATE_UNKNOWN;
186 LM_ERR("Unknown State %s\n", s);
187 return STATE_UNKNOWN;
190 int reginfo_parse_event(char * s) {
192 return EVENT_UNKNOWN;
196 if (strncmp(s, "created", 7) == 0) return EVENT_CREATED;
197 if (strncmp(s, "expired", 7) == 0) return EVENT_EXPIRED;
200 if (strncmp(s, "refreshed", 9) == 0) return EVENT_CREATED;
203 if (strncmp(s, "registered", 10) == 0) return EVENT_REGISTERED;
204 if (strncmp(s, "terminated", 10) == 0) return EVENT_TERMINATED;
207 if (strncmp(s, "unregistered", 12) == 0) return EVENT_UNREGISTERED;
210 LM_ERR("Unknown Event %s\n", s);
211 return EVENT_UNKNOWN;
213 LM_ERR("Unknown Event %s\n", s);
214 return EVENT_UNKNOWN;
217 int process_body(str notify_body, udomain_t * domain) {
219 xmlNodePtr doc_root = NULL, registrations = NULL, contacts = NULL, uris = NULL, params = NULL;
220 char uri[MAX_URI_SIZE];
221 str aor_key = {0, 0};
224 str contact = {0, 0};
225 str contact_uri = {0, 0};
226 str contact_params = {0, 0};
228 str received = {0,0};
230 str user_agent = {0, 0};
231 int state, event, expires, result, final_result = RESULT_ERROR;
232 char * expires_char, * cseq_char;
235 urecord_t * ul_record = NULL;
236 ucontact_t * ul_contact = NULL;
237 struct sip_uri parsed_aor;
242 doc = xmlParseMemory(notify_body.s, notify_body.len);
244 LM_ERR("Error while parsing the xml body message, Body is:\n%.*s\n",
245 notify_body.len, notify_body.s);
248 doc_root = xmlGetNodeByName(doc->children, "reginfo");
249 if(doc_root == NULL) {
250 LM_ERR("while extracting the reginfo node\n");
253 registrations = doc_root->children;
254 while (registrations) {
255 /* Only process registration sub-items */
256 if (xmlStrcasecmp(registrations->name, BAD_CAST "registration") != 0)
257 goto next_registration;
258 state = reginfo_parse_state(xmlGetAttrContentByName(registrations, "state"));
259 if (state == STATE_UNKNOWN) {
260 LM_ERR("No state for this contact!\n");
261 goto next_registration;
263 aor.s = xmlGetAttrContentByName(registrations, "aor");
265 LM_ERR("No AOR for this contact!\n");
266 goto next_registration;
268 aor.len = strlen(aor.s);
269 LM_DBG("AOR %.*s has state \"%d\"\n", aor.len, aor.s, state);
271 /* Get username part of the AOR, search for @ in the AOR. */
272 if (parse_uri(aor.s, aor.len, &parsed_aor) < 0) {
273 LM_ERR("failed to parse Address of Record (%.*s)\n",
275 goto next_registration;
278 if (reginfo_use_domain) {
281 aor_key.s = parsed_aor.user.s;
283 aor_key.len = strlen(aor_key.s);
284 /* Now let's lock that domain for this AOR: */
285 ul.lock_udomain(domain, &aor_key);
286 /* and retrieve the user-record for this user: */
287 result = ul.get_urecord(domain, &aor_key, &ul_record);
289 ul.unlock_udomain(domain, &aor_key);
290 LM_ERR("failed to query usrloc (AOR %.*s)\n",
291 aor_key.len, aor_key.s);
292 goto next_registration;
294 /* If no contacts found, then set the ul_record to NULL */
295 if (result != 0) ul_record = NULL;
297 /* If the state is terminated, we just can delete all bindings */
298 if (state == STATE_TERMINATED) {
300 ul_contact = ul_record->contacts;
303 ul_contact->flags |= FL_MEM;
305 ul_contact->flags &= ~FL_MEM;
307 ul_contact = ul_contact->next;
309 if (ul.delete_urecord(domain, &aor_key, ul_record) < 0) {
310 LM_ERR("failed to remove record from usrloc\n");
312 /* If already a registration with contacts was found, then keep that result.
313 otherwise the result is now "No contacts found" */
314 if (final_result != RESULT_CONTACTS_FOUND) final_result = RESULT_NO_CONTACTS;
316 /* Otherwise, process the content */
318 /* Now lets process the Contact's from this Registration: */
319 contacts = registrations->children;
321 if (xmlStrcasecmp(contacts->name, BAD_CAST "contact") != 0)
324 callid.s = xmlGetAttrContentByName(contacts, "callid");
325 // If no CallID present, try the "id"-field instead:
326 if (callid.s == NULL)
327 callid.s = xmlGetAttrContentByName(contacts, "id");
328 callid.len = strlen(callid.s);
329 LM_DBG("Got Call-ID \"%.*s\"\n", callid.len, callid.s);
330 received.s = xmlGetAttrContentByName(contacts, "received");
331 if (received.s == NULL) {
332 LM_DBG("No received for this contact!\n");
335 received.len = strlen(received.s);
338 path.s = xmlGetAttrContentByName(contacts, "path");
339 if (path.s == NULL) {
340 LM_DBG("No path for this contact!\n");
343 path.len = strlen(path.s);
346 user_agent.s = xmlGetAttrContentByName(contacts, "user_agent");
347 if (user_agent.s == NULL) {
348 LM_DBG("No user_agent for this contact!\n");
351 user_agent.len = strlen(user_agent.s);
353 event = reginfo_parse_event(xmlGetAttrContentByName(contacts, "event"));
354 if (event == EVENT_UNKNOWN) {
355 LM_ERR("No event for this contact!\n");
358 expires_char = xmlGetAttrContentByName(contacts, "expires");
359 if (expires_char == NULL) {
360 LM_ERR("No expires for this contact!\n");
363 expires = atoi(expires_char);
365 LM_ERR("No valid expires for this contact!\n");
368 LM_DBG("%.*s: Event \"%d\", expires %d\n",
369 callid.len, callid.s, event, expires);
371 cseq_char = xmlGetAttrContentByName(contacts, "cseq");
372 if (cseq_char == NULL) {
373 LM_DBG("No cseq for this contact!\n");
375 cseq = atoi(cseq_char);
377 LM_WARN("No valid cseq for this contact!\n");
381 // <unknown-param name="+g.oma.sip-im"></unknown-param>
382 // <unknown-param name="language">en,fr</unknown-param>
383 /* 1) Iterate through the params, to get the full content-length */
384 params = contacts->children;
385 contact_params.len = 0;
388 if (xmlStrcasecmp(params->name, BAD_CAST "unknown-param") != 0)
390 len += 1 /* ; */ + strlen(xmlGetAttrContentByName(params, "name"));
391 param.s = (char*)xmlNodeGetContent(params);
392 param.len = strlen(param.s);
394 len += 1 /* = */ + param.len;
396 params = params->next;
398 LM_DBG("Calculated length for params: %i\n", len);
402 if (contact_params.s) pkg_free(contact_params.s);
403 contact_params.s = pkg_malloc(len);
404 params = contacts->children;
405 contact_params.len = 0;
407 if (xmlStrcasecmp(params->name, BAD_CAST "unknown-param") != 0)
409 contact_params.len += snprintf(contact_params.s + contact_params.len, len - contact_params.len, ";%s", xmlGetAttrContentByName(params, "name"));
410 param.s = (char*)xmlNodeGetContent(params);
411 param.len = strlen(param.s);
413 contact_params.len += snprintf(contact_params.s + contact_params.len, len - contact_params.len, "=%.*s", param.len, param.s);
415 LM_DBG("Contact params are: %.*s\n", contact_params.len, contact_params.s);
418 params = params->next;
420 LM_DBG("Contact params are: %.*s\n", contact_params.len, contact_params.s);
423 /* Now lets process the URI's from this Contact: */
424 uris = contacts->children;
426 if (xmlStrcasecmp(uris->name, BAD_CAST "uri") != 0)
428 contact_uri.s = (char*)xmlNodeGetContent(uris);
429 if (contact_uri.s == NULL) {
430 LM_ERR("No URI for this contact!\n");
431 goto next_registration;
433 contact_uri.len = strlen(contact_uri.s);
434 if (contact_params.len > 0) {
435 if (contact.s) pkg_free(contact.s);
436 contact.s = (char*)pkg_malloc(contact_uri.len + contact_params.len + 1);
437 contact.len = snprintf(contact.s, contact_uri.len + contact_params.len + 1, "%.*s%.*s", contact_uri.len, contact_uri.s, contact_params.len, contact_params.s);
438 LM_DBG("Contact: %.*s\n",
439 contact.len, contact.s);
442 result = process_contact(domain, &ul_record, aor_key, callid, cseq, expires, event, contact);
444 LM_DBG("Contact: %.*s\n",
445 contact_uri.len, contact_uri.s);
448 result = process_contact(domain, &ul_record, aor_key, callid, cseq, expires, event, contact_uri);
451 /* Process the result */
452 if (final_result != RESULT_CONTACTS_FOUND) final_result = result;
457 contacts = contacts->next;
461 if (ul_record) ul.release_urecord(ul_record);
462 /* Unlock the domain for this AOR: */
464 ul.unlock_udomain(domain, &aor_key);
466 registrations = registrations->next;
469 if (contact.s) pkg_free(contact.s);
470 if (contact_params.s) pkg_free(contact_params.s);
471 /* Free the XML-Document */
473 if(doc) xmlFreeDoc(doc);
477 int reginfo_handle_notify(struct sip_msg* msg, char* domain, char* s2) {
481 /* If not done yet, parse the whole message now: */
482 if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
483 LM_ERR("Error parsing headers\n");
486 if (get_content_length(msg) == 0) {
487 LM_DBG("Content length = 0\n");
488 /* No Body? Then there is no published information available, which is ok. */
491 body.s=get_body(msg);
493 LM_ERR("cannot extract body from msg\n");
496 body.len = get_content_length(msg);
499 LM_DBG("Body is %.*s\n", body.len, body.s);
501 result = process_body(body, (udomain_t*)domain);