keepalive: early start of OPTIONS checking
[sip-router] / src / modules / pua_reginfo / usrloc_cb.c
1 /*
2  * pua_reginfo module - Presence-User-Agent Handling of reg events
3  *
4  * Copyright (C) 2011 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 "usrloc_cb.h"
25 #include "pua_reginfo.h"
26 #include <libxml/parser.h>
27 #include "../pua/pua.h"
28 #include "../pua/send_publish.h"
29
30 /*
31 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.
32 Call-ID: 9ad9f89f-164d-bb86-1072-52e7e9eb5025.
33 */
34
35 /*<?xml version="1.0"?>
36 <reginfo xmlns="urn:ietf:params:xml:ns:reginfo" version="0" state="full">
37 .<registration aor="sip:carsten@ng-voice.com" id="0xb33fa860" state="active">
38 ..<contact id="0xb33fa994" state="active" event="registered" expires="3600">
39 ...<uri>sip:carsten@10.157.87.36:43582;transport=udp</uri>
40 ...<unknown-param name="+g.3gpp.cs-voice"></unknown-param>
41 ...<unknown-param name="+g.3gpp.icsi-ref">urn0X0.0041FB74E7B54P-1022urn-70X0P+03gpp-application.ims.iari.gsma-vs</unknown-param>
42 ...<unknown-param name="audio"></unknown-param>
43 ...<unknown-param name="+g.oma.sip-im.large-message"></unknown-param>
44 ...<unknown-param name="+g.3gpp.smsip"></unknown-param>
45 ...<unknown-param name="language">en,fr</unknown-param>
46 ...<unknown-param name="+g.oma.sip-im"></unknown-param>
47 ...<unknown-param name="expires">600000</unknown-param>
48 ..</contact>
49 .</registration>
50 </reginfo> */
51
52 static int _pua_reginfo_self_op = 0;
53
54 void pua_reginfo_update_self_op(int v)
55 {
56         _pua_reginfo_self_op = v;
57 }
58
59 str* build_reginfo_full(urecord_t * record, str uri, ucontact_t* c, int type) {
60         xmlDocPtr  doc = NULL; 
61         xmlNodePtr root_node = NULL;
62         xmlNodePtr registration_node = NULL;
63         xmlNodePtr contact_node = NULL;
64         xmlNodePtr uri_node = NULL;
65         str * body= NULL;
66         ucontact_t * ptr;
67         char buf[512];
68         int reg_active = 0;
69         time_t cur_time = time(0);
70
71         /* create the XML-Body */
72         doc = xmlNewDoc(BAD_CAST "1.0");
73         if(doc==0) {
74                 LM_ERR("Unable to create XML-Doc\n");
75                 return NULL;
76         }
77
78         root_node = xmlNewNode(NULL, BAD_CAST "reginfo");
79         if(root_node==0) {
80                 LM_ERR("Unable to create reginfo-XML-Element\n");
81                 return NULL;
82         }
83         /* This is our Root-Element: */
84         xmlDocSetRootElement(doc, root_node);
85         
86         xmlNewProp(root_node, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:reginfo");
87
88         /* we set the version to 0 but it should be set to the correct value in the pua module */
89         xmlNewProp(root_node, BAD_CAST "version", BAD_CAST "0");
90         xmlNewProp(root_node, BAD_CAST "state", BAD_CAST "full" );
91
92         /* Registration Node */
93         registration_node =xmlNewChild(root_node, NULL, BAD_CAST "registration", NULL) ;
94         if( registration_node ==NULL) {
95                 LM_ERR("while adding child\n");
96                 goto error;
97         }
98
99         /* Add the properties to this Node for AOR and ID: */
100         xmlNewProp(registration_node, BAD_CAST "aor", BAD_CAST uri.s);
101         snprintf(buf, sizeof(buf), "%p", record);
102         xmlNewProp(registration_node, BAD_CAST "id", BAD_CAST buf);
103
104         LM_DBG("Updated Contact %.*s[%.*s]\n", c->c.len, c->c.s,
105                 c->ruid.len, c->ruid.s);
106
107         ptr = record->contacts;
108         while (ptr) {
109                 if (VALID_CONTACT(ptr, cur_time)) {
110                         LM_DBG("Contact %.*s[%.*s]\n", ptr->c.len, ptr->c.s,
111                                 ptr->ruid.len, ptr->ruid.s);
112                         /* Contact-Node */
113                         contact_node =xmlNewChild(registration_node, NULL, BAD_CAST "contact", NULL) ;
114                         if( contact_node ==NULL) {
115                                 LM_ERR("while adding child\n");
116                                 goto error;
117                         }
118                         memset(buf, 0, sizeof(buf));
119                         snprintf(buf, sizeof(buf), "%p", ptr);
120                         xmlNewProp(contact_node, BAD_CAST "id", BAD_CAST buf);
121                         /* Check, if this is the modified contact: */
122                         if ((c->ruid.len == ptr->ruid.len) &&
123                                 !memcmp(c->ruid.s, ptr->ruid.s, c->ruid.len))
124                         {
125                                 if ((type & UL_CONTACT_INSERT) || (type & UL_CONTACT_UPDATE)) {
126                                         reg_active = 1;
127                                         xmlNewProp(contact_node, BAD_CAST "state", BAD_CAST "active");
128                                 } else
129                                         xmlNewProp(contact_node, BAD_CAST "state", BAD_CAST "terminated");
130                                 if (type & UL_CONTACT_INSERT) xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "created");
131                                 else if (type & UL_CONTACT_UPDATE) xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "refreshed");
132                                 else if (type & UL_CONTACT_EXPIRE) xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "expired");
133                                 else if (type & UL_CONTACT_DELETE) xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "unregistered");
134                                 else xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "unknown");
135                                 memset(buf, 0, sizeof(buf));
136                                 snprintf(buf, sizeof(buf), "%i", (int)(ptr->expires-cur_time));
137                                 xmlNewProp(contact_node, BAD_CAST "expires", BAD_CAST buf);
138                         } else {
139                                 reg_active = 1;
140                                 xmlNewProp(contact_node, BAD_CAST "state", BAD_CAST "active");
141                                 xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "registered");
142                                 memset(buf, 0, sizeof(buf));
143                                 snprintf(buf, sizeof(buf), "%i", (int)(ptr->expires-cur_time));
144                                 xmlNewProp(contact_node, BAD_CAST "expires", BAD_CAST buf);
145                         }
146                         if (ptr->q != Q_UNSPECIFIED) {
147                                 float q = (float)ptr->q/1000;
148                                 memset(buf, 0, sizeof(buf));
149                                 snprintf(buf, sizeof(buf), "%.3f", q);
150                                 xmlNewProp(contact_node, BAD_CAST "q", BAD_CAST buf);
151                         }
152                         /* CallID Attribute */
153                         memset(buf, 0, sizeof(buf));
154                         snprintf(buf, sizeof(buf), "%.*s", ptr->callid.len, ptr->callid.s);
155                         xmlNewProp(contact_node, BAD_CAST "callid", BAD_CAST buf);
156
157                         /* CSeq Attribute */
158                         memset(buf, 0, sizeof(buf));
159                         snprintf(buf, sizeof(buf), "%d", ptr->cseq);
160                         xmlNewProp(contact_node, BAD_CAST "cseq", BAD_CAST buf);
161
162                         /* received Attribute */
163                         memset(buf, 0, sizeof(buf));
164                         snprintf(buf, sizeof(buf), "%.*s", ptr->received.len, ptr->received.s);
165                         xmlNewProp(contact_node, BAD_CAST "received", BAD_CAST buf);
166                         
167                         /* path Attribute */
168                         memset(buf, 0, sizeof(buf));
169                         snprintf(buf, sizeof(buf), "%.*s", ptr->path.len, ptr->path.s);
170                         xmlNewProp(contact_node, BAD_CAST "path", BAD_CAST buf);
171
172                         /* user_agent Attribute */
173                         memset(buf, 0, sizeof(buf));
174                         snprintf(buf, sizeof(buf), "%.*s", ptr->user_agent.len, ptr->user_agent.s);
175                         xmlNewProp(contact_node, BAD_CAST "user_agent", BAD_CAST buf);
176
177                         /* URI-Node */
178                         memset(buf, 0, sizeof(buf));
179                         snprintf(buf, sizeof(buf), "%.*s", ptr->c.len, ptr->c.s);
180                         uri_node = xmlNewChild(contact_node, NULL, BAD_CAST "uri", BAD_CAST buf) ;
181                         if(uri_node == NULL) {
182                                 LM_ERR("while adding child\n");
183                                 goto error;
184                         }
185                 }
186                 ptr = ptr->next;
187         }
188
189         /* add registration state (at least one active contact): */
190         if (reg_active==0)
191                 xmlNewProp(registration_node, BAD_CAST "state", BAD_CAST "terminated");
192         else
193                 xmlNewProp(registration_node, BAD_CAST "state", BAD_CAST "active");
194
195
196         /* create the body */
197         body = (str*)pkg_malloc(sizeof(str));
198         if(body == NULL) {
199                 LM_ERR("while allocating memory\n");
200                 return NULL;
201         }
202         memset(body, 0, sizeof(str));
203
204         /* Write the XML into the body */
205         xmlDocDumpFormatMemory(doc,(unsigned char**)(void*)&body->s,&body->len,1);
206
207         /*free the document */
208         xmlFreeDoc(doc);
209         xmlCleanupParser();
210
211         return body;
212 error:
213         if(body) {
214                 if(body->s) xmlFree(body->s);
215                 pkg_free(body);
216         }
217         if(doc) xmlFreeDoc(doc);
218         return NULL;
219 }       
220
221 void reginfo_usrloc_cb(ucontact_t* c, int type, void* param) {
222         str* body= NULL;
223         publ_info_t publ;
224         str content_type;
225         udomain_t * domain;
226         urecord_t * record = NULL;
227         int res;
228         str uri = {NULL, 0};
229         str user = {NULL, 0};
230
231         char* at = NULL;
232         char id_buf[512];
233         int id_buf_len;
234
235         if(_pua_reginfo_self_op == 1) {
236                 LM_DBG("operation triggered by own action for aor: %.*s (%d)\n",
237                                 c->aor->len, c->aor->s, type);
238                 return;
239         }
240
241         content_type.s = "application/reginfo+xml";
242         content_type.len = 23;
243         
244         /* Debug Output: */
245         LM_DBG("AOR: %.*s (%.*s)\n", c->aor->len, c->aor->s, c->domain->len, c->domain->s);
246         if(type & UL_CONTACT_INSERT) LM_DBG("type= UL_CONTACT_INSERT\n");
247         else if(type & UL_CONTACT_UPDATE) LM_DBG("type= UL_CONTACT_UPDATE\n");
248         else if(type & UL_CONTACT_EXPIRE) LM_DBG("type= UL_CONTACT_EXPIRE\n");
249         else if(type & UL_CONTACT_DELETE) LM_DBG("type= UL_CONTACT_DELETE\n");
250         else {
251                 LM_ERR("Unknown Type %i\n", type);
252                 return;
253         }
254         /* make a local copy of the AOR */
255         user.len = c->aor->len;
256         user.s = c->aor->s;
257
258         /* Get the UDomain for this account */
259         res = ul.get_udomain(c->domain->s, &domain);
260         if(res < 0) {
261                 LM_ERR("no domain found\n");
262                 return;
263         }
264
265         /* Get the URecord for this AOR */
266         res = ul.get_urecord(domain, &user, &record);
267         if (res > 0) {
268                 LM_ERR("' %.*s (%.*s)' Not found in usrloc\n", c->aor->len, c->aor->s, c->domain->len, c->domain->s);
269                 return;
270         }
271
272         /* Create AOR to be published */
273         /* Search for @ in the AOR. In case no domain was provided, we will add the "default domain" */
274         at = memchr(record->aor.s, '@', record->aor.len);
275         if (!at) {
276                 uri.len = record->aor.len + default_domain.len + 6;
277                 uri.s = (char*)pkg_malloc(sizeof(char) * uri.len);
278                 if(uri.s == NULL) {
279                         LM_ERR("Error allocating memory for URI!\n");
280                         goto error;
281                 }
282                 if (record->aor.len > 0)
283                         uri.len = snprintf(uri.s, uri.len, "sip:%.*s@%.*s", record->aor.len, record->aor.s, default_domain.len, default_domain.s);
284                 else
285                         uri.len = snprintf(uri.s, uri.len, "sip:%.*s", default_domain.len, default_domain.s);
286         } else {
287                 uri.len = record->aor.len + 6;
288                 uri.s = (char*)pkg_malloc(sizeof(char) * uri.len);
289                 if(uri.s == NULL) {
290                         LM_ERR("Error allocating memory for URI!\n");
291                         goto error;
292                 }
293                 uri.len = snprintf(uri.s, uri.len, "sip:%.*s", record->aor.len, record->aor.s);
294         }
295         
296         /* Build the XML-Body: */
297         body = build_reginfo_full(record, uri, c, type);
298
299         if(body == NULL || body->s == NULL) {
300                 LM_ERR("Error on creating XML-Body for publish\n");
301                 goto error;
302         }
303         LM_DBG("XML-Body:\n%.*s\n", body->len, body->s);
304
305         LM_DBG("Contact %.*s, %p\n", c->c.len, c->c.s, c);
306
307         memset(&publ, 0, sizeof(publ_info_t));
308
309         publ.pres_uri = &uri;
310         publ.body = body;
311         id_buf_len = snprintf(id_buf, sizeof(id_buf), "REGINFO_PUBLISH.%.*s@%.*s",
312                 c->aor->len, c->aor->s,
313                 c->domain->len, c->domain->s);
314         publ.id.s = id_buf;
315         publ.id.len = id_buf_len;
316         publ.content_type = content_type;
317         publ.expires = 3600;
318         
319         /* make UPDATE_TYPE, as if this "publish dialog" is not found 
320            by pua it will fallback to INSERT_TYPE anyway */
321         publ.flag|= UPDATE_TYPE;
322         publ.source_flag |= REGINFO_PUBLISH;
323         publ.event |= REGINFO_EVENT;
324         publ.extra_headers= NULL;
325
326         if(outbound_proxy.s && outbound_proxy.len)
327                 publ.outbound_proxy= &outbound_proxy;
328
329         if(pua.send_publish(&publ) < 0) {
330                 LM_ERR("Error while sending publish\n");
331         }       
332 error:
333         if (uri.s) pkg_free(uri.s);
334         if(body) {
335                 if(body->s) xmlFree(body->s);
336                 pkg_free(body);
337         }
338         if(record) ul.release_urecord(record);
339
340         return;
341 }