Browse code

lost: added Geolocation header value list support

- the Geolocation header value parsing now supports
a list of location URIs of type (cid, http and https).
- types may be filtered and the list sequence may be
changed via new module parameters.
- besides: README update and code refactoring.

Wolfgang Kampichler authored on 12/01/2021 10:56:47
Showing 7 changed files
... ...
@@ -169,6 +169,62 @@
169 169
                 <programlisting format="linespecific">
170 170
     ...
171 171
     modparam("lost", "location_type, "civic geodetic locationURI")
172
+    ...
173
+                </programlisting>
174
+            </example>
175
+        </section>
176
+        <section id="lost.p.geoheader_type">
177
+            <title><varname>geoheader_type</varname> (int)</title>
178
+            <para>
179
+            A Geolocation header may include a list of locationValues pointing
180
+            to either a Presence Information Data Format Location Object
181
+            (PIDF-LO) in the SIP body using a content-indirection (cid:) URI
182
+            per RFC4483 (<ulink url="https://tools.ietf.org/html/rfc4483"/>),
183
+            or an http(s) URI pointing to an external source. This parameter
184
+            supports filtering of the following types:
185
+            </para>
186
+			<itemizedlist>
187
+                <listitem><para>
188
+                    <emphasis>0 (any)</emphasis> - any URI (first or last)
189
+                </para></listitem>
190
+                <listitem><para>
191
+                    <emphasis>1 (cid)</emphasis> - cid URI (aka Location-by-Value)
192
+                </para></listitem>
193
+                <listitem><para>
194
+                    <emphasis>2 (http)</emphasis> - http URI (aka Location-by-Reference)
195
+                </para></listitem>
196
+                <listitem><para>
197
+                    <emphasis>3 (https)</emphasis> - https URI (aka Location-by-Reference)
198
+                </para></listitem>
199
+            </itemizedlist>
200
+            <para>
201
+            Default: 0 (any)
202
+            </para>
203
+            <example>
204
+            <title>Set <varname>geoheader_type</varname> parameter</title>
205
+                <programlisting format="linespecific">
206
+    ...
207
+    modparam("lost", "geoheader_type", 1)
208
+    ...
209
+                </programlisting>
210
+            </example>
211
+        </section>
212
+        <section id="lost.p.geoheader_order">
213
+            <title><varname>geoheader_order</varname> (int)</title>
214
+            <para>
215
+            A Geolocation header may include a list of locationValues. This
216
+            parameter sets the order of the URI used to retrieve location
217
+            information, either the first element of a certain type or the
218
+            last. Values are 0 (first) or 1 (last).
219
+            </para>
220
+            <para>
221
+            Default: 0 (first)
222
+            </para>
223
+            <example>
224
+            <title>Set <varname>geoheader_order</varname> parameter</title>
225
+                <programlisting format="linespecific">
226
+    ...
227
+    modparam("lost", "geoheader_order", 0)
172 228
     ...
173 229
                 </programlisting>
174 230
             </example>
... ...
@@ -328,11 +384,12 @@ xlog("L_INFO", "LOST findService: Result code $var(res)\nUri: $var(uri)\nName: $
328 384
             <ulink url="https://www.digitalocean.com/community/questions/how-to-upgrade-curl-in-centos6"></ulink>.
329 385
         </para>
330 386
         <para>
331
-            Note: http_client_query exported by the http_client API returns by default the first line of
332
-            the HTTP response, but the lost module requires the complete response message, otherwise
333
-            dereferencing location via the HTTP URL provided with the Geolocation header causes an error.
334
-            Therefore, to work properly, it is required to set the http_client module parameter query_result
335
-            to 0. More details at:
387
+            Note: http_client_query exported by the http_client API returns by
388
+            default the first line of the HTTP response, but the lost module requires
389
+            the complete response message, otherwise dereferencing location via the
390
+            HTTP URL provided with the Geolocation header causes an error. Therefore,
391
+            to work properly, it is required to set the http_client module parameter
392
+            query_result to 0. More details at:
336 393
             <ulink url="https://www.kamailio.org/docs/modules/devel/modules/http_client.html#http_client.p.query_result"></ulink>.
337 394
         </para>
338 395
         <para>
... ...
@@ -340,5 +397,9 @@ xlog("L_INFO", "LOST findService: Result code $var(res)\nUri: $var(uri)\nName: $
340 397
             following link (search for "GET ACCESS"):
341 398
             <ulink url="https://gridgears.at/"></ulink>.
342 399
         </para>
400
+        <para>
401
+            Note: in case modparam "geoheader_type" is set to 2 (http), the module may
402
+            use 3 (https) as fallback and vice versa.
403
+        </para>
343 404
     </section>
344 405
 </chapter>
... ...
@@ -54,6 +54,8 @@
54 54
 
55 55
 extern httpc_api_t httpapi;
56 56
 
57
+extern int lost_geoloc_type;
58
+extern int lost_geoloc_order;
57 59
 extern int held_resp_time;
58 60
 extern int held_exact_type;
59 61
 extern str held_loc_type;
... ...
@@ -83,7 +85,8 @@ char *lost_held_type(char *type, int *exact, int *lgth)
83 85
 	if(strstr(type, HELD_TYPE_ANY)) {
84 86
 		len = strlen(ret) + strlen(HELD_TYPE_ANY) + 1;
85 87
 		tmp = pkg_realloc(ret, len);
86
-		if(tmp == NULL) goto err;
88
+		if(tmp == NULL)
89
+			goto err;
87 90
 		ret = tmp;
88 91
 		strcat(ret, HELD_TYPE_ANY);
89 92
 		*exact = 0;
... ...
@@ -91,7 +94,8 @@ char *lost_held_type(char *type, int *exact, int *lgth)
91 94
 		if(strstr(type, HELD_TYPE_CIV)) {
92 95
 			len = strlen(ret) + strlen(HELD_TYPE_CIV) + 1;
93 96
 			tmp = pkg_realloc(ret, len);
94
-			if(tmp == NULL) goto err;
97
+			if(tmp == NULL)
98
+				goto err;
95 99
 			ret = tmp;
96 100
 			strcat(ret, HELD_TYPE_CIV);
97 101
 		}
... ...
@@ -99,13 +103,15 @@ char *lost_held_type(char *type, int *exact, int *lgth)
99 103
 			if(strlen(ret) > 1) {
100 104
 				len = strlen(ret) + strlen(HELD_TYPE_SEP) + 1;
101 105
 				tmp = pkg_realloc(ret, len);
102
-				if(tmp == NULL) goto err;
106
+				if(tmp == NULL)
107
+					goto err;
103 108
 				ret = tmp;
104 109
 				strcat(ret, HELD_TYPE_SEP);
105 110
 			}
106 111
 			len = strlen(ret) + strlen(HELD_TYPE_GEO) + 1;
107 112
 			tmp = pkg_realloc(ret, len);
108
-			if(tmp == NULL) goto err;
113
+			if(tmp == NULL)
114
+				goto err;
109 115
 			ret = tmp;
110 116
 			strcat(ret, HELD_TYPE_GEO);
111 117
 		}
... ...
@@ -113,13 +119,15 @@ char *lost_held_type(char *type, int *exact, int *lgth)
113 119
 			if(strlen(ret) > 1) {
114 120
 				len = strlen(ret) + strlen(HELD_TYPE_SEP) + 1;
115 121
 				tmp = pkg_realloc(ret, len);
116
-				if(tmp == NULL) goto err;
122
+				if(tmp == NULL)
123
+					goto err;
117 124
 				ret = tmp;
118 125
 				strcat(ret, HELD_TYPE_SEP);
119 126
 			}
120 127
 			len = strlen(ret) + strlen(HELD_TYPE_URI) + 1;
121 128
 			tmp = pkg_realloc(ret, len);
122
-			if(tmp == NULL) goto err;
129
+			if(tmp == NULL)
130
+				goto err;
123 131
 			ret = tmp;
124 132
 			strcat(ret, HELD_TYPE_URI);
125 133
 		}
... ...
@@ -130,7 +138,7 @@ char *lost_held_type(char *type, int *exact, int *lgth)
130 138
 
131 139
 err:
132 140
 	LM_ERR("no more private memory\n");
133
-	if (ret != NULL) {
141
+	if(ret != NULL) {
134 142
 		pkg_free(ret);
135 143
 	}
136 144
 	*lgth = 0;
... ...
@@ -287,8 +295,8 @@ int lost_held_function(struct sip_msg *_m, char *_con, char *_pidf, char *_url,
287 295
 
288 296
 		for(cur_node = root->children; cur_node; cur_node = cur_node->next) {
289 297
 			if(cur_node->type == XML_ELEMENT_NODE) {
290
-				if(xmlStrcmp(cur_node->name,
291
-							(const xmlChar *)"locationUriSet") == 0) {
298
+				if(xmlStrcmp(cur_node->name, (const xmlChar *)"locationUriSet")
299
+						== 0) {
292 300
 
293 301
 					LM_DBG("*** node '%s' found\n", cur_node->name);
294 302
 
... ...
@@ -302,8 +310,8 @@ int lost_held_function(struct sip_msg *_m, char *_con, char *_pidf, char *_url,
302 310
 						geo.s = lost_trim_content(geo.s, &geo.len);
303 311
 					}
304 312
 				}
305
-				if(xmlStrcmp(cur_node->name,
306
-							(const xmlChar *)"presence") == 0) {
313
+				if(xmlStrcmp(cur_node->name, (const xmlChar *)"presence")
314
+						== 0) {
307 315
 
308 316
 					LM_DBG("*** node '%s' found\n", cur_node->name);
309 317
 
... ...
@@ -413,25 +421,29 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
413 421
 	pv_value_t pverr;
414 422
 
415 423
 	p_loc_t loc = NULL;
424
+	p_geolist_t geolist = NULL;
425
+	int geotype;
416 426
 
417
-	xmlDocPtr doc = NULL;
418
-	xmlNodePtr root = NULL;
419
-
427
+	str url = {NULL, 0};
420 428
 	str uri = {NULL, 0};
421 429
 	str urn = {NULL, 0};
422 430
 	str err = {NULL, 0};
423
-	str res = {NULL, 0};
431
+	str req = {NULL, 0};
424 432
 	str con = {NULL, 0};
425 433
 	str ret = {NULL, 0};
426
-	str geo = {NULL, 0};
427
-	str geohdr = {NULL, 0};
428 434
 	str name = {NULL, 0};
429 435
 	str pidf = {NULL, 0};
436
+	str geohdr = {NULL, 0};
430 437
 	str pidfhdr = {NULL, 0};
431 438
 
432 439
 	struct msg_start *fl;
433 440
 	char *search = NULL;
441
+	char *geoval = NULL;
434 442
 	int curlres = 0;
443
+	int geoitems = 0;
444
+
445
+	xmlDocPtr doc = NULL;
446
+	xmlNodePtr root = NULL;
435 447
 
436 448
 	if(_con == NULL || _uri == NULL || _name == NULL || _err == NULL) {
437 449
 		LM_ERR("invalid parameter\n");
... ...
@@ -462,7 +474,7 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
462 474
 				&& ((*(search + 1) == 'r') || (*(search + 1) == 'R'))
463 475
 				&& ((*(search + 2) == 'n') || (*(search + 2) == 'N'))
464 476
 				&& (*(search + 3) == ':')) {
465
-			LM_INFO("### LOST urn [%.*s]\n", urn.len, urn.s);
477
+			LM_INFO("### LOST urn\t[%.*s]\n", urn.len, urn.s);
466 478
 		} else {
467 479
 			LM_ERR("service urn not found\n");
468 480
 			goto err;
... ...
@@ -474,159 +486,178 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
474 486
 	/* pidf from parameter */
475 487
 	if(_pidf) {
476 488
 		if(fixup_get_svalue(_m, (gparam_p)_pidf, &pidf) != 0) {
477
-			LM_ERR("cannot get pidf-lo\n");
478
-			goto err;
489
+			LM_WARN("cannot get pidf-lo parameter\n");
490
+		} else {
491
+
492
+			LM_DBG("parsing pidf-lo from paramenter\n");
493
+
494
+			if(pidf.len > 0) {
495
+
496
+				LM_DBG("pidf-lo: [%.*s]\n", pidf.len, pidf.s);
497
+
498
+				/* parse the pidf-lo */
499
+				loc = lost_parse_pidf(pidf, urn);
500
+				/* free memory */
501
+				pidf.s = NULL;
502
+				pidf.len = 0;
503
+			} else {
504
+				LM_WARN("no valid pidf parameter ...\n");
505
+			}
479 506
 		}
480 507
 	}
481
-	/* pidf from geolocation header */
482
-	if(pidf.len == 0) {
483
-		LM_WARN("no pidf parameter, trying geolocation header ...\n");
508
+
509
+	/* no pidf-lo so far ... check geolocation header */
510
+	if(loc == NULL) {
511
+
512
+		LM_DBG("looking for geolocation header ...\n");
513
+
484 514
 		geohdr.s = lost_get_geolocation_header(_m, &geohdr.len);
485 515
 		if(geohdr.len == 0) {
486 516
 			LM_ERR("geolocation header not found\n");
487 517
 			goto err;
488
-		} else {
518
+		}
489 519
 
490
-			LM_DBG("geolocation header found\n");
491
-
492
-			/* pidf from multipart body, check cid scheme */
493
-			if(geohdr.len > 6) {
494
-				search = geohdr.s;
495
-				if((*(search + 0) == '<')
496
-						&& ((*(search + 1) == 'c') || (*(search + 1) == 'C'))
497
-						&& ((*(search + 2) == 'i') || (*(search + 2) == 'I'))
498
-						&& ((*(search + 3) == 'd') || (*(search + 3) == 'D'))
499
-						&& (*(search + 4) == ':')) {
500
-					search += 4;
501
-					*search = '<';
502
-					geo.s = search;
503
-					geo.len = geo.len - 4;
504
-
505
-					LM_DBG("cid: [%.*s]\n", geo.len, geo.s);
506
-
507
-					/* get body part - filter=>content id */
508
-					pidf.s = get_body_part_by_filter(
509
-							_m, 0, 0, geo.s, NULL, &pidf.len);
510
-					if(pidf.len == 0) {
511
-						LM_ERR("no multipart body found\n");
512
-						/* free memory */
513
-						geo.s = NULL;
514
-						geo.len = 0;
515
-						lost_free_string(&geohdr);
516
-						goto err;
517
-					}
518
-				}
519
-				/* no pidf-lo so far ... check http(s) scheme */
520
-				if(((*(search + 0) == 'h') || (*(search + 0) == 'H'))
521
-						&& ((*(search + 1) == 't') || (*(search + 1) == 'T'))
522
-						&& ((*(search + 2) == 't') || (*(search + 2) == 'T'))
523
-						&& ((*(search + 3) == 'p') || (*(search + 3) == 'P'))) {
524
-					geo.s = geohdr.s;
525
-					geo.len = geohdr.len;
520
+		LM_DBG("geolocation header found\n");
526 521
 
527
-					if(*(search + 4) == ':') {
522
+		/* parse Geolocation header */
523
+		geolist = lost_new_geoheader_list(geohdr, &geoitems);
524
+		if(geoitems == 0) {
525
+			LM_ERR("invalid geolocation header\n");
526
+			lost_free_string(&geohdr);
527
+			goto err;
528
+		}
528 529
 
529
-						LM_DBG("http url: [%.*s]\n", geo.len, geo.s);
530
+		LM_DBG("number of location URIs: %d\n", geoitems);
530 531
 
531
-					} else if(((*(search + 4) == 's') || (*(search + 4) == 'S'))
532
-							  && (*(search + 5) == ':')) {
532
+		if(lost_geoloc_order == 0) {
533 533
 
534
-						LM_DBG("https url: [%.*s]\n", geo.len, geo.s);
534
+			LM_DBG("reversing location URI sequence\n");
535 535
 
536
-					} else {
537
-						LM_ERR("invalid url: [%.*s]\n", geo.len, geo.s);
538
-						/* free memory */
539
-						geo.s = NULL;
540
-						geo.len = 0;
541
-						lost_free_string(&geohdr);
542
-						goto err;
543
-					}
536
+			lost_reverse_geoheader_list(&geolist);
537
+		}
544 538
 
545
-					/* ! dereference pidf.lo at location server - HTTP GET */
546
-					/* ! requires hack in http_client module */
547
-					/* ! functions.c => http_client_query => query_params.oneline = 0; */
548
-					curlres = httpapi.http_client_query(
549
-							_m, geo.s, &pidfhdr, NULL, NULL);
550
-					/* free memory */
551
-					geo.s = NULL;
552
-					geo.len = 0;
553
-					lost_free_string(&geohdr);
554
-					/* only HTTP 2xx responses are accepted */
555
-					if(curlres >= 300 || curlres < 100) {
556
-						LM_ERR("http GET failed with error: %d\n", curlres);
557
-						/* free memory */
558
-						lost_free_string(&pidfhdr);
559
-						goto err;
560
-					}
539
+		switch(lost_geoloc_type) {
540
+			case ANY: /* type: 0 */
541
+				geoval = lost_get_geoheader_value(geolist, ANY, &geotype);
561 542
 
562
-					LM_DBG("http GET returned: %d\n", curlres);
543
+				LM_DBG("geolocation header field (any): %s\n", geoval);
563 544
 
564
-					if(pidfhdr.len == 0) {
565
-						LM_ERR("dereferencing location failed\n");
566
-						goto err;
567
-					}
568
-					pidf.s = pidfhdr.s;
569
-					pidf.len = pidfhdr.len;
545
+				break;
546
+			case CID: /* type: 1 */
547
+				geoval = lost_get_geoheader_value(geolist, CID, &geotype);
548
+
549
+				LM_DBG("geolocation header field (LbV): %s\n", geoval);
550
+
551
+				break;
552
+			case HTTP: /* type: 2 */
553
+				geoval = lost_get_geoheader_value(geolist, HTTP, &geotype);
554
+				/* fallback to https */
555
+				if(geoval == NULL) {
556
+					LM_WARN("no valid http URL ... trying https\n");
557
+					geoval = lost_get_geoheader_value(geolist, HTTPS, &geotype);
570 558
 				}
571
-			} else {
572
-				LM_ERR("invalid geolocation header\n");
573
-				goto err;
574
-			}
575
-		}
576
-	}
577 559
 
578
-	/* no pidf-lo return error */
579
-	if(pidf.len == 0) {
580
-		LM_ERR("pidf-lo not found\n");
581
-		goto err;
582
-	}
560
+				LM_DBG("geolocation header field (LbR): %s\n", geoval);
583 561
 
584
-	LM_DBG("pidf-lo: [%.*s]\n", pidf.len, pidf.s);
562
+				break;
563
+			case HTTPS: /* type: 3 */
564
+				/* prefer https */
565
+				geoval = lost_get_geoheader_value(geolist, HTTPS, &geotype);
566
+				/* fallback to http */
567
+				if(geoval == NULL) {
568
+					LM_WARN("no valid https URL ... trying http\n");
569
+					geoval = lost_get_geoheader_value(geolist, HTTP, &geotype);
570
+				}
585 571
 
586
-	/* read and parse pidf-lo */
587
-	doc = xmlReadMemory(pidf.s, pidf.len, 0, NULL,
588
-			XML_PARSE_NOBLANKS | XML_PARSE_NONET | XML_PARSE_NOCDATA);
572
+				LM_DBG("geolocation header field (LbR): %s\n", geoval);
589 573
 
590
-	if(doc == NULL) {
591
-		LM_WARN("invalid xml (pidf-lo): [%.*s]\n", pidf.len, pidf.s);
592
-		doc = xmlRecoverMemory(pidf.s, pidf.len);
593
-		if(doc == NULL) {
594
-			LM_ERR("xml (pidf-lo) recovery failed on: [%.*s]\n", pidf.len,
595
-					pidf.s);
574
+				break;
575
+			default:
576
+				LM_WARN("unknown module parameter value\n");
577
+				geoval = lost_get_geoheader_value(geolist, UNKNOWN, &geotype);
578
+
579
+				LM_DBG("geolocation header field (any): %s\n", geoval);
580
+
581
+				break;
582
+		}
583
+
584
+		if(geoval == NULL) {
585
+			LM_ERR("invalid geolocation header\n");
586
+			/* free memory */
587
+			lost_delete_geoheader_list(geolist);
588
+			lost_free_string(&geohdr);
596 589
 			goto err;
597 590
 		}
598 591
 
599
-		LM_DBG("xml (pidf-lo) recovered\n");
600
-	}
592
+		LM_INFO("### LOST loc\t[%s]\n", geoval);
601 593
 
602
-	root = xmlDocGetRootElement(doc);
603
-	if(root == NULL) {
604
-		LM_ERR("empty pidf-lo document\n");
605
-		goto err;
606
-	}
607
-	if((!xmlStrcmp(root->name, (const xmlChar *)"presence"))
608
-			|| (!xmlStrcmp(root->name, (const xmlChar *)"locationResponse"))) {
609
-		/* get the geolocation: point or circle, urn, ... */
610
-		loc = lost_new_loc(urn);
611
-		if(loc == NULL) {
612
-			LM_ERR("location object allocation failed\n");
613
-			goto err;
594
+		/* use location by value */
595
+		if(geotype == CID) {
596
+
597
+			/* get body part - filter=>content-indirection */
598
+			pidf.s = get_body_part_by_filter(_m, 0, 0, geoval, NULL, &pidf.len);
599
+			if(pidf.len > 0) {
600
+
601
+				LM_DBG("LbV pidf-lo: [%.*s]\n", pidf.len, pidf.s);
602
+
603
+				/* parse the pidf-lo */
604
+				loc = lost_parse_pidf(pidf, urn);
605
+				/* free memory */
606
+				pidf.s = NULL;
607
+				pidf.len = 0;
608
+			} else {
609
+				LM_WARN("no multipart body found\n");
610
+			}
614 611
 		}
615
-		if(lost_parse_location_info(root, loc) < 0) {
616
-			LM_ERR("location element not found\n");
617
-			goto err;
612
+
613
+		/* use location by reference */
614
+		if((geotype == HTTPS) || (geotype == HTTP)) {
615
+			url.s = geoval;
616
+			url.len = strlen(geoval);
617
+			/* ! dereference pidf.lo at location server - HTTP GET */
618
+			/* ! requires hack in http_client module */
619
+			/* ! functions.c => http_client_query => query_params.oneline = 0; */
620
+			curlres =
621
+					httpapi.http_client_query(_m, url.s, &pidfhdr, NULL, NULL);
622
+			/* free memory */
623
+			url.s = NULL;
624
+			url.len = 0;
625
+			/* only HTTP 2xx responses are accepted */
626
+			if(curlres >= 300 || curlres < 100) {
627
+				LM_ERR("http GET failed with error: %d\n", curlres);
628
+				/* free memory */
629
+				lost_delete_geoheader_list(geolist);
630
+				lost_free_string(&pidfhdr);
631
+				lost_free_string(&geohdr);
632
+				goto err;
633
+			}
634
+
635
+			pidf.s = pidfhdr.s;
636
+			pidf.len = pidfhdr.len;
637
+
638
+			if(pidf.len > 0) {
639
+
640
+				LM_DBG("LbR pidf-lo: [%.*s]\n", pidf.len, pidf.s);
641
+
642
+				/* parse the pidf-lo */
643
+				loc = lost_parse_pidf(pidf, urn);
644
+				/* free memory */
645
+				pidf.s = NULL;
646
+				pidf.len = 0;
647
+			} else {
648
+				LM_WARN("dereferencing location failed\n");
649
+			}
618 650
 		}
619
-	} else {
620
-		LM_ERR("findServiceResponse or presence element not found in "
621
-			   "[%.*s]\n",
622
-				pidf.len, pidf.s);
623
-		goto err;
651
+		/* free memory */
652
+		lost_delete_geoheader_list(geolist);
653
+		lost_free_string(&geohdr);
654
+		lost_free_string(&pidfhdr);
624 655
 	}
625 656
 
626
-	/* free memory */
627
-	pidf.s = NULL;
628
-	pidf.len = 0;
629
-	lost_free_string(&pidfhdr);
657
+	if(loc == NULL) {
658
+		LM_ERR("location object not found\n");
659
+		goto err;
660
+	}
630 661
 
631 662
 	/* check if connection exits */
632 663
 	if(httpapi.http_connection_exists(&con) == 0) {
... ...
@@ -634,22 +665,20 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
634 665
 		goto err;
635 666
 	}
636 667
 	/* assemble findService request */
637
-	res.s = lost_find_service_request(loc, &res.len);
668
+	req.s = lost_find_service_request(loc, &req.len);
638 669
 	/* free memory */
639 670
 	lost_free_loc(loc);
640 671
 	loc = NULL;
641
-	xmlFreeDoc(doc);
642
-	doc = NULL;
643 672
 
644
-	if(res.len == 0) {
673
+	if(req.len == 0) {
645 674
 		LM_ERR("lost request failed\n");
646 675
 		goto err;
647 676
 	}
648 677
 
649
-	LM_DBG("findService request: [%.*s]\n", res.len, res.s);
678
+	LM_DBG("findService request: [%.*s]\n", req.len, req.s);
650 679
 
651 680
 	/* send findService request to mapping server - HTTP POST */
652
-	curlres = httpapi.http_connect(_m, &con, NULL, &ret, mtlost, &res);
681
+	curlres = httpapi.http_connect(_m, &con, NULL, &ret, mtlost, &req);
653 682
 	/* only HTTP 2xx responses are accepted */
654 683
 	if(curlres >= 300 || curlres < 100) {
655 684
 		LM_ERR("[%.*s] failed with error: %d\n", con.len, con.s, curlres);
... ...
@@ -660,7 +689,7 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
660 689
 	LM_DBG("[%.*s] returned: %d\n", con.len, con.s, curlres);
661 690
 
662 691
 	/* free memory */
663
-	lost_free_string(&res);
692
+	lost_free_string(&req);
664 693
 
665 694
 	if(ret.len == 0) {
666 695
 		LM_ERR("findService request failed\n");
... ...
@@ -700,7 +729,7 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
700 729
 			lost_free_string(&ret);
701 730
 			goto err;
702 731
 		}
703
-		LM_INFO("### LOST uri [%.*s]\n", uri.len, uri.s);
732
+		LM_INFO("### LOST uri\t[%.*s]\n", uri.len, uri.s);
704 733
 		/* get the displayName element */
705 734
 		name.s = lost_get_content(root, name_element, &name.len);
706 735
 		if(name.len == 0) {
... ...
@@ -709,7 +738,7 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
709 738
 			lost_free_string(&ret);
710 739
 			goto err;
711 740
 		}
712
-		LM_INFO("### LOST name [%.*s]\n", name.len, name.s);
741
+		LM_INFO("### LOST din\t[%.*s]\n", name.len, name.s);
713 742
 	} else if((!xmlStrcmp(root->name, (const xmlChar *)"errors"))) {
714 743
 
715 744
 		LM_DBG("findService error response received\n");
... ...
@@ -733,8 +762,6 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
733 762
 
734 763
 	/* free memory */
735 764
 	lost_free_string(&ret);
736
-	xmlFreeDoc(doc);
737
-	doc = NULL;
738 765
 
739 766
 	/* set writable pvars */
740 767
 	pvname.rs = name;
... ...
@@ -764,11 +791,13 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
764 791
 	return (err.len > 0) ? LOST_SERVER_ERROR : LOST_SUCCESS;
765 792
 
766 793
 err:
767
-	if(loc != NULL) {
768
-		lost_free_loc(loc);
769
-	}
794
+	/* free memory */
770 795
 	if(doc != NULL) {
771 796
 		xmlFreeDoc(doc);
797
+		doc = NULL;
798
+	}
799
+	if(loc != NULL) {
800
+		lost_free_loc(loc);
772 801
 	}
773 802
 
774 803
 	return LOST_CLIENT_ERROR;
... ...
@@ -49,6 +49,10 @@ MODULE_VERSION
49 49
 /* Module parameter variables */
50 50
 httpc_api_t httpapi;
51 51
 
52
+/* lost: any (0), cid (1), http (2) or https (3) (default: 0) */
53
+int lost_geoloc_type = 0;
54
+/* lost: Geolocation header value order: first (0) or last (1) (default: 0) */
55
+int lost_geoloc_order = 0;
52 56
 /* held request: response time (default: 0 = no timeout) */
53 57
 int held_resp_time = 0;
54 58
 /* held request: exact is true (1) or false (0) (default: false) */
... ...
@@ -96,16 +100,14 @@ static cmd_export_t cmds[] = {
96 100
 		{"lost_query", (cmd_function)w_lost_query_all, 6, fixup_lost_query_all,
97 101
 				fixup_free_lost_query_all,
98 102
 				REQUEST_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE},
99
-		{0, 0, 0, 0, 0, 0}
100
-};
103
+		{0, 0, 0, 0, 0, 0}};
101 104
 
102 105
 /* Exported parameters */
103
-static param_export_t params[] = {
104
-		{"exact_type", PARAM_INT, &held_exact_type},
106
+static param_export_t params[] = {{"exact_type", PARAM_INT, &held_exact_type},
105 107
 		{"response_time", PARAM_INT, &held_resp_time},
106 108
 		{"location_type", PARAM_STR, &held_loc_type},
107
-		{0, 0, 0}
108
-};
109
+		{"geoheader_type", PARAM_INT, &lost_geoloc_type},
110
+		{"geoheader_order", PARAM_INT, &lost_geoloc_order}, {0, 0, 0}};
109 111
 
110 112
 /* Module interface */
111 113
 struct module_exports exports = {
... ...
@@ -98,8 +98,8 @@ xmlNodePtr xmlNodeGetNodeByName(
98 98
 	while(cur) {
99 99
 		xmlNodePtr match = NULL;
100 100
 		if(xmlStrcasecmp(cur->name, (unsigned char *)name) == 0) {
101
-			if(!ns || (cur->ns &&
102
-				xmlStrcasecmp(cur->ns->prefix, (unsigned char *)ns) == 0))
101
+			if(!ns || (cur->ns && xmlStrcasecmp(cur->ns->prefix,
102
+									(unsigned char *)ns) == 0))
103 103
 				return cur;
104 104
 		}
105 105
 		match = xmlNodeGetNodeByName(cur->children, name, ns);
... ...
@@ -39,7 +39,7 @@
39 39
 #include <libxml/xpath.h>
40 40
 #include <libxml/xpathInternals.h>
41 41
 
42
-#define BUFSIZE 128	/* temporary buffer to hold geolocation */
42
+#define BUFSIZE 128	   /* temporary buffer to hold geolocation */
43 43
 #define RANDSTRSIZE 16 /* temporary id in a findService request */
44 44
 
45 45
 xmlNodePtr xmlNodeGetNodeByName(
... ...
@@ -380,7 +380,7 @@ char *lost_get_geolocation_header(struct sip_msg *msg, int *lgth)
380 380
 				&& (hf->name.len == LOST_GEOLOC_HEADER_SIZE - 2)) {
381 381
 			/* possible hit */
382 382
 			if(strncasecmp(hf->name.s, LOST_GEOLOC_HEADER,
383
-					   LOST_GEOLOC_HEADER_SIZE)	== 0) {
383
+								LOST_GEOLOC_HEADER_SIZE) == 0) {
384 384
 
385 385
 				res = (char *)pkg_malloc((hf->body.len + 1) * sizeof(char));
386 386
 				if(res == NULL) {
... ...
@@ -427,7 +427,7 @@ char *lost_get_pai_header(struct sip_msg *msg, int *lgth)
427 427
 				&& (hf->name.len == LOST_PAI_HEADER_SIZE - 2)) {
428 428
 			/* possible hit */
429 429
 			if(strncasecmp(hf->name.s, LOST_PAI_HEADER,
430
-						LOST_PAI_HEADER_SIZE) == 0) {
430
+								LOST_PAI_HEADER_SIZE) == 0) {
431 431
 
432 432
 				LM_DBG("P-A-I body:  [%.*s]\n", hf->body.len, hf->body.s);
433 433
 
... ...
@@ -512,6 +512,303 @@ char *lost_get_from_header(struct sip_msg *msg, int *lgth)
512 512
 	return res;
513 513
 }
514 514
 
515
+/*
516
+ * lost_delete_geoheader_list(list)
517
+ * removes geoheader list from private memory
518
+ */
519
+void lost_delete_geoheader_list(p_geolist_t list)
520
+{
521
+
522
+	p_geolist_t curr;
523
+
524
+	while((curr = list) != NULL) {
525
+		list = curr->next;
526
+		if(curr->value != NULL) {
527
+			pkg_free(curr->value);
528
+		}
529
+		if(curr->param != NULL) {
530
+			pkg_free(curr->param);
531
+		}
532
+		pkg_free(curr);
533
+	}
534
+
535
+	list = NULL;
536
+
537
+	return;
538
+}
539
+
540
+/*
541
+ * lost_get_geoheader_value(list, type, rtype)
542
+ * returns geoheader value and type (rtype) of given type
543
+ */
544
+char *lost_get_geoheader_value(p_geolist_t list, geotype_t type, int *rtype)
545
+{
546
+
547
+	p_geolist_t head = list;
548
+	char *value = NULL;
549
+
550
+	if(head == NULL) {
551
+		return value;
552
+	}
553
+
554
+	/* type is not important, take first element value and type */
555
+	if((type == ANY) || (type == UNKNOWN)) {
556
+		*rtype = head->type;
557
+		return head->value;
558
+	}
559
+
560
+	/* take first element value and type of given type */
561
+	while(head) {
562
+		if(type == head->type) {
563
+			value = head->value;
564
+			*rtype = head->type;
565
+			break;
566
+		}
567
+		head = head->next;
568
+	}
569
+
570
+	return value;
571
+}
572
+
573
+
574
+/*
575
+ * lost_reverse_geoheader_list(list)
576
+ * reverses list order
577
+ */
578
+void lost_reverse_geoheader_list(p_geolist_t *head)
579
+{
580
+
581
+	p_geolist_t prev = NULL;
582
+	p_geolist_t next = NULL;
583
+	p_geolist_t current = *head;
584
+
585
+	while(current != NULL) {
586
+		next = current->next;
587
+		current->next = prev;
588
+		prev = current;
589
+		current = next;
590
+	}
591
+
592
+	*head = prev;
593
+}
594
+
595
+/*
596
+ * lost_copy_geoheader_value(src, len)
597
+ * returns a header vaule string (src to src + len) allocated in private memory
598
+ */
599
+char *lost_copy_geoheader_value(char *src, int len)
600
+{
601
+
602
+	char *res = NULL;
603
+
604
+	res = (char *)pkg_malloc((len + 1) * sizeof(char));
605
+	if(res == NULL) {
606
+		LM_ERR("no more private memory\n");
607
+		return res;
608
+	} else {
609
+		memset(res, 0, len + 1);
610
+		memcpy(res, src, len + 1);
611
+		res[len] = '\0';
612
+	}
613
+
614
+	return res;
615
+}
616
+
617
+/*
618
+ * lost_new_geoheader_list(hdr, items)
619
+ * searches and parses Geolocation header and returns a list
620
+ * allocated in private memory and an item count
621
+ */
622
+p_geolist_t lost_new_geoheader_list(str hdr, int *items)
623
+{
624
+
625
+	char *search = NULL;
626
+	char *cidptr = NULL;
627
+	char *urlptr = NULL;
628
+	char *ptr = NULL;
629
+
630
+	int count = 0;
631
+	int len = 0;
632
+
633
+	p_geolist_t list = NULL;
634
+	p_geolist_t new = NULL;
635
+
636
+	LM_DBG("parsing geolocation header value ...\n");
637
+
638
+	/* search the complete header field */
639
+	search = hdr.s;
640
+	for(int i = 0; i < hdr.len; i++) {
641
+		/* check for cid content */
642
+		/* <cid:x> might be the shortest */
643
+		if(strlen(search) > 6) {
644
+			if((*(search + 0) == '<')
645
+					&& ((*(search + 1) == 'c') || (*(search + 1) == 'C'))
646
+					&& ((*(search + 2) == 'i') || (*(search + 2) == 'I'))
647
+					&& ((*(search + 3) == 'd') || (*(search + 3) == 'D'))
648
+					&& (*(search + 4) == ':')) {
649
+				cidptr = search + 4;
650
+				*cidptr = LAQUOT;
651
+				ptr = cidptr;
652
+				len = 1;
653
+				while(*(ptr + len) != '>') {
654
+					if((len == strlen(ptr)) || (*(ptr + len) == '<')) {
655
+						LM_WARN("invalid cid: [%.*s]\n", hdr.len, hdr.s);
656
+						break;
657
+					}
658
+					len++;
659
+				}
660
+				if((*(ptr + len) == '>') && (len > 6)) {
661
+					new = (p_geolist_t)pkg_malloc(sizeof(s_geolist_t));
662
+					if(new == NULL) {
663
+						LM_ERR("no more private memory\n");
664
+					} else {
665
+
666
+						LM_DBG("\t[%.*s]\n", len + 1, cidptr);
667
+
668
+						new->value = lost_copy_geoheader_value(cidptr, len + 1);
669
+						new->param = NULL;
670
+						new->type = CID;
671
+						new->next = list;
672
+						list = new;
673
+						count++;
674
+
675
+						LM_DBG("adding cid [%s]\n", new->value);
676
+					}
677
+				} else {
678
+					LM_WARN("invalid value: [%.*s]\n", hdr.len, hdr.s);
679
+				}
680
+			}
681
+		}
682
+
683
+		/* check for http(s) content */
684
+		/* <http://goo.gl/> might be the shortest */
685
+		if(strlen(search) > 10) {
686
+			if((*(search + 0) == '<')
687
+					&& ((*(search + 1) == 'h') || (*(search + 1) == 'H'))
688
+					&& ((*(search + 2) == 't') || (*(search + 2) == 'T'))
689
+					&& ((*(search + 3) == 't') || (*(search + 3) == 'T'))
690
+					&& ((*(search + 4) == 'p') || (*(search + 4) == 'P'))) {
691
+				urlptr = search + 1;
692
+				ptr = urlptr;
693
+				len = 0;
694
+				while(*(ptr + len) != '>') {
695
+					if((len == strlen(ptr)) || (*(ptr + len) == '<')) {
696
+						LM_WARN("invalid url: [%.*s]\n", hdr.len, hdr.s);
697
+						break;
698
+					}
699
+					len++;
700
+				}
701
+				if((*(ptr + len) == '>') && (len > 10)) {
702
+					new = (p_geolist_t)pkg_malloc(sizeof(s_geolist_t));
703
+					if(new == NULL) {
704
+						LM_ERR("no more private memory\n");
705
+					} else {
706
+
707
+						LM_DBG("\t[%.*s]\n", len, urlptr);
708
+
709
+						new->value = lost_copy_geoheader_value(urlptr, len);
710
+						new->param = NULL;
711
+						if(*(search + 5) == ':') {
712
+
713
+							LM_DBG("adding http url [%s]\n", new->value);
714
+
715
+							new->type = HTTP;
716
+						} else if(((*(search + 5) == 's')
717
+										  || (*(search + 5) == 'S'))
718
+								  && (*(search + 6) == ':')) {
719
+
720
+							LM_DBG("adding https url [%s]\n", new->value);
721
+
722
+							new->type = HTTPS;
723
+						}
724
+						new->next = list;
725
+						list = new;
726
+						count++;
727
+					}
728
+				} else {
729
+					LM_WARN("invalid value: [%.*s]\n", hdr.len, hdr.s);
730
+				}
731
+			}
732
+		}
733
+		search++;
734
+	}
735
+
736
+	*items = count;
737
+
738
+	return list;
739
+}
740
+
741
+/*
742
+ * lost_parse_pidf(pidf, urn)
743
+ * parses pidf and returns a new location object
744
+ */
745
+p_loc_t lost_parse_pidf(str pidf, str urn)
746
+{
747
+
748
+	p_loc_t loc = NULL;
749
+
750
+	xmlDocPtr doc = NULL;
751
+	xmlNodePtr root = NULL;
752
+
753
+	/* read and parse pidf-lo */
754
+	doc = xmlReadMemory(pidf.s, pidf.len, 0, NULL,
755
+			XML_PARSE_NOBLANKS | XML_PARSE_NONET | XML_PARSE_NOCDATA);
756
+
757
+	if(doc == NULL) {
758
+		LM_WARN("invalid xml (pidf-lo): [%.*s]\n", pidf.len, pidf.s);
759
+		doc = xmlRecoverMemory(pidf.s, pidf.len);
760
+		if(doc == NULL) {
761
+			LM_ERR("xml (pidf-lo) recovery failed on: [%.*s]\n", pidf.len,
762
+					pidf.s);
763
+			goto err;
764
+		}
765
+
766
+		LM_DBG("xml (pidf-lo) recovered\n");
767
+	}
768
+
769
+	root = xmlDocGetRootElement(doc);
770
+	if(root == NULL) {
771
+		LM_ERR("empty pidf-lo document\n");
772
+		goto err;
773
+	}
774
+	if((!xmlStrcmp(root->name, (const xmlChar *)"presence"))
775
+			|| (!xmlStrcmp(root->name, (const xmlChar *)"locationResponse"))) {
776
+		/* get the geolocation: point or circle, urn, ... */
777
+		loc = lost_new_loc(urn);
778
+		if(loc == NULL) {
779
+			LM_ERR("location object allocation failed\n");
780
+			goto err;
781
+		}
782
+		if(lost_parse_location_info(root, loc) < 0) {
783
+			LM_ERR("location element not found\n");
784
+			goto err;
785
+		}
786
+	} else {
787
+		LM_ERR("findServiceResponse or presence element not found in "
788
+			   "[%.*s]\n",
789
+				pidf.len, pidf.s);
790
+		goto err;
791
+	}
792
+
793
+	/* free memory */
794
+	xmlFreeDoc(doc);
795
+	doc = NULL;
796
+
797
+	return loc;
798
+
799
+err:
800
+	LM_ERR("pidflo parsing error\n");
801
+	/* free memory */
802
+	if(doc != NULL) {
803
+		xmlFreeDoc(doc);
804
+		doc = NULL;
805
+	}
806
+	if(loc != NULL) {
807
+		lost_free_loc(loc);
808
+	}
809
+	return NULL;
810
+}
811
+
515 812
 /*
516 813
  * lost_parse_geo(node, loc)
517 814
  * parses locationResponse (pos|circle) and writes 
... ...
@@ -586,6 +883,7 @@ err:
586 883
 	LM_ERR("no more private memory\n");
587 884
 	return -1;
588 885
 }
886
+
589 887
 /*
590 888
  * lost_xpath_location(doc, path, loc)
591 889
  * performs xpath expression on locationResponse and writes 
... ...
@@ -733,7 +1031,7 @@ int lost_xpath_location(xmlDocPtr doc, char *path, p_loc_t loc)
733 1031
 					memset(ptr, 0, buffersize);
734 1032
 					memcpy(ptr, (char *)(xmlbuff + remove), buffersize);
735 1033
 					ptr[buffersize] = '\0';
736
-					
1034
+
737 1035
 					/* trim the result */
738 1036
 					tmp = lost_trim_content(ptr, &len);
739 1037
 
... ...
@@ -32,6 +32,8 @@
32 32
 #ifndef LOST_UTILITIES_H
33 33
 #define LOST_UTILITIES_H
34 34
 
35
+#define LAQUOT '<'
36
+
35 37
 #define LOST_GEOLOC_HEADER "Geolocation: "
36 38
 #define LOST_GEOLOC_HEADER_SIZE strlen(LOST_GEOLOC_HEADER)
37 39
 #define LOST_PAI_HEADER "P-Asserted-Identity: "
... ...
@@ -67,35 +69,54 @@
67 69
 #define HELD_EXACT_TRUE 1
68 70
 #define HELD_EXACT_FALSE 0
69 71
 
70
-#define BUFSIZE 128	/* temporary buffer to hold geolocation */
72
+#define BUFSIZE 128	   /* temporary buffer to hold geolocation */
71 73
 #define RANDSTRSIZE 16 /* temporary id in a findService request */
72 74
 
73 75
 typedef struct LOC
74 76
 {
75
-	char *identity;		/* location idendity (findServiceRequest) */
76
-	char *urn;			/* service URN (findServiceRequest) */ 
77
-	char *xpath;		/* civic address (findServiceRequest) */
78
-	char *geodetic;		/* geodetic location (findServiceRequest) */
79
-	char *longitude;	/* geo longitude */
80
-	char *latitude;		/* geo latitude */
81
-	char *profile;		/* location profile (findServiceRequest) */
82
-	int radius;			/* geo radius (findServiceRequest) */
83
-	int recursive;		/* recursion true|false (findServiceRequest)*/
84
-	int boundary;       /* boundary ref|value (findServiceRequest)*/
77
+	char *identity;	 /* location idendity (findServiceRequest) */
78
+	char *urn;		 /* service URN (findServiceRequest) */
79
+	char *xpath;	 /* civic address (findServiceRequest) */
80
+	char *geodetic;	 /* geodetic location (findServiceRequest) */
81
+	char *longitude; /* geo longitude */
82
+	char *latitude;	 /* geo latitude */
83
+	char *profile;	 /* location profile (findServiceRequest) */
84
+	int radius;		 /* geo radius (findServiceRequest) */
85
+	int recursive;	 /* recursion true|false (findServiceRequest)*/
86
+	int boundary;	 /* boundary ref|value (findServiceRequest)*/
85 87
 } s_loc_t, *p_loc_t;
86 88
 
87 89
 typedef struct HELD
88 90
 {
89
-	char *identity;		/* location idendity (locationRequest) */
90
-	char *type;			/* location type (locationRequest) */ 
91
-	int time;			/* response time (locationRequest) */
92
-	int exact;			/* exact true|false (locationRequest)*/
91
+	char *identity; /* location idendity (locationRequest) */
92
+	char *type;		/* location type (locationRequest) */
93
+	int time;		/* response time (locationRequest) */
94
+	int exact;		/* exact true|false (locationRequest)*/
93 95
 } s_held_t, *p_held_t;
94 96
 
97
+typedef enum GEOTYPE
98
+{
99
+	ANY,		 /* any type */
100
+	CID,		 /* content-indirection */
101
+	HTTP,		 /* http uri */
102
+	HTTPS,		 /* https uri */
103
+	UNKNOWN = -1 /* unknown */
104
+} geotype_t;
105
+
106
+typedef struct GEOLIST
107
+{
108
+	char *value;	/* geolocation header value */
109
+	char *param;	/* value parameter */
110
+	geotype_t type; /* type */
111
+	struct GEOLIST *next;
112
+} s_geolist_t, *p_geolist_t;
113
+
95 114
 void lost_rand_str(char *, size_t);
96 115
 void lost_free_loc(p_loc_t);
97 116
 void lost_free_held(p_held_t);
98 117
 void lost_free_string(str *);
118
+void lost_reverse_geoheader_list(p_geolist_t *);
119
+void lost_delete_geoheader_list(p_geolist_t);
99 120
 
100 121
 int lost_parse_location_info(xmlNodePtr, p_loc_t);
101 122
 int lost_xpath_location(xmlDocPtr, char *, p_loc_t);
... ...
@@ -110,8 +131,12 @@ char *lost_get_from_header(struct sip_msg *, int *);
110 131
 char *lost_get_pai_header(struct sip_msg *, int *);
111 132
 char *lost_get_childname(xmlNodePtr, const char *, int *);
112 133
 char *lost_trim_content(char *, int *);
134
+char *lost_copy_geoheader_value(char *, int);
135
+char *lost_get_geoheader_value(p_geolist_t, geotype_t, int *);
113 136
 
114 137
 p_loc_t lost_new_loc(str);
138
+p_loc_t lost_parse_pidf(str, str);
115 139
 p_held_t lost_new_held(str, str, int, int);
140
+p_geolist_t lost_new_geoheader_list(str, int *);
116 141
 
117 142
 #endif