lost: added civic address parsing via xpath
[kamailio] / src / modules / lost / utilities.c
1 /*
2  * lost module utility functions
3  *
4  * Copyright (C) 2020 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 :: utilities
28  * \ingroup lost
29  * Module: \ref lost
30  */
31
32 #include <stdio.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <ctype.h>
36 #include <time.h>
37
38 #include "../../core/parser/msg_parser.h"
39 #include "../../core/parser/parse_content.h"
40 #include "../../core/parser/parse_uri.h"
41 #include "../../core/parser/parse_from.h"
42 #include "../../core/parser/parse_ppi_pai.h"
43 #include "../../core/dprint.h"
44 #include "../../core/mem/mem.h"
45 #include "../../core/mem/shm_mem.h"
46 #include "../../core/rand/kam_rand.h"
47
48 #include "pidf.h"
49 #include "utilities.h"
50
51 /*
52  * lost_trim_content(dest, lgth)
53  * removes whitespace that my occur in a content of an xml element
54  */
55 char *lost_trim_content(char *str, int *lgth)
56 {
57         char *end;
58
59         while(isspace(*str))
60                 str++;
61
62         if(*str == 0)
63                 return NULL;
64
65         end = str + strlen(str) - 1;
66
67         while(end > str && isspace(*end))
68                 end--;
69
70         *(end + 1) = '\0';
71
72         *lgth = (end + 1) - str;
73
74         return str;
75 }
76
77 /*
78  * lost_rand_str(dest, length)
79  * creates a random string used as temporary id in a findService request
80  */
81 void lost_rand_str(char *dest, size_t lgth)
82 {
83         size_t index;
84         char charset[] = "0123456789"
85                                          "abcdefghijklmnopqrstuvwxyz"
86                                          "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
87         while(lgth-- > 0) {
88                 index = (double)kam_rand() / RAND_MAX * (sizeof charset - 1);
89                 *dest++ = charset[index];
90         }
91         *dest = '\0';
92 }
93
94 /*
95  * lost_new_loc(urn)
96  * creates a new location object in private memory and returns a pointer
97  */
98 p_loc_t lost_new_loc(str rurn)
99 {
100         s_loc_t *ptr = NULL;
101
102         char *id = NULL;
103         char *urn = NULL;
104
105         ptr = (s_loc_t *)pkg_malloc(sizeof(s_loc_t));
106         if(ptr == NULL) {
107                 goto err;
108         }
109
110         id = (char *)pkg_malloc(RANDSTRSIZE * sizeof(char) + 1);
111         if(id == NULL) {
112                 pkg_free(ptr);
113                 goto err;
114         }
115
116         urn = (char *)pkg_malloc(rurn.len + 1);
117         if(urn == NULL) {
118                 pkg_free(id);
119                 pkg_free(ptr);
120                 goto err;
121         }
122
123         memset(urn, 0, rurn.len + 1);
124         memcpy(urn, rurn.s, rurn.len);
125         urn[rurn.len] = '\0';
126
127         lost_rand_str(id, RANDSTRSIZE);
128
129         ptr->identity = id;
130         ptr->urn = urn;
131         ptr->longitude = NULL;
132         ptr->latitude = NULL;
133         ptr->geodetic = NULL;
134         ptr->civic = NULL;
135         ptr->profile = NULL;
136         ptr->radius = 0;
137         ptr->recursive = LOST_RECURSION_TRUE;   /* set recursion to true */
138         ptr->boundary = 0;      /* set boundary to reference */
139
140         return ptr;
141
142 err:
143         LM_ERR("no more private memory\n");
144         return NULL;
145 }
146
147 /*
148  * lost_new_held(uri, type, time, exact)
149  * creates a new held object in private memory and returns a pointer
150  */
151 p_held_t lost_new_held(str s_uri, str s_type, str s_time, int exact)
152 {
153         s_held_t *ptr = NULL;
154         
155         char *uri = NULL;
156         char *type = NULL;
157         char *time = NULL;
158
159         ptr = (s_held_t *)pkg_malloc(sizeof(s_held_t));
160         if(ptr == NULL) {
161                 goto err;
162         }
163
164         uri = (char *)pkg_malloc(s_uri.len + 1);
165         if(uri == NULL) {
166                 pkg_free(ptr);
167                 goto err;
168         }
169
170         type = (char *)pkg_malloc(s_type.len + 1);
171         if(type == NULL) {
172                 pkg_free(uri);
173                 pkg_free(ptr);
174                 goto err;
175         }
176
177         time = (char *)pkg_malloc(s_time.len + 1);
178         if(type == NULL) {
179                 pkg_free(type);
180                 pkg_free(uri);
181                 pkg_free(ptr);
182                 goto err;
183         }
184
185         memset(uri, 0, s_uri.len + 1);
186         memcpy(uri, s_uri.s, s_uri.len);
187         uri[s_uri.len] = '\0';
188
189         memset(type, 0, s_type.len + 1);
190         memcpy(type, s_type.s, s_type.len);
191         type[s_type.len] = '\0';
192
193         memset(time, 0, s_time.len + 1);
194         memcpy(time, s_time.s, s_time.len);
195         time[s_time.len] = '\0';
196
197         ptr->identity = uri;
198         ptr->type = type;
199         ptr->time = time;
200         ptr->exact = exact;
201
202         return ptr;
203
204 err:
205         LM_ERR("no more private memory\n");
206         return NULL;
207 }
208
209 /*
210  * lost_free_loc(ptr)
211  * frees a location object
212  */
213 void lost_free_loc(p_loc_t ptr)
214 {
215         pkg_free(ptr->identity);
216         pkg_free(ptr->urn);
217         if (ptr->civic)
218         pkg_free(ptr->civic);
219     if (ptr->geodetic)
220                 pkg_free(ptr->geodetic);
221     if (ptr->longitude)
222                 pkg_free(ptr->longitude);
223     if (ptr->latitude)
224                 pkg_free(ptr->latitude);
225     if (ptr->profile)
226             pkg_free(ptr->profile);
227         
228         pkg_free(ptr);
229         ptr = NULL;
230 }
231
232 /*
233  * lost_free_loc(ptr)
234  * frees a held location request object
235  */
236 void lost_free_held(p_held_t ptr)
237 {
238         pkg_free(ptr->identity);
239         pkg_free(ptr->type);
240         pkg_free(ptr->time);
241
242         pkg_free(ptr);
243         ptr = NULL;
244 }
245
246 /*
247  * lost_free_string(ptr)
248  * frees and resets a string
249  */
250 void lost_free_string(str *string)
251 {
252         str ptr = *string;
253
254         if(ptr.s) {
255                 pkg_free(ptr.s);
256                 ptr.s = NULL;
257                 ptr.len = 0;
258         }
259 }
260
261 /*
262  * lost_get_content(node, name, lgth)
263  * gets a nodes "name" content and returns string allocated in private memory
264  */
265 char *lost_get_content(xmlNodePtr node, const char *name, int *lgth)
266 {
267         xmlNodePtr cur = node;
268         char *content = NULL;
269         char *cnt = NULL;
270         int len;
271
272         *lgth = 0;
273
274         content = xmlNodeGetNodeContentByName(cur, name, NULL);
275         if(content == NULL) {
276                 LM_ERR("could not get XML node content\n");
277                 return cnt;
278         } else {
279                 len = strlen(content);
280                 cnt = (char *)pkg_malloc((len + 1) * sizeof(char));
281                 if(cnt == NULL) {
282                         LM_ERR("no more private memory\n");
283                         xmlFree(content);
284                         return cnt;
285                 }
286                 memset(cnt, 0, len + 1);
287                 memcpy(cnt, content, len);
288                 cnt[len] = '\0';
289         }
290
291         xmlFree(content);
292         *lgth = strlen(cnt);
293
294         return cnt;
295 }
296
297 /*
298  * lost_get_property(node, name, lgth)
299  * gets a nodes property "name" and returns string allocated in private memory
300  */
301 char *lost_get_property(xmlNodePtr node, const char *name, int *lgth)
302 {
303         xmlNodePtr cur = node;
304         char *content;
305         char *cnt = NULL;
306         int len;
307
308         *lgth = 0;
309
310         content = xmlNodeGetAttrContentByName(cur, name);
311         if(content == NULL) {
312                 LM_ERR("could not get XML node content\n");
313                 return cnt;
314         } else {
315                 len = strlen(content);
316                 cnt = (char *)pkg_malloc((len + 1) * sizeof(char));
317                 if(cnt == NULL) {
318                         LM_ERR("no more private memory\n");
319                         xmlFree(content);
320                         return cnt;
321                 }
322                 memset(cnt, 0, len + 1);
323                 memcpy(cnt, content, len);
324                 cnt[len] = '\0';
325         }
326
327         xmlFree(content);
328         *lgth = strlen(cnt);
329
330         return cnt;
331 }
332
333 /*
334  * lost_get_childname(name, lgth)
335  * gets a nodes child name and returns string allocated in private memory
336  */
337 char *lost_get_childname(xmlNodePtr node, const char *name, int *lgth)
338 {
339         xmlNodePtr cur = node;
340         xmlNodePtr parent = NULL;
341         xmlNodePtr child = NULL;
342         char *cnt = NULL;
343         int len;
344
345         *lgth = 0;
346
347         parent = xmlNodeGetNodeByName(cur, name, NULL);
348         if(parent == NULL) {
349                 LM_ERR("xmlNodeGetNodeByName() failed\n");
350                 return cnt;
351         }
352
353         child = parent->children;
354         if(child == NULL) {
355                 LM_ERR("%s has no children '%s'\n", parent->name, name);
356                 return cnt;
357         }
358
359         len = strlen((char *)child->name);
360         cnt = (char *)pkg_malloc((len + 1) * sizeof(char));
361         if(cnt == NULL) {
362                 LM_ERR("no more private memory\n");
363                 return cnt;
364         }
365
366         memset(cnt, 0, len + 1);
367         memcpy(cnt, child->name, len);
368         cnt[len] = '\0';
369
370         *lgth = strlen(cnt);
371
372         return cnt;
373 }
374
375 /*
376  * lost_get_geolocation_header(msg, lgth)
377  * gets the Geolocation header value and returns string allocated in
378  * private memory
379  */
380 char *lost_get_geolocation_header(struct sip_msg *msg, int *lgth)
381 {
382         struct hdr_field *hf;
383         char *res = NULL;
384
385         *lgth = 0;
386
387         if(parse_headers(msg, HDR_EOH_F, 0) == -1) {
388                 LM_ERR("failed to parse geolocation header\n");
389                 return res;
390         }
391
392         for(hf = msg->headers; hf; hf = hf->next) {
393                 if((hf->type == HDR_OTHER_T)
394                                 && (hf->name.len == LOST_GEOLOC_HEADER_SIZE - 2)) {
395                         /* possible hit */
396                         if(strncasecmp(hf->name.s, LOST_GEOLOC_HEADER,
397                                                         LOST_GEOLOC_HEADER_SIZE) == 0) {
398
399                                 res = (char *)pkg_malloc((hf->body.len + 1) * sizeof(char));
400                                 if(res == NULL) {
401                                         LM_ERR("no more private memory\n");
402                                         return res;
403                                 } else {
404                                         memset(res, 0, hf->body.len + 1);
405                                         memcpy(res, hf->body.s, hf->body.len + 1);
406                                         res[hf->body.len] = '\0';
407
408                                         *lgth = strlen(res);
409                                 }
410                         } else {
411                                 LM_ERR("header '%.*s' length %d\n", hf->body.len, hf->body.s,
412                                                 hf->body.len);
413                         }
414                         break;
415                 }
416         }
417
418         return res;
419 }
420
421 /*
422  * lost_get_pai_header(msg, lgth)
423  * gets the P-A-I header value and returns string allocated in
424  * private memory
425  */
426 char *lost_get_pai_header(struct sip_msg *msg, int *lgth)
427 {
428         struct hdr_field *hf;
429         to_body_t *pai_body;
430         char *res = NULL;
431
432         *lgth = 0;
433
434         if(parse_headers(msg, HDR_PAI_F, 0) == -1) {
435                 LM_ERR("could not parse P-A-I header\n");
436                 return res;
437         }
438
439         for(hf = msg->headers; hf; hf = hf->next) {
440                 if((hf->type == HDR_PAI_T)
441                                 && (hf->name.len == LOST_PAI_HEADER_SIZE - 2)) {
442                         /* possible hit */
443                         if(strncasecmp(hf->name.s, LOST_PAI_HEADER,
444                                                         LOST_PAI_HEADER_SIZE) == 0) {
445
446                                 LM_DBG("P-A-I body:  [%.*s]\n", hf->body.len, hf->body.s);
447
448                                 /* first, get some memory */
449                                 pai_body = pkg_malloc(sizeof(to_body_t));
450                                 if(pai_body == NULL) {
451                                         LM_ERR("no more private memory\n");
452                                         return res;
453                                 }
454                                 /* parse P-A-I body */
455                                 memset(pai_body, 0, sizeof(to_body_t));
456                                 parse_to(hf->body.s, hf->body.s + hf->body.len + 1, pai_body);
457                                 if(pai_body->error == PARSE_ERROR) {
458                                         LM_ERR("bad P-A-I header\n");
459                                         pkg_free(pai_body);
460                                         return res;
461                                 }
462                                 if(pai_body->error == PARSE_OK) {
463                                         res = (char *)pkg_malloc(
464                                                         (pai_body->uri.len + 1) * sizeof(char));
465                                         if(res == NULL) {
466                                                 LM_ERR("no more private memory\n");
467                                                 pkg_free(pai_body);
468                                                 return res;
469                                         } else {
470                                                 memset(res, 0, pai_body->uri.len + 1);
471                                                 memcpy(res, pai_body->uri.s, pai_body->uri.len + 1);
472                                                 res[pai_body->uri.len] = '\0';
473                                                 pkg_free(pai_body);
474
475                                                 *lgth = strlen(res);
476                                         }
477                                 }
478                         } else {
479                                 LM_ERR("header '%.*s' length %d\n", hf->body.len, hf->body.s,
480                                                 hf->body.len);
481                         }
482                         break;
483                 }
484         }
485
486         return res;
487 }
488
489 /*
490  * lost_get_from_header(msg, lgth)
491  * gets the From header value and returns string allocated in
492  * private memory
493  */
494 char *lost_get_from_header(struct sip_msg *msg, int *lgth)
495 {
496         to_body_t *f_body;
497         char *res = NULL;
498
499         *lgth = 0;
500
501         if(parse_headers(msg, HDR_FROM_F, 0) == -1) {
502                 LM_ERR("failed to parse From header\n");
503                 return res;
504         }
505
506         if(msg->from == NULL || get_from(msg) == NULL) {
507                 LM_ERR("From header not found\n");
508                 return res;
509         }
510         f_body = get_from(msg);
511
512         LM_DBG("From body:  [%.*s]\n", f_body->body.len, f_body->body.s);
513
514         res = (char *)pkg_malloc((f_body->uri.len + 1) * sizeof(char));
515         if(res == NULL) {
516                 LM_ERR("no more private memory\n");
517                 return res;
518         } else {
519                 memset(res, 0, f_body->uri.len + 1);
520                 memcpy(res, f_body->uri.s, f_body->uri.len + 1);
521                 res[f_body->uri.len] = '\0';
522
523                 *lgth = strlen(res);
524         }
525
526         return res;
527 }
528
529 /*
530  * lost_parse_geo(node, loc)
531  * parses locationResponse (pos|circle) and writes 
532  * results to location object
533  */
534 int lost_parse_geo(xmlNodePtr node, p_loc_t loc)
535 {
536         xmlNodePtr cur = NULL;
537
538         char bufLat[BUFSIZE];
539         char bufLon[BUFSIZE];
540         char *content = NULL;
541
542         char s_profile[] = LOST_PRO_GEO2D;
543
544         int iRadius = 0;
545         int len = 0;
546
547         cur = node;
548         /* find <pos> element */
549         content = xmlNodeGetNodeContentByName(cur, "pos", NULL);
550
551         if(content == NULL) {
552                 LM_WARN("could not find pos element\n");
553                 return -1;
554         }
555
556         sscanf(content, "%s %s", bufLat, bufLon);
557         xmlFree(content);
558
559         len = strlen((char *)bufLat);
560         loc->latitude = (char *)pkg_malloc(len + 1);
561         if(loc->latitude == NULL)
562                 goto err;
563
564         snprintf(loc->latitude, len, "%s", (char *)bufLat);
565
566         len = strlen((char *)bufLon);
567         loc->longitude = (char *)pkg_malloc(len + 1);
568         if(loc->longitude == NULL) {
569                 pkg_free(loc->latitude);
570                 goto err;
571         }
572
573         snprintf(loc->longitude, len, "%s", (char *)bufLon);
574
575         len = strlen((char *)bufLat) + strlen((char *)bufLon) + 1;
576         loc->geodetic = (char *)pkg_malloc(len + 1);
577         if(loc->longitude == NULL) {
578                 pkg_free(loc->latitude);
579                 pkg_free(loc->longitude);
580                 goto err;
581         }
582         
583         snprintf(loc->geodetic, len, "%s %s", (char *)bufLat, (char *)bufLon);
584
585         /* find <radius> element */
586         content = xmlNodeGetNodeContentByName(cur, "radius", NULL);
587         if(content != NULL) {
588                 sscanf(content, "%d", &iRadius);
589                 xmlFree(content);
590         }
591
592         /* write results */
593         loc->radius = iRadius;
594         loc->profile = (char *)pkg_malloc(strlen(s_profile) + 1);
595     strcpy(loc->profile, s_profile);
596
597         return 0;
598
599 err:
600         LM_ERR("no more private memory\n");
601         return -1;
602 }
603 /*
604  * lost_xpath_location(doc, path, loc)
605  * performs xpath expression on locationResponse and writes 
606  * results (location-info child element) to location object
607  */
608 int lost_xpath_location(xmlDocPtr doc, char *path, p_loc_t loc)
609 {
610         xmlXPathObjectPtr result = NULL;
611         xmlNodeSetPtr nodes = NULL;
612         xmlNodePtr root = NULL;
613         xmlNodePtr cur = NULL;
614         xmlDocPtr new = NULL;
615         xmlChar *xpath = NULL;
616         xmlChar *xmlbuff = NULL;
617         xmlChar *cname = NULL;
618
619         const unsigned char s_point[] = LOST_PNT;
620         const unsigned char s_circle[] = LOST_CIR;
621         const unsigned char s_civic[] = LOST_CIV;
622
623         char *ptr = NULL;
624         char *s_profile = NULL;
625
626         int buffersize = 0;
627         int remove = 0;
628         int size = 0;
629         int len = 0;
630         int i = 0;
631         int nok = -1;
632
633         xpath = (xmlChar *)path;
634         /* get location via xpath expression */
635         result = xmlGetNodeSet(doc, xpath, BAD_CAST XPATH_NS);
636
637         if(result == NULL) {
638                 LM_DBG("xmlGetNodeSet() returned no result\n");
639                 return -1;
640         }
641
642         nodes = result->nodesetval;
643         if(nodes) {
644                 size = (nodes) ? nodes->nodeNr : 0;
645                 for(i = 0; i < size; ++i) {
646                         if(nodes->nodeTab[i]->type == XML_ELEMENT_NODE) {
647                                 cur = nodes->nodeTab[i];
648                                 /* check if child element is point, circle or civic */
649                                 while(nok < LOST_XPATH_DPTH) {
650                                         if(cur->children) {
651                                                 nok++;
652                                                 cname = BAD_CAST cur->name;
653                                                 if(xmlStrcasecmp(cname, s_point) == 0) {
654                                                         s_profile = LOST_PRO_GEO2D;
655                                                         break;
656                                                 }
657                                                 if(xmlStrcasecmp(cname, s_circle) == 0) {
658                                                         s_profile = LOST_PRO_GEO2D;
659                                                         break;
660                                                 }
661                                                 if(xmlStrcasecmp(cname, s_civic) == 0) {
662                                                         s_profile = LOST_PRO_CIVIC;
663                                                         break;
664                                                 }
665                                         }
666                                         /* nothing found ... try next DOM level */
667                                         cur = cur->children;
668                                 }
669
670                                 if(nok == 0) {
671                                         LM_DBG("xpath '%s' returned valid element (level %d/%d)\n",
672                                                         xpath, nok, LOST_XPATH_DPTH - 1);
673                                 } else if(nok < LOST_XPATH_DPTH) {
674                                         /* malformed pidf-lo but still ok */
675                                         LM_WARN("xpath '%s' returned malformed pidf-lo (level "
676                                                         "%d/%d)\n",
677                                                         xpath, nok, LOST_XPATH_DPTH - 1);
678                                 } else {
679                                         /* really bad pidf-lo */
680                                         LM_WARN("xpath '%s' failed (level %d/%d)\n", xpath, nok,
681                                                         LOST_XPATH_DPTH - 1);
682                                         xmlXPathFreeObject(result);
683                                         return -1;
684                                 }
685                                 nok = -1;
686
687                                 if(!cur) {
688                                         LM_ERR("xpath xmlCopyNode() failed\n");
689                                         xmlXPathFreeObject(result);
690                                         return -1;
691                                 }
692
693                                 root = xmlCopyNode(cur, 1);
694                                 if(!root) {
695                                         LM_ERR("xpath xmlCopyNode() failed\n");
696                                         xmlXPathFreeObject(result);
697                                         return -1;
698                                 }
699                                 new = xmlNewDoc(BAD_CAST "1.0");
700                                 if(!new) {
701                                         LM_ERR("xpath xmlNewDoc() failed\n");
702                                         xmlXPathFreeObject(result);
703                                         return -1;
704                                 }
705                                 xmlDocSetRootElement(new, root);
706                                 xmlDocDumpFormatMemory(new, &xmlbuff, &buffersize, 0);
707                                 if(!xmlbuff) {
708                                         LM_ERR("xpath xmlDocDumpFormatMemory() failed\n");
709                                         xmlFreeDoc(new);
710                                         xmlXPathFreeObject(result);
711                                         return -1;
712                                 }
713                                 /* take the first location-info element only */
714                                 if(i == 0) {
715                                         remove = strlen("<?xml version='1.0'?>\n");
716                                         buffersize = buffersize - remove;
717                                         ptr = (char *)pkg_malloc((buffersize + 1) * sizeof(char));
718                                         if(ptr == NULL) {
719                                                 xmlFree(xmlbuff);
720                                                 xmlFreeDoc(new);
721                                                 xmlXPathFreeObject(result);
722                                                 goto err;
723                                         }
724
725                                         loc->profile = (char *)pkg_malloc(strlen(s_profile) + 1);
726                                         if(loc->profile == NULL) {
727                                                 pkg_free(ptr);
728                                                 xmlFree(xmlbuff);
729                                                 xmlFreeDoc(new);
730                                                 xmlXPathFreeObject(result);
731                                                 goto err;
732                                         }
733
734                                         memset(ptr, 0, buffersize);
735                                         memcpy(ptr, (char *)(xmlbuff + remove), buffersize);
736                                         ptr[buffersize] = '\0';
737                                         loc->civic = lost_trim_content(ptr, &len);
738
739                                         memset(loc->profile, 0, strlen(s_profile) + 1);
740                                         memcpy(loc->profile, (char *)s_profile, strlen(s_profile));
741                                 } else {
742                                         LM_WARN("xpath location-info element(%d) ignored\n", i + 1);
743                                 }
744                                 xmlFree(xmlbuff);
745                                 xmlFreeDoc(new);
746                         }
747                 }
748         }
749         xmlXPathFreeObject(result);
750
751         return 0;
752
753 err:
754         LM_ERR("no more private memory\n");
755         return -1;
756 }
757
758 /*
759  * lost_parse_location_info(node, loc)
760  * wrapper to call xpath or simple pos|circle parser (last resort)
761  */
762 int lost_parse_location_info(xmlNodePtr root, p_loc_t loc)
763 {
764
765         if(lost_xpath_location(root->doc, LOST_XPATH_GP, loc) == 0) {
766                 return 0;
767         }
768
769         LM_WARN("xpath expression failed ... trying pos|circle\n");
770
771         if(lost_parse_geo(root, loc) == 0) {
772                 return 0;
773         }
774
775         return -1;
776 }
777
778 /*
779  * lost_held_location_request(id, lgth, responsetime, exact, type)
780  * assembles and returns locationRequest string (allocated in private memory)
781  */
782 char *lost_held_location_request(p_held_t held, int *lgth)
783 {
784         int buffersize = 0;
785
786         char *doc = NULL;
787
788         xmlChar *xmlbuff = NULL;
789         xmlDocPtr request = NULL;
790
791         xmlNodePtr ptrLocationRequest = NULL;
792         xmlNodePtr ptrLocationType = NULL;
793         xmlNodePtr ptrDevice = NULL;
794
795         xmlKeepBlanksDefault(1);
796         *lgth = 0;
797
798         /*
799 https://tools.ietf.org/html/rfc6155
800
801 <?xml version="1.0" encoding="UTF-8"?>
802 <locationRequest xmlns="urn:ietf:params:xml:ns:geopriv:held" responseTime="8">
803     <locationType exact="true">geodetic locationURI</locationType>
804     <device xmlns="urn:ietf:params:xml:ns:geopriv:held:id">
805         <uri>sip:user@example.net</uri>
806     </device>
807 </locationRequest>
808 */
809
810         /* create request */
811         request = xmlNewDoc(BAD_CAST "1.0");
812         if(!request) {
813                 LM_ERR("locationRequest xmlNewDoc() failed\n");
814                 return doc;
815         }
816         /* locationRequest - element */
817         ptrLocationRequest = xmlNewNode(NULL, BAD_CAST "locationRequest");
818         if(!ptrLocationRequest) {
819                 LM_ERR("locationRequest xmlNewNode() failed\n");
820                 xmlFreeDoc(request);
821                 return doc;
822         }
823         xmlDocSetRootElement(request, ptrLocationRequest);
824         /* properties */
825         xmlNewProp(ptrLocationRequest, BAD_CAST "xmlns",
826                         BAD_CAST "urn:ietf:params:xml:ns:geopriv:held");
827         xmlNewProp(ptrLocationRequest, BAD_CAST "responseTime",
828                                 BAD_CAST held->time);
829         /* locationType - element */
830         ptrLocationType = xmlNewChild(ptrLocationRequest, NULL,
831                         BAD_CAST "locationType", BAD_CAST held->type);
832         /* properties */
833         xmlNewProp(ptrLocationType, BAD_CAST "exact",
834                         (held->exact == HELD_EXACT_TRUE) ?
835                         BAD_CAST "true" : BAD_CAST "false");
836         /* device - element */
837         ptrDevice = xmlNewChild(ptrLocationRequest, NULL, BAD_CAST "device", NULL);
838         if(!ptrDevice) {
839                 LM_ERR("locationRequest xmlNewChild() failed\n");
840                 xmlFreeDoc(request);
841                 return doc;
842         }
843         /* properties */
844         xmlNewProp(ptrDevice, BAD_CAST "xmlns",
845                         BAD_CAST "urn:ietf:params:xml:ns:geopriv:held:id");
846         /* uri - element */
847         xmlNewChild(ptrDevice, NULL, BAD_CAST "uri", BAD_CAST held->identity);
848
849         xmlDocDumpFormatMemory(request, &xmlbuff, &buffersize, 0);
850         if(!xmlbuff) {
851                 LM_ERR("locationRequest xmlDocDumpFormatMemory() failed\n");
852                 xmlFreeDoc(request);
853                 return doc;
854         }
855
856         doc = (char *)pkg_malloc((buffersize + 1) * sizeof(char));
857         if(doc == NULL) {
858                 LM_ERR("no more private memory\n");
859                 xmlFree(xmlbuff);
860                 xmlFreeDoc(request);
861                 return doc;
862         }
863
864         memset(doc, 0, buffersize + 1);
865         memcpy(doc, (char *)xmlbuff, buffersize);
866         doc[buffersize] = '\0';
867
868         *lgth = strlen(doc);
869
870         xmlFree(xmlbuff);
871         xmlFreeDoc(request);
872
873         return doc;
874 }
875
876 /*
877  * lost_find_service_request(loc, lgth)
878  * assembles and returns findService request string (allocated in private memory)
879  */
880 char *lost_find_service_request(p_loc_t loc, int *lgth)
881 {
882         int buffersize = 0;
883
884         char buf[BUFSIZE];
885         char *doc = NULL;
886
887         xmlChar *xmlbuff = NULL;
888         xmlDocPtr request = NULL;
889
890         xmlNodePtr ptrFindService = NULL;
891         xmlNodePtr ptrLocation = NULL;
892         xmlNodePtr ptrPoint = NULL;
893         xmlNodePtr ptrCircle = NULL;
894         xmlNodePtr ptrRadius = NULL;
895         xmlNodePtr ptrNode = NULL;
896
897         xmlKeepBlanksDefault(1);
898
899         *lgth = 0;
900
901         /*
902 https://tools.ietf.org/html/rfc5222
903
904 <?xml version="1.0" encoding="UTF-8"?>
905 <findService
906  xmlns="urn:ietf:params:xml:ns:lost1"
907  xmlns:p2="http://www.opengis.net/gml"
908  serviceBoundary="value"
909  recursive="true">
910     <location id="6020688f1ce1896d" profile="geodetic-2d">
911         <p2:Point id="point1" srsName="urn:ogc:def:crs:EPSG::4326">
912             <p2:pos>37.775 -122.422</p2:pos>
913         </p2:Point>
914     </location>
915     <service>urn:service:sos.police</service>
916 </findService>
917  */
918         /* create request */
919         request = xmlNewDoc(BAD_CAST "1.0");
920         if(!request) {
921                 LM_ERR("findService request xmlNewDoc() failed\n");
922                 return doc;
923         }
924         /* findService - element */
925         ptrFindService = xmlNewNode(NULL, BAD_CAST "findService");
926         if(!ptrFindService) {
927                 LM_ERR("findService xmlNewNode() failed\n");
928                 xmlFreeDoc(request);
929                 return doc;
930         }
931         xmlDocSetRootElement(request, ptrFindService);
932         /* set properties */
933         xmlNewProp(ptrFindService, BAD_CAST "xmlns",
934                         BAD_CAST "urn:ietf:params:xml:ns:lost1");
935         xmlNewProp(ptrFindService, BAD_CAST "xmlns:p2",
936                         BAD_CAST "http://www.opengis.net/gml");
937         xmlNewProp(ptrFindService, BAD_CAST "serviceBoundary",
938                         (loc->boundary == 1) ? BAD_CAST "value" : BAD_CAST "reference");
939         xmlNewProp(ptrFindService, BAD_CAST "recursive",
940                         (loc->recursive == 1) ? BAD_CAST "true" : BAD_CAST "false");
941         /* location - element */
942         ptrLocation = xmlNewChild(ptrFindService, NULL, BAD_CAST "location", NULL);
943         xmlNewProp(ptrLocation, BAD_CAST "id", BAD_CAST loc->identity);
944         xmlNewProp(ptrLocation, BAD_CAST "profile", BAD_CAST loc->profile);
945         /* set pos */
946         snprintf(buf, BUFSIZE, "%s %s", loc->latitude, loc->longitude);
947         /* xpath result */
948         if(loc->civic) {
949                 xmlParseInNodeContext(
950                                 ptrLocation, loc->civic, strlen(loc->civic), 0, &ptrNode);
951                 if(!ptrNode) {
952                         LM_ERR("locationRequest xmlParseInNodeContext() failed\n");
953                         xmlFreeDoc(request);
954                         return doc;
955                 } else {
956                         xmlAddChild(ptrLocation, ptrNode);
957                 }
958         }
959         /* Point */
960         else if(loc->radius == 0) {
961                 ptrPoint = xmlNewChild(ptrLocation, NULL, BAD_CAST "Point", NULL);
962                 if(!ptrPoint) {
963                         LM_ERR("locationRequest xmlNewChild() failed\n");
964                         xmlFreeDoc(request);
965                         return doc;
966                 }
967                 xmlNewProp(ptrPoint, BAD_CAST "xmlns",
968                                 BAD_CAST "http://www.opengis.net/gml");
969                 xmlNewProp(ptrPoint, BAD_CAST "srsName",
970                                 BAD_CAST "urn:ogc:def:crs:EPSG::4326");
971                 /* pos */
972                 xmlNewChild(ptrPoint, NULL, BAD_CAST "pos", BAD_CAST buf);
973         }
974         /* circle - Point */
975         else {
976                 ptrCircle = xmlNewChild(ptrLocation, NULL, BAD_CAST "gs:Circle", NULL);
977                 if(!ptrCircle) {
978                         LM_ERR("locationRequest xmlNewChild() failed\n");
979                         xmlFreeDoc(request);
980                         return doc;
981                 }
982                 xmlNewProp(ptrCircle, BAD_CAST "xmlns:gml",
983                                 BAD_CAST "http://www.opengis.net/gml");
984                 xmlNewProp(ptrCircle, BAD_CAST "xmlns:gs",
985                                 BAD_CAST "http://www.opengis.net/pidflo/1.0");
986                 xmlNewProp(ptrCircle, BAD_CAST "srsName",
987                                 BAD_CAST "urn:ogc:def:crs:EPSG::4326");
988                 /* pos */
989                 xmlNewChild(ptrCircle, NULL, BAD_CAST "gml:pos", BAD_CAST buf);
990                 /* circle - radius */
991                 snprintf(buf, BUFSIZE, "%d", loc->radius);
992                 ptrRadius = xmlNewChild(
993                                 ptrCircle, NULL, BAD_CAST "gs:radius", BAD_CAST buf);
994                 if(!ptrRadius) {
995                         LM_ERR("locationRequest xmlNewChild() failed\n");
996                         xmlFreeDoc(request);
997                         return doc;
998                 }
999                 xmlNewProp(ptrRadius, BAD_CAST "uom",
1000                                 BAD_CAST "urn:ogc:def:uom:EPSG::9001");
1001         }
1002         /* service - element */
1003         snprintf(buf, BUFSIZE, "%s", loc->urn);
1004         xmlNewChild(ptrFindService, NULL, BAD_CAST "service", BAD_CAST buf);
1005
1006         xmlDocDumpFormatMemory(request, &xmlbuff, &buffersize, 0);
1007         if(!xmlbuff) {
1008                 LM_ERR("findService request xmlDocDumpFormatMemory() failed\n");
1009                 xmlFreeDoc(request);
1010                 return doc;
1011         }
1012
1013         doc = (char *)pkg_malloc((buffersize + 1) * sizeof(char));
1014         if(doc == NULL) {
1015                 LM_ERR("no more private memory\n");
1016                 xmlFree(xmlbuff);
1017                 xmlFreeDoc(request);
1018                 return doc;
1019         }
1020
1021         memset(doc, 0, buffersize + 1);
1022         memcpy(doc, (char *)xmlbuff, buffersize);
1023         doc[buffersize] = '\0';
1024
1025         *lgth = strlen(doc);
1026
1027         xmlFree(xmlbuff);
1028         xmlFreeDoc(request);
1029
1030         return doc;
1031 }