pua_reginfo: init vars to avoid accessing invalid value on error
[sip-router] / src / modules / pua_reginfo / notify.c
1 /*
2  * pua_reginfo module - Presence-User-Agent Handling of reg events
3  *
4  * Copyright (C) 2011-2012 Carsten Bock, carsten@ng-voice.com
5  * http://www.ng-voice.com
6  *
7  * This file is part of Kamailio, a free SIP server.
8  *
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
13  *
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.
18  *
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
22  */
23
24 #include "notify.h"
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"
33
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>
47 ..</contact>
48 .</registration>
49 </reginfo> */
50
51 #define STATE_ACTIVE 1
52 #define STATE_TERMINATED 0
53 #define STATE_UNKNOWN -1
54
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
62
63 #define RESULT_ERROR -1
64 #define RESULT_CONTACTS_FOUND 1
65 #define RESULT_NO_CONTACTS 2
66
67 extern sruid_t _reginfo_sruid;
68
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) {
71         str no_str = {0, 0};
72         static str no_ua = str_init("n/a");
73         static ucontact_info_t ci;
74         ucontact_t * ul_contact;
75         int ret;
76
77         pua_reginfo_update_self_op(1);
78         if (*ul_record == NULL) {
79                 switch(event) {
80                         case EVENT_REGISTERED:
81                         case EVENT_CREATED:
82                         case EVENT_REFRESHED:
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");
87                                         ret = RESULT_ERROR;
88                                         goto done;
89                                 }
90                                 break;
91                         default:
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;
95                                 goto done;
96                 }
97         }
98         
99         /* Make sure, no crap is in the structure: */
100         memset( &ci, 0, sizeof(ucontact_info_t));       
101         /* Get callid of the message */
102         ci.callid = &callid;
103         /* Get CSeq number of the message */
104         ci.cseq = cseq;
105         ci.sock = NULL;
106         /* additional info from message */
107         ci.user_agent = &no_ua;
108         ci.last_modified = time(0);
109
110         /* set expire time */
111         ci.expires = time(0) + expires;
112
113         /* set ruid */
114         if(sruid_next(&_reginfo_sruid) < 0) {
115                 LM_ERR("failed to generate ruid");
116         } else {
117                 ci.ruid = _reginfo_sruid.uid;
118         }
119
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");
125                         ret = RESULT_ERROR;
126                         goto done;
127                 }
128         } else {
129                 if (ul.update_ucontact(*ul_record, ul_contact, &ci) < 0) {
130                         LM_ERR("failed to update contact\n");
131                         ret = RESULT_ERROR;
132                         goto done;
133                 }
134         }
135         ul_contact = (*ul_record)->contacts;
136         while (ul_contact) {
137                 if (VALID_CONTACT(ul_contact, time(0))) return RESULT_CONTACTS_FOUND;
138                 ul_contact = ul_contact->next;
139         }
140
141         ret = RESULT_NO_CONTACTS;
142 done:
143         pua_reginfo_update_self_op(0);
144         return ret;
145 }
146
147 xmlNodePtr xmlGetNodeByName(xmlNodePtr parent, const char *name) {
148         xmlNodePtr cur = parent;
149         xmlNodePtr match = NULL;
150         while (cur) {
151                 if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0)
152                         return cur;
153                 match = xmlGetNodeByName(cur->children, name);
154                 if (match)
155                         return match;
156                 cur = cur->next;
157         }
158         return NULL;
159 }
160
161 char * xmlGetAttrContentByName(xmlNodePtr node, const char *name) {
162         xmlAttrPtr attr = node->properties;
163         while (attr) {
164                 if (xmlStrcasecmp(attr->name, (unsigned char*)name) == 0)
165                         return (char*)xmlNodeGetContent(attr->children);
166                 attr = attr->next;
167         }
168         return NULL;
169 }
170
171 int reginfo_parse_state(char * s) {
172         if (s == NULL) {
173                 return STATE_UNKNOWN;
174         }
175         switch (strlen(s)) {
176                 case 6:
177                         if (strncmp(s, "active", 6) ==  0) return STATE_ACTIVE;
178                         break;
179                 case 10:
180                         if (strncmp(s, "terminated", 10) ==  0) return STATE_TERMINATED;
181                         break;
182                 default:
183                         LM_ERR("Unknown State %s\n", s);
184                         return STATE_UNKNOWN;
185         }
186         LM_ERR("Unknown State %s\n", s);
187         return STATE_UNKNOWN;
188 }
189
190 int reginfo_parse_event(char * s) {
191         if (s == NULL) {
192                 return EVENT_UNKNOWN;
193         }
194         switch (strlen(s)) {
195                 case 7:
196                         if (strncmp(s, "created", 7) ==  0) return EVENT_CREATED;
197                         if (strncmp(s, "expired", 7) ==  0) return EVENT_EXPIRED;
198                         break;
199                 case 9:
200                         if (strncmp(s, "refreshed", 9) ==  0) return EVENT_CREATED;
201                         break;
202                 case 10:
203                         if (strncmp(s, "registered", 10) ==  0) return EVENT_REGISTERED;
204                         if (strncmp(s, "terminated", 10) ==  0) return EVENT_TERMINATED;
205                         break;
206                 case 12:
207                         if (strncmp(s, "unregistered", 12) ==  0) return EVENT_UNREGISTERED;
208                         break;
209                 default:
210                         LM_ERR("Unknown Event %s\n", s);
211                         return EVENT_UNKNOWN;
212         }
213         LM_ERR("Unknown Event %s\n", s);
214         return EVENT_UNKNOWN;
215 }
216
217 int process_body(str notify_body, udomain_t * domain) {
218         xmlDocPtr doc= NULL;
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};
222         str aor = {0, 0};
223         str callid = {0, 0};
224         str contact = {0, 0};
225         str contact_uri = {0, 0};
226         str contact_params = {0, 0};
227         str param = {0, 0};
228         str received = {0,0};
229         str path = {0,0};
230         str user_agent = {0, 0};
231         int state, event, expires, result, final_result = RESULT_ERROR;
232         char * expires_char,  * cseq_char;
233         int cseq = 0;
234         int len;
235         urecord_t * ul_record = NULL;
236         ucontact_t * ul_contact = NULL;
237         struct sip_uri parsed_aor;
238
239         /* Temporary */
240         int mem_only = 1;
241
242         doc = xmlParseMemory(notify_body.s, notify_body.len);
243         if(doc== NULL)  {
244                 LM_ERR("Error while parsing the xml body message, Body is:\n%.*s\n",
245                         notify_body.len, notify_body.s);
246                 return -1;
247         }
248         doc_root = xmlGetNodeByName(doc->children, "reginfo");
249         if(doc_root == NULL) {
250                 LM_ERR("while extracting the reginfo node\n");
251                 goto error;
252         }
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;
262                 }
263                 aor.s = xmlGetAttrContentByName(registrations, "aor");
264                 if (aor.s == NULL) {
265                         LM_ERR("No AOR for this contact!\n");           
266                         goto next_registration;
267                 }
268                 aor.len = strlen(aor.s);
269                 LM_DBG("AOR %.*s has state \"%d\"\n", aor.len, aor.s, state);
270
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",
274                                 aor.len, aor.s);
275                         goto next_registration;
276                 }
277
278                 if (reginfo_use_domain) {
279                         aor_key.s = uri;
280                 } else {
281                         aor_key.s = parsed_aor.user.s;
282                 }
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);
288                 if (result < 0) {
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;
293                 }
294                 /* If no contacts found, then set the ul_record to NULL */
295                 if (result != 0) ul_record = NULL;
296
297                 /* If the state is terminated, we just can delete all bindings */
298                 if (state == STATE_TERMINATED) {
299                         if (ul_record) {
300                                 ul_contact = ul_record->contacts;
301                                 while(ul_contact) {
302                                         if (mem_only) {
303                                                 ul_contact->flags |= FL_MEM;
304                                         } else {
305                                                 ul_contact->flags &= ~FL_MEM;
306                                         }
307                                         ul_contact = ul_contact->next;
308                                 }
309                                 if (ul.delete_urecord(domain, &aor_key, ul_record) < 0) {
310                                         LM_ERR("failed to remove record from usrloc\n");
311                                 }
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;
315                         }
316                 /* Otherwise, process the content */
317                 } else {
318                         /* Now lets process the Contact's from this Registration: */
319                         contacts = registrations->children;
320                         while (contacts) {
321                                 if (xmlStrcasecmp(contacts->name, BAD_CAST "contact") != 0)
322                                         goto next_contact;
323                                 callid.len = 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");
333                                         received.len = 0;
334                 } else {
335                                         received.len = strlen(received.s);
336                                 }
337
338                                 path.s = xmlGetAttrContentByName(contacts, "path");     
339                                 if (path.s == NULL) {
340                     LM_DBG("No path for this contact!\n");
341                                         path.len = 0;
342                 } else {
343                                         path.len = strlen(path.s);
344                                 }
345
346                                 user_agent.s = xmlGetAttrContentByName(contacts, "user_agent");
347                                 if (user_agent.s == NULL) {
348                     LM_DBG("No user_agent for this contact!\n");
349                                         user_agent.len = 0;
350                 } else {
351                                         user_agent.len = strlen(user_agent.s);
352                                 }
353                                 event = reginfo_parse_event(xmlGetAttrContentByName(contacts, "event"));
354                                 if (event == EVENT_UNKNOWN) {
355                                         LM_ERR("No event for this contact!\n");         
356                                         goto next_contact;
357                                 }
358                                 expires_char = xmlGetAttrContentByName(contacts, "expires");
359                                 if (expires_char == NULL) {
360                                         LM_ERR("No expires for this contact!\n");               
361                                         goto next_contact;
362                                 }
363                                 expires = atoi(expires_char);
364                                 if (expires < 0) {
365                                         LM_ERR("No valid expires for this contact!\n");         
366                                         goto next_contact;
367                                 }
368                                 LM_DBG("%.*s: Event \"%d\", expires %d\n",
369                                         callid.len, callid.s, event, expires);
370
371                                 cseq_char = xmlGetAttrContentByName(contacts, "cseq");
372                                 if (cseq_char == NULL) {
373                                         LM_DBG("No cseq for this contact!\n");          
374                                 } else {
375                                         cseq = atoi(cseq_char);
376                                         if (cseq < 0) {
377                                                 LM_WARN("No valid cseq for this contact!\n");           
378                                         }
379                                 }
380
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;
386                                 len = 0;
387                                 while (params) {
388                                         if (xmlStrcasecmp(params->name, BAD_CAST "unknown-param") != 0)
389                                                 goto next_param;
390                                         len += 1 /* ; */ + strlen(xmlGetAttrContentByName(params, "name"));
391                                         param.s = (char*)xmlNodeGetContent(params);
392                                         param.len = strlen(param.s);
393                                         if (param.len > 0)
394                                                 len += 1 /* = */ + param.len;
395 next_param:
396                                         params = params->next;
397                                 }
398                                 LM_DBG("Calculated length for params: %i\n", len);
399
400                                 if (len > 0) {
401                                         len += 1;
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;
406                                         while (params) {
407                                                 if (xmlStrcasecmp(params->name, BAD_CAST "unknown-param") != 0)
408                                                         goto next_param2;
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);
412                                                 if (param.len > 0)
413                                                         contact_params.len += snprintf(contact_params.s + contact_params.len, len - contact_params.len, "=%.*s", param.len, param.s);
414
415                                                 LM_DBG("Contact params are: %.*s\n", contact_params.len, contact_params.s);
416                         
417 next_param2:
418                                                 params = params->next;
419                                         }
420                                         LM_DBG("Contact params are: %.*s\n", contact_params.len, contact_params.s);
421                                 }
422
423                                 /* Now lets process the URI's from this Contact: */
424                                 uris = contacts->children;
425                                 while (uris) {
426                                         if (xmlStrcasecmp(uris->name, BAD_CAST "uri") != 0)
427                                                 goto next_uri;
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;
432                                         }
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);
440
441                                                 /* Add to Usrloc: */
442                                                 result = process_contact(domain, &ul_record, aor_key, callid, cseq, expires, event, contact);
443                                         } else {
444                                                 LM_DBG("Contact: %.*s\n",
445                                                         contact_uri.len, contact_uri.s);
446
447                                                 /* Add to Usrloc: */
448                                                 result = process_contact(domain, &ul_record, aor_key, callid, cseq, expires, event, contact_uri);
449                                         }
450                                 
451                                         /* Process the result */
452                                         if (final_result != RESULT_CONTACTS_FOUND) final_result = result;
453 next_uri:
454                                         uris = uris->next;
455                                 }
456 next_contact:
457                                 contacts = contacts->next;
458                         }
459                 }
460 next_registration:
461                 if (ul_record) ul.release_urecord(ul_record);
462                 /* Unlock the domain for this AOR: */
463                 if (aor_key.len > 0)
464                         ul.unlock_udomain(domain, &aor_key);
465
466                 registrations = registrations->next;
467         }
468 error:
469         if (contact.s) pkg_free(contact.s);
470         if (contact_params.s) pkg_free(contact_params.s);
471         /* Free the XML-Document */
472
473         if(doc) xmlFreeDoc(doc);
474         return final_result;
475 }
476
477 int reginfo_handle_notify(struct sip_msg* msg, char* domain, char* s2) {
478         str body;
479         int result = 1;
480
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");
484                 return -1;
485         }
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. */
489                 return 1;
490         } else {
491                 body.s=get_body(msg);
492                 if (body.s== NULL) {
493                         LM_ERR("cannot extract body from msg\n");
494                         return -1;
495                 }
496                 body.len = get_content_length(msg);
497         }
498
499         LM_DBG("Body is %.*s\n", body.len, body.s);
500         
501         result = process_body(body, (udomain_t*)domain);
502
503         return result;
504 }
505