all: updated FSF address in GPL text
[sip-router] / modules / ims_registrar_pcscf / 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  * * History:
24  * ========
25  * 
26  * Nov 2013 Richard Good migrated pua_reginfo funtionality to ims_registrar_pcscf
27  * 
28  */
29
30 #include "notify.h"
31 #include "reg_mod.h"
32 #include "../../parser/parse_from.h"
33 #include "../../parser/parse_content.h"
34 #include "../../parser/parse_uri.h"
35 #include "../../modules/ims_usrloc_pcscf/usrloc.h"
36 #include "ul_callback.h"
37 #include <libxml/parser.h>
38
39 #include "subscribe.h"
40
41 #include "../pua/pua_bind.h"
42
43 /*<?xml version="1.0"?>
44 <reginfo xmlns="urn:ietf:params:xml:ns:reginfo" version="0" state="full">
45 .<registration aor="sip:carsten@ng-voice.com" id="0xb33fa860" state="active">
46 ..<contact id="0xb33fa994" state="active" event="registered" expires="3600">
47 ...<uri>sip:carsten@10.157.87.36:43582;transport=udp</uri>
48 ...<unknown-param name="+g.3gpp.cs-voice"></unknown-param>
49 ...<unknown-param name="+g.3gpp.icsi-ref">urn0X0.0041FB74E7B54P-1022urn-70X0P+03gpp-application.ims.iari.gsma-vs</unknown-param>
50 ...<unknown-param name="audio"></unknown-param>
51 ...<unknown-param name="+g.oma.sip-im.large-message"></unknown-param>
52 ...<unknown-param name="+g.3gpp.smsip"></unknown-param>
53 ...<unknown-param name="language">en,fr</unknown-param>
54 ...<unknown-param name="+g.oma.sip-im"></unknown-param>
55 ...<unknown-param name="expires">600000</unknown-param>
56 ..</contact>
57 .</registration>
58 </reginfo> */
59
60 #define STATE_ACTIVE 1
61 #define STATE_TERMINATED 0
62 #define STATE_UNKNOWN -1
63
64 #define EVENT_UNKNOWN -1
65 #define EVENT_REGISTERED 0
66 #define EVENT_UNREGISTERED 1
67 #define EVENT_TERMINATED 2
68 #define EVENT_CREATED 3
69 #define EVENT_REFRESHED 4
70 #define EVENT_EXPIRED 5
71 #define EVENT_DEACTIVATED 6
72
73 #define RESULT_ERROR -1
74 #define RESULT_CONTACTS_FOUND 1
75
76 extern usrloc_api_t ul;
77 extern time_t time_now;
78
79 int process_contact(udomain_t * _d, int expires, str contact_uri, int contact_state) {
80         
81         pcontact_t* pcontact;
82         
83         struct pcontact_info ci;
84         
85         int local_time_now;
86         int ret = RESULT_CONTACTS_FOUND;
87
88         pcscf_act_time();
89         local_time_now = time_now;
90         
91         //get contact
92         //if does not exist then add it
93         //if it does exist check if state it terminated - if so delete it, if not update it
94         
95         memset(&ci, 0, sizeof(struct pcontact_info));
96         ci.num_public_ids=0;
97         ci.num_service_routes=0;
98         
99         expires = local_time_now + expires;  //turn expires into correct time since epoch format
100         LM_DBG("Changed expires to format time since the epoch: %d", expires);
101         ci.expires=expires;
102         ci.reg_state = PCONTACT_REGISTERED;
103         
104         
105         ul.lock_udomain(_d, &contact_uri);
106         
107         if (ul.get_pcontact(_d, &contact_uri, &pcontact) != 0) { //contact does not exist
108             if (contact_state == STATE_TERMINATED) {
109                 LM_DBG("This contact: <%.*s> is in state terminated and is not in usrloc, ignore\n", contact_uri.len, contact_uri.s);
110                 ret = RESULT_CONTACTS_FOUND;
111                 goto done;
112             }
113                 LM_DBG("This contact: <%.*s> is in state active and is not in usrloc so adding it to usrloc, expires: %d which is in %d seconds\n", contact_uri.len, contact_uri.s, expires, expires-local_time_now);
114                 if (ul.insert_pcontact(_d, &contact_uri, &ci, &pcontact) != 0) {
115                         LM_ERR("Failed inserting new pcontact\n");
116                         ret = RESULT_ERROR;
117                         goto done;
118                 } else {
119                         //register for callbacks on this contact so we can send PUBLISH to SCSCF should status change
120                         LM_DBG("registering for UL callback\n");
121                         ul.register_ulcb(pcontact, PCSCF_CONTACT_DELETE | PCSCF_CONTACT_EXPIRE, callback_pcscf_contact_cb, NULL);
122                 }
123         } else {//contact exists
124                 if (contact_state == STATE_TERMINATED) {
125                         //delete contact
126                         LM_DBG("This contact <%.*s> is in state terminated and is in usrloc so removing it from usrloc\n", contact_uri.len, contact_uri.s);
127                         if (ul.delete_pcontact(_d, &contact_uri, pcontact) != 0) {
128                                 LM_DBG("failed to delete pcscf contact <%.*s> - not a problem this may have been removed by de registration", contact_uri.len, contact_uri.s);
129                         }
130                 }else {//state is active
131                         //update this contact
132                         LM_DBG("This contact: <%.*s> is in state active and is in usrloc so just updating - old expires: %li, new expires: %i which is in %i seconds\n", contact_uri.len, contact_uri.s,
133                                                                         pcontact->expires,
134                                                                         expires,
135                                                                         expires-local_time_now);
136                         if (ul.update_pcontact(_d, &ci, pcontact) != 0) {
137                                 LM_ERR("failed to update pcscf contact\n");
138                                 ret = RESULT_ERROR;
139                                 goto done;
140                         }
141                         pcontact->expires = expires;
142                 }
143         }
144         
145 done:    
146         ul.unlock_udomain(_d, &contact_uri);
147         return ret;
148 }
149
150
151 int reginfo_parse_state(char * s) {
152         if (s == NULL) {
153                 return STATE_UNKNOWN;
154         }
155         switch (strlen(s)) {
156                 case 6:
157                         if (strncmp(s, "active", 6) ==  0) return STATE_ACTIVE;
158                         break;
159                 case 10:
160                         if (strncmp(s, "terminated", 10) ==  0) return STATE_TERMINATED;
161                         break;
162                 default:
163                         LM_ERR("Unknown State %s\n", s);
164                         return STATE_UNKNOWN;
165         }
166         LM_ERR("Unknown State %s\n", s);
167         return STATE_UNKNOWN;
168 }
169
170 int reginfo_parse_event(char * s) {
171         if (s == NULL) {
172                 return EVENT_UNKNOWN;
173         }
174         switch (strlen(s)) {
175                 case 7:
176                         if (strncmp(s, "created", 7) ==  0) return EVENT_CREATED;
177                         if (strncmp(s, "expired", 7) ==  0) return EVENT_EXPIRED;
178                         break;
179                 case 9:
180                         if (strncmp(s, "refreshed", 9) ==  0) return EVENT_CREATED;
181                         break;
182                 case 10:
183                         if (strncmp(s, "registered", 10) ==  0) return EVENT_REGISTERED;
184                         if (strncmp(s, "terminated", 10) ==  0) return EVENT_TERMINATED;
185                         break;
186             case 11:
187                         if (strncmp(s, "deactivated", 11) ==  0) return EVENT_DEACTIVATED;
188                         break;
189                 case 12:
190                         if (strncmp(s, "unregistered", 12) ==  0) return EVENT_UNREGISTERED;
191                         break;
192                 default:
193                         LM_ERR("Unknown Event %s\n", s);
194                         return EVENT_UNKNOWN;
195         }
196         LM_ERR("Unknown Event %s\n", s);
197         return EVENT_UNKNOWN;
198 }
199
200 xmlNodePtr xmlGetNodeByName(xmlNodePtr parent, const char *name) {
201         xmlNodePtr cur = parent;
202         xmlNodePtr match = NULL;
203         while (cur) {
204                 if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0)
205                         return cur;
206                 match = xmlGetNodeByName(cur->children, name);
207                 if (match)
208                         return match;
209                 cur = cur->next;
210         }
211         return NULL;
212 }
213
214 char * xmlGetAttrContentByName(xmlNodePtr node, const char *name) {
215         xmlAttrPtr attr = node->properties;
216         while (attr) {
217                 if (xmlStrcasecmp(attr->name, (unsigned char*)name) == 0)
218                         return (char*)xmlNodeGetContent(attr->children);
219                 attr = attr->next;
220         }
221         return NULL;
222 }
223
224 int process_body(struct sip_msg* msg, str notify_body, udomain_t * domain) {
225         xmlDocPtr doc= NULL;
226         xmlNodePtr doc_root = NULL, registrations = NULL, contacts = NULL, uris = NULL;
227         str aor = {0, 0};
228         str callid = {0, 0};
229         str contact_uri = {0, 0};
230         str received = {0,0};
231         str path = {0,0};
232         str user_agent = {0, 0};
233         int reg_state, contact_state, event, expires, result, final_result = RESULT_ERROR;
234         char * expires_char,  * cseq_char;
235         int cseq = 0;
236         pv_elem_t *presentity_uri_pv;
237         
238         doc = xmlParseMemory(notify_body.s, notify_body.len);
239         if(doc== NULL)  {
240                 LM_ERR("Error while parsing the xml body message, Body is:\n%.*s\n",
241                         notify_body.len, notify_body.s);
242                 return -1;
243         }
244         doc_root = xmlGetNodeByName(doc->children, "reginfo");
245         if(doc_root == NULL) {
246                 LM_ERR("while extracting the reginfo node\n");
247                 goto error;
248         }
249         registrations = doc_root->children;
250         while (registrations) {
251                 /* Only process registration sub-items */
252                 if (xmlStrcasecmp(registrations->name, BAD_CAST "registration") != 0)
253                         goto next_registration;
254                 reg_state = reginfo_parse_state(xmlGetAttrContentByName(registrations, "state"));
255                 if (reg_state == STATE_UNKNOWN) {
256                         LM_ERR("No state for this registration!\n");            
257                         goto next_registration;
258                 }
259                 aor.s = xmlGetAttrContentByName(registrations, "aor");
260                 if (aor.s == NULL) {
261                         LM_ERR("No AOR for this registration!\n");              
262                         goto next_registration;
263                 }
264                 aor.len = strlen(aor.s);
265                 LM_DBG("AOR %.*s has reg_state \"%d\"\n", aor.len, aor.s, reg_state);
266                 
267                 if (reg_state == STATE_TERMINATED) {
268                     //TODO we if there is a IMPU record state here we should delete all contacts associated to it
269                     //Right now we do it go through all the contacts
270                     LM_DBG("AOR %.*s is in state terminated so unsubscribing from reginfo\n", aor.len, aor.s);
271                     if(pv_parse_format(&aor, &presentity_uri_pv)<0) {
272                             LM_ERR("wrong format[%.*s] - failed unsubscribing to reginfo\n",aor.len, aor.s);
273                     }
274                     reginfo_subscribe_real(msg, presentity_uri_pv, 0, 0);
275                     pv_elem_free_all(presentity_uri_pv);
276                 }
277                 
278                 /* Now lets process the Contact's from this Registration: */
279                 contacts = registrations->children;
280                 while (contacts) {
281                         if (xmlStrcasecmp(contacts->name, BAD_CAST "contact") != 0)
282                                 goto next_contact;
283                         callid.s = xmlGetAttrContentByName(contacts, "callid");
284                         if (callid.s == NULL) {
285                                 LM_DBG("No Call-ID for this contact!\n");               
286                                 callid.len = 0;
287                         } else {
288                                 callid.len = strlen(callid.s);
289                                 LM_DBG("contact has callid <%.*s>\n", callid.len, callid.s);            
290                         }       
291                         
292                         received.s = xmlGetAttrContentByName(contacts, "received");
293                         if (received.s == NULL) {
294                                 LM_DBG("No received for this contact!\n");
295                                 received.len = 0;
296                         } else {
297                                 received.len = strlen(received.s);
298                                 LM_DBG("contact has received <%.*s>\n", received.len, received.s);
299                         }
300
301                         path.s = xmlGetAttrContentByName(contacts, "path");     
302                         if (path.s == NULL) {
303                                 LM_DBG("No path for this contact!\n");
304                                 path.len = 0;
305                         } else {
306                                 path.len = strlen(path.s);
307                                 LM_DBG("contact has path <%.*s>\n", path.len, path.s);
308                         }
309
310                         user_agent.s = xmlGetAttrContentByName(contacts, "user_agent");
311                         if (user_agent.s == NULL) {
312                                 LM_DBG("No user_agent for this contact!\n");
313                                 user_agent.len = 0;
314                         } else {
315                                 user_agent.len = strlen(user_agent.s);
316                                 LM_DBG("contact has user_agent <%.*s>\n", user_agent.len, user_agent.s);
317                         }
318                         event = reginfo_parse_event(xmlGetAttrContentByName(contacts, "event"));
319                         if (event == EVENT_UNKNOWN) {
320                                 LM_ERR("No event for this contact - going to next contact!\n");         
321                                 goto next_contact;
322                         }
323                         expires_char = xmlGetAttrContentByName(contacts, "expires");
324                         if (expires_char == NULL) {
325                                 LM_ERR("No expires for this contact - going to next contact!\n");               
326                                 goto next_contact;
327                         }
328                         expires = atoi(expires_char);
329                         if (expires < 0) {
330                                 LM_ERR("No valid expires for this contact - going to next contact!\n");         
331                                 goto next_contact;
332                         }
333                         
334                         contact_state = reginfo_parse_state(xmlGetAttrContentByName(contacts, "state"));
335                         if (contact_state == STATE_UNKNOWN) {
336                             LM_ERR("No state for this contact - going to next contact!\n");             
337                             goto next_contact;
338                         } 
339                         
340                         LM_DBG("Contact state %d: Event \"%d\", expires %d\n", contact_state, event, expires);
341
342                         
343                         
344                         cseq_char = xmlGetAttrContentByName(contacts, "cseq");
345                         if (cseq_char == NULL) {
346                                 LM_DBG("No cseq for this contact!\n");          
347                         } else {
348                                 cseq = atoi(cseq_char);
349                                 if (cseq < 0) {
350                                         LM_DBG("No valid cseq for this contact!\n");            
351                                 }
352                         }
353
354                         /* Now lets process the URI's from this Contact: */
355                         uris = contacts->children;
356                         while (uris) {
357                                 if (xmlStrcasecmp(uris->name, BAD_CAST "uri") != 0)
358                                         goto next_uri;
359                                 contact_uri.s = (char*)xmlNodeGetContent(uris); 
360                                 if (contact_uri.s == NULL) {
361                                         LM_ERR("No URI for this contact - going to next registration!\n");              
362                                         goto next_registration;
363                                 }
364                                 contact_uri.len = strlen(contact_uri.s);
365                                 LM_DBG("Contact: %.*s\n",
366                                         contact_uri.len, contact_uri.s);
367
368                                 /* Add to Usrloc: */
369                                 result = process_contact(domain, expires, contact_uri, contact_state);
370
371                                 /* Process the result */
372                                 if (final_result != RESULT_CONTACTS_FOUND) final_result = result;
373 next_uri:
374                                 uris = uris->next;
375                         }
376 next_contact:
377                         contacts = contacts->next;
378                 }
379                 
380 next_registration:
381                 // if (ul_record) ul.release_urecord(ul_record);                
382                 /* Unlock the domain for this AOR: */
383                 //if (aor_key.len > 0)
384                 //      ul.unlock_udomain(domain, &aor_key);
385
386                 registrations = registrations->next;
387         }
388 error:
389         /* Free the XML-Document */
390         if(doc) xmlFreeDoc(doc);
391         return final_result;
392 }
393
394
395
396 int reginfo_handle_notify(struct sip_msg* msg, char* domain, char* s2) {
397         
398         LM_DBG("Handling notify\n");
399         str body;
400         int result = 1;
401
402         /* If not done yet, parse the whole message now: */
403         if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
404                 LM_ERR("Error parsing headers\n");
405                 return -1;
406         }
407         if (get_content_length(msg) == 0) {
408                 LM_DBG("Content length = 0\n");
409                 /* No Body? Then there is no published information available, which is ok. */
410                 return 1;
411         } else {
412                 body.s=get_body(msg);
413                 if (body.s== NULL) {
414                         LM_ERR("cannot extract body from msg\n");
415                         return -1;
416                 }
417                 body.len = get_content_length(msg);
418         }
419
420         LM_DBG("Body is %.*s\n", body.len, body.s);
421         
422         result = process_body(msg, body, (udomain_t*)domain);
423
424         
425         return result;
426 }
427