9f042650ce9b09ec871f82a6dd0d486c7ea1588e
[kamailio] / src / modules / lost / functions.c
1 /*
2  * lost module functions
3  *
4  * Copyright (C) 2019 Wolfgang Kampichler
5  * DEC112, FREQUENTIS AG
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
25 /*!
26  * \file
27  * \brief Kamailio lost :: functions
28  * \ingroup lost
29  * Module: \ref lost
30  */
31 /*****************/
32
33 #include "../../modules/http_client/curl_api.h"
34
35 #include "../../core/mod_fix.h"
36 #include "../../core/pvar.h"
37 #include "../../core/route_struct.h"
38 #include "../../core/ut.h"
39 #include "../../core/trim.h"
40 #include "../../core/mem/mem.h"
41 #include "../../core/parser/msg_parser.h"
42 #include "../../core/parser/parse_body.h"
43 #include "../../core/lvalue.h"
44
45 #include "pidf.h"
46 #include "utilities.h"
47
48 #define LOST_SUCCESS 200
49 #define LOST_CLIENT_ERROR 400
50 #define LOST_SERVER_ERROR 500
51
52 extern httpc_api_t httpapi;
53
54 char mtheld[] = "application/held+xml;charset=utf-8";
55 char mtlost[] = "application/lost+xml;charset=utf-8";
56
57 char uri_element[] = "uri";
58 char name_element[] = "displayName";
59 char errors_element[] = "errors";
60
61 /*
62  * lost_function_held(msg, con, pidf, url, err, id)
63  * assembles and runs HELD locationRequest, parses results
64  */
65 int lost_function_held(struct sip_msg *_m, char *_con, char *_pidf, char *_url,
66                 char *_err, char *_id)
67 {
68         pv_spec_t *pspidf;
69         pv_spec_t *psurl;
70         pv_spec_t *pserr;
71
72         pv_value_t pvpidf;
73         pv_value_t pvurl;
74         pv_value_t pverr;
75
76         xmlDocPtr doc = NULL;
77         xmlNodePtr root = NULL;
78
79         str did = {NULL, 0};
80         str que = {NULL, 0};
81         str con = {NULL, 0};
82         str geo = {NULL, 0};
83         str err = {NULL, 0};
84         str res = {NULL, 0};
85         str idhdr = {NULL, 0};
86
87         int curlres = 0;
88
89         if(_con == NULL || _pidf == NULL || _url == NULL || _err == NULL) {
90                 LM_ERR("invalid parameter\n");
91                 goto err;
92         }
93         /* connection from parameter */
94         if(fixup_get_svalue(_m, (gparam_p)_con, &con) != 0) {
95                 LM_ERR("cannot get connection string\n");
96                 goto err;
97         }
98         /* id from parameter */
99         if(_id) {
100                 if(fixup_get_svalue(_m, (gparam_p)_id, &did) != 0) {
101                         LM_ERR("cannot get device id\n");
102                         goto err;
103                 }
104                 if(!did.s) {
105                         LM_ERR("no device found\n");
106                         goto err;
107                 }
108         } else {
109
110                 LM_DBG("parsing P-A-I header\n");
111
112                 /* id from P-A-I header */
113                 idhdr.s = lost_get_pai_header(_m, &idhdr.len);
114                 if(idhdr.len == 0) {
115                         LM_WARN("P-A-I header not found, trying From header ...\n");
116
117                         LM_DBG("parsing From header\n");
118                         
119                         /* id from From header */
120                         idhdr.s = lost_get_from_header(_m, &idhdr.len);
121                         if(idhdr.len == 0) {
122                                 LM_ERR("no device id found\n");
123                                 goto err;
124                         }
125                 }
126                 did.s = idhdr.s;
127                 did.len = idhdr.len;
128         }
129         LM_INFO("### HELD id [%.*s]\n", did.len, did.s);
130
131         /* check if connection exists */
132         if(httpapi.http_connection_exists(&con) == 0) {
133                 LM_ERR("connection: [%s] does not exist\n", con.s);
134                 goto err;
135         }
136
137         /* assemble locationRequest */
138         que.s = lost_held_location_request(did.s, &que.len);
139         /* free memory */
140         lost_free_string(&idhdr);
141         did.s = NULL;
142         did.len = 0;
143         if(!que.s) {
144                 LM_ERR("held request document error\n");
145                 goto err;
146         }
147
148         LM_DBG("held location request: [%s]\n", que.s);
149
150         /* send locationRequest to location server - HTTP POST */
151         curlres = httpapi.http_connect(_m, &con, NULL, &res, mtheld, &que);
152         /* only HTTP 2xx responses are accepted */ 
153         if(curlres >= 300 || curlres < 100) {
154                 LM_ERR("[%.*s] failed with error: %d\n", con.len, con.s, curlres);
155                 res.s = NULL;
156                 res.len = 0;
157                 goto err;
158         }
159
160         LM_DBG("[%.*s] returned: %d\n", con.len, con.s, curlres);
161
162         /* free memory */
163         lost_free_string(&que);
164         /* read and parse the returned xml */
165         doc = xmlReadMemory(res.s, res.len, 0, NULL,
166                         XML_PARSE_NOBLANKS | XML_PARSE_NONET | XML_PARSE_NOCDATA);
167         if(!doc) {
168                 LM_WARN("invalid xml document: [%.*s]\n", res.len, res.s);
169                 doc = xmlRecoverMemory(res.s, res.len);
170                 if(!doc) {
171                         LM_ERR("xml document recovery failed on: [%.*s]\n", res.len,
172                                         res.s);
173                         goto err;
174                 }
175
176                 LM_DBG("xml document recovered\n");
177         }
178         root = xmlDocGetRootElement(doc);
179         if(!root) {
180                 LM_ERR("empty xml document\n");
181                 goto err;
182         }
183         /* check the root element, shall be locationResponse, or errors */
184         if(!xmlStrcmp(root->name, (const xmlChar *)"locationResponse")) {
185
186                 LM_DBG("HELD location response [%.*s]\n", res.len, res.s);
187
188                 /* get the locationUri element */
189                 geo.s = lost_get_content(root, (char *)"locationURI", &geo.len);
190                 if(!geo.s) {
191                         LM_ERR("no locationURI element found\n");
192                         goto err;
193                 }
194         } else if(!xmlStrcmp(root->name, (const xmlChar *)"error")) {
195
196                 LM_DBG("HELD error response [%.*s]\n", res.len, res.s);
197
198                 /* get the error patterm */
199                 err.s = lost_get_property(root, (char *)"code", &err.len);
200                 if(!err.s) {
201                         LM_ERR("error - code property not found: [%.*s]\n", res.len,
202                                         res.s);
203                         goto err;
204                 }
205                 LM_WARN("locationRequest error response: [%.*s]\n", err.len, err.s);
206         } else {
207                 LM_ERR("root element is not valid: [%.*s]\n", res.len, res.s);
208                 goto err;
209         }
210         xmlFreeDoc(doc);
211
212         /* set writeable pvars */
213         pvpidf.rs = res;
214         pvpidf.rs.s = res.s;
215         pvpidf.rs.len = res.len;
216
217         pvpidf.flags = PV_VAL_STR;
218         pspidf = (pv_spec_t *)_pidf;
219         pspidf->setf(_m, &pspidf->pvp, (int)EQ_T, &pvpidf);
220
221         pvurl.rs = geo;
222         pvurl.rs.s = geo.s;
223         pvurl.rs.len = geo.len;
224
225         pvurl.flags = PV_VAL_STR;
226         psurl = (pv_spec_t *)_url;
227         psurl->setf(_m, &psurl->pvp, (int)EQ_T, &pvurl);
228
229         pverr.rs = err;
230         pverr.rs.s = err.s;
231         pverr.rs.len = err.len;
232
233         pverr.flags = PV_VAL_STR;
234         pserr = (pv_spec_t *)_err;
235         pserr->setf(_m, &pserr->pvp, (int)EQ_T, &pverr);
236
237         return (err.len > 0) ? LOST_SERVER_ERROR : LOST_SUCCESS;
238
239 err:
240         if(doc)
241                 xmlFreeDoc(doc);
242         
243         lost_free_string(&idhdr);
244         lost_free_string(&que);
245
246         return LOST_CLIENT_ERROR;
247 }
248
249 /*
250  * lost_function(msg, con, pidf, uri, name, err, pidf, urn)
251  * assembles and runs LOST findService request, parses results
252  */
253 int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
254                 char *_err, char *_pidf, char *_urn)
255 {
256         pv_spec_t *psname;
257         pv_spec_t *psuri;
258         pv_spec_t *pserr;
259
260         pv_value_t pvname;
261         pv_value_t pvuri;
262         pv_value_t pverr;
263
264         p_loc_t loc = NULL;
265
266         xmlDocPtr doc = NULL;
267         xmlNodePtr root = NULL;
268
269         str uri = {NULL, 0};
270         str urn = {NULL, 0};
271         str err = {NULL, 0};
272         str res = {NULL, 0};
273         str con = {NULL, 0};
274         str ret = {NULL, 0};
275         str geo = {NULL, 0};
276         str geohdr = {NULL, 0};
277         str name = {NULL, 0};
278         str pidf = {NULL, 0};
279         str pidfhdr = {NULL, 0};
280
281         struct msg_start *fl;
282         char *search = NULL;
283         int curlres = 0;
284         
285         if(_con == NULL || _uri == NULL || _name == NULL || _err == NULL) {
286                 LM_ERR("invalid parameter\n");
287                 goto err;
288         }
289         if(fixup_get_svalue(_m, (gparam_p)_con, &con) != 0) {
290                 LM_ERR("cannot get connection string\n");
291                 goto err;
292         }
293         /* urn from parameter */
294         if(_urn) {
295                 if(fixup_get_svalue(_m, (gparam_p)_urn, &urn) != 0) {
296                         LM_ERR("cannot get service urn\n");
297                         goto err;
298                 }
299         }
300         /* urn from request line */
301         if(urn.len == 0) {
302                 LM_WARN("no sevice urn parameter, trying request line ...\n");
303                 fl = &(_m->first_line);
304                 urn.len = fl->u.request.uri.len;
305                 urn.s = fl->u.request.uri.s;
306         }
307         /* check urn scheme */
308         if(urn.len > 3) {
309                 search = urn.s;
310                 if(((*(search + 0) == 'u') || (*(search + 0) == 'U'))
311                                 && ((*(search + 1) == 'r') || (*(search + 1) == 'R'))
312                                 && ((*(search + 2) == 'n') || (*(search + 2) == 'N'))
313                                 && (*(search + 3) == ':')) {
314                         LM_INFO("### LOST urn [%.*s]\n", urn.len, urn.s);
315                 } else {
316                         LM_ERR("service urn not found\n");
317                         goto err;
318                 }
319         } else {
320                 LM_ERR("service urn not found\n");
321                 goto err;
322         }
323         /* pidf from parameter */
324         if(_pidf) {
325                 if(fixup_get_svalue(_m, (gparam_p)_pidf, &pidf) != 0) {
326                         LM_ERR("cannot get pidf-lo\n");
327                         goto err;
328                 }
329         }
330         /* pidf from geolocation header */
331         if(pidf.len == 0) {
332                 LM_WARN("no pidf parameter, trying geolocation header ...\n");
333                 geohdr.s = lost_get_geolocation_header(_m, &geohdr.len);
334                 if(!geohdr.s) {
335                         LM_ERR("geolocation header not found\n");
336                         goto err;
337                 } else {
338
339                         LM_DBG("geolocation header found\n");
340
341                         /* pidf from multipart body, check cid scheme */
342                         if(geohdr.len > 6) {
343                                 search = geohdr.s;
344                                 if((*(search + 0) == '<')
345                                                 && ((*(search + 1) == 'c') || (*(search + 1) == 'C'))
346                                                 && ((*(search + 2) == 'i') || (*(search + 2) == 'I'))
347                                                 && ((*(search + 3) == 'd') || (*(search + 3) == 'D'))
348                                                 && (*(search + 4) == ':')) {
349                                         search += 4;
350                                         *search = '<';
351                                         geo.s = search;
352                                         geo.len = geo.len - 4;
353
354                                         LM_DBG("cid: [%.*s]\n", geo.len, geo.s);
355
356                                         /* get body part - filter=>content id */
357                                         pidf.s = get_body_part_by_filter(
358                                                         _m, 0, 0, geo.s, NULL, &pidf.len);
359                                         if(!pidf.s) {
360                                                 LM_ERR("no multipart body found\n");
361                                                 goto err;
362                                         }
363                                 }
364                                 /* no pidf-lo so far ... check http(s) scheme */
365                                 if(((*(search + 0) == 'h') || (*(search + 0) == 'H'))
366                                                 && ((*(search + 1) == 't') || (*(search + 1) == 'T'))
367                                                 && ((*(search + 2) == 't') || (*(search + 2) == 'T'))
368                                                 && ((*(search + 3) == 'p') || (*(search + 3) == 'P'))) {
369                                         geo.s = geohdr.s;
370                                         geo.len = geohdr.len;
371
372                                         if(*(search + 4) == ':') {
373                                         
374                                                 LM_DBG("http url: [%.*s]\n", geo.len, geo.s);
375                                         
376                                         } else if(((*(search + 4) == 's') || (*(search + 4) == 'S'))
377                                                         && (*(search + 5) == ':')) {
378                                         
379                                                 LM_DBG("https url: [%.*s]\n", geo.len, geo.s);
380                                         
381                                         } else {
382                                                 LM_ERR("invalid url: [%.*s]\n", geo.len, geo.s);
383                                                 goto err;
384                                         }
385
386                                         /* ! dereference pidf.lo at location server - HTTP GET */
387                                         /* ! requires hack in http_client module */
388                                         /* ! functions.c => http_client_query => query_params.oneline = 0; */
389                                         curlres = httpapi.http_client_query(_m, geo.s, &pidfhdr, NULL, NULL);
390                                         /* free memory */
391                                         lost_free_string(&geohdr);
392                                         geo.s = NULL;
393                                         geo.len = 0;
394                                         /* only HTTP 2xx responses are accepted */ 
395                                         if(curlres >= 300 || curlres < 100) {
396                                                 LM_ERR("http GET failed with error: %d\n", curlres);
397                                                 pidfhdr.s = NULL;
398                                                 pidfhdr.len = 0;
399                                                 goto err;
400                                         }
401
402                                         LM_DBG("http GET returned: %d\n", curlres);
403
404                                         if(!pidfhdr.s) {
405                                                 LM_ERR("dereferencing location failed\n");
406                                                 goto err;
407                                         }
408                                         pidf.s = pidfhdr.s;
409                                         pidf.len = pidfhdr.len;
410                                 }
411                         } else {
412                                 LM_ERR("invalid geolocation header\n");
413                                 goto err;
414                         }
415                 }
416         }
417
418         /* no pidf-lo return error */
419         if(!pidf.s) {
420                 LM_ERR("pidf-lo not found\n");
421                 goto err;
422         }
423
424         LM_DBG("pidf-lo: [%.*s]\n", pidf.len, pidf.s);
425
426         /* read and parse pidf-lo */
427         doc = xmlReadMemory(pidf.s, pidf.len, 0, NULL,
428                         XML_PARSE_NOBLANKS | XML_PARSE_NONET | XML_PARSE_NOCDATA);
429
430         if(!doc) {
431                 LM_WARN("invalid xml (pidf-lo): [%.*s]\n", pidf.len, pidf.s);
432                 doc = xmlRecoverMemory(pidf.s, pidf.len);
433                 if(!doc) {
434                         LM_ERR("xml (pidf-lo) recovery failed on: [%.*s]\n", pidf.len,
435                                         pidf.s);
436                         goto err;
437                 }
438
439                 LM_DBG("xml (pidf-lo) recovered\n");
440         }
441
442         root = xmlDocGetRootElement(doc);
443         if(!root) {
444                 LM_ERR("empty pidf-lo document\n");
445                 goto err;
446         }
447         if((!xmlStrcmp(root->name, (const xmlChar *)"presence"))
448                         || (!xmlStrcmp(root->name, (const xmlChar *)"locationResponse"))) {
449                 /* get the geolocation: point or circle, urn, ... */
450                 loc = lost_new_loc(urn);
451                 if(!loc) {
452                         LM_ERR("location object allocation failed\n");
453                         goto err;                       
454                 }
455                 if(lost_parse_location_info(root, loc) < 0) {
456                         LM_ERR("location element not found\n");
457                         goto err;
458                 }
459         } else {
460                 LM_ERR("findServiceResponse or presence element not found in "
461                            "[%.*s]\n",
462                                 pidf.len, pidf.s);
463                 goto err;
464         }
465
466         /* free memory */
467         lost_free_string(&pidfhdr);
468         pidf.s = NULL;
469         pidf.len = 0;
470
471         /* check if connection exits */
472         if(httpapi.http_connection_exists(&con) == 0) {
473                 LM_ERR("connection: [%.*s] does not exist\n", con.len, con.s);
474                 goto err;
475         }
476         /* assemble findService request */
477         res.s = lost_find_service_request(loc, &res.len);
478         /* free memory */
479         if(loc) {
480                 lost_free_loc(loc);
481                 loc = NULL;
482         }
483         xmlFreeDoc(doc);
484         doc = NULL;
485
486         if(!res.s) {
487                 LM_ERR("lost request failed\n");
488                 goto err;
489         }
490
491         LM_DBG("findService request: [%.*s]\n", res.len, res.s);
492
493         /* send findService request to mapping server - HTTP POST */
494         curlres = httpapi.http_connect(_m, &con, NULL, &ret, mtlost, &res);
495         /* only HTTP 2xx responses are accepted */ 
496         if(curlres >= 300 || curlres < 100) {
497                 LM_ERR("[%.*s] failed with error: %d\n", con.len, con.s, curlres);
498                 ret.s = NULL;
499                 ret.len = 0;
500                 goto err;
501         }
502
503         LM_DBG("[%.*s] returned: %d\n", con.len, con.s, curlres);
504
505         /* free memory */
506         lost_free_string(&res);
507
508         if(!ret.s) {
509                 LM_ERR("findService request failed\n");
510                 goto err;
511         }
512
513         LM_DBG("findService response: [%.*s]\n", ret.len, ret.s);
514
515         /* read and parse the returned xml */
516         doc = xmlReadMemory(ret.s, ret.len, 0, 0,
517                         XML_PARSE_NOBLANKS | XML_PARSE_NONET | XML_PARSE_NOCDATA);
518
519         if(!doc) {
520                 LM_ERR("invalid xml document: [%.*s]\n", ret.len, ret.s);
521                 doc = xmlRecoverMemory(ret.s, ret.len);
522                 if(!doc) {
523                         LM_ERR("xml document recovery failed on: [%.*s]\n", ret.len,
524                                         ret.s);
525                         goto err;
526                 }
527
528                 LM_DBG("xml document recovered\n");
529         }
530         root = xmlDocGetRootElement(doc);
531         if(!root) {
532                 LM_ERR("empty xml document: [%.*s]\n", ret.len, ret.s);
533                 goto err;
534         }
535         /* check the root element, shall be findServiceResponse, or errors */
536         if((!xmlStrcmp(root->name, (const xmlChar *)"findServiceResponse"))) {
537                 /* get the uri element */
538                 uri.s = lost_get_content(root, uri_element, &uri.len);
539                 if(!uri.s) {
540                         LM_ERR("uri element not found: [%.*s]\n", ret.len, ret.s);
541                         goto err;
542                 }
543                 LM_INFO("### LOST uri [%.*s]\n", uri.len, uri.s);
544                 /* get the displayName element */
545                 name.s = lost_get_content(root, name_element, &name.len);
546                 if(!name.s) {
547                         LM_ERR("displayName element not found: [%.*s]\n", ret.len, ret.s);
548                         goto err;
549                 }
550                 LM_INFO("### LOST name [%.*s]\n", name.len, name.s);
551         } else if((!xmlStrcmp(root->name, (const xmlChar *)"errors"))) {
552
553                 LM_DBG("findService error response received\n");
554
555                 /* get the error patterm */
556                 err.s = lost_get_childname(root, errors_element, &err.len);
557                 if(!err.s) {
558                         LM_ERR("error pattern element not found: [%.*s]\n", ret.len,
559                                         ret.s);
560                         goto err;
561                 }
562                 LM_WARN("findService error response: [%.*s]\n", err.len, err.s);
563         } else {
564                 LM_ERR("root element is not valid: [%.*s]\n", ret.len, ret.s);
565                 goto err;
566         }
567
568         /* free memory */
569         xmlFreeDoc(doc);
570         doc = NULL;
571         lost_free_string(&ret);
572
573         /* set writable pvars */
574         pvname.rs = name;
575         pvname.rs.s = name.s;
576         pvname.rs.len = name.len;
577
578         pvname.flags = PV_VAL_STR;
579         psname = (pv_spec_t *)_name;
580         psname->setf(_m, &psname->pvp, (int)EQ_T, &pvname);
581
582         pvuri.rs = uri;
583         pvuri.rs.s = uri.s;
584         pvuri.rs.len = uri.len;
585
586         pvuri.flags = PV_VAL_STR;
587         psuri = (pv_spec_t *)_uri;
588         psuri->setf(_m, &psuri->pvp, (int)EQ_T, &pvuri);
589
590         pverr.rs = err;
591         pverr.rs.s = err.s;
592         pverr.rs.len = err.len;
593
594         pverr.flags = PV_VAL_STR;
595         pserr = (pv_spec_t *)_err;
596         pserr->setf(_m, &pserr->pvp, (int)EQ_T, &pverr);
597
598         return (err.len > 0) ? LOST_SERVER_ERROR : LOST_SUCCESS;
599
600 err:
601         if(loc)
602                 lost_free_loc(loc);
603         if(doc)
604                 xmlFreeDoc(doc);
605
606         lost_free_string(&pidfhdr);
607         lost_free_string(&geohdr);
608         lost_free_string(&ret);
609
610         return LOST_CLIENT_ERROR;
611 }