Browse code

lost: implemented sectors

use can use this examaple

== kamailio.cfg ===============================================

listen=udp:127.0.0.1:5060
listen=udp:[::1]:5060
loadmodule "xlog.so"
loadmodule "pv.so"
loadmodule "http_client.so"
loadmodule "lost.so"
loadmodule "textopsx.so"

request_route {

$var(new_test0)=@lost.nena;
$var(new_test1)=@lost.nena.call_id;
$var(new_test2)=@lost.nena.incident_id;
$var(new_test3)=@lost.emergency_call_data;
$var(new_test4)=@lost.emergency_call_data.provider_info;
$var(new_test5)=@lost.emergency_call_data.provider_info.counter;
$var(new_test6)=@lost.emergency_call_data.provider_info[1].uri_type;
$var(new_test7)=@lost.emergency_call_data.provider_info[-1].urib;
$var(new_test8)=@lost.emergency_call_data.provider_info[2].raw;
$var(new_test9)=@lost.emergency_call_data.provider_info[-1].body;
$var(new_test10)=@lost.emergency_call_data.provider_info[-1].header.content_type[2];
$var(new_test11)=@lost.emergency_call_data.provider_info[2].header.content_type;
$var(new_test12)=@lost.emergency_call_data.provider_info[2].header.content_disposition[1];
$var(new_test13)=@lost.emergency_call_data.provider_info[2].header.content_disposition[-2];

$var(new_test14)=@lost.emergency_call_data.service_info;
$var(new_test15)=@lost.emergency_call_data.service_info.counter;
$var(new_test16)=@lost.emergency_call_data.service_info[1].uri_type;
$var(new_test17)=@lost.emergency_call_data.service_info[-1].uri;
$var(new_test18)=@lost.emergency_call_data.service_info[2].raw;
$var(new_test19)=@lost.emergency_call_data.service_info[-2].body;
$var(new_test20)=@lost.emergency_call_data.service_info[3].header.content_type[1];
$var(new_test21)=@lost.emergency_call_data.service_info[-3].header.content_type;
$var(new_test22)=@lost.emergency_call_data.service_info[4].header.content_disposition[-1];
$var(new_test23)=@lost.emergency_call_data.service_info[-4].header.content_disposition[-2];

$var(new_test24)=@lost.emergency_call_data.device_info;
$var(new_test25)=@lost.emergency_call_data.device_info.counter;
$var(new_test26)=@lost.emergency_call_data.device_info[1].uri_type;
$var(new_test27)=@lost.emergency_call_data.device_info[-1].uri;
$var(new_test28)=@lost.emergency_call_data.device_info[2].raw;
$var(new_test29)=@lost.emergency_call_data.device_info[-2].body;
$var(new_test30)=@lost.emergency_call_data.device_info[3].header.content_type[1];
$var(new_test31)=@lost.emergency_call_data.device_info[-3].header.content_type;
$var(new_test32)=@lost.emergency_call_data.device_info[4].header.content_disposition[-1];
$var(new_test33)=@lost.emergency_call_data.device_info[-4].header.content_disposition[-2];

$var(new_test34)=@lost.emergency_call_data.subscriber_info;
$var(new_test35)=@lost.emergency_call_data.subscriber_info.counter;
$var(new_test36)=@lost.emergency_call_data.subscriber_info[1].uri_type;
$var(new_test37)=@lost.emergency_call_data.subscriber_info[-1].uri;
$var(new_test38)=@lost.emergency_call_data.subscriber_info[2].raw;
$var(new_test39)=@lost.emergency_call_data.subscriber_info[-2].body;
$var(new_test40)=@lost.emergency_call_data.subscriber_info[3].header.content_type[1];
$var(new_test41)=@lost.emergency_call_data.subscriber_info[-3].header.content_type;
$var(new_test42)=@lost.emergency_call_data.subscriber_info[4].header.content_disposition[-1];
$var(new_test43)=@lost.emergency_call_data.subscriber_info[-4].header.content_disposition[-2];

$var(new_test44)=@lost.emergency_call_data.comment;
$var(new_test45)=@lost.emergency_call_data.comment.counter;
$var(new_test46)=@lost.emergency_call_data.comment[1].uri_type;
$var(new_test47)=@lost.emergency_call_data.comment[-1].uri;
$var(new_test48)=@lost.emergency_call_data.comment[2].raw;
$var(new_test49)=@lost.emergency_call_data.comment[-2].body;
$var(new_test50)=@lost.emergency_call_data.comment[3].header.content_type[1];
$var(new_test51)=@lost.emergency_call_data.comment[-3].header.content_type;
$var(new_test52)=@lost.emergency_call_data.comment[4].header.content_disposition[-1];
$var(new_test53)=@lost.emergency_call_data.comment[-4].header.content_disposition[-2];
}

Eugene Sukhanov authored on 05/03/2021 19:16:13 • Sergey Safarov committed on 06/06/2021 07:57:42
Showing 3 changed files
... ...
@@ -41,8 +41,10 @@
41 41
 #include "../../core/dprint.h"
42 42
 
43 43
 #include "../../core/script_cb.h"
44
+#include "../../core/re.h"
44 45
 
45 46
 #include "functions.h"
47
+#include "lost_select.h"
46 48
 
47 49
 MODULE_VERSION
48 50
 
... ...
@@ -161,11 +163,14 @@ static int mod_init(void)
161 163
 
162 164
 	LM_DBG("**** init lost: held response time: %d \n", held_resp_time);
163 165
 
166
+	register_select_table(lost_sel);
167
+
164 168
 	LM_DBG("**** init lost module done.\n");
165 169
 
166 170
 	return 0;
167 171
 }
168 172
 
173
+
169 174
 /* Child initialization function */
170 175
 static int child_init(int rank)
171 176
 {
172 177
new file mode 100644
... ...
@@ -0,0 +1,945 @@
1
+/*
2
+ * lost module - select interface
3
+ *
4
+ * Copyright (C) 2021 Eugene Sukhanov
5
+ * NGA911
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 :: Select interface.
28
+ * \ingroup lost
29
+ * Module: \ref lost
30
+ */
31
+
32
+#include "../../core/select.h"
33
+#include "../../core/select_buf.h"
34
+#include "../../core/parser/msg_parser.h"
35
+#include "../../core/parser/hf.h"
36
+#include "../../core/parser/parse_content.h"
37
+#include "../../core/parser/parse_param.h"
38
+#include "../../core/parser/parse_uri.h"
39
+#include "../../core/strutils.h"
40
+#include "../../core/trim.h"
41
+#include "../../core/msg_translator.h"
42
+
43
+
44
+/*
45
+@lost.nena - all Call-Info headers with “purpose” param “nena-CallId” or “nena-IncidentId”;
46
+@lost.nena.call_id - the value of the first Call-Info header with purpose param of “nena-CallId”;
47
+@lost.nena.incident_id - the value of the first Call-Info header with purpose param of “nena-IncidentId”;
48
+
49
+@lost.emergency_call_data - all Call-Info headers with “purpose=EmergencyCallData”;
50
+
51
+@lost.emergency_call_data.provider_info - all Call-Info headers with “purpose=EmergencyCallData.ProviderInfo”;
52
+@lost.emergency_call_data.provider_info.counter - counter of present Call-Info headers with relevant emergency call data type;
53
+@lost.emergency_call_data.provider_info[%i].uri_type - one of `http`, `https`, `cid` or `sip`;
54
+@lost.emergency_call_data.provider_info[%i].uri - content of header inside “<>”;
55
+@lost.emergency_call_data.provider_info[%i].raw - see details below;
56
+@lost.emergency_call_data.provider_info[%i].body - if call-info header contains `cid` value (emergency call data passed BY_VALUE),
57
+	then contains `body` of relevant multipart part;
58
+@lost.emergency_call_data.provider_info[%i].header.%s[%i] - if call-info header contains `cid` value (emergency call data passed BY_VALUE),
59
+	then provide access to multipart part headers like `Content-Type`, `Content-Disposition`;
60
+@lost.emergency_call_data.provider_info[%i].header.%s - the short form of @lost.emergency_call_data.provider_info[%i].header.%s[1];
61
+
62
+@lost.emergency_call_data.service_info - all Call-Info headers with “purpose=EmergencyCallData.ServiceInfo”;
63
+@lost.emergency_call_data.service_info.counter - same as provider_info;
64
+@lost.emergency_call_data.service_info[%i].uri_type - same as provider_info
65
+@lost.emergency_call_data.service_info[%i].uri - content of header inside “<>”;
66
+@lost.emergency_call_data.service_info[%i].raw - same as provider_info
67
+@lost.emergency_call_data.service_info[%i].body - same as provider_info
68
+@lost.emergency_call_data.service_info[%i].header.%s[%i] - if call-info header contains `cid` value (emergency call data passed BY_VALUE), then provide access to multipart part headers like `Content-Type`, `Content-Disposition`;
69
+@lost.emergency_call_data.service_info[%i].header.%s - the short form of @lost.emergency_call_data.service_info[%i].header.%s[1];
70
+
71
+@lost.emergency_call_data.device_info - all Call-Info headers with “purpose=EmergencyCallData.DeviceInfo”;
72
+@lost.emergency_call_data.device_info.counter - same as provider_info;
73
+@lost.emergency_call_data.device_info[%i].uri_type - same as provider_info
74
+@lost.emergency_call_data.device_info[%i].uri - content of header inside “<>”;
75
+@lost.emergency_call_data.device_info[%i].raw - same as provider_info
76
+@lost.emergency_call_data.device_info[%i].body - same as provider_info
77
+@lost.emergency_call_data.device_info[%i].header.%s[%i] - if call-info header contains `cid` value (emergency call data passed BY_VALUE), then provide access to multipart part headers like `Content-Type`, `Content-Disposition`;
78
+@lost.emergency_call_data.device_info[%i].header.%s - the short form of @lost.emergency_call_data.device_info[%i].header.%s[1];
79
+
80
+@lost.emergency_call_data.subscriber_info - all Call-Info headers with “purpose=EmergencyCallData.SubscriberInfo”;
81
+@lost.emergency_call_data.subscriber_info.counter - same as provider_info;
82
+@lost.emergency_call_data.subscriber_info[%i].uri_type - same as provider_info
83
+@lost.emergency_call_data.subscriber_info[%i].uri - content of header inside “<>”;
84
+@lost.emergency_call_data.subscriber_info[%i].raw - same as provider_info
85
+@lost.emergency_call_data.subscriber_info[%i].body - same as provider_info
86
+@lost.emergency_call_data.subscriber_info[%i].header.%s[%i] - if call-info header contains `cid` value (emergency call data passed BY_VALUE), then provide access to multipart part headers like `Content-Type`, `Content-Disposition`;
87
+@lost.emergency_call_data.subscriber_info[%i].header.%s - the short form of @lost.emergency_call_data.subscriber_info[%i].header.%s[1];
88
+
89
+@lost.emergency_call_data.comment - all Call-Info headers with “purpose=EmergencyCallData.Comment”;
90
+@lost.emergency_call_data.comment.counter - same as provider_info;
91
+@lost.emergency_call_data.comment[%i].uri_type - same as provider_info
92
+@lost.emergency_call_data.comment[%i].uri - content of header inside “<>”;
93
+@lost.emergency_call_data.comment[%i].raw - same as provider_info
94
+@lost.emergency_call_data.comment[%i].body - same as provider_info
95
+@lost.emergency_call_data.comment[%i].header.%s[%i] - if call-info header contains `cid` value (emergency call data passed BY_VALUE), then provide access to multipart part headers like `Content-Type`, `Content-Disposition`;
96
+@lost.emergency_call_data.comment[%i].header.%s - the short form of @lost.emergency_call_data.comment[%i].header.%s[1];
97
+ */
98
+
99
+static char* strstrin(char* string, int len, char* substring, int n)
100
+{
101
+    const char *a, *b;
102
+    int l;
103
+    b = substring;
104
+    if ((*b == 0) || (n <= 0)) return string;
105
+    for(l = 0; (string[l] != 0) && (l < len); l++)
106
+    {
107
+        if (tolower(string[l]) != tolower(*b)) continue;
108
+        a = string + l;
109
+        while(1)
110
+        {
111
+            if((b - substring) >= n) return string + l;
112
+            if(tolower(*a++) != tolower(*b++)) break;
113
+        }
114
+        b = substring;
115
+    }
116
+    return NULL;
117
+}
118
+
119
+static char* strstri(char* string, int len, char* substring)
120
+{
121
+	return strstrin(string, len, substring, strlen(substring));
122
+}
123
+
124
+static int sel_lost(str* res, select_t* s, struct sip_msg* msg)
125
+{
126
+	// test
127
+	static char buf[128];
128
+	strcpy(buf, "Lost module root selector\n");
129
+	res->s = buf;
130
+	res->len = strlen(buf);
131
+	return 0;
132
+}
133
+
134
+char* CheckParamHdr(str* s, char* paramname, char* paramval)
135
+{
136
+	str ts;
137
+	char *pos, *respos;
138
+	ts = *s;
139
+	pos = strstri(ts.s, ts.len, paramname);
140
+	if (pos)	//paramname found
141
+	{
142
+		respos = pos;
143
+		ts.len = ts.len - (pos - ts.s);
144
+		ts.s = pos;
145
+		trim_leading(&ts);
146
+		pos = strstri(ts.s, ts.len, "=");
147
+		if (pos)	//"=" found
148
+		{
149
+			ts.len = ts.len - (pos - ts.s);
150
+			ts.s = pos;
151
+			trim_leading(&ts);
152
+			pos = strstri(ts.s, ts.len, paramval);
153
+			if (pos) return respos;	//param found, return paramname ptr
154
+		}
155
+	}
156
+	return NULL;
157
+}
158
+
159
+char *GetHdrValueEnd(str* hs)
160
+{
161
+	int l, q = 0;
162
+	for (l = 0; l < hs->len; l++)
163
+	{
164
+		switch (hs->s[l])
165
+		{
166
+		case '"': q ^= 1; break;
167
+		case ',': if (!q) return hs->s + l;
168
+		}
169
+	}
170
+	return hs->s + l;
171
+}
172
+
173
+int GetNextHdrValue(str* body, str* res)
174
+{
175
+	char *pos;
176
+
177
+	res->s += res->len;
178
+	res->len = body->len - (res->s - body->s);
179
+	for(; res->len > 0; res->len--, res->s++)
180
+	{
181
+		if ((*(res->s) != ' ') && (*(res->s) != '\t') && (*(res->s) != '\r') && (*(res->s) != '\n') && (*(res->s) != ','))
182
+			break;
183
+	}
184
+	if (!res->len) return 0;	// no more values
185
+
186
+	pos = GetHdrValueEnd(res);
187
+	res->len = pos - res->s;
188
+	return 1;	// OK
189
+}
190
+
191
+#define _ALLOC_INC_SIZE 1024
192
+// select all headers with purpose=prps or purpose=prps2
193
+// return headers count or negative value on error
194
+static int sel_hdrs(str* res, select_t* s, struct sip_msg* msg, char *prps, char *prps2)
195
+{
196
+	int indx = 0;
197
+	int l, check, cnt = 0;
198
+	str ts;
199
+	struct hdr_field *hf;
200
+	char *buf = NULL;
201
+	int buf_len = _ALLOC_INC_SIZE;
202
+	char* posarr[1024];
203
+	res->s = NULL;
204
+	res->len = 0;
205
+
206
+	if(parse_headers(msg, HDR_CALLINFO_F, 0) < 0)
207
+	{
208
+		LM_ERR("error while parsing message headers\n");
209
+		return -1;
210
+	}
211
+	buf = pkg_malloc(buf_len);
212
+	if(!buf) {
213
+		LM_ERR("out of memory\n");
214
+		res->len = 0;
215
+		return E_OUT_OF_MEM;
216
+	}
217
+	buf[0] = 0;
218
+
219
+	l = 0;
220
+	for (hf = msg->headers; hf; hf = hf->next)
221
+	{
222
+		if (HDR_CALLINFO_T == hf->type)
223
+		{
224
+			ts.s = hf->body.s;
225
+			ts.len = 0;
226
+			while (GetNextHdrValue(&hf->body, &ts))
227
+			{
228
+				check = CheckParamHdr(&ts, "purpose", prps)?1:0;
229
+				if (prps2) check |= (CheckParamHdr(&ts, "purpose", prps2)?2:0);
230
+				if (check)
231
+				{
232
+					l = strlen(buf);
233
+					if (l + ts.len + 1 > buf_len)
234
+					{
235
+						buf_len = l + ts.len + _ALLOC_INC_SIZE;
236
+						res->s = pkg_realloc(buf, buf_len);
237
+						if (!res->s)
238
+						{
239
+							pkg_free(buf);
240
+							LM_ERR("cannot realloc buffer\n");
241
+							res->len = 0;
242
+							return E_OUT_OF_MEM;
243
+						}
244
+						buf = res->s;
245
+					}
246
+					if (l) { strcat(buf, ","); l++; }
247
+					posarr[cnt] = buf + l;
248
+					memcpy(buf + l, ts.s, ts.len);
249
+					l += ts.len;
250
+					buf[l] = 0;
251
+					cnt++;
252
+				}
253
+			}
254
+			posarr[cnt] = buf + strlen(buf);
255
+		}
256
+	}
257
+	if ((s->n > 3) && (s->params[3].type==SEL_PARAM_INT))
258
+		indx = s->params[3].v.i;
259
+
260
+	ts.s = buf;
261
+	ts.len = strlen(buf);
262
+
263
+	if (indx)
264
+	{
265
+		// select only data_name[indx]
266
+		if (indx < 0)
267
+		{	// take real positive index for negative indx
268
+			indx = 1 + cnt + indx;
269
+		}
270
+
271
+		if ((indx < 1) || (indx > cnt))
272
+		{
273
+			ts.s = NULL;
274
+			ts.len = 0;
275
+		}
276
+		else
277
+		{
278
+			ts.s = posarr[indx - 1];
279
+			ts.len = posarr[indx] - posarr[indx - 1] - 1;
280
+		}
281
+	}
282
+
283
+	res->len = ts.len;
284
+	res->s = get_static_buffer(res->len);
285
+	if(!res->s)
286
+	{
287
+		pkg_free(buf);
288
+		res->len = 0;
289
+		LM_ERR("cannot allocate static buffer\n");
290
+		return E_OUT_OF_MEM;
291
+	}
292
+	memcpy(res->s, ts.s, res->len);
293
+	pkg_free(buf);
294
+
295
+	return cnt;
296
+}
297
+
298
+// @lost.nena
299
+static int sel_nena(str* res, select_t* s, struct sip_msg* msg)
300
+{
301
+	// @lost.nena - all Call-Info headers with “purpose” param “nena-CallId” or “nena-IncidentId”;
302
+	int err = sel_hdrs(res, s, msg, "nena-CallId", "nena-IncidentId");
303
+	return (err < 0)?err:0;
304
+}
305
+
306
+// 	@lost.nena.call_id
307
+static int sel_call_id(str* res, select_t* s, struct sip_msg* msg)
308
+{
309
+	str ts;
310
+	int err = sel_hdrs(res, s, msg, "nena-CallId", NULL);
311
+	err = (err < 0)?err:0;
312
+	if (!err)
313
+	{
314
+		ts.s = res->s;
315
+		ts.len = 0;
316
+		if (!GetNextHdrValue(res, &ts)) return -1;
317
+		res->s = ts.s;
318
+		res->len = ts.len;
319
+	}
320
+	return err;
321
+}
322
+
323
+// 	@lost.nena.incident_id
324
+static int sel_incident_id(str* res, select_t* s, struct sip_msg* msg)
325
+{
326
+	str ts;
327
+	int err = sel_hdrs(res, s, msg, "nena-IncidentId", NULL);
328
+	err = (err < 0)?err:0;
329
+	if (!err)
330
+	{
331
+		ts.s = res->s;
332
+		ts.len = 0;
333
+		if (!GetNextHdrValue(res, &ts)) return -1;
334
+		res->s = ts.s;
335
+		res->len = ts.len;
336
+	}
337
+	return err;
338
+}
339
+
340
+// 	@lost.emergency_call_data
341
+static int sel_emergency_call_data(str* res, select_t* s, struct sip_msg* msg)
342
+{
343
+	// “purpose=EmergencyCallData”
344
+	int err = sel_hdrs(res, s, msg, "EmergencyCallData", NULL);
345
+	return (err < 0)?err:0;
346
+}
347
+
348
+// @lost.emergency_call_data.provider_info -
349
+static int sel_provider_info(str* res, select_t* s, struct sip_msg* msg)
350
+{
351
+	// all Call-Info headers with “purpose=EmergencyCallData.ProviderInfo”;
352
+	int err = sel_hdrs(res, s, msg, "EmergencyCallData.ProviderInfo", NULL);
353
+	return (err < 0)?err:0;
354
+}
355
+
356
+//@lost.emergency_call_data.provider_info.counter - counter of present Call-Info headers with relevant emergency call data type;
357
+static int sel_provider_info_cnt(str* res, select_t* s, struct sip_msg* msg)
358
+{
359
+	// all Call-Info headers with “purpose=EmergencyCallData.ProviderInfo”;
360
+	s->params[3].v.i = 0;
361
+	int err = sel_hdrs(res, s, msg, "EmergencyCallData.ProviderInfo", NULL);
362
+	res->s = int2str((err > 0)?err:0, &res->len);
363
+	return (err < 0)?err:0;
364
+}
365
+
366
+static int sel_hdr_uri(str* res)
367
+{
368
+	char *pos, *pos2;
369
+	pos = strstri(res->s, res->len, "<");
370
+	if (!pos) return -1;
371
+	pos2 = strstri(pos+1, res->len - (pos - res->s), ">");
372
+	if (!pos2) return -2;
373
+
374
+	res->len = pos2 - pos - 1;
375
+	res->s = pos + 1;
376
+	return 0;
377
+}
378
+
379
+// @lost.emergency_call_data.provider_info[%i].uri - content of header inside “<>”;
380
+static int sel_provider_info_uri(str* res, select_t* s, struct sip_msg* msg)
381
+{
382
+	// @lost.emergency_call_data.provider_info[%i]
383
+	int err = sel_provider_info(res, s, msg);
384
+	if (!err) err = sel_hdr_uri(res);
385
+	return err;
386
+}
387
+
388
+SELECT_F(select_uri_type)
389
+
390
+//static struct sip_uri uri;
391
+
392
+// @lost.emergency_call_data.provider_info[%i].uri_type;
393
+static int sel_provider_info_uri_type(str* res, select_t* s, struct sip_msg* msg)
394
+{
395
+	// @lost.emergency_call_data.provider_info[%i]
396
+	int err = sel_provider_info_uri(res, s, msg);
397
+	if (!err)
398
+	{
399
+		// `http`, `https`, `cid` or `sip`
400
+		char* pos = strstri(res->s, res->len, ":");
401
+		if (pos) res->len = pos - res->s;
402
+		else res->len = 0;
403
+	}
404
+	return err;
405
+}
406
+
407
+static int GetNextBodyPart(str* body, str* boundary, str* bodypart)
408
+{
409
+	char *start = body->s;
410
+	int len = body->len;
411
+
412
+	if (bodypart->len > 0)
413
+	{
414
+		start = bodypart->s + bodypart->len;
415
+		len -= start - body->s;
416
+	}
417
+	if (len > 0)
418
+	{
419
+		char* pos = strstri(start, len, "Content-Type:");
420
+		if (pos)
421
+		{
422
+			len -= pos - start;
423
+			start = pos;
424
+			pos = strstrin(start, len, boundary->s, boundary->len);
425
+			if (pos) len = pos - start;
426
+			bodypart->s = start;
427
+			bodypart->len = len;
428
+			return 1;
429
+		}
430
+	}
431
+
432
+	return 0;	// no more parts
433
+}
434
+
435
+static int GetNextBodyPartHdr(str *bodypart, str *bhr)
436
+{
437
+	str ts = *bodypart;
438
+	char *start = bodypart->s;
439
+	int len = bodypart->len;
440
+
441
+	trim_leading(&ts);
442
+	start = ts.s;
443
+	len = ts.len;
444
+	if (bhr->len > 0)
445
+	{
446
+		start = bhr->s + bhr->len;
447
+		len -= start - ts.s;
448
+	}
449
+	if (len > 0)
450
+	{
451
+		char* pos = strstri(start, len, "\r\n");
452
+		if (pos)
453
+		{
454
+			len = 2 + pos - start;
455
+			bhr->s = start;
456
+			bhr->len = len;
457
+			ts = *bhr;
458
+			trim(&ts);
459
+			return (ts.len > 0)?1:0;	// stop on void line
460
+		}
461
+	}
462
+	return 0;	// no more headers
463
+}
464
+
465
+static int sel_uri_raw(str* res, select_t* s, struct sip_msg* msg)
466
+{
467
+	char* pos = strstri(res->s, res->len, "cid:");
468
+	if (pos)
469
+	{
470
+		str boundary = {0, 0};
471
+		str body = {0, 0};
472
+		str bodypart = {0, 0};
473
+		str cid, bhr;
474
+		int l = strlen("cid:");
475
+
476
+		// get message body
477
+		body.len = 0;
478
+		body.s = get_body(msg);
479
+		if (!body.s)
480
+		{
481
+			LM_ERR("failed to get the content message body\n");
482
+			res->len = 0;
483
+			return -1;
484
+		}
485
+		body.len = msg->buf + msg->len - body.s;
486
+		if (body.len <= 0)
487
+		{
488
+			LM_DBG("empty body in the message\n");
489
+			res->len = 0;
490
+			return 1;
491
+		}
492
+
493
+		// get Content-Id:
494
+		cid.len = res->len - l - (pos - res->s);
495
+		cid.s = pos + l;
496
+
497
+		// find relevant to Content-Id multipart part
498
+		if (get_boundary(msg, &boundary))
499
+		{
500
+			// Content is not multipart
501
+			boundary.len = 0;
502
+		}
503
+
504
+		res->len = 0;
505
+		while (!res->len && GetNextBodyPart(&body, &boundary, &bodypart))
506
+		{
507
+			bhr.len = 0;
508
+			while (!res->len && GetNextBodyPartHdr(&bodypart, &bhr))
509
+			{
510
+				if (!strncasecmp(bhr.s, "Content-Id:", strlen("Content-Id:")))
511
+				{
512
+					// check Content-Id in cid->s
513
+					pos = strstrin(bhr.s, bhr.len, cid.s, cid.len);
514
+					if (pos)	//the cid found
515
+					{
516
+						res->len = bodypart.len;
517
+						res->s = bodypart.s;
518
+					}
519
+				}
520
+			}
521
+		}
522
+
523
+		if(boundary.s) pkg_free(boundary.s);
524
+	}
525
+	else res->len = 0;
526
+	return 0;
527
+}
528
+
529
+// @lost.emergency_call_data.provider_info[%i].raw;
530
+static int sel_provider_info_raw(str* res, select_t* s, struct sip_msg* msg)
531
+{
532
+	int err = sel_provider_info_uri(res, s, msg);
533
+	if (!err) err = sel_uri_raw(res, s, msg);
534
+	return err;
535
+}
536
+
537
+static int sel_raw_body(str* res, select_t* s, struct sip_msg* msg)
538
+{
539
+	str bodypart;
540
+	str bhr;
541
+	bodypart = *res;
542
+	bhr.len = 0;
543
+	while (GetNextBodyPartHdr(&bodypart, &bhr))
544
+	{
545
+		res->s = bhr.s + bhr.len;
546
+		res->len = bodypart.len - (res->s - bodypart.s);
547
+	}
548
+	return 0;
549
+}
550
+
551
+//@lost.emergency_call_data.provider_info[%i].body
552
+// - if call-info header contains `cid` value (emergency call data passed BY_VALUE), then contains `body` of relevant multipart part;
553
+static int sel_provider_info_body(str* res, select_t* s, struct sip_msg* msg)
554
+{
555
+	// @lost.emergency_call_data.provider_info[%i]
556
+	int err = sel_provider_info_raw(res, s, msg);
557
+	if (!err) err = sel_raw_body(res, s, msg);
558
+	return err;
559
+}
560
+
561
+static int sel_provider_info_header(str *res, select_t *s, struct sip_msg *msg)
562
+{ /* dummy */
563
+	return 0;
564
+}
565
+
566
+static int sel_raw_header_name(str *res, select_t *s, struct sip_msg *msg)
567
+{
568
+	str hname = {0, 0};
569
+	str bodypart;
570
+	str bhr;
571
+	int i, indx = 1, cnt;
572
+
573
+	if(s->params[5].type == SEL_PARAM_STR)
574
+	{
575
+		for(i = s->params[5].v.s.len - 1; i > 0; i--)
576
+		{
577
+			if(s->params[5].v.s.s[i] == '_')
578
+				s->params[5].v.s.s[i] = '-';
579
+		}
580
+		hname.len = s->params[5].v.s.len;
581
+		hname.s = s->params[5].v.s.s;
582
+
583
+	}
584
+	if ((s->n > 6) && (s->params[6].type==SEL_PARAM_INT))
585
+		indx = s->params[6].v.i;
586
+
587
+	bodypart = *res;
588
+	cnt = indx;
589
+	if (indx < 0)
590
+	{	// take real positive index for negative indx
591
+		cnt = 0;
592
+		bhr.len = 0;
593
+		while (GetNextBodyPartHdr(&bodypart, &bhr))
594
+			if (!strncasecmp(bhr.s, hname.s, hname.len)) cnt++;
595
+
596
+		indx = 1 + cnt + indx;
597
+	}
598
+
599
+	res->s = NULL;
600
+	res->len = 0;
601
+	if ((indx > 0) && (indx <= cnt))
602
+	{
603
+		bhr.len = 0;
604
+		cnt = 0;
605
+		while (!res->len && GetNextBodyPartHdr(&bodypart, &bhr))
606
+		{
607
+			if (!strncasecmp(bhr.s, hname.s, hname.len))
608
+			{
609
+				cnt++;
610
+				if (indx == cnt)
611
+				{
612
+					res->s = bhr.s;
613
+					res->len = bhr.len;
614
+					break;
615
+				}
616
+			}
617
+		}
618
+	}
619
+	return 0;
620
+}
621
+
622
+//@lost.emergency_call_data.provider_info[%i].header.%s[%i] - if call-info header contains `cid` value 
623
+// (emergency call data passed BY_VALUE), then provide access to multipart part headers like 
624
+// `Content-Type`, `Content-Disposition`;
625
+static int sel_provider_info_header_name(str *res, select_t *s, struct sip_msg *msg)
626
+{
627
+	int err = sel_provider_info_raw(res, s, msg);
628
+	if (!err) err = sel_raw_header_name(res, s, msg);
629
+	return err;
630
+}
631
+
632
+
633
+/*
634
+ * @lost.emergency_call_data.service_info -
635
+ */
636
+
637
+static int sel_service_info(str* res, select_t* s, struct sip_msg* msg)
638
+{
639
+	// all Call-Info headers with “purpose=EmergencyCallData.ServiceInfo”;
640
+	int err = sel_hdrs(res, s, msg, "EmergencyCallData.ServiceInfo", NULL);
641
+	return (err < 0)?err:0;
642
+}
643
+
644
+static int sel_service_info_cnt(str* res, select_t* s, struct sip_msg* msg)
645
+{
646
+	s->params[3].v.i = 0;
647
+	int err = sel_hdrs(res, s, msg, "EmergencyCallData.ServiceInfo", NULL);
648
+	res->s = int2str((err > 0)?err:0, &res->len);
649
+	return (err < 0)?err:0;
650
+}
651
+
652
+static int sel_service_info_uri(str* res, select_t* s, struct sip_msg* msg)
653
+{
654
+	// @lost.emergency_call_data.provider_info[%i]
655
+	int err = sel_service_info(res, s, msg);
656
+	if (!err) err = sel_hdr_uri(res);
657
+	return err;
658
+}
659
+
660
+static int sel_service_info_uri_type(str* res, select_t* s, struct sip_msg* msg)
661
+{
662
+	int err = sel_service_info_uri(res, s, msg);
663
+	if (!err)
664
+	{
665
+		// `http`, `https`, `cid` or `sip`
666
+		char* pos = strstri(res->s, res->len, ":");
667
+		if (pos) res->len = pos - res->s;
668
+		else res->len = 0;
669
+	}
670
+	return err;
671
+}
672
+
673
+static int sel_service_info_raw(str* res, select_t* s, struct sip_msg* msg)
674
+{
675
+	int err = sel_service_info_uri(res, s, msg);
676
+	if (!err) err = sel_uri_raw(res, s, msg);
677
+	return err;
678
+}
679
+
680
+static int sel_service_info_body(str* res, select_t* s, struct sip_msg* msg)
681
+{
682
+	int err = sel_service_info_raw(res, s, msg);
683
+	if (!err) err = sel_raw_body(res, s, msg);
684
+	return err;
685
+}
686
+
687
+static int sel_service_info_header(str *res, select_t *s, struct sip_msg *msg)
688
+{ /* dummy */
689
+	return 0;
690
+}
691
+
692
+static int sel_service_info_header_name(str *res, select_t *s, struct sip_msg *msg)
693
+{
694
+	int err = sel_service_info_raw(res, s, msg);
695
+	if (!err) err = sel_raw_header_name(res, s, msg);
696
+	return err;
697
+}
698
+
699
+
700
+
701
+/*
702
+ * @lost.emergency_call_data.device_info -
703
+ */
704
+
705
+static int sel_device_info(str* res, select_t* s, struct sip_msg* msg)
706
+{
707
+	int err = sel_hdrs(res, s, msg, "EmergencyCallData.DeviceInfo", NULL);
708
+	return (err < 0)?err:0;
709
+}
710
+
711
+static int sel_device_info_cnt(str* res, select_t* s, struct sip_msg* msg)
712
+{
713
+	s->params[3].v.i = 0;
714
+	int err = sel_hdrs(res, s, msg, "EmergencyCallData.DeviceInfo", NULL);
715
+	res->s = int2str((err > 0)?err:0, &res->len);
716
+	return (err < 0)?err:0;
717
+}
718
+
719
+static int sel_device_info_uri(str* res, select_t* s, struct sip_msg* msg)
720
+{
721
+	int err = sel_device_info(res, s, msg);
722
+	if (!err) err = sel_hdr_uri(res);
723
+	return err;
724
+}
725
+
726
+static int sel_device_info_uri_type(str* res, select_t* s, struct sip_msg* msg)
727
+{
728
+	int err = sel_device_info_uri(res, s, msg);
729
+	if (!err)
730
+	{
731
+		char* pos = strstri(res->s, res->len, ":");
732
+		if (pos) res->len = pos - res->s;
733
+		else res->len = 0;
734
+	}
735
+	return err;
736
+}
737
+
738
+static int sel_device_info_raw(str* res, select_t* s, struct sip_msg* msg)
739
+{
740
+	int err = sel_device_info_uri(res, s, msg);
741
+	if (!err) err = sel_uri_raw(res, s, msg);
742
+	return err;
743
+}
744
+
745
+static int sel_device_info_body(str* res, select_t* s, struct sip_msg* msg)
746
+{
747
+	int err = sel_device_info_raw(res, s, msg);
748
+	if (!err) err = sel_raw_body(res, s, msg);
749
+	return err;
750
+}
751
+
752
+static int sel_device_info_header(str *res, select_t *s, struct sip_msg *msg)
753
+{ /* dummy */
754
+	return 0;
755
+}
756
+
757
+static int sel_device_info_header_name(str *res, select_t *s, struct sip_msg *msg)
758
+{
759
+	int err = sel_device_info_raw(res, s, msg);
760
+	if (!err) err = sel_raw_header_name(res, s, msg);
761
+	return err;
762
+}
763
+
764
+/*
765
+ * @lost.emergency_call_data.subscriber_info -
766
+ */
767
+
768
+static int sel_subscriber_info(str* res, select_t* s, struct sip_msg* msg)
769
+{
770
+	int err = sel_hdrs(res, s, msg, "EmergencyCallData.SubscriberInfo", NULL);
771
+	return (err < 0)?err:0;
772
+}
773
+
774
+static int sel_subscriber_info_cnt(str* res, select_t* s, struct sip_msg* msg)
775
+{
776
+	s->params[3].v.i = 0;
777
+	int err = sel_hdrs(res, s, msg, "EmergencyCallData.SubscriberInfo", NULL);
778
+	res->s = int2str((err > 0)?err:0, &res->len);
779
+	return (err < 0)?err:0;
780
+}
781
+
782
+static int sel_subscriber_info_uri(str* res, select_t* s, struct sip_msg* msg)
783
+{
784
+	int err = sel_subscriber_info(res, s, msg);
785
+	if (!err) err = sel_hdr_uri(res);
786
+	return err;
787
+}
788
+
789
+static int sel_subscriber_info_uri_type(str* res, select_t* s, struct sip_msg* msg)
790
+{
791
+	int err = sel_subscriber_info_uri(res, s, msg);
792
+	if (!err)
793
+	{
794
+		char* pos = strstri(res->s, res->len, ":");
795
+		if (pos) res->len = pos - res->s;
796
+		else res->len = 0;
797
+	}
798
+	return err;
799
+}
800
+
801
+static int sel_subscriber_info_raw(str* res, select_t* s, struct sip_msg* msg)
802
+{
803
+	int err = sel_subscriber_info_uri(res, s, msg);
804
+	if (!err) err = sel_uri_raw(res, s, msg);
805
+	return err;
806
+}
807
+
808
+static int sel_subscriber_info_body(str* res, select_t* s, struct sip_msg* msg)
809
+{
810
+	int err = sel_subscriber_info_raw(res, s, msg);
811
+	if (!err) err = sel_raw_body(res, s, msg);
812
+	return err;
813
+}
814
+
815
+static int sel_subscriber_info_header(str *res, select_t *s, struct sip_msg *msg)
816
+{ /* dummy */
817
+	return 0;
818
+}
819
+
820
+static int sel_subscriber_info_header_name(str *res, select_t *s, struct sip_msg *msg)
821
+{
822
+	int err = sel_subscriber_info_raw(res, s, msg);
823
+	if (!err) err = sel_raw_header_name(res, s, msg);
824
+	return err;
825
+}
826
+
827
+/*
828
+ * @lost.emergency_call_data.comment -
829
+ */
830
+
831
+static int sel_comment(str* res, select_t* s, struct sip_msg* msg)
832
+{
833
+	int err = sel_hdrs(res, s, msg, "EmergencyCallData.Comment", NULL);
834
+	return (err < 0)?err:0;
835
+}
836
+
837
+static int sel_comment_cnt(str* res, select_t* s, struct sip_msg* msg)
838
+{
839
+	s->params[3].v.i = 0;
840
+	int err = sel_hdrs(res, s, msg, "EmergencyCallData.Comment", NULL);
841
+	res->s = int2str((err > 0)?err:0, &res->len);
842
+	return (err < 0)?err:0;
843
+}
844
+
845
+static int sel_comment_uri(str* res, select_t* s, struct sip_msg* msg)
846
+{
847
+	int err = sel_comment(res, s, msg);
848
+	if (!err) err = sel_hdr_uri(res);
849
+	return err;
850
+}
851
+
852
+static int sel_comment_uri_type(str* res, select_t* s, struct sip_msg* msg)
853
+{
854
+	int err = sel_comment_uri(res, s, msg);
855
+	if (!err)
856
+	{
857
+		char* pos = strstri(res->s, res->len, ":");
858
+		if (pos) res->len = pos - res->s;
859
+		else res->len = 0;
860
+	}
861
+	return err;
862
+}
863
+
864
+static int sel_comment_raw(str* res, select_t* s, struct sip_msg* msg)
865
+{
866
+	int err = sel_comment_uri(res, s, msg);
867
+	if (!err) err = sel_uri_raw(res, s, msg);
868
+	return err;
869
+}
870
+
871
+static int sel_comment_body(str* res, select_t* s, struct sip_msg* msg)
872
+{
873
+	int err = sel_comment_raw(res, s, msg);
874
+	if (!err) err = sel_raw_body(res, s, msg);
875
+	return err;
876
+}
877
+
878
+static int sel_comment_header(str *res, select_t *s, struct sip_msg *msg)
879
+{ /* dummy */
880
+	return 0;
881
+}
882
+
883
+static int sel_comment_header_name(str *res, select_t *s, struct sip_msg *msg)
884
+{
885
+	int err = sel_comment_raw(res, s, msg);
886
+	if (!err) err = sel_raw_header_name(res, s, msg);
887
+	return err;
888
+}
889
+
890
+select_row_t lost_sel[] = {
891
+	/* Current cipher parameters */
892
+	{ NULL, SEL_PARAM_STR, STR_STATIC_INIT("lost"), sel_lost, SEL_PARAM_EXPECTED},
893
+
894
+	{ sel_lost, SEL_PARAM_STR, STR_STATIC_INIT("nena"), sel_nena, 0},
895
+	{ sel_nena, SEL_PARAM_STR, STR_STATIC_INIT("call_id"), sel_call_id, 0},
896
+	{ sel_nena, SEL_PARAM_STR, STR_STATIC_INIT("incident_id"), sel_incident_id, 0},
897
+
898
+	{ sel_lost, SEL_PARAM_STR, STR_STATIC_INIT("emergency_call_data"), sel_emergency_call_data, 0},
899
+	{ sel_emergency_call_data, SEL_PARAM_STR, STR_STATIC_INIT("provider_info"), sel_provider_info, CONSUME_NEXT_INT | OPTIONAL},
900
+	{ sel_provider_info, SEL_PARAM_STR, STR_STATIC_INIT("counter"), sel_provider_info_cnt, 0},
901
+	{ sel_provider_info, SEL_PARAM_STR, STR_STATIC_INIT("urib"), sel_provider_info_uri, 0},
902
+	{ sel_provider_info, SEL_PARAM_STR, STR_STATIC_INIT("uri_type"), sel_provider_info_uri_type, 0},
903
+	{ sel_provider_info, SEL_PARAM_STR, STR_STATIC_INIT("raw"), sel_provider_info_raw, 0},
904
+	{ sel_provider_info, SEL_PARAM_STR, STR_STATIC_INIT("body"), sel_provider_info_body, 0},
905
+	{ sel_provider_info, SEL_PARAM_STR, STR_STATIC_INIT("header"), sel_provider_info_header, SEL_PARAM_EXPECTED},
906
+	{ sel_provider_info_header, SEL_PARAM_STR, STR_NULL, sel_provider_info_header_name, CONSUME_NEXT_INT | OPTIONAL },
907
+
908
+	{ sel_emergency_call_data, SEL_PARAM_STR, STR_STATIC_INIT("service_info"), sel_service_info, CONSUME_NEXT_INT | OPTIONAL},
909
+	{ sel_service_info, SEL_PARAM_STR, STR_STATIC_INIT("counter"), sel_service_info_cnt, 0},
910
+	{ sel_service_info, SEL_PARAM_STR, STR_STATIC_INIT("urib"), sel_service_info_uri, 0},
911
+	{ sel_service_info, SEL_PARAM_STR, STR_STATIC_INIT("uri_type"), sel_service_info_uri_type, 0},
912
+	{ sel_service_info, SEL_PARAM_STR, STR_STATIC_INIT("raw"), sel_service_info_raw, 0},
913
+	{ sel_service_info, SEL_PARAM_STR, STR_STATIC_INIT("body"), sel_service_info_body, 0},
914
+	{ sel_service_info, SEL_PARAM_STR, STR_STATIC_INIT("header"), sel_service_info_header, SEL_PARAM_EXPECTED},
915
+	{ sel_service_info_header, SEL_PARAM_STR, STR_NULL, sel_service_info_header_name, CONSUME_NEXT_INT | OPTIONAL },
916
+
917
+	{ sel_emergency_call_data, SEL_PARAM_STR, STR_STATIC_INIT("device_info"), sel_device_info, CONSUME_NEXT_INT | OPTIONAL},
918
+	{ sel_device_info, SEL_PARAM_STR, STR_STATIC_INIT("counter"), sel_device_info_cnt, 0},
919
+	{ sel_device_info, SEL_PARAM_STR, STR_STATIC_INIT("urib"), sel_device_info_uri, 0},
920
+	{ sel_device_info, SEL_PARAM_STR, STR_STATIC_INIT("uri_type"), sel_device_info_uri_type, 0},
921
+	{ sel_device_info, SEL_PARAM_STR, STR_STATIC_INIT("raw"), sel_device_info_raw, 0},
922
+	{ sel_device_info, SEL_PARAM_STR, STR_STATIC_INIT("body"), sel_device_info_body, 0},
923
+	{ sel_device_info, SEL_PARAM_STR, STR_STATIC_INIT("header"), sel_device_info_header, SEL_PARAM_EXPECTED},
924
+	{ sel_device_info_header, SEL_PARAM_STR, STR_NULL, sel_device_info_header_name, CONSUME_NEXT_INT | OPTIONAL },
925
+
926
+	{ sel_emergency_call_data, SEL_PARAM_STR, STR_STATIC_INIT("subscriber_info"), sel_subscriber_info, CONSUME_NEXT_INT | OPTIONAL},
927
+	{ sel_subscriber_info, SEL_PARAM_STR, STR_STATIC_INIT("counter"), sel_subscriber_info_cnt, 0},
928
+	{ sel_subscriber_info, SEL_PARAM_STR, STR_STATIC_INIT("urib"), sel_subscriber_info_uri, 0},
929
+	{ sel_subscriber_info, SEL_PARAM_STR, STR_STATIC_INIT("uri_type"), sel_subscriber_info_uri_type, 0},
930
+	{ sel_subscriber_info, SEL_PARAM_STR, STR_STATIC_INIT("raw"), sel_subscriber_info_raw, 0},
931
+	{ sel_subscriber_info, SEL_PARAM_STR, STR_STATIC_INIT("body"), sel_subscriber_info_body, 0},
932
+	{ sel_subscriber_info, SEL_PARAM_STR, STR_STATIC_INIT("header"), sel_subscriber_info_header, SEL_PARAM_EXPECTED},
933
+	{ sel_subscriber_info_header, SEL_PARAM_STR, STR_NULL, sel_subscriber_info_header_name, CONSUME_NEXT_INT | OPTIONAL },
934
+
935
+	{ sel_emergency_call_data, SEL_PARAM_STR, STR_STATIC_INIT("comment"), sel_comment, CONSUME_NEXT_INT | OPTIONAL},
936
+	{ sel_comment, SEL_PARAM_STR, STR_STATIC_INIT("counter"), sel_comment_cnt, 0},
937
+	{ sel_comment, SEL_PARAM_STR, STR_STATIC_INIT("urib"), sel_comment_uri, 0},
938
+	{ sel_comment, SEL_PARAM_STR, STR_STATIC_INIT("uri_type"), sel_comment_uri_type, 0},
939
+	{ sel_comment, SEL_PARAM_STR, STR_STATIC_INIT("raw"), sel_comment_raw, 0},
940
+	{ sel_comment, SEL_PARAM_STR, STR_STATIC_INIT("body"), sel_comment_body, 0},
941
+	{ sel_comment, SEL_PARAM_STR, STR_STATIC_INIT("header"), sel_comment_header, SEL_PARAM_EXPECTED},
942
+	{ sel_comment_header, SEL_PARAM_STR, STR_NULL, sel_comment_header_name, CONSUME_NEXT_INT | OPTIONAL },
943
+
944
+	{ NULL, SEL_PARAM_INT, STR_NULL, NULL, 0}
945
+};
0 946
new file mode 100644
... ...
@@ -0,0 +1,39 @@
1
+/*
2
+ * lost module - select interface
3
+ *
4
+ * Copyright (C) 2021 Eugene Sukhanov
5
+ * NGA911
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 :: Select interface.
28
+ * \ingroup lost
29
+ * Module: \ref lost
30
+ */
31
+
32
+#ifndef _LOST_SELECT_H
33
+#define _LOST_SELECT_H
34
+
35
+#include "../../core/select.h"
36
+
37
+extern select_row_t lost_sel[];
38
+
39
+#endif /* _LOST_SELECT_H */