1 /*
2  * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3  * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
4  *
5  * Version: MPL 1.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18  *
19  * The Initial Developer of the Original Code is
20  * Anthony Minessale II <anthm@freeswitch.org>
21  * Portions created by the Initial Developer are Copyright (C)
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Anthony Minessale II <anthm@freeswitch.org>
27  * Ken Rice <krice@freeswitch.org>
28  * Paul D. Tinsley <pdt at jackhammer.org>
29  * Bret McDanel <trixter AT 0xdecafbad.com>
30  * Raymond Chandler <intralanman@freeswitch.org>
31  * William King <william.king@quentustech.com>
32  * Emmanuel Schmidbauer <e.schmidbauer@gmail.com>
33  * David Knell <david.knell@telng.com>
34  *
35  * sofia_presence.c -- SOFIA SIP Endpoint (presence code)
36  *
37  */
38 #include "mod_sofia.h"
39 #include "switch_stun.h"
40 
41 #define SUB_OVERLAP 300
42 struct state_helper {
43 	switch_hash_t *hash;
44 	sofia_profile_t *profile;
45 	switch_memory_pool_t *pool;
46 	int total;
47 };
48 
49 
50 static int sofia_presence_mwi_callback(void *pArg, int argc, char **argv, char **columnNames);
51 static int sofia_presence_mwi_callback2(void *pArg, int argc, char **argv, char **columnNames);
52 static int sofia_presence_sub_reg_callback(void *pArg, int argc, char **argv, char **columnNames);
53 static int sofia_presence_resub_callback(void *pArg, int argc, char **argv, char **columnNames);
54 static int sofia_presence_sub_callback(void *pArg, int argc, char **argv, char **columnNames);
55 static int broadsoft_sla_gather_state_callback(void *pArg, int argc, char **argv, char **columnNames);
56 static int broadsoft_sla_notify_callback(void *pArg, int argc, char **argv, char **columnNames);
57 static int sync_sla(sofia_profile_t *profile, const char *to_user, const char *to_host, switch_bool_t clear, switch_bool_t unseize, const char *call_id);
58 static int sofia_dialog_probe_callback(void *pArg, int argc, char **argv, char **columnNames);
59 static int sofia_dialog_probe_notify_callback(void *pArg, int argc, char **argv, char **columnNames);
60 
61 struct pres_sql_cb {
62 	sofia_profile_t *profile;
63 	int ttl;
64 };
65 
66 static int sofia_presence_send_sql(void *pArg, int argc, char **argv, char **columnNames);
67 
68 struct dialog_helper {
69 	char state[128];
70 	char status[512];
71 	char rpid[512];
72 	char presence_id[1024];
73 	int hits;
74 };
75 
76 struct resub_helper {
77 	sofia_profile_t *profile;
78 	switch_event_t *event;
79 	int rowcount;
80 	int noreg;
81 };
82 
83 struct rfc4235_helper {
84 	switch_hash_t *hash;
85 	sofia_profile_t *profile;
86 	switch_memory_pool_t *pool;
87 	switch_event_t *event;
88 	int rowcount;
89 };
90 
91 struct presence_helper {
92 	sofia_profile_t *profile;
93 	switch_event_t *event;
94 	switch_stream_handle_t stream;
95 	char last_uuid[512];
96 	int hup;
97 	int calls_up;
98 
99 };
100 
sofia_presence_chat_send(switch_event_t * message_event)101 switch_status_t sofia_presence_chat_send(switch_event_t *message_event)
102 
103 {
104 	char *prof = NULL, *user = NULL, *host = NULL;
105 	sofia_profile_t *profile = NULL;
106 	char *ffrom = NULL;
107 	nua_handle_t *msg_nh;
108 	char *contact = NULL;
109 	char *dup = NULL;
110 	switch_status_t status = SWITCH_STATUS_FALSE;
111 	const char *ct = "text/html";
112 	sofia_destination_t *dst = NULL;
113 	char *to_uri = NULL;
114 	switch_console_callback_match_t *list = NULL;
115 	switch_console_callback_match_node_t *m;
116 	char *remote_ip = NULL;
117 	char *user_via = NULL;
118 	//char *contact_str = NULL;
119 	char *dup_dest = NULL;
120 	char *p = NULL;
121 	char *remote_host = NULL;
122 	const char *proto;
123 	const char *from;
124 	const char *to;
125 	//const char *subject;
126 	const char *body;
127 	const char *type;
128 	const char *from_full;
129 	char header[256] = "";
130 	char *route_uri = NULL;
131 	const char *network_ip = NULL, *network_port = NULL, *from_proto;
132 	char *extra_headers = NULL;
133 	char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
134 	int mstatus = 0, sanity = 0;
135 	const char *blocking;
136 	int is_blocking = 0;
137 
138 	proto = switch_event_get_header(message_event, "proto");
139 	from_proto = switch_event_get_header(message_event, "from_proto");
140 	from = switch_event_get_header(message_event, "from");
141 	to = switch_event_get_header(message_event, "to");
142 	//subject = switch_event_get_header(message_event, "subject");
143 	body = switch_event_get_body(message_event);
144 	type = switch_event_get_header(message_event, "type");
145 	from_full = switch_event_get_header(message_event, "from_full");
146 	blocking = switch_event_get_header(message_event, "blocking");
147 	is_blocking = switch_true(blocking);
148 
149 	network_ip = switch_event_get_header(message_event, "to_sip_ip");
150 	network_port = switch_event_get_header(message_event, "to_sip_port");
151 
152 	extra_headers = sofia_glue_get_extra_headers_from_event(message_event, SOFIA_SIP_HEADER_PREFIX);
153 
154 	if (!to) {
155 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing To: header.\n");
156 		goto end;
157 	}
158 
159 	if (!from) {
160 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing From: header.\n");
161 		goto end;
162 	}
163 
164 	if (!zstr(type)) {
165 		ct = type;
166 	}
167 
168 	dup = strdup(to);
169 	switch_assert(dup);
170 	prof = dup;
171 
172 	/* Do we have a user of the form profile/user[@host]? */
173 	if ((user = strchr(prof, '/'))) {
174 		*user++ = '\0';
175 	} else {
176 		user = prof;
177 		prof = NULL;
178 	}
179 
180 	if (!prof) {
181 		prof = switch_event_get_header(message_event, "sip_profile");
182 	}
183 
184 	if (!strncasecmp(user, "sip:", 4)) {
185 		to_uri = user;
186 	}
187 
188 	if ((host = strchr(user, '@'))) {
189 		if (!to_uri) {
190 			*host++ = '\0';
191 		} else {
192 			host++;
193 		}
194 		if (!prof) {
195 			prof = host;
196 		}
197 	}
198 
199 	if (!prof || !(profile = sofia_glue_find_profile(prof))) {
200 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
201 		"Chat proto [%s]\nfrom [%s]\nto [%s]\n%s\nInvalid Profile %s\n", proto, from, to,
202 						  body ? body : "[no body]", prof ? prof : "NULL");
203 		goto end;
204 	}
205 
206 	if (zstr(host)) {
207 		host = profile->domain_name;
208 		if (zstr(host)) {
209 			host = prof;
210 		}
211 	}
212 
213 
214 	if (to_uri) {
215 		switch_console_push_match(&list, to_uri);
216 	}  else if (!(list = sofia_reg_find_reg_url_multi(profile, user, host))) {
217 		sofia_profile_t *test;
218 
219 		if ((test = sofia_glue_find_profile(host))) {
220 			sofia_glue_release_profile(test);
221 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Not sending to local box for %s@%s\n", user, host);
222 			/* our box let's not send it */
223 		} else {
224 			char *tmp;
225 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Can't find registered user %s@%s\n", user, host);
226 			tmp = switch_mprintf("sip:%s@%s", user, host);
227 			switch_console_push_match(&list, tmp);
228 			free(tmp);
229 		}
230 
231 	}
232 
233 	if (!strcasecmp(proto, SOFIA_CHAT_PROTO)) {
234 		from = from_full;
235 	} else {
236 		char *fp, *p = NULL;
237 
238 
239 		fp = strdup(from);
240 		switch_assert(fp);
241 
242 
243 		if ((p = strchr(fp, '@'))) {
244 			*p++ = '\0';
245 		}
246 
247 		if (zstr(p)) {
248 			p = profile->domain_name;
249 			if (zstr(p)) {
250 				p = host;
251 			}
252 		}
253 
254 		if (switch_stristr("global", proto)) {
255 			if (!from_proto || !strcasecmp(from_proto, SOFIA_CHAT_PROTO)) {
256 				ffrom = switch_mprintf("\"%s\" <sip:%s@%s>", fp, fp, p);
257 			} else {
258 				ffrom = switch_mprintf("\"%s\" <sip:%s+%s@%s>", fp, from_proto, fp, p);
259 			}
260 
261 		} else {
262 			ffrom = switch_mprintf("\"%s\" <sip:%s+%s@%s>", fp, from_proto ? from_proto : proto, fp, p);
263 		}
264 
265 		from = ffrom;
266 		switch_safe_free(fp);
267 	}
268 
269 	if (!list) {
270 		switch_event_t *event;
271 
272 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
273 		"Chat proto [%s]\nfrom [%s]\nto [%s]\n%s\nNobody to send to: Profile %s\n", proto, from, to,
274 						  body ? body : "[no body]", prof);
275 		// emit no recipient event
276 		if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_ERROR) == SWITCH_STATUS_SUCCESS) {
277 			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Error-Type", "chat");
278 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Error-Reason", "no recipient");
279 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Chat-Send-To", to);
280 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Chat-Send-From", from);
281 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Chat-Send-Profile", prof);
282 			switch_event_add_body(event, "%s", body);
283 			switch_event_fire(&event);
284 		}
285 
286 		goto end;
287 	}
288 
289 	for (m = list->head; m; m = m->next) {
290 
291 		if (!(dst = sofia_glue_get_destination(m->val))) {
292 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
293 			break;
294 		}
295 
296 		/* sofia_glue is running sofia_overcome_sip_uri_weakness we do not, not sure if it matters */
297 
298 		if (dst->route_uri) {
299 			dup_dest = strdup(dst->route_uri);
300 		} else  {
301 			dup_dest = strdup(dst->to);
302 		}
303 
304 		switch_assert(dup_dest);
305 
306 		remote_host = strdup(dup_dest);
307 		if (!zstr(remote_host)) {
308 			switch_split_user_domain(remote_host, NULL, &remote_ip);
309 		}
310 
311 		if (!zstr(remote_ip) && sofia_glue_check_nat(profile, remote_ip)) {
312 			char *ptr = NULL;
313 			if ((ptr = sofia_glue_find_parameter(dst->contact, "transport="))) {
314 				sofia_transport_t transport = sofia_glue_str2transport( ptr + 10 );
315 				user_via = sofia_glue_create_external_via(NULL, profile, transport);
316 			} else {
317 				user_via = sofia_glue_create_external_via(NULL, profile, SOFIA_TRANSPORT_UDP);
318 			}
319 		}
320 
321 		status = SWITCH_STATUS_SUCCESS;
322 
323 		if ((p = strstr(dup_dest, ";fs_"))) {
324 			*p = '\0';
325 		}
326 
327 		/* if this cries, add contact here too, change the 1 to 0 and omit the safe_free */
328 
329 		//printf("DEBUG To: [%s] From: [%s] Contact: [%s] RURI [%s] ip [%s] port [%s]\n", to, from, contact, dst->route_uri, network_ip, network_port);
330 
331 		//DUMP_EVENT(message_event);
332 
333 		if (zstr(dst->route_uri) && !zstr(user) && !zstr(network_ip) && (zstr(host) || strcmp(network_ip, host))) {
334 			route_uri = switch_mprintf("sip:%s@%s:%s", user, network_ip, network_port);
335 		}
336 
337 		msg_nh = nua_handle(profile->nua, NULL,
338 							TAG_END());
339 
340 		nua_handle_bind(msg_nh, &mod_sofia_globals.destroy_private);
341 
342 		switch_snprintf(header, sizeof(header), "X-FS-Sending-Message: %s", switch_core_get_uuid());
343 
344 		switch_uuid_str(uuid_str, sizeof(uuid_str));
345 
346 		if (is_blocking) {
347 			switch_mutex_lock(profile->flag_mutex);
348 			switch_core_hash_insert(profile->chat_hash, uuid_str, &mstatus);
349 			switch_mutex_unlock(profile->flag_mutex);
350 		}
351 
352 		nua_message(msg_nh,
353 					TAG_IF(dst->route_uri, NUTAG_PROXY(dst->route_uri)),
354 					TAG_IF(route_uri, NUTAG_PROXY(route_uri)),
355 					TAG_IF(dst->route, SIPTAG_ROUTE_STR(dst->route)),
356 					SIPTAG_FROM_STR(from),
357 					TAG_IF(contact, NUTAG_URL(contact)),
358 					SIPTAG_TO_STR(dup_dest),
359 					SIPTAG_CALL_ID_STR(uuid_str),
360 					TAG_IF(user_via, SIPTAG_VIA_STR(user_via)),
361 					SIPTAG_CONTENT_TYPE_STR(ct),
362 					SIPTAG_PAYLOAD_STR(body),
363 					SIPTAG_HEADER_STR(header),
364 					TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)),
365 					TAG_END());
366 
367 
368 		if (is_blocking) {
369 			sanity = 200;
370 
371 			while(!mstatus && --sanity && !msg_nh->nh_destroyed) {
372 				switch_yield(100000);
373 			}
374 
375 			if (!(mstatus > 199 && mstatus < 300)) {
376 				status = SWITCH_STATUS_FALSE;
377 			}
378 
379 			switch_event_add_header(message_event, SWITCH_STACK_BOTTOM, "Delivery-Result-Code", "%d", mstatus);
380 
381 			switch_mutex_lock(profile->flag_mutex);
382 			switch_core_hash_delete(profile->chat_hash, uuid_str);
383 			switch_mutex_unlock(profile->flag_mutex);
384 		}
385 
386 		sofia_glue_free_destination(dst);
387 		switch_safe_free(dup_dest);
388 		switch_safe_free(user_via);
389 		switch_safe_free(remote_host);
390 	}
391 
392   end:
393 
394 	if (list) {
395 		switch_console_free_matches(&list);
396 	}
397 
398 	switch_safe_free(contact);
399 	switch_safe_free(route_uri);
400 	switch_safe_free(ffrom);
401 	switch_safe_free(dup);
402 
403 	if (profile) {
404 		switch_thread_rwlock_unlock(profile->rwlock);
405 	}
406 
407 	return status;
408 }
409 
sofia_presence_cancel(void)410 void sofia_presence_cancel(void)
411 {
412 	char *sql;
413 	sofia_profile_t *profile;
414 	struct presence_helper helper = { 0 };
415 	switch_console_callback_match_t *matches;
416 	switch_bool_t r;
417 
418 	if (!mod_sofia_globals.profile_hash) {
419 		return;
420 	}
421 
422 	if (list_profiles_full(NULL, NULL, &matches, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
423 		switch_console_callback_match_node_t *m;
424 
425 
426 		for (m = matches->head; m; m = m->next) {
427 			if ((profile = sofia_glue_find_profile(m->val))) {
428 				if (profile->pres_type == PRES_TYPE_FULL) {
429 					helper.profile = profile;
430 					helper.event = NULL;
431 
432 					sql = switch_mprintf("select proto,sip_user,sip_host,sub_to_user,sub_to_host,event,contact,call_id,full_from,"
433 										 "full_via,expires,user_agent,accept,profile_name,network_ip"
434 										 ",-1,'unavailable','unavailable' from sip_subscriptions where "
435 										 "event='presence' and hostname='%q' and profile_name='%q'",
436 										 mod_sofia_globals.hostname, profile->name);
437 
438 					r = sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_presence_sub_callback, &helper);
439 					switch_safe_free(sql);
440 
441 					if (r != SWITCH_TRUE) {
442 						sofia_glue_release_profile(profile);
443 						continue;
444 					}
445 				}
446 				sofia_glue_release_profile(profile);
447 			}
448 		}
449 
450 		switch_console_free_matches(&matches);
451 
452 	}
453 }
454 
sofia_presence_translate_rpid(char * in,char * ext)455 char *sofia_presence_translate_rpid(char *in, char *ext)
456 {
457 	char *r = in;
458 
459 	if (in && (switch_stristr("null", in))) {
460 		in = NULL;
461 	}
462 
463 	if (!in) {
464 		in = ext;
465 	}
466 
467 	if (!in) {
468 		return NULL;
469 	}
470 
471 	if (!strcasecmp(in, "dnd") || !strcasecmp(in, "idle")) {
472 		r = "busy";
473 	}
474 
475 	return r;
476 }
477 
478 struct mwi_helper {
479 	sofia_profile_t *profile;
480 	int total;
481 };
482 
actual_sofia_presence_mwi_event_handler(switch_event_t * event)483 static void actual_sofia_presence_mwi_event_handler(switch_event_t *event)
484 {
485 	char *account, *dup_account, *yn, *host, *user;
486 	char *sql;
487 	sofia_profile_t *profile = NULL;
488 	switch_stream_handle_t stream = { 0 };
489 	switch_event_header_t *hp;
490 	struct mwi_helper h = { 0 };
491 	const char *pname = NULL;
492 	const char *call_id;
493 	const char *sub_call_id;
494 	int for_everyone = 0;
495 
496 	switch_assert(event != NULL);
497 
498 	if (!(account = switch_event_get_header(event, "mwi-message-account"))) {
499 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing required Header 'MWI-Message-Account'\n");
500 		return;
501 	}
502 
503 	if (!(yn = switch_event_get_header(event, "mwi-messages-waiting"))) {
504 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing required Header 'MWI-Messages-Waiting'\n");
505 		return;
506 	}
507 
508 	call_id = switch_event_get_header(event, "call-id");
509 	sub_call_id = switch_event_get_header(event, "sub-call-id");
510 
511 	if (!call_id && !sub_call_id) {
512 		for_everyone = 1;
513 	}
514 
515 
516 	dup_account = strdup(account);
517 	switch_assert(dup_account != NULL);
518 	switch_split_user_domain(dup_account, &user, &host);
519 
520 
521 	if ((pname = switch_event_get_header(event, "sofia-profile"))) {
522 		profile = sofia_glue_find_profile(pname);
523 	}
524 
525 	if (!profile) {
526 		if (!host || !(profile = sofia_glue_find_profile(host))) {
527 			char *sql;
528 			char buf[512] = "";
529 			switch_console_callback_match_t *matches;
530 
531 			sql = switch_mprintf("select profile_name from sip_registrations where hostname='%q' and (sip_host='%q' or mwi_host='%q')",
532 								 mod_sofia_globals.hostname, host, host);
533 
534 			if (list_profiles_full(NULL, NULL, &matches, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
535 				switch_console_callback_match_node_t *m;
536 
537 				for (m = matches->head; m; m = m->next) {
538 					if ((profile = sofia_glue_find_profile(m->val))) {
539 
540 						sofia_glue_execute_sql2str(profile, profile->dbh_mutex, sql, buf, sizeof(buf));
541 						if (!zstr(buf)) {
542 							break;
543 						}
544 						sofia_glue_release_profile(profile);
545 					}
546 				}
547 
548 				switch_console_free_matches(&matches);
549 			}
550 
551 			switch_safe_free(sql);
552 
553 			if (!(profile = sofia_glue_find_profile(buf))) {
554 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find profile %s\n", switch_str_nil(host));
555 				switch_safe_free(dup_account);
556 				return;
557 			}
558 		}
559 	}
560 
561 
562 	if (profile->domain_name && strcasecmp(profile->domain_name, host)) {
563 		host = profile->domain_name;
564 	}
565 
566 	h.profile = profile;
567 	h.total = 0;
568 
569 	SWITCH_STANDARD_STREAM(stream);
570 
571 	for (hp = event->headers; hp; hp = hp->next) {
572 		if (!strncasecmp(hp->name, "mwi-", 4)) {
573 			char *tmp = NULL;
574 			char *value = hp->value;
575 			if (!strcasecmp(hp->name, "mwi-message-account") && strncasecmp(hp->value, "sip:", 4)) {
576 				tmp = switch_mprintf("sip:%s", hp->value);
577 				value = tmp;
578 			}
579 			stream.write_function(&stream, "%s: %s\r\n", hp->name + 4, value);
580 			switch_safe_free(tmp);
581 		}
582 	}
583 
584 	stream.write_function(&stream, "\r\n");
585 
586 	sql = NULL;
587 
588 	if (for_everyone) {
589 		sql = switch_mprintf("select proto,sip_user,sip_host,sub_to_user,sub_to_host,event,contact,call_id,full_from,"
590 							 "full_via,expires,user_agent,accept,profile_name,network_ip"
591 							 ",'%q',full_to,network_ip,network_port from sip_subscriptions "
592 							 "where hostname='%q' and event='message-summary' "
593 							 "and sub_to_user='%q' and (sub_to_host='%q' or presence_hosts like '%%%q%%')",
594 							 stream.data, mod_sofia_globals.hostname, user, host, host);
595 	} else if (sub_call_id) {
596 		sql = switch_mprintf("select proto,sip_user,sip_host,sub_to_user,sub_to_host,event,contact,call_id,full_from,"
597 							 "full_via,expires,user_agent,accept,profile_name,network_ip"
598 							 ",'%q',full_to,network_ip,network_port from sip_subscriptions where "
599 							 "hostname='%q' and event='message-summary' "
600 							 "and sub_to_user='%q' and (sub_to_host='%q' or presence_hosts like '%%%q%%') and call_id='%q'",
601 							 stream.data, mod_sofia_globals.hostname, user, host, host, sub_call_id);
602 	}
603 
604 
605 	if (sql) {
606 		sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_presence_mwi_callback, &h);
607 		free(sql);
608 		sql = NULL;
609 
610 	}
611 
612 	if (for_everyone) {
613 		sql = switch_mprintf("select sip_user,sip_host,contact,profile_name,network_ip,'%q',call_id "
614 							 "from sip_registrations where hostname='%q' and mwi_user='%q' and mwi_host='%q'",
615 							 stream.data, mod_sofia_globals.hostname, user, host);
616 	} else if (call_id) {
617 		sql = switch_mprintf("select sip_user,sip_host,contact,profile_name,network_ip,'%q',call_id "
618 							 "from sip_registrations where hostname='%q' and call_id='%q'",
619 							 stream.data, mod_sofia_globals.hostname, call_id);
620 	}
621 
622 	if (sql) {
623 		switch_assert(sql != NULL);
624 		sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_presence_mwi_callback2, &h);
625 		free(sql);
626 		sql = NULL;
627 	}
628 
629 	switch_safe_free(stream.data);
630 	switch_safe_free(dup_account);
631 	sofia_glue_release_profile(profile);
632 }
633 
sofia_presence_dialog_callback(void * pArg,int argc,char ** argv,char ** columnNames)634 static int sofia_presence_dialog_callback(void *pArg, int argc, char **argv, char **columnNames)
635 {
636 	struct dialog_helper *helper = (struct dialog_helper *) pArg;
637 	switch_core_session_t *session = NULL;
638 	switch_channel_t *channel = NULL;
639 	int done = 0;
640 
641 	if (argc >= 4) {
642 
643 		if (argc == 5 && !zstr(argv[4])) {
644 			if ((session = switch_core_session_locate(argv[4]))) {
645 				channel = switch_core_session_get_channel(session);
646 
647 				if (!switch_channel_test_flag(channel, CF_ANSWERED) &&
648 					switch_true(switch_channel_get_variable_dup(channel, "presence_disable_early", SWITCH_FALSE, -1))) {
649 					done++;
650 				}
651 
652 				switch_core_session_rwunlock(session);
653 			} else {
654 				return 0;
655 			}
656 		}
657 
658 		if (done) {
659 			return 0;
660 		}
661 
662 		if (mod_sofia_globals.debug_presence > 0) {
663 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "CHECK DIALOG state[%s] status[%s] rpid[%s] pres[%s] uuid[%s]\n",
664 							  argv[0], argv[1], argv[2], argv[3], argv[4]);
665 		}
666 
667 		if (!helper->hits) {
668 			switch_set_string(helper->state, argv[0]);
669 			switch_set_string(helper->status, argv[1]);
670 			switch_set_string(helper->rpid, argv[2]);
671 			switch_set_string(helper->presence_id, argv[3]);
672 		}
673 		helper->hits++;
674 	}
675 
676 	return 0;
677 }
678 
679 
do_normal_probe(switch_event_t * event)680 static void do_normal_probe(switch_event_t *event)
681 {
682 	char *sql;
683 	struct resub_helper h = { 0 };
684 	char *to = switch_event_get_header(event, "to");
685 	char *proto = switch_event_get_header(event, "proto");
686 	char *profile_name = switch_event_get_header(event, "sip_profile");
687 	char *probe_user = NULL, *probe_euser, *probe_host, *p;
688 	struct dialog_helper dh = { { 0 } };
689 	char *sub_call_id = switch_event_get_header(event, "sub-call-id");
690 	sofia_profile_t *profile;
691 
692 	//DUMP_EVENT(event);
693 
694 	if (!proto || strcasecmp(proto, SOFIA_CHAT_PROTO) != 0) {
695 		return;
696 	}
697 
698 	if (!to || !(probe_user = strdup(to))) {
699 		return;
700 	}
701 
702 	if ((probe_host = strchr(probe_user, '@'))) {
703 		*probe_host++ = '\0';
704 	}
705 	probe_euser = probe_user;
706 	if ((p = strchr(probe_euser, '+')) && p != probe_euser) {
707 		probe_euser = (p + 1);
708 	}
709 
710 	if (probe_host &&
711 		((profile = sofia_glue_find_profile(probe_host)) || (profile_name && (profile = sofia_glue_find_profile(profile_name))))) {
712 		sql = switch_mprintf("select state,status,rpid,presence_id,uuid from sip_dialogs "
713 							 "where hostname='%q' and profile_name='%q' and call_info_state != 'seized' and "
714 							 "((sip_from_user='%q' and sip_from_host='%q') or presence_id='%q@%q') order by rcd desc",
715 							 mod_sofia_globals.hostname, profile->name, probe_euser, probe_host, probe_euser, probe_host);
716 
717 
718 		sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_presence_dialog_callback, &dh);
719 
720 		h.profile = profile;
721 
722 		switch_safe_free(sql);
723 
724 		sql = switch_mprintf("select sip_registrations.sip_user, "
725 								 "sip_registrations.sub_host, "
726 								 "sip_registrations.status, "
727 								 "sip_registrations.rpid, "
728 								 "'', "
729 								 "sip_dialogs.uuid, "
730 								 "sip_dialogs.state, "
731 								 "sip_dialogs.direction, "
732 								 "sip_dialogs.sip_to_user, "
733 								 "sip_dialogs.sip_to_host, "
734 
735 								 "sip_presence.status,"
736 								 "sip_presence.rpid,"
737 								 "sip_dialogs.presence_id, "
738 								 "sip_presence.open_closed,"
739 								 "'%q','%q','%q' "
740 								 "from sip_registrations "
741 
742 								 "left join sip_dialogs on "
743 								 "sip_dialogs.hostname = sip_registrations.hostname and sip_dialogs.profile_name = sip_registrations.profile_name and ("
744 								 "sip_dialogs.presence_id = sip_registrations.sip_user %q '@' %q sip_registrations.sub_host "
745 								 "or (sip_dialogs.sip_from_user = sip_registrations.sip_user "
746 								 "and sip_dialogs.sip_from_host = sip_registrations.sip_host)) "
747 
748 								 "left join sip_presence on "
749 								 "sip_presence.hostname=sip_registrations.hostname and "
750 								 "(sip_registrations.sip_user=sip_presence.sip_user and sip_registrations.orig_server_host=sip_presence.sip_host and "
751 								 "sip_registrations.profile_name=sip_presence.profile_name) "
752 								 "where sip_registrations.hostname='%q' and sip_registrations.profile_name='%q' and sip_dialogs.call_info_state != 'seized' "
753 								 "and sip_dialogs.presence_id='%q@%q' or (sip_registrations.sip_user='%q' and "
754 								 "(sip_registrations.orig_server_host='%q' or sip_registrations.sub_host='%q' "
755 								 "))",
756 								 dh.status, dh.rpid, switch_str_nil(sub_call_id),
757 								 switch_sql_concat(), switch_sql_concat(),
758 								 mod_sofia_globals.hostname, profile->name, probe_euser, probe_host,  probe_euser, probe_host, probe_host);
759 
760 
761 
762 		switch_assert(sql);
763 
764 		if (mod_sofia_globals.debug_presence > 0) {
765 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s START_PRESENCE_PROBE_SQL\n", profile->name);
766 		}
767 
768 		if (mod_sofia_globals.debug_presence > 1) {
769 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s DUMP PRESENCE_PROBE_SQL:\n%s\n", profile->name, sql);
770 		}
771 
772 		sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_presence_resub_callback, &h);
773 		switch_safe_free(sql);
774 
775 		if (!h.rowcount) {
776 			h.noreg++;
777 
778 			/* find ones with presence_id defined that are not registred */
779 			sql = switch_mprintf("select sip_from_user, sip_from_host, 'Registered', '', '', "
780 								 "uuid, state, direction, "
781 								 "sip_to_user, sip_to_host,"
782 								 "'%q','%q',presence_id, '','','' "
783 
784 								 "from sip_dialogs "
785 
786 								 "where call_info_state != 'seized' and hostname='%q' and profile_name='%q' and (presence_id='%q@%q' or "
787 								 "(sip_from_user='%q' and (sip_from_host='%q' or sip_to_host='%q')))",
788 								 mod_sofia_globals.hostname, profile->name,
789 								 dh.status, dh.rpid, probe_euser, probe_host,  probe_euser, probe_host, probe_host);
790 
791 			if (mod_sofia_globals.debug_presence > 0) {
792 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s START_PRESENCE_PROBE_SQL\n", profile->name);
793 			}
794 
795 			if (mod_sofia_globals.debug_presence > 1) {
796 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s DUMP PRESENCE_PROBE_SQL:\n%s\n", profile->name, sql);
797 			}
798 
799 			sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_presence_resub_callback, &h);
800 			switch_safe_free(sql);
801 
802 			if (mod_sofia_globals.debug_presence > 0) {
803 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s END_PRESENCE_PROBE_SQL\n\n", profile->name);
804 			}
805 		}
806 
807 		if (!h.rowcount) {
808 			switch_event_t *sevent;
809 			if (switch_event_create(&sevent, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
810 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO);
811 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "login", profile->name);
812 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "sip_profile", profile->name);
813 				switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "from", "%s@%s", probe_euser, probe_host);
814 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "status", "Unregistered");
815 				switch_event_fire(&sevent);
816 			}
817 		}
818 
819 
820 		sofia_glue_release_profile(profile);
821 	}
822 
823 
824 	switch_safe_free(probe_user);
825 }
826 
do_dialog_probe(switch_event_t * event)827 static void do_dialog_probe(switch_event_t *event)
828 {
829 	// Received SUBSCRIBE for "dialog" events.
830 	// Return a complete list of dialogs for the monitored entity.
831 	char *sql;
832 	char *to = switch_event_get_header(event, "to");
833 	char *probe_user = NULL, *probe_euser, *probe_host, *p;
834 
835 	if (!to || !(probe_user = strdup(to))) {
836 		return;
837 	}
838 
839 	if ((probe_host = strchr(probe_user, '@'))) {
840 		*probe_host++ = '\0';
841 	}
842 	probe_euser = probe_user;
843 	if ((p = strchr(probe_euser, '+')) && p != probe_euser) {
844 		probe_euser = (p + 1);
845 	}
846 
847 	if (probe_host) {
848 		char *sub_call_id = switch_event_get_header(event, "sub-call-id");
849 		char *profile_name = switch_event_get_header(event, "sip_profile");
850 		sofia_profile_t *profile = sofia_glue_find_profile(probe_host);
851 		struct rfc4235_helper *h4235 = {0};
852 		switch_memory_pool_t *pool;
853 
854 		if (!profile && profile_name) {
855 			profile = sofia_glue_find_profile(profile_name);
856 		}
857 
858 		if (!profile) {
859 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot find profile for domain %s\n", probe_host);
860 			goto end;
861 		}
862 
863 		// We need all dialogs with presence_id matching the subscription entity,
864 		// or from a registered set matching the subscription entity.
865 		// We need the "proto" of the subscription in case it is for the special "conf" or "park".
866 		sql = switch_mprintf(
867 							 "select sip_subscriptions.proto, '%q','%q',"
868 							 "sip_dialogs.uuid, sip_dialogs.call_id, sip_dialogs.state, sip_dialogs.direction, "
869 							 "sip_dialogs.sip_to_user, sip_dialogs.sip_to_host, "
870 							 "sip_dialogs.sip_from_user, sip_dialogs.sip_from_host, "
871 							 "sip_dialogs.contact, sip_dialogs.contact_user, sip_dialogs.contact_host, "
872 							 "sip_dialogs.sip_to_tag, sip_dialogs.sip_from_tag, sip_subscriptions.orig_proto "
873 							 "from sip_dialogs "
874 							 "left join sip_subscriptions on sip_subscriptions.hostname=sip_dialogs.hostname and "
875 							 "sip_subscriptions.profile_name=sip_dialogs.profile_name and "
876 							 "sip_subscriptions.call_id='%q' "
877 							 "left join sip_registrations on sip_registrations.hostname=sip_dialogs.hostname and "
878 							 "sip_registrations.profile_name=sip_dialogs.profile_name and "
879 							 "(sip_dialogs.sip_from_user = sip_registrations.sip_user and sip_dialogs.sip_from_host = '%q' and "
880 							 "(sip_dialogs.sip_from_host = sip_registrations.orig_server_host or "
881 							 "sip_dialogs.sip_from_host = sip_registrations.sip_host) ) "
882 							 "where sip_dialogs.hostname='%q' and sip_dialogs.profile_name='%q' and "
883 							 "sip_dialogs.call_info_state != 'seized' and sip_dialogs.presence_id='%q@%q' or (sip_registrations.sip_user='%q' and "
884 							 "(sip_registrations.orig_server_host='%q' or sip_registrations.sub_host='%q' "
885 							 "or sip_registrations.presence_hosts like '%%%q%%'))",
886 							 probe_euser, probe_host,
887 							 sub_call_id, probe_host,
888 							 mod_sofia_globals.hostname, profile->name,
889 							 probe_euser, probe_host,
890 							 probe_euser, probe_host, probe_host, probe_host);
891 		switch_assert(sql);
892 
893 		if (mod_sofia_globals.debug_presence > 0) {
894 			switch_log_printf(SWITCH_CHANNEL_LOG,SWITCH_LOG_INFO, "%s START DIALOG_PROBE_SQL %s@%s\n", profile->name,probe_euser, probe_host);
895 		}
896 
897 		if (mod_sofia_globals.debug_presence > 1) {
898 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s DUMP DIALOG_PROBE_SQL:\n%s\n", profile->name, sql);
899 		}
900 
901 		switch_core_new_memory_pool(&pool);
902 		h4235 = switch_core_alloc(pool, sizeof(*h4235));
903 		h4235->pool = pool;
904 		h4235->profile = profile;
905 		switch_core_hash_init(&h4235->hash);
906 		sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_dialog_probe_callback, h4235);
907 		switch_safe_free(sql);
908 		if (mod_sofia_globals.debug_presence > 0) {
909 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s END DIALOG_PROBE_SQL\n\n", profile->name);
910 		}
911 
912 
913 		sql = switch_mprintf("update sip_subscriptions set version=version+1 where call_id='%q'", sub_call_id);
914 
915 		if (mod_sofia_globals.debug_presence > 1) {
916 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s DUMP DIALOG_PROBE set version sql:\n%s\n", profile->name, sql);
917 		}
918 		sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
919 		switch_safe_free(sql);
920 
921 
922 		// The dialog_probe_callback has built up the dialogs to be included in the NOTIFY.
923 		// Now send the "full" dialog event to the triggering subscription.
924 		sql = switch_mprintf("select call_id,expires,sub_to_user,sub_to_host,event,version, "
925 							 "'full',full_to,full_from,contact,network_ip,network_port "
926 							 "from sip_subscriptions "
927 							 "where hostname='%q' and profile_name='%q' and sub_to_user='%q' and sub_to_host='%q' and call_id='%q'",
928 							 mod_sofia_globals.hostname, profile->name, probe_euser, probe_host, sub_call_id);
929 
930 		if (mod_sofia_globals.debug_presence > 1) {
931 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s DUMP DIALOG_PROBE subscription sql:\n%s\n", profile->name, sql);
932 		}
933 
934 		sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_dialog_probe_notify_callback, h4235);
935 		switch_safe_free(sql);
936 
937 		sofia_glue_release_profile(profile);
938 		switch_core_hash_destroy(&h4235->hash);
939 		h4235 = NULL;
940 		switch_core_destroy_memory_pool(&pool);
941 	}
942 
943  end:
944 
945 	switch_safe_free(probe_user);
946 }
947 
send_conference_data(sofia_profile_t * profile,switch_event_t * event)948 static void send_conference_data(sofia_profile_t *profile, switch_event_t *event)
949 {
950 	char *sql;
951 	struct pres_sql_cb cb = {profile, 0};
952 	const char *call_id = switch_event_get_header(event, "call_id");
953 	const char *from_user = switch_event_get_header(event, "conference-name");
954 	const char *from_host = switch_event_get_header(event, "conference-domain");
955 	const char *event_str = switch_event_get_header(event, "conference-event");
956 	const char *notfound = switch_event_get_header(event, "notfound");
957 	const char *body = switch_event_get_body(event);
958 	const char *type = "application/conference-info+xml";
959 	const char *final = switch_event_get_header(event, "final");
960 
961 	if (!event_str) {
962 		event_str = "conference";
963 	}
964 
965 	if (!strcasecmp(event_str, "refer")) {
966 		type = "message/sipfrag";
967 	}
968 
969 
970 	if (!(from_user && from_host)) {
971 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Event information not given\n");
972 		return;
973 	}
974 
975 	if (switch_true(notfound)) {
976 		sql = switch_mprintf("update sip_subscriptions set expires=%ld where "
977 							 "hostname='%q' and profile_name='%q' and sub_to_user='%q' and sub_to_host='%q' and event='%q'",
978 							 (long)switch_epoch_time_now(NULL),
979 							 mod_sofia_globals.hostname, profile->name,
980 							 from_user, from_host, event_str);
981 
982 		sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
983 	}
984 
985 	if (call_id) {
986 	   if (switch_true(final)) {
987 		  sql = switch_mprintf("update sip_subscriptions set expires=%ld where "
988 							  "hostname='%q' and profile_name='%q' and sub_to_user='%q' and sub_to_host='%q' and event='%q' "
989 							   "and call_id = '%q' ",
990 							   (long)0,
991 							   mod_sofia_globals.hostname, profile->name,
992 							   from_user, from_host, event_str, call_id);
993 
994 		  sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
995 	   }
996 
997 		sql = switch_mprintf("select full_to, full_from, contact %q ';_;isfocus', expires, call_id, event, network_ip, network_port, "
998 							 "'%q' as ct,'%q' as pt "
999 							 " from sip_subscriptions where "
1000 							 "hostname='%q' and profile_name='%q' and sub_to_user='%q' and sub_to_host='%q' and event='%q' "
1001 							 "and call_id = '%q' ",
1002 							 switch_sql_concat(),
1003 							 type,
1004 							 switch_str_nil(body),
1005 							 mod_sofia_globals.hostname, profile->name,
1006 							 from_user, from_host, event_str, call_id);
1007 	} else {
1008 	  if (switch_true(final)) {
1009 		 sql = switch_mprintf("update sip_subscriptions set expires=%ld where "
1010 							  "hostname='%q' and profile_name='%q' and sub_to_user='%q' and sub_to_host='%q' and event='%q'",
1011 							  (long)0,
1012 							  mod_sofia_globals.hostname, profile->name,
1013 							  from_user, from_host, event_str);
1014 
1015 		 sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
1016 	  }
1017 
1018 		sql = switch_mprintf("select full_to, full_from, contact %q ';_;isfocus', expires, call_id, event, network_ip, network_port, "
1019 							 "'%q' as ct,'%q' as pt "
1020 							 " from sip_subscriptions where "
1021 							 "hostname='%q' and profile_name='%q' and sub_to_user='%q' and sub_to_host='%q' and event='%q'",
1022 							 switch_sql_concat(),
1023 							 type,
1024 							 switch_str_nil(body),
1025 							 mod_sofia_globals.hostname, profile->name,
1026 							 from_user, from_host, event_str);
1027 	}
1028 
1029 	sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_presence_send_sql, &cb);
1030 	switch_safe_free(sql);
1031 
1032 	if (switch_true(final)) {
1033 		if (call_id) {
1034 			sql = switch_mprintf("delete from sip_subscriptions where "
1035 								 "hostname='%q' and profile_name='%q' and sub_to_user='%q' and sub_to_host='%q' and event='%q' "
1036 								 "and call_id = '%q' ",
1037 								 mod_sofia_globals.hostname, profile->name,
1038 								 from_user, from_host, event_str, call_id);
1039 
1040 		} else {
1041 			sql = switch_mprintf("delete from sip_subscriptions where "
1042 								 "hostname='%q' and profile_name='%q' and sub_to_user='%q' and sub_to_host='%q' and event='%q'",
1043 								 mod_sofia_globals.hostname, profile->name,
1044 								 from_user, from_host, event_str);
1045 		}
1046 
1047 		sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
1048 	}
1049 
1050 
1051 }
1052 
conference_data_event_handler(switch_event_t * event)1053 static void conference_data_event_handler(switch_event_t *event)
1054 {
1055 	const char *pname;
1056 	//const char *from_user = switch_event_get_header(event, "conference-name");
1057 	//const char *from_host = switch_event_get_header(event, "conference-domain");
1058 	const char *host = switch_event_get_header(event, "conference-domain");
1059 	char *dup_domain = NULL;
1060 	sofia_profile_t *profile = NULL;
1061 
1062 	if (zstr(host)) {
1063 		dup_domain = switch_core_get_domain(SWITCH_TRUE);
1064 		host = dup_domain;
1065 	}
1066 
1067 	if ((pname = switch_event_get_header(event, "sofia-profile"))) {
1068 		profile = sofia_glue_find_profile(pname);
1069 	}
1070 
1071 	if (host && !profile) {
1072 		profile = sofia_glue_find_profile(host);
1073 	}
1074 
1075 	if (profile) {
1076 		send_conference_data(profile, event);
1077 		sofia_glue_release_profile(profile);
1078 	} else {
1079 		switch_console_callback_match_t *matches;
1080 
1081 		if (list_profiles_full(NULL, NULL, &matches, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
1082 			switch_console_callback_match_node_t *m;
1083 
1084 			for (m = matches->head; m; m = m->next) {
1085 				if ((profile = sofia_glue_find_profile(m->val))) {
1086 					send_conference_data(profile, event);
1087 					sofia_glue_release_profile(profile);
1088 				}
1089 			}
1090 
1091 			switch_console_free_matches(&matches);
1092 		}
1093 	}
1094 
1095 	switch_safe_free(dup_domain);
1096 }
1097 
actual_sofia_presence_event_handler(switch_event_t * event)1098 static switch_event_t *actual_sofia_presence_event_handler(switch_event_t *event)
1099 {
1100 	sofia_profile_t *profile = NULL;
1101 	char *from = switch_event_get_header(event, "from");
1102 	char *proto = switch_event_get_header(event, "proto");
1103 	char *rpid = switch_event_get_header(event, "rpid");
1104 	char *status = switch_event_get_header(event, "status");
1105 	char *event_type = switch_event_get_header(event, "event_type");
1106 	char *alt_event_type = switch_event_get_header(event, "alt_event_type");
1107 	//char *event_subtype = switch_event_get_header(event, "event_subtype");
1108 	char *sql = NULL;
1109 	char *euser = NULL, *user = NULL, *host = NULL;
1110 	char *call_info = switch_event_get_header(event, "presence-call-info");
1111 	char *call_id = switch_event_get_header(event, "call-id");
1112 	char *presence_source = switch_event_get_header(event, "presence-source");
1113 	char *call_info_state = switch_event_get_header(event, "presence-call-info-state");
1114 	const char *uuid = switch_event_get_header(event, "unique-id");
1115 	switch_console_callback_match_t *matches = NULL;
1116 	struct presence_helper helper = { 0 };
1117 	int hup = 0;
1118 	switch_event_t *s_event = NULL;
1119 
1120 	if (!mod_sofia_globals.running) {
1121 		goto done;
1122 	}
1123 
1124 	if (zstr(proto) || !strcasecmp(proto, "any")) {
1125 		proto = SOFIA_CHAT_PROTO;
1126 	}
1127 
1128 	//DUMP_EVENT(event);
1129 
1130 	if (rpid && !strcasecmp(rpid, "n/a")) {
1131 		rpid = NULL;
1132 	}
1133 
1134 	if (status && !strcasecmp(status, "n/a")) {
1135 		status = NULL;
1136 	}
1137 
1138 	if (!zstr(uuid) && !switch_ivr_uuid_exists(uuid)) {
1139 		status = "CS_HANGUP";
1140 	}
1141 
1142 
1143 	if ((status && switch_stristr("CS_HANGUP", status)) || (!zstr(uuid) && !switch_ivr_uuid_exists(uuid))) {
1144 		status = "Available";
1145 		hup = 1;
1146 	}
1147 
1148 	if (rpid) {
1149 		rpid = sofia_presence_translate_rpid(rpid, status);
1150 	}
1151 
1152 	if (event->event_id == SWITCH_EVENT_ROSTER) {
1153 		if (list_profiles_full(NULL, NULL, &matches, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
1154 			switch_console_callback_match_node_t *m;
1155 
1156 			for (m = matches->head; m; m = m->next) {
1157 				if ((profile = sofia_glue_find_profile(m->val))) {
1158 					if (profile->pres_type != PRES_TYPE_FULL) {
1159 
1160 
1161 						if (!mod_sofia_globals.profile_hash) {
1162 							switch_console_free_matches(&matches);
1163 							sofia_glue_release_profile(profile);
1164 							goto done;
1165 						}
1166 
1167 						if (from) {
1168 
1169 							sql = switch_mprintf("update sip_subscriptions set version=version+1 where hostname='%q' and profile_name='%q' and "
1170 												 "sip_subscriptions.event='presence' and sip_subscriptions.full_from like '%%%q%%'",
1171 												 mod_sofia_globals.hostname, profile->name, from);
1172 
1173 							if (mod_sofia_globals.debug_presence > 1) {
1174 								switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PRES SQL %s\n", sql);
1175 							}
1176 
1177 							sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
1178 
1179 
1180 							sql = switch_mprintf("select sip_subscriptions.proto,sip_subscriptions.sip_user,sip_subscriptions.sip_host,"
1181 												 "sip_subscriptions.sub_to_user,sip_subscriptions.sub_to_host,sip_subscriptions.event,"
1182 												 "sip_subscriptions.contact,sip_subscriptions.call_id,sip_subscriptions.full_from,"
1183 												 "sip_subscriptions.full_via,sip_subscriptions.expires,sip_subscriptions.user_agent,"
1184 												 "sip_subscriptions.accept,sip_subscriptions.profile_name,sip_subscriptions.network_ip"
1185 												 ",1,'%q','%q',sip_presence.status,sip_presence.rpid,sip_presence.open_closed,'','','','','sip',"
1186 												 " sip_subscriptions.full_to,sip_subscriptions.network_ip,sip_subscriptions.network_port "
1187 												 "from sip_subscriptions left join sip_presence on "
1188 												 "(sip_subscriptions.sub_to_user=sip_presence.sip_user and "
1189 												 "sip_subscriptions.sub_to_host=sip_presence.sip_host and "
1190 												 "sip_subscriptions.profile_name=sip_presence.profile_name and "
1191 												 "sip_presence.profile_name=sip_subscriptions.profile_name) "
1192 												 "where sip_subscriptions.hostname='%q' and sip_subscriptions.profile_name='%q' and "
1193 												 "sip_subscriptions.event='presence' and sip_subscriptions.full_from like '%%%q%%'",
1194 												 switch_str_nil(status), switch_str_nil(rpid), mod_sofia_globals.hostname, profile->name, from);
1195 						} else {
1196 
1197 							sql = switch_mprintf("update sip_subscriptions set version=version+1 where hostname='%q' and profile_name='%q' and "
1198 												 "sip_subscriptions.event='presence'", mod_sofia_globals.hostname, profile->name);
1199 
1200 							if (mod_sofia_globals.debug_presence > 1) {
1201 								switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PRES SQL %s\n", sql);
1202 							}
1203 
1204 							sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
1205 
1206 							sql = switch_mprintf("select sip_subscriptions.proto,sip_subscriptions.sip_user,sip_subscriptions.sip_host,"
1207 												 "sip_subscriptions.sub_to_user,sip_subscriptions.sub_to_host,sip_subscriptions.event,"
1208 												 "sip_subscriptions.contact,sip_subscriptions.call_id,sip_subscriptions.full_from,"
1209 												 "sip_subscriptions.full_via,sip_subscriptions.expires,sip_subscriptions.user_agent,"
1210 												 "sip_subscriptions.accept,sip_subscriptions.profile_name,sip_subscriptions.network_ip"
1211 												 ",1,'%q','%q',sip_presence.status,sip_presence.rpid,sip_presence.open_closed,'','','','','sip',"
1212 												 "sip_subscriptions.full_to,sip_subscriptions.network_ip,sip_subscriptions.network_port "
1213 												 "from sip_subscriptions left join sip_presence on "
1214 												 "(sip_subscriptions.sub_to_user=sip_presence.sip_user and "
1215 												 "sip_subscriptions.sub_to_host=sip_presence.sip_host and "
1216 												 "sip_subscriptions.profile_name=sip_presence.profile_name and "
1217 												 "sip_subscriptions.hostname = sip_presence.hostname) "
1218 												 "where sip_subscriptions.hostname='%q' and sip_subscriptions.profile_name='%q' and "
1219 												 "sip_subscriptions.event='presence'", switch_str_nil(status),
1220 												 switch_str_nil(rpid), mod_sofia_globals.hostname, profile->name);
1221 						}
1222 
1223 						switch_assert(sql != NULL);
1224 
1225 
1226 						if (mod_sofia_globals.debug_presence > 0) {
1227 							switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "%s is passive, skipping\n", (char *) profile->name);
1228 						}
1229 						sofia_glue_release_profile(profile);
1230 						continue;
1231 					}
1232 					memset(&helper, 0, sizeof(helper));
1233 					helper.profile = profile;
1234 					helper.event = NULL;
1235 					sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_presence_sub_callback, &helper);
1236 					switch_safe_free(sql);
1237 					sofia_glue_release_profile(profile);
1238 				}
1239 			}
1240 			switch_console_free_matches(&matches);
1241 		}
1242 
1243 		switch_safe_free(sql);
1244 		goto done;
1245 	}
1246 
1247 	if (zstr(event_type)) {
1248 		event_type = "presence";
1249 	}
1250 
1251 	if (zstr(alt_event_type)) {
1252 		if (!strcasecmp(event_type, "presence")) {
1253 			alt_event_type = "dialog";
1254 		} else {
1255 			alt_event_type = "presence";
1256 		}
1257 	}
1258 
1259 	if (from && (user = strdup(from))) {
1260 		if ((host = strchr(user, '@'))) {
1261 			char *p;
1262 			*host++ = '\0';
1263 			if ((p = strchr(host, '/'))) {
1264 				*p = '\0';
1265 			}
1266 		} else {
1267 			switch_safe_free(user);
1268 			goto done;
1269 		}
1270 		if ((euser = strchr(user, '+')) && euser != user) {
1271 			euser++;
1272 		} else {
1273 			euser = user;
1274 		}
1275 	} else {
1276 		goto done;
1277 	}
1278 
1279 	switch (event->event_id) {
1280 	case SWITCH_EVENT_PRESENCE_PROBE:
1281 		{
1282 			char *probe_type = switch_event_get_header(event, "probe-type");
1283 
1284 			if (!probe_type || strcasecmp(probe_type, "dialog")) {
1285 				/* NORMAL PROBE */
1286 				do_normal_probe(event);
1287 			} else {
1288 				/* DIALOG PROBE */
1289 				do_dialog_probe(event);
1290 			}
1291 		}
1292 		goto done;
1293 
1294 	default:
1295 		break;
1296 	}
1297 
1298 	if (!mod_sofia_globals.profile_hash) {
1299 		goto done;
1300 	}
1301 
1302 	if (list_profiles_full(NULL, NULL, &matches, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
1303 		switch_console_callback_match_node_t *m;
1304 
1305 		for (m = matches->head; m; m = m->next) {
1306 			struct dialog_helper dh = { { 0 } };
1307 
1308 			if ((profile = sofia_glue_find_profile(m->val))) {
1309 				if (profile->pres_type != PRES_TYPE_FULL) {
1310 					if (mod_sofia_globals.debug_presence > 0) {
1311 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "%s is passive, skipping\n", (char *) profile->name);
1312 					}
1313 					sofia_glue_release_profile(profile);
1314 					continue;
1315 				}
1316 
1317 
1318 				if (mod_sofia_globals.debug_sla > 1) {
1319 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SLA EVENT:\n");
1320 					DUMP_EVENT(event);
1321 
1322 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "CHECK CALL_INFO [%s]\n", switch_str_nil(call_info));
1323 				}
1324 
1325 				if (call_info) {
1326 
1327 					if (uuid) {
1328 						sql = switch_mprintf("update sip_dialogs set call_info='%q',call_info_state='%q' where "
1329 											 "hostname='%q' and profile_name='%q' and uuid='%q'",
1330 											 call_info, call_info_state, mod_sofia_globals.hostname, profile->name, uuid);
1331 					} else {
1332 						sql = switch_mprintf("update sip_dialogs set call_info='%q', call_info_state='%q' where hostname='%q' and profile_name='%q' and "
1333 											 "((sip_dialogs.sip_from_user='%q' and sip_dialogs.sip_from_host='%q') or presence_id='%q@%q') and call_info='%q'",
1334 
1335 											 call_info, call_info_state, mod_sofia_globals.hostname, profile->name, euser, host, euser, host, call_info);
1336 
1337 					}
1338 
1339 					if (mod_sofia_globals.debug_sla > 1) {
1340 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "STATE SQL %s\n", sql);
1341 					}
1342 					sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
1343 
1344 
1345 
1346 					if (mod_sofia_globals.debug_sla > 1) {
1347 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PROCESS PRESENCE EVENT\n");
1348 					}
1349 
1350 					sync_sla(profile, euser, host, SWITCH_TRUE, SWITCH_TRUE, call_id);
1351 				}
1352 
1353 				if (!strcmp(proto, "dp")) {
1354 					sql = switch_mprintf("update sip_presence set rpid='%q',status='%q' where hostname='%q' and profile_name='%q' and "
1355 										 "sip_user='%q' and sip_host='%q'",
1356 										 rpid, status, mod_sofia_globals.hostname, profile->name, euser, host);
1357 					sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
1358 					proto = SOFIA_CHAT_PROTO;
1359 				}
1360 
1361 				if (zstr(uuid)) {
1362 
1363 					sql = switch_mprintf("select state,status,rpid,presence_id,uuid from sip_dialogs "
1364 										 "where call_info_state != 'seized' and hostname='%q' and profile_name='%q' and "
1365 										 "((sip_from_user='%q' and sip_from_host='%q') or presence_id='%q@%q') order by rcd desc",
1366 										 mod_sofia_globals.hostname, profile->name, euser, host, euser, host);
1367 				} else {
1368 					sql = switch_mprintf("select state,status,rpid,presence_id,uuid from sip_dialogs "
1369 										 "where uuid != '%q' and call_info_state != 'seized' and hostname='%q' and profile_name='%q' and "
1370 										 "((sip_from_user='%q' and sip_from_host='%q') or presence_id='%q@%q') order by rcd desc",
1371 										 uuid, mod_sofia_globals.hostname, profile->name, euser, host, euser, host);
1372 				}
1373 
1374 				sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_presence_dialog_callback, &dh);
1375 
1376 				if (mod_sofia_globals.debug_presence > 0) {
1377 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "CHECK SQL: %s@%s [%s]\nhits: %d\n", euser, host, sql, dh.hits);
1378 				}
1379 
1380 				switch_safe_free(sql);
1381 
1382 				if (hup && dh.hits > 0) {
1383 					/* sigh, mangle this packet to simulate a call that is up instead of hungup */
1384 					hup = 0;
1385 					event->flags |= EF_UNIQ_HEADERS;
1386 
1387 					if (!strcasecmp(dh.state, "early")) {
1388 						status = "CS_ROUTING";
1389 						if (rpid) {
1390 							rpid = sofia_presence_translate_rpid(rpid, status);
1391 						}
1392 
1393 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Answer-State", "early");
1394 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "status", status);
1395 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-State", status);
1396 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Call-State", "EARLY");
1397 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "astate", "early");
1398 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "early");
1399 					} else {
1400 						status = "CS_EXECUTE";
1401 						if (rpid) {
1402 							rpid = sofia_presence_translate_rpid(rpid, status);
1403 						}
1404 
1405 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Answer-State", "answered");
1406 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "status", status);
1407 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-State", status);
1408 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Channel-Call-State", "ACTIVE");
1409 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "astate", "confirmed");
1410 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "confirmed");
1411 					}
1412 				}
1413 
1414 
1415 
1416 				if (zstr(call_id) && (dh.hits && presence_source && (!strcasecmp(presence_source, "register") || switch_stristr("register", status)))) {
1417 					sofia_glue_release_profile(profile);
1418 					goto done;
1419 				}
1420 
1421 				if (zstr(call_id)) {
1422 
1423 					sql = switch_mprintf("update sip_subscriptions set version=version+1 where hostname='%q' and profile_name='%q' and "
1424 										 "sip_subscriptions.event != 'line-seize' "
1425 										 "and sip_subscriptions.proto='%q' and (event='%q' or event='%q') and sub_to_user='%q' and "
1426 										 "(sub_to_host='%q' or sub_to_host='%q' or sub_to_host='%q' or "
1427 										 "presence_hosts like '%%%q%%') and "
1428 										 "(sip_subscriptions.profile_name = '%q' or presence_hosts like '%%%q%%')",
1429 										 mod_sofia_globals.hostname, profile->name,
1430 										 proto, event_type, alt_event_type, euser, host, profile->sipip,
1431 										 profile->extsipip ? profile->extsipip : "N/A", host, profile->name, host);
1432 
1433 
1434 					if (mod_sofia_globals.debug_presence > 1) {
1435 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PRES SQL %s\n", sql);
1436 					}
1437 
1438 					sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
1439 
1440 
1441 
1442 					sql = switch_mprintf("select distinct sip_subscriptions.proto,sip_subscriptions.sip_user,sip_subscriptions.sip_host,"
1443 										 "sip_subscriptions.sub_to_user,sip_subscriptions.sub_to_host,sip_subscriptions.event,"
1444 										 "sip_subscriptions.contact,sip_subscriptions.call_id,sip_subscriptions.full_from,"
1445 										 "sip_subscriptions.full_via,sip_subscriptions.expires,sip_subscriptions.user_agent,"
1446 										 "sip_subscriptions.accept,sip_subscriptions.profile_name"
1447 										 ",'%q','%q','%q',sip_presence.status,sip_presence.rpid,sip_presence.open_closed,'%q','%q',"
1448 										 "sip_subscriptions.version, '%q',sip_subscriptions.orig_proto,sip_subscriptions.full_to,"
1449 										 "sip_subscriptions.network_ip, sip_subscriptions.network_port "
1450 										 "from sip_subscriptions "
1451 										 "left join sip_presence on "
1452 										 "(sip_subscriptions.sub_to_user=sip_presence.sip_user and sip_subscriptions.sub_to_host=sip_presence.sip_host and "
1453 										 "sip_subscriptions.profile_name=sip_presence.profile_name and sip_subscriptions.hostname=sip_presence.hostname) "
1454 
1455 										 "where sip_subscriptions.hostname='%q' and sip_subscriptions.profile_name='%q' and "
1456 										 "sip_subscriptions.event != 'line-seize' and "
1457 										 "sip_subscriptions.proto='%q' and "
1458 										 "(event='%q' or event='%q') and sub_to_user='%q' "
1459 										 "and (sub_to_host='%q' or sub_to_host='%q' or sub_to_host='%q' or presence_hosts like '%%%q%%') ",
1460 
1461 
1462 										 switch_str_nil(status), switch_str_nil(rpid), host,
1463 										 dh.status,dh.rpid,dh.presence_id, mod_sofia_globals.hostname, profile->name, proto,
1464 										 event_type, alt_event_type, euser, host, profile->sipip,
1465 										 profile->extsipip ? profile->extsipip : "N/A", host);
1466 				} else {
1467 
1468 					sql = switch_mprintf("update sip_subscriptions set version=version+1 where sip_subscriptions.event != 'line-seize' and "
1469 										 "sip_subscriptions.call_id='%q'", call_id);
1470 
1471 
1472 
1473 					if (mod_sofia_globals.debug_presence > 1) {
1474 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PRES SQL %s\n", sql);
1475 					}
1476 
1477 					sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
1478 
1479 
1480 					sql = switch_mprintf("select distinct sip_subscriptions.proto,sip_subscriptions.sip_user,sip_subscriptions.sip_host,"
1481 										 "sip_subscriptions.sub_to_user,sip_subscriptions.sub_to_host,sip_subscriptions.event,"
1482 										 "sip_subscriptions.contact,sip_subscriptions.call_id,sip_subscriptions.full_from,"
1483 										 "sip_subscriptions.full_via,sip_subscriptions.expires,sip_subscriptions.user_agent,"
1484 										 "sip_subscriptions.accept,sip_subscriptions.profile_name"
1485 										 ",'%q','%q','%q',sip_presence.status,sip_presence.rpid,sip_presence.open_closed,'%q','%q',"
1486 										 "sip_subscriptions.version, '%q',sip_subscriptions.orig_proto,sip_subscriptions.full_to,"
1487 										 "sip_subscriptions.network_ip, sip_subscriptions.network_port "
1488 										 "from sip_subscriptions "
1489 										 "left join sip_presence on "
1490 										 "(sip_subscriptions.sub_to_user=sip_presence.sip_user and sip_subscriptions.sub_to_host=sip_presence.sip_host and "
1491 										 "sip_subscriptions.profile_name=sip_presence.profile_name and sip_subscriptions.hostname=sip_presence.hostname) "
1492 
1493 										 "where sip_subscriptions.hostname='%q' and sip_subscriptions.profile_name='%q' and "
1494 										 "sip_subscriptions.event != 'line-seize' and "
1495 										 "sip_subscriptions.call_id='%q'",
1496 
1497 										 switch_str_nil(status), switch_str_nil(rpid), host,
1498 										 dh.status,dh.rpid,dh.presence_id, mod_sofia_globals.hostname, profile->name, call_id);
1499 
1500 				}
1501 
1502 				helper.hup = hup;
1503 				helper.calls_up = dh.hits;
1504 				helper.profile = profile;
1505 				helper.event = event;
1506 				SWITCH_STANDARD_STREAM(helper.stream);
1507 				switch_assert(helper.stream.data);
1508 
1509 				if (mod_sofia_globals.debug_presence > 0) {
1510 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s START_PRESENCE_SQL (%s)\n",
1511 									  event->event_id == SWITCH_EVENT_PRESENCE_IN ? "IN" : "OUT", profile->name);
1512 				}
1513 
1514 				if (mod_sofia_globals.debug_presence) {
1515 					char *buf;
1516 					switch_event_serialize(event, &buf, SWITCH_FALSE);
1517 					switch_assert(buf);
1518 					if (mod_sofia_globals.debug_presence > 1) {
1519 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DUMP PRESENCE SQL:\n%s\nEVENT DUMP:\n%s\n", sql, buf);
1520 					} else {
1521 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "EVENT DUMP:\n%s\n", buf);
1522 					}
1523 					free(buf);
1524 				}
1525 
1526 				sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_presence_sub_callback, &helper);
1527 				switch_safe_free(sql);
1528 
1529 				if (mod_sofia_globals.debug_presence > 0) {
1530 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s END_PRESENCE_SQL (%s)\n",
1531 									  event->event_id == SWITCH_EVENT_PRESENCE_IN ? "IN" : "OUT", profile->name);
1532 				}
1533 
1534 #if 0
1535 				if (hup && dh.hits < 1) {
1536 					/* so many phones get confused when whe hangup we have to reprobe to get them all to reset to absolute states so the lights stay correct */
1537 					if (switch_event_create(&s_event, SWITCH_EVENT_PRESENCE_PROBE) == SWITCH_STATUS_SUCCESS) {
1538 						switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO);
1539 						switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "login", profile->name);
1540 						switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "sip_profile", profile->name);
1541 						switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "from", "%s@%s", euser, host);
1542 						switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "to", "%s@%s", euser, host);
1543 						switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "event_type", "presence");
1544 						switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
1545 						sofia_event_fire(profile, &s_event);
1546 					}
1547 				}
1548 #endif
1549 
1550 				if (!zstr((char *) helper.stream.data)) {
1551 					char *this_sql = (char *) helper.stream.data;
1552 					char *next = NULL;
1553 					char *last = NULL;
1554 
1555 					do {
1556 						if ((next = strchr(this_sql, ';'))) {
1557 							*next++ = '\0';
1558 							while (*next == '\n' || *next == ' ' || *next == '\r') {
1559 								*next++ = '\0';
1560 							}
1561 						}
1562 
1563 						if (!zstr(this_sql) && (!last || strcmp(last, this_sql))) {
1564 							sofia_glue_execute_sql(profile, &this_sql, SWITCH_FALSE);
1565 							last = this_sql;
1566 						}
1567 						this_sql = next;
1568 					} while (this_sql);
1569 				}
1570 				switch_safe_free(helper.stream.data);
1571 				helper.stream.data = NULL;
1572 
1573 				sofia_glue_release_profile(profile);
1574 			}
1575 		}
1576 		switch_console_free_matches(&matches);
1577 	}
1578 
1579  done:
1580 
1581 	switch_safe_free(sql);
1582 	switch_safe_free(user);
1583 
1584 	return s_event;
1585 }
1586 
1587 static int EVENT_THREAD_RUNNING = 0;
1588 static int EVENT_THREAD_STARTED = 0;
1589 
do_flush(void)1590 static void do_flush(void)
1591 {
1592 	void *pop = NULL;
1593 
1594 	while (mod_sofia_globals.presence_queue && switch_queue_trypop(mod_sofia_globals.presence_queue, &pop) == SWITCH_STATUS_SUCCESS && pop) {
1595 		switch_event_t *event = (switch_event_t *) pop;
1596 		switch_event_destroy(&event);
1597 	}
1598 
1599 }
1600 
sofia_presence_event_thread_run(switch_thread_t * thread,void * obj)1601 void *SWITCH_THREAD_FUNC sofia_presence_event_thread_run(switch_thread_t *thread, void *obj)
1602 {
1603 	void *pop;
1604 	int done = 0;
1605 
1606 	switch_mutex_lock(mod_sofia_globals.mutex);
1607 	if (!EVENT_THREAD_RUNNING) {
1608 		EVENT_THREAD_RUNNING++;
1609 		mod_sofia_globals.threads++;
1610 	} else {
1611 		done = 1;
1612 	}
1613 	switch_mutex_unlock(mod_sofia_globals.mutex);
1614 
1615 	if (done) {
1616 		return NULL;
1617 	}
1618 
1619 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Event Thread Started\n");
1620 
1621 	while (mod_sofia_globals.running == 1) {
1622 		int count = 0;
1623 
1624 		if (switch_queue_pop(mod_sofia_globals.presence_queue, &pop) == SWITCH_STATUS_SUCCESS) {
1625 			switch_event_t *event = (switch_event_t *) pop;
1626 
1627 			if (!pop) {
1628 				break;
1629 			}
1630 
1631 			if (mod_sofia_globals.presence_flush) {
1632 				switch_mutex_lock(mod_sofia_globals.mutex);
1633 				if (mod_sofia_globals.presence_flush) {
1634 					do_flush();
1635 					mod_sofia_globals.presence_flush = 0;
1636 				}
1637 				switch_mutex_unlock(mod_sofia_globals.mutex);
1638 			}
1639 
1640 			switch(event->event_id) {
1641 			case SWITCH_EVENT_MESSAGE_WAITING:
1642 				actual_sofia_presence_mwi_event_handler(event);
1643 				break;
1644 			case SWITCH_EVENT_CONFERENCE_DATA:
1645 				conference_data_event_handler(event);
1646 				break;
1647 			default:
1648 				do {
1649 					switch_event_t *ievent = event;
1650 					event = actual_sofia_presence_event_handler(ievent);
1651 					switch_event_destroy(&ievent);
1652 				} while (event);
1653 				break;
1654 			}
1655 
1656 			switch_event_destroy(&event);
1657 			count++;
1658 		}
1659 	}
1660 
1661 	do_flush();
1662 
1663 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Event Thread Ended\n");
1664 
1665 	switch_mutex_lock(mod_sofia_globals.mutex);
1666 	mod_sofia_globals.threads--;
1667 	EVENT_THREAD_RUNNING = EVENT_THREAD_STARTED = 0;
1668 	switch_mutex_unlock(mod_sofia_globals.mutex);
1669 
1670 	return NULL;
1671 }
1672 
sofia_presence_event_thread_start(void)1673 void sofia_presence_event_thread_start(void)
1674 {
1675 	//switch_thread_t *thread;
1676 	switch_threadattr_t *thd_attr = NULL;
1677 	int done = 0;
1678 
1679 	switch_mutex_lock(mod_sofia_globals.mutex);
1680 	if (!EVENT_THREAD_STARTED) {
1681 		EVENT_THREAD_STARTED++;
1682 	} else {
1683 		done = 1;
1684 	}
1685 	switch_mutex_unlock(mod_sofia_globals.mutex);
1686 
1687 	if (done) {
1688 		return;
1689 	}
1690 
1691 	switch_threadattr_create(&thd_attr, mod_sofia_globals.pool);
1692 	//switch_threadattr_detach_set(thd_attr, 1);
1693 	switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
1694 	switch_threadattr_priority_set(thd_attr, SWITCH_PRI_IMPORTANT);
1695 	switch_thread_create(&mod_sofia_globals.presence_thread, thd_attr, sofia_presence_event_thread_run, NULL, mod_sofia_globals.pool);
1696 }
1697 
1698 
sofia_presence_event_handler(switch_event_t * event)1699 void sofia_presence_event_handler(switch_event_t *event)
1700 {
1701 	switch_event_t *cloned_event;
1702 
1703 	if (!EVENT_THREAD_STARTED) {
1704 		sofia_presence_event_thread_start();
1705 		switch_yield(500000);
1706 	}
1707 
1708 	switch_event_dup(&cloned_event, event);
1709 	switch_assert(cloned_event);
1710 
1711 	if (switch_queue_trypush(mod_sofia_globals.presence_queue, cloned_event) != SWITCH_STATUS_SUCCESS) {
1712 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Presence queue overloaded.... Flushing queue\n");
1713 		switch_mutex_lock(mod_sofia_globals.mutex);
1714 		mod_sofia_globals.presence_flush = 1;
1715 		switch_mutex_unlock(mod_sofia_globals.mutex);
1716 		switch_event_destroy(&cloned_event);
1717 	}
1718 
1719 
1720 }
1721 
1722 
sofia_presence_sub_reg_callback(void * pArg,int argc,char ** argv,char ** columnNames)1723 static int sofia_presence_sub_reg_callback(void *pArg, int argc, char **argv, char **columnNames)
1724 {
1725 	sofia_profile_t *profile = (sofia_profile_t *) pArg;
1726 	char *user = argv[3];
1727 	char *host = argv[2];
1728 	switch_event_t *event;
1729 	char *event_name = argv[5];
1730 	char *expires = argv[10];
1731 
1732 
1733 
1734 	if (!strcasecmp(event_name, "message-summary")) {
1735 
1736 		if (switch_event_create(&event, SWITCH_EVENT_MESSAGE_QUERY) == SWITCH_STATUS_SUCCESS) {
1737 			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Message-Account", "sip:%s@%s", user, host);
1738 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "VM-Sofia-Profile", profile->name);
1739 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "VM-sub-call-id", argv[7]);
1740 
1741 			if (mod_sofia_globals.debug_presence > 0) {
1742 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Create MESSAGE QUERY EVENT...\n");
1743 				DUMP_EVENT(event);
1744 			}
1745 
1746 
1747 			sofia_event_fire(profile, &event);
1748 		}
1749 		return 0;
1750 	}
1751 
1752 	if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_PROBE) == SWITCH_STATUS_SUCCESS) {
1753 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO);
1754 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", profile->url);
1755 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "sip_profile", profile->name);
1756 		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", user, host);
1757 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
1758 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_subtype", "probe");
1759 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto-specific-event-name", event_name);
1760 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "expires", expires);
1761 		sofia_event_fire(profile, &event);
1762 	}
1763 
1764 	return 0;
1765 }
1766 
sofia_presence_resub_callback(void * pArg,int argc,char ** argv,char ** columnNames)1767 static int sofia_presence_resub_callback(void *pArg, int argc, char **argv, char **columnNames)
1768 {
1769 	struct resub_helper *h = (struct resub_helper *) pArg;
1770 	sofia_profile_t *profile = h->profile;
1771 	char *user = argv[0];
1772 	char *host = argv[1];
1773 	char *status = argv[2];
1774 	char *rpid = argv[3];
1775 	char *proto = argv[4];
1776 	char *call_id = NULL;
1777 	char *presence_id = NULL;
1778 	char *to_user = NULL;
1779 	char *uuid = NULL;
1780 	char *state = NULL;
1781 	char *direction = NULL;
1782 	switch_event_t *event;
1783 	char to_buf[128] = "";
1784 	switch_event_header_t *hp;
1785 	char *free_me = NULL;
1786 	int do_event = 1, i;
1787 
1788 
1789 	if (mod_sofia_globals.debug_presence > 1) {
1790 		for (i = 0; i < argc; i++) {
1791 			switch_log_printf(SWITCH_CHANNEL_LOG,SWITCH_LOG_WARNING,  "sofia_presence_resub_callback: %d [%s]=[%s]\n", i, columnNames[i], argv[i]);
1792 		}
1793 	}
1794 
1795 	if (argc > 5) {
1796 		uuid = argv[5];
1797 		state = switch_str_nil(argv[6]);
1798 		direction = switch_str_nil(argv[7]);
1799 		if (argc > 8) {
1800 			switch_set_string(to_buf, argv[8]);
1801 			switch_url_decode(to_buf);
1802 			to_user = to_buf;
1803 		}
1804 		if (argc > 10 && !zstr(argv[10]) && !zstr(argv[11])) {
1805 			status = argv[10];
1806 			rpid = argv[11];
1807 		}
1808 
1809 		if (argc > 12 && !zstr(argv[12]) && strchr(argv[12], '@')) {
1810 			char *p;
1811 
1812 			presence_id = argv[12];
1813 			free_me = strdup(presence_id);
1814 			switch_assert(free_me);
1815 			if ((p = strchr(free_me, '@'))) *p = '\0';
1816 			user = free_me;
1817 		}
1818 
1819 		if (argc > 16) {
1820 			call_id = argv[16];
1821 		}
1822 
1823 	}
1824 
1825 	if (!zstr(uuid) && !switch_ivr_uuid_exists(uuid)) {
1826 		if (mod_sofia_globals.debug_presence > 0) {
1827 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "%s SKIPPING NOT FOUND UUID %s\n", profile->name, uuid);
1828 		}
1829 		do_event = 0;
1830 	}
1831 
1832 	if (zstr(proto)) {
1833 		proto = NULL;
1834 	}
1835 
1836 	if (mod_sofia_globals.debug_presence > 0) {
1837 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "%s PRESENCE_PROBE %s@%s\n", profile->name, user, host);
1838 	}
1839 
1840 	if (do_event && switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
1841 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", proto ? proto : SOFIA_CHAT_PROTO);
1842 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", profile->url);
1843 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "sip_profile", profile->name);
1844 
1845 		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", user, host);
1846 
1847 		if (h->noreg) {
1848 			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Force-Direction", "inbound");
1849 		}
1850 
1851 		if (!zstr(call_id)) {
1852 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-id", call_id);
1853 		}
1854 
1855 		//switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "resub", "true");
1856 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "status", status);
1857 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", rpid);
1858 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
1859 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
1860 		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", 0);
1861 
1862 		if (!zstr(to_user)) {
1863 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to-user", to_user);
1864 		}
1865 
1866 		if (zstr(state)) {
1867 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_HANGUP");
1868 			//switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "resubscribe");
1869 		} else {
1870 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_ROUTING");
1871 			if (uuid) {
1872 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", uuid);
1873 			}
1874 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", state);
1875 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "astate", state);
1876 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", direction);
1877 		}
1878 
1879 		if (h->event) {
1880 			for (hp = h->event->headers; hp; hp = hp->next) {
1881 				if (!strncasecmp(hp->name, "fwd-", 4)) {
1882 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, hp->name + 4, hp->value);
1883 				}
1884 			}
1885 		}
1886 
1887 		sofia_event_fire(profile, &event);
1888 	}
1889 
1890 	switch_safe_free(free_me);
1891 
1892 
1893 	h->rowcount++;
1894 
1895 
1896 	return 0;
1897 }
1898 
get_display_name_from_contact(const char * in,char * dst)1899 char *get_display_name_from_contact(const char *in, char* dst)
1900 {
1901 	// name-addr      =  [ display-name ] LAQUOT addr-spec RAQUOT
1902 	// display-name   =  *(token LWS)/ quoted-string
1903 	// return whatever comes before the left angle bracket, stripped of whitespace and quotes
1904 	char *p;
1905 	char *buf;
1906 
1907 	strcpy(dst, "");
1908 	if (strchr(in, '<') && strchr(in, '>')) {
1909 		buf = strdup(in);
1910 		switch_assert(buf);
1911 		p = strchr(buf, '<');
1912 		*p = '\0';
1913 		if (!zstr(buf)) {
1914 			p = switch_strip_whitespace(buf);
1915 			if (p) {
1916 				if (*p == '"') {
1917 					if (end_of(p+1) == '"') {
1918 						char *q = strdup(p + 1);
1919 						switch_assert(q);
1920 						end_of(q) = '\0';
1921 						strcpy(dst, q);
1922 						switch_safe_free(q);
1923 					}
1924 				} else {
1925 					strcpy(dst, p);
1926 				}
1927 				switch_safe_free(p);
1928 			}
1929 		}
1930 		switch_safe_free(buf);
1931 	}
1932 	return dst;
1933 }
1934 
sofia_dialog_probe_callback(void * pArg,int argc,char ** argv,char ** columnNames)1935 static int sofia_dialog_probe_callback(void *pArg, int argc, char **argv, char **columnNames)
1936 {
1937 	struct rfc4235_helper *h = (struct rfc4235_helper *) pArg;
1938 
1939 	char *proto = argv[0];
1940 	char *user = argv[1];
1941 	char *host = argv[2];
1942 	char *uuid = argv[3];
1943 	char *call_id = argv[4];
1944 	char *state = argv[5];
1945 	char *direction = argv[6];
1946 	char *to_user = argv[7];
1947 	char *to_host = argv[8];
1948 	char *from_user = argv[9];
1949 	//    char *from_host = argv[10];
1950 	char *contact = switch_str_nil(argv[11]);
1951 	char *contact_user = switch_str_nil(argv[12]);
1952 	char *contact_host = switch_str_nil(argv[13]);
1953 	char *to_tag = switch_str_nil(argv[14]);
1954 	char *from_tag = switch_str_nil(argv[15]);
1955 	char *orig_proto = switch_str_nil(argv[16]);
1956 
1957 	const char *event_status = "";
1958 	char *data = NULL, *tmp;
1959 	char key[256] = "";
1960 	char *local_user;
1961 	char *local_host;
1962 	char *remote_user;
1963 	char *remote_host;
1964 	char *remote_uri;
1965 	char *local_user_param = NULL;
1966 	char remote_display_buf[512];
1967 	char *buf_to_free = NULL;
1968 	int bInternal = 0;
1969 	int i;
1970 	int skip_proto = 0;
1971 
1972 	if (mod_sofia_globals.debug_presence > 1) {
1973 		for (i = 0; i < argc; i++) {
1974 			switch_log_printf(SWITCH_CHANNEL_LOG,SWITCH_LOG_WARNING,  "sofia_dialog_probe_callback: %d [%s]=[%s]\n", i, columnNames[i], argv[i]);
1975 		}
1976 	}
1977 
1978 	if (zstr(to_user) || zstr(contact_user)) {
1979 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "sofia_dialog_probe_callback: not enough info to generate a dialog entry\n");
1980 		return 0;
1981 	}
1982 
1983 	// Usually we report the dialogs FROM the probed user.  The exception is when the monitored endpoint is internal,
1984 	// and its presence_id is set in the dialplan.  Reverse the direction if this is not a registered entity.
1985 	if (!strcmp(direction, "inbound") && strcmp(user, from_user) ) {
1986 		// If inbound and the entity is not the caller (i.e. internal to FS), then the direction is reversed
1987 		// because it is not going through the B2BUA
1988 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "sofia_dialog_probe_callback: endpt is internal\n");
1989 		direction = !strcasecmp(direction, "outbound") ? "inbound" : "outbound";
1990 		bInternal = 1;
1991 	}
1992 
1993 	if (!strcasecmp(direction, "outbound")) {
1994 		direction = "recipient";
1995 	}
1996 	else {
1997 		direction = "initiator";
1998 	}
1999 
2000 	if (!zstr(orig_proto) && !strcmp(orig_proto, SOFIA_CHAT_PROTO)) {
2001 		skip_proto = 1;
2002 	}
2003 
2004 	local_host = to_host;
2005 	if (proto && !strcasecmp(proto, "queue")) {
2006 		local_user = to_user;
2007 		local_user_param = switch_mprintf(";proto=%s", proto);
2008 		event_status = "hold";
2009 		if (skip_proto) {
2010 			buf_to_free = switch_mprintf("sip:%s", to_user);
2011 		} else {
2012 			buf_to_free = switch_mprintf("sip:queue+%s", to_user);
2013 		}
2014 		remote_uri = buf_to_free;
2015 		strcpy(remote_display_buf, "queue");
2016 		remote_user = to_user;
2017 		remote_host = local_host;
2018 	}
2019 	else if (proto && !strcasecmp(proto, "park")) {
2020 		local_user = to_user;
2021 		local_user_param = switch_mprintf(";proto=%s", proto);
2022 		event_status = "hold";
2023 		if (skip_proto) {
2024 			buf_to_free = switch_mprintf("sip:%s", to_user);
2025 		} else {
2026 			buf_to_free = switch_mprintf("sip:park+%s", to_user);
2027 		}
2028 		remote_uri = buf_to_free;
2029 		strcpy(remote_display_buf, "park");
2030 		remote_user = to_user;
2031 		remote_host = local_host;
2032 	}
2033 	else if (proto && !strcasecmp(proto, "pickup")) {
2034 		local_user = to_user;
2035 		local_user_param = switch_mprintf(";proto=%s", proto);
2036 		event_status = "hold";
2037 		if (skip_proto) {
2038 			buf_to_free = switch_mprintf("sip:%s", to_user);
2039 		} else {
2040 			buf_to_free = switch_mprintf("sip:pickup+%s", to_user);
2041 		}
2042 		remote_uri = buf_to_free;
2043 		strcpy(remote_display_buf, "pickup");
2044 		remote_user = to_user;
2045 		remote_host = local_host;
2046 	}
2047 	else if (proto && !strcasecmp(proto, "conf")) {
2048 		local_user = to_user;
2049 		local_user_param = switch_mprintf(";proto=%s", proto);
2050 		if (skip_proto) {
2051 			buf_to_free = switch_mprintf("sip:%s@%s", to_user, host);
2052 		} else {
2053 			buf_to_free = switch_mprintf("sip:conf+%s@%s", to_user, host);
2054 		}
2055 		remote_uri = buf_to_free;
2056 		strcpy(remote_display_buf, "conference");
2057 		remote_user = to_user;
2058 		remote_host = local_host;
2059 	}
2060 	else if (bInternal) {
2061 		local_user = to_user;
2062 		get_display_name_from_contact(contact, remote_display_buf);
2063 		buf_to_free = sofia_glue_strip_uri(contact);
2064 		remote_uri = buf_to_free;
2065 		remote_user = contact_user;
2066 		remote_host = contact_host;
2067 	} else {
2068 		local_user = from_user;
2069 		buf_to_free = switch_mprintf("**%s@%s", from_user, local_host);
2070 		remote_uri = buf_to_free;
2071 		strcpy(remote_display_buf, to_user);
2072 		remote_user = to_user;
2073 		remote_host = local_host;
2074 	}
2075 
2076 	switch_snprintf(key, sizeof(key), "%s%s", user, host);
2077 	data = switch_core_hash_find(h->hash, key);
2078 	if (!data) {
2079 		data = "";
2080 	}
2081 	tmp = switch_core_sprintf(h->pool, "%s"
2082 							  "<dialog id=\"%s\" call-id=\"%s\" local-tag=\"%s\" remote-tag=\"%s\" direction=\"%s\">\n"
2083 							  " <state>%s</state>\n"
2084 							  " <local>\n"
2085 							  "  <identity display=\"%s\">sip:%s@%s%s</identity>\n"
2086 							  "  <target uri=\"sip:%s@%s\">\n"
2087 							  "   <param pname=\"+sip.rendering\" pvalue=\"%s\"/>\n"
2088 							  "  </target>\n"
2089 							  " </local>\n"
2090 							  " <remote>\n"
2091 							  "  <identity display=\"%s\">sip:%s@%s</identity>\n"
2092 							  "  <target uri=\"%s\"/>\n"
2093 							  " </remote>\n"
2094 							  "</dialog>\n",
2095 							  data,
2096 							  uuid, call_id, to_tag, from_tag, direction,
2097 							  state,
2098 							  local_user, local_user, local_host, switch_str_nil(local_user_param),
2099 							  local_user, local_host,
2100 							  !strcasecmp(event_status, "hold") ? "no" : "yes",
2101 							  remote_display_buf, remote_user, remote_host,
2102 							  remote_uri
2103 							  );
2104 	switch_core_hash_insert(h->hash, key, tmp);
2105 	switch_safe_free(local_user_param);
2106 	switch_safe_free(buf_to_free);
2107 
2108 	h->rowcount++;
2109 
2110 	return 0;
2111 }
2112 
2113 #define SOFIA_PRESENCE_COLLISION_DELTA 50
2114 #define SOFIA_PRESENCE_ROLLOVER_YEAR (86400 * 365 * SOFIA_PRESENCE_COLLISION_DELTA)
check_presence_epoch(void)2115 static uint32_t check_presence_epoch(void)
2116 {
2117 	time_t now = switch_epoch_time_now(NULL);
2118 	uint32_t callsequence = (uint32_t)((now - mod_sofia_globals.presence_epoch) * SOFIA_PRESENCE_COLLISION_DELTA);
2119 
2120 	if (!mod_sofia_globals.presence_year || callsequence >= SOFIA_PRESENCE_ROLLOVER_YEAR) {
2121 		struct tm tm;
2122 		switch_mutex_lock(mod_sofia_globals.mutex);
2123 		tm = *(localtime(&now));
2124 
2125 		if (tm.tm_year != mod_sofia_globals.presence_year) {
2126 			mod_sofia_globals.presence_epoch = (uint32_t)now - (tm.tm_yday * 86400) - (tm.tm_hour * 60 * 60) - (tm.tm_min * 60) - tm.tm_sec;
2127 			mod_sofia_globals.presence_year = tm.tm_year;
2128 			callsequence = (uint32_t)(((uint32_t)now - mod_sofia_globals.presence_epoch) * SOFIA_PRESENCE_COLLISION_DELTA);
2129 		}
2130 
2131 		switch_mutex_unlock(mod_sofia_globals.mutex);
2132 	}
2133 
2134 	return callsequence;
2135 }
2136 
sofia_presence_get_cseq(sofia_profile_t * profile)2137 uint32_t sofia_presence_get_cseq(sofia_profile_t *profile)
2138 {
2139 	uint32_t callsequence;
2140 	int diff = 0;
2141 
2142 	switch_mutex_lock(profile->ireg_mutex);
2143 
2144 	callsequence = check_presence_epoch();
2145 
2146 	if (profile->last_cseq) {
2147 		diff = callsequence - profile->last_cseq;
2148 		if (diff <= 0 && diff > -100000) {
2149 			callsequence = ++profile->last_cseq;
2150 		}
2151 	}
2152 
2153 	profile->last_cseq = callsequence;
2154 
2155 	switch_mutex_unlock(profile->ireg_mutex);
2156 
2157 	return callsequence;
2158 
2159 }
2160 
2161 
2162 #define send_presence_notify(_a,_b,_c,_d,_e,_f,_g,_h,_i,_j,_k,_l) \
2163 _send_presence_notify(_a,_b,_c,_d,_e,_f,_g,_h,_i,_j,_k,_l,__FILE__, __SWITCH_FUNC__, __LINE__)
2164 
_send_presence_notify(sofia_profile_t * profile,const char * full_to,const char * full_from,const char * o_contact,const char * expires,const char * call_id,const char * event,const char * remote_ip,const char * remote_port,const char * ct,const char * pl,const char * call_info,const char * file,const char * func,int line)2165 static void _send_presence_notify(sofia_profile_t *profile,
2166 								  const char *full_to,
2167 								  const char *full_from,
2168 								  const char *o_contact,
2169 								  const char *expires,
2170 								  const char *call_id,
2171 								  const char *event,
2172 								  const char *remote_ip,
2173 								  const char *remote_port,
2174 								  const char *ct,
2175 								  const char *pl,
2176 								  const char *call_info,
2177 								  const char *file, const char *func, int line
2178 								 )
2179 {
2180 	char sstr[128] = "";
2181 	nua_handle_t *nh;
2182 	int exptime = 0;
2183 	char expires_str[10] = "";
2184 	sip_cseq_t *cseq = NULL;
2185 	uint32_t callsequence;
2186 	uint32_t now = (uint32_t) switch_epoch_time_now(NULL);
2187 
2188 	sofia_destination_t *dst = NULL;
2189 	char *contact_str, *contact, *user_via = NULL, *send_contact = NULL;
2190 	char *route_uri = NULL, *o_contact_dup = NULL, *tmp, *to_uri, *dcs = NULL;
2191 	const char *tp;
2192 	char *cparams = NULL;
2193 	char *path = NULL;
2194 
2195 	if (zstr(full_to) || zstr(full_from) || zstr(o_contact)) {
2196 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "MISSING DATA TO SEND NOTIFY.\n");
2197 		return;
2198 	}
2199 
2200 	if ((cparams = strstr(o_contact, ";_;"))) {
2201 		cparams += 3;
2202 	}
2203 
2204 	if (!switch_stristr("fs_nat=yes", o_contact)) {
2205 		path = sofia_glue_get_path_from_contact((char *) o_contact);
2206 	}
2207 
2208 	dst = sofia_glue_get_destination((char *) o_contact);
2209 	switch_assert(dst);
2210 
2211 	if (!zstr(dst->contact)) {
2212 		contact = sofia_glue_get_url_from_contact(dst->contact, 1);
2213 	} else {
2214 		contact = strdup(o_contact);
2215 	}
2216 
2217 	if (dst->route_uri) {
2218 		route_uri = sofia_glue_strip_uri(dst->route_uri);
2219 		tmp = (char *)route_uri;
2220 	} else {
2221 		tmp = (char *)o_contact;
2222 	}
2223 
2224 	o_contact_dup = sofia_glue_get_url_from_contact(tmp, 1);
2225 
2226 	if ((tp = switch_stristr("transport=", o_contact_dup))) {
2227 		tp += 10;
2228 	}
2229 
2230 	if (zstr(tp)) {
2231 		tp = "udp";
2232 	}
2233 
2234 	if (!zstr(remote_ip) && sofia_glue_check_nat(profile, remote_ip)) {
2235 		sofia_transport_t transport = sofia_glue_str2transport(tp);
2236 
2237 		switch (transport) {
2238 		case SOFIA_TRANSPORT_TCP:
2239 			contact_str = profile->tcp_public_contact;
2240 			break;
2241 		case SOFIA_TRANSPORT_TCP_TLS:
2242 			contact_str = sofia_test_pflag(profile, PFLAG_TLS) ?
2243 				profile->tls_public_contact : profile->tcp_public_contact;
2244 			break;
2245 		default:
2246 			contact_str = profile->public_url;
2247 			break;
2248 		}
2249 		user_via = sofia_glue_create_external_via(NULL, profile, transport);
2250 	} else {
2251 		sofia_transport_t transport = sofia_glue_str2transport(tp);
2252 		switch (transport) {
2253 		case SOFIA_TRANSPORT_TCP:
2254 			contact_str = profile->tcp_contact;
2255 			break;
2256 		case SOFIA_TRANSPORT_TCP_TLS:
2257 			contact_str = sofia_test_pflag(profile, PFLAG_TLS) ?
2258 				profile->tls_contact : profile->tcp_contact;
2259 			break;
2260 		default:
2261 			contact_str = profile->url;
2262 			break;
2263 		}
2264 	}
2265 
2266 
2267 	if ((to_uri = sofia_glue_get_url_from_contact((char *)full_to, 1))) {
2268 		char *p;
2269 
2270 		if ((p = strstr(to_uri, "sip:"))) {
2271 			char *q;
2272 
2273 			p += 4;
2274 			if ((q = strchr(p, '@'))) {
2275 				*q++ = '\0';
2276 
2277 				if ((dcs = switch_string_replace(contact_str, "mod_sofia", p))) {
2278 					contact_str = dcs;
2279 				}
2280 
2281 			}
2282 		}
2283 
2284 		free(to_uri);
2285 	}
2286 
2287 	if (expires) {
2288 		long ltmp = atol(expires);
2289 
2290 		if (ltmp > 0) {
2291 			exptime = (ltmp - now);
2292 		} else {
2293 			exptime = 0;
2294 		}
2295 	}
2296 
2297 	if (exptime <= 0) {
2298 		switch_snprintf(sstr, sizeof(sstr), "terminated;reason=noresource");
2299 	} else {
2300 		switch_snprintf(sstr, sizeof(sstr), "active;expires=%u", (unsigned) exptime);
2301 	}
2302 
2303 	if (mod_sofia_globals.debug_presence > 1 || mod_sofia_globals.debug_sla > 1) {
2304 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SEND PRES NOTIFY:\n"
2305 						  "file[%s]\nfunc[%s]\nline[%d]\n"
2306 						  "profile[%s]\nvia[%s]\nip[%s]\nport[%s]\nroute[%s]\ncontact[%s]\nto[%s]\nfrom[%s]\nurl[%s]\ncall_id[%s]\nexpires_str[%s]\n"
2307 						  "event[%s]\nct[%s]\npl[%s]\ncall_info[%s]\nexptime[%ld]\n",
2308 						  file, func, line,
2309 						  profile->name,
2310 						  switch_str_nil(user_via),
2311 						  remote_ip,
2312 						  remote_port,
2313 						  route_uri,
2314 						  o_contact,
2315 						  full_to,
2316 						  full_from,
2317 						  contact,
2318 						  call_id,
2319 						  expires_str,
2320 						  event,
2321 						  switch_str_nil(ct),
2322 						  switch_str_nil(pl),
2323 						  switch_str_nil(call_info),
2324 						  (long)exptime
2325 						  );
2326 	}
2327 
2328 
2329 	callsequence = sofia_presence_get_cseq(profile);
2330 
2331 	if (cparams) {
2332 		send_contact = switch_mprintf("%s;%s", contact_str, cparams);
2333 		contact_str = send_contact;
2334 	}
2335 
2336 	nh = nua_handle(profile->nua, NULL, NUTAG_URL(contact), SIPTAG_CONTACT_STR(contact_str), TAG_END());
2337 	cseq = sip_cseq_create(nh->nh_home, callsequence, SIP_METHOD_NOTIFY);
2338 	nua_handle_bind(nh, &mod_sofia_globals.destroy_private);
2339 
2340 
2341 	nua_notify(nh,
2342 			   NUTAG_NEWSUB(1),
2343 			   TAG_IF(route_uri, NUTAG_PROXY(route_uri)),
2344 			   TAG_IF(dst->route, SIPTAG_ROUTE_STR(dst->route)),
2345 			   TAG_IF(user_via, SIPTAG_VIA_STR(user_via)),
2346 			   TAG_IF(path, SIPTAG_RECORD_ROUTE_STR(path)),
2347 
2348 			   SIPTAG_FROM_STR(full_to),
2349 			   SIPTAG_TO_STR(full_from),
2350 
2351 			   SIPTAG_CALL_ID_STR(call_id),
2352 			   TAG_IF(*expires_str, SIPTAG_EXPIRES_STR(expires_str)),
2353 			   SIPTAG_SUBSCRIPTION_STATE_STR(sstr),
2354 			   SIPTAG_EVENT_STR(event),
2355 			   TAG_IF(!zstr(ct), SIPTAG_CONTENT_TYPE_STR(ct)),
2356 			   TAG_IF(!zstr(pl), SIPTAG_PAYLOAD_STR(pl)),
2357 			   TAG_IF(!zstr(call_info), SIPTAG_CALL_INFO_STR(call_info)),
2358 			   TAG_IF(!exptime, SIPTAG_EXPIRES_STR("0")),
2359 			   SIPTAG_CSEQ(cseq),
2360 			   TAG_END());
2361 
2362 
2363 	switch_safe_free(route_uri);
2364 	switch_safe_free(dcs);
2365 	switch_safe_free(contact);
2366 
2367 	sofia_glue_free_destination(dst);
2368 	switch_safe_free(user_via);
2369 	switch_safe_free(o_contact_dup);
2370 	switch_safe_free(send_contact);
2371 	switch_safe_free(path);
2372 }
2373 
2374 
sofia_dialog_probe_notify_callback(void * pArg,int argc,char ** argv,char ** columnNames)2375 static int sofia_dialog_probe_notify_callback(void *pArg, int argc, char **argv, char **columnNames)
2376 {
2377 	struct rfc4235_helper *sh = (struct rfc4235_helper *) pArg;
2378 	char key[256] = "";
2379 	char *data = NULL;
2380 	char *call_id = argv[0];
2381 	char *expires = argv[1];
2382 	char *user = argv[2];
2383 	char *host = argv[3];
2384 	char *event = argv[4];
2385 	char *version = argv[5];
2386 	char *notify_state = argv[6];
2387 	char *full_to = argv[7];
2388 	char *full_from = argv[8];
2389 	char *contact = argv[9];
2390 	char *remote_ip = argv[10];
2391 	char *remote_port = argv[11];
2392 
2393 	switch_stream_handle_t stream = { 0 };
2394 	char *to;
2395 	const char *pl = NULL;
2396 	const char *ct = "application/dialog-info+xml";
2397 
2398 	if (mod_sofia_globals.debug_presence > 0) {
2399 		int i;
2400 		for(i = 0; i < argc; i++) {
2401 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "arg %d[%s] = [%s]\n", i, columnNames[i], argv[i]);
2402 		}
2403 	}
2404 
2405 
2406 	if (mod_sofia_globals.debug_presence > 0) {
2407 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE,
2408 						  "SEND DIALOG\nTo:      \t%s@%s\nFrom:    \t%s@%s\nCall-ID:  \t%s\n",
2409 						  user, host, user, host, call_id);
2410 	}
2411 
2412 	to = switch_mprintf("sip:%s@%s", user, host);
2413 
2414 	SWITCH_STANDARD_STREAM(stream);
2415 
2416 	if (zstr(version)) {
2417 		version = "0";
2418 	}
2419 
2420 	stream.write_function(&stream,
2421 						  "<?xml version=\"1.0\"?>\n"
2422 						  "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" "
2423 						  "version=\"%s\" state=\"%s\" entity=\"%s\">\n",
2424 						  version,
2425 						  notify_state, to);
2426 
2427 	switch_snprintf(key, sizeof(key), "%s%s", user, host);
2428 
2429 	data = switch_core_hash_find(sh->hash, key);
2430 
2431 	if (data) {
2432 		stream.write_function(&stream, "%s\n", data);
2433 	}
2434 
2435 	stream.write_function(&stream, "</dialog-info>\n");
2436 	pl = stream.data;
2437 	ct = "application/dialog-info+xml";
2438 
2439 	if (mod_sofia_globals.debug_presence > 0 && pl) {
2440 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "send payload:\n%s\n", pl);
2441 	}
2442 
2443 
2444 	send_presence_notify(sh->profile,
2445 						 full_to,
2446 						 full_from,
2447 						 contact,
2448 						 expires,
2449 						 call_id,
2450 						 event,
2451 						 remote_ip,
2452 						 remote_port,
2453 						 ct,
2454 						 pl,
2455 						 NULL
2456 						 );
2457 
2458 
2459 	switch_safe_free(to);
2460 	switch_safe_free(stream.data);
2461 
2462 	return 0;
2463 }
2464 
translate_rpid(char * in)2465 static char *translate_rpid(char *in)
2466 {
2467 	char *r = in;
2468 
2469 	if (in && (strstr(in, "null") || strstr(in, "NULL"))) {
2470 		in = NULL;
2471 	}
2472 
2473 	if (zstr(in)) {
2474 		return NULL;
2475 	}
2476 
2477 	if (!strcasecmp(in, "unknown")) {
2478 		r = NULL;
2479 		goto end;
2480 	}
2481 
2482 	if (!strcasecmp(in, "busy")) {
2483 		r = in;
2484 		goto end;
2485 	}
2486 
2487 	if (!strcasecmp(in, "unavailable")) {
2488 		r = "away";
2489 		goto end;
2490 	}
2491 
2492 	if (!strcasecmp(in, "idle")) {
2493 		r = "busy";
2494 	}
2495 
2496  end:
2497 	return r;
2498 }
2499 
2500 
gen_pidf(char * user_agent,char * id,char * url,char * open,char * rpid,char * prpid,char * status,const char ** ct)2501 static char *gen_pidf(char *user_agent, char *id, char *url, char *open, char *rpid, char *prpid, char *status, const char **ct)
2502 {
2503 	char *ret = NULL;
2504 
2505 	if (switch_stristr("polycom", user_agent)) {
2506 		*ct = "application/xpidf+xml";
2507 
2508 		/* If unknown/none prpid is provided, just show the user as online. */
2509 		if (!prpid || !strcasecmp(prpid, "unknown")) {
2510 			prpid = "online";
2511 		}
2512 
2513 		/* of course!, lets make a big deal over dashes. Now the stupidity is complete. */
2514 		if (!strcmp(prpid, "on-the-phone")) {
2515 			prpid = "onthephone";
2516 		}
2517 
2518 		if (zstr(open)) {
2519 			open = "open";
2520 		}
2521 
2522 		ret = switch_mprintf("<?xml version=\"1.0\"?>\n"
2523 							 "<!DOCTYPE presence PUBLIC \"-//IETF//DTD RFCxxxx XPIDF 1.0//EN\" \"xpidf.dtd\">\n"
2524 							 "<presence>\n"
2525 							 " <status>\n"
2526 							 "  <note>%s</note>\n"
2527 							 " </status>\n"
2528 							 " <presentity uri=\"%s;method=SUBSCRIBE\" />\n"
2529 							 " <atom id=\"%s\">\n"
2530 							 "  <address uri=\"%s;user=ip\" priority=\"0.800000\">\n"
2531 							 "   <status status=\"%s\" />\n"
2532 							 "   <msnsubstatus substatus=\"%s\" />\n"
2533 							 "  </address>\n"
2534 							 " </atom>\n"
2535 							 "</presence>\n", status, id, id, url, open, prpid);
2536 	} else {
2537 		char *xml_rpid = NULL;
2538 
2539 		*ct = "application/pidf+xml";
2540 
2541 		if (!strcasecmp(open, "closed")) {
2542 			status = "Unregistered";
2543 			prpid = NULL;
2544 		}
2545 
2546 		if (!strncasecmp(status, "Registered", 10)) {
2547 			status = "Available";
2548 		}
2549 
2550 		if (!strcasecmp(status, "Available")) {
2551 			prpid = NULL;
2552 		}
2553 
2554 
2555 		if (!strcasecmp(status, "Unregistered")) {
2556 			prpid = NULL;
2557 			open = "closed";
2558 		}
2559 
2560 		if (zstr(rpid)) {
2561 			prpid = NULL;
2562 		}
2563 
2564 
2565 		if (zstr(status) && !zstr(prpid)) {
2566 			status = "Available";
2567 			prpid = NULL;
2568 		}
2569 
2570 		if (prpid) {
2571 			xml_rpid = switch_mprintf("  <rpid:activities>\r\n"
2572 									  "   <rpid:%s/>\n"
2573 									  "  </rpid:activities>\n", prpid);
2574 		}
2575 
2576 		ret = switch_mprintf("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?> \n"
2577 							 "<presence xmlns='urn:ietf:params:xml:ns:pidf' \n"
2578 							 "xmlns:dm='urn:ietf:params:xml:ns:pidf:data-model' \n"
2579 							 "xmlns:rpid='urn:ietf:params:xml:ns:pidf:rpid' \n"
2580 							 "xmlns:c='urn:ietf:params:xml:ns:pidf:cipid' entity='%s'>\n"
2581 							 " <tuple id='t6a5ed77e'>\n"
2582 							 "  <status>\r\n"
2583 							 "   <basic>%s</basic>\n"
2584 							 "  </status>\n"
2585 							 " </tuple>\n"
2586 							 " <dm:person id='p06360c4a'>\n"
2587 							 "%s"
2588 							 "  <dm:note>%s</dm:note>\n"
2589 							 " </dm:person>\n"
2590 							 "</presence>", id, open, switch_str_nil(xml_rpid), status);
2591 
2592 
2593 		switch_safe_free(xml_rpid);
2594 	}
2595 
2596 
2597 	return ret;
2598 }
2599 
sofia_presence_sub_callback(void * pArg,int argc,char ** argv,char ** columnNames)2600 static int sofia_presence_sub_callback(void *pArg, int argc, char **argv, char **columnNames)
2601 {
2602 	struct presence_helper *helper = (struct presence_helper *) pArg;
2603 	char *pl = NULL;
2604 	char *clean_id = NULL, *id = NULL;
2605 	char *proto = argv[0];
2606 	char *user = argv[1];
2607 	char *host = argv[2];
2608 	char *sub_to_user = argv[3];
2609 	char *event = argv[5];
2610 	char *contact = argv[6];
2611 	char *call_id = argv[7];
2612 	char *full_from = argv[8];
2613 	//char *full_via = argv[9];
2614 	char *expires = argv[10];
2615 	char *user_agent = argv[11];
2616 	char *profile_name = argv[13];
2617 	uint32_t in = 0;
2618 	char *status = argv[14];
2619 	char *rpid = argv[15];
2620 	char *sub_to_host = argv[16];
2621 	char *open_closed = NULL;
2622 	char *dialog_status = NULL;
2623 	char *dialog_rpid = NULL;
2624 	//char *default_dialog = "partial";
2625 	char *default_dialog = "full";
2626 	const char *ct = "no/idea";
2627 
2628 	char *to = NULL;
2629 	char *open;
2630 	char *prpid;
2631 
2632 	int is_dialog = 0;
2633 	sofia_profile_t *ext_profile = NULL, *profile = helper->profile;
2634 
2635 	char status_line[256] = "";
2636 	char *version = "0";
2637 	char *presence_id = NULL;
2638 	char *free_me = NULL;
2639 	int holding = 0;
2640 	char *orig_proto = NULL;
2641 	int skip_proto = 0;
2642 	char *full_to = NULL;
2643 	char *ip = NULL;
2644 	char *port = 0;
2645 	const char *call_state = NULL;
2646 	const char *astate = NULL;
2647 	const char *event_status = NULL;
2648 	const char *force_event_status = NULL;
2649 	char *contact_str, *contact_stripped;
2650 
2651 	if (mod_sofia_globals.debug_presence > 0) {
2652 		int i;
2653 		for(i = 0; i < argc; i++) {
2654 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "arg %d[%s] = [%s]\n", i, columnNames[i], argv[i]);
2655 		}
2656 		DUMP_EVENT(helper->event);
2657 	}
2658 
2659 	if (argc > 18) {
2660 		if (!zstr(argv[17])) {
2661 			status = argv[17];
2662 		}
2663 		if (!zstr(argv[18])) {
2664 			rpid = argv[18];
2665 		}
2666 		open_closed = argv[19];
2667 	}
2668 
2669 	if (argc > 20) {
2670 		dialog_status = argv[20];
2671 		dialog_rpid = argv[21];
2672 		version = argv[22];
2673 		presence_id = argv[23];
2674 		orig_proto = argv[24];
2675 		full_to = argv[25];
2676 		ip = argv[26];
2677 		port = argv[27];
2678 	}
2679 
2680 	if (!zstr(ip) && sofia_glue_check_nat(profile, ip)) {
2681 		char *ptr;
2682 		if ((ptr = sofia_glue_find_parameter(contact, "transport="))) {
2683 			sofia_transport_t transport = sofia_glue_str2transport( ptr + 10 );
2684 
2685 			switch (transport) {
2686 			case SOFIA_TRANSPORT_TCP:
2687 				contact_str = profile->tcp_public_contact;
2688 				break;
2689 			case SOFIA_TRANSPORT_TCP_TLS:
2690 				contact_str = sofia_test_pflag(profile, PFLAG_TLS) ?
2691 					profile->tls_public_contact : profile->tcp_public_contact;
2692 				break;
2693 			default:
2694 				contact_str = profile->public_url;
2695 				break;
2696 			}
2697 		} else {
2698 			contact_str = profile->public_url;
2699 		}
2700 	} else {
2701 		char *ptr;
2702 		if ((ptr = sofia_glue_find_parameter(contact, "transport="))) {
2703 			sofia_transport_t transport = sofia_glue_str2transport( ptr + 10 );
2704 
2705 			switch (transport) {
2706 			case SOFIA_TRANSPORT_TCP:
2707 				contact_str = profile->tcp_contact;
2708 				break;
2709 			case SOFIA_TRANSPORT_TCP_TLS:
2710 				contact_str = sofia_test_pflag(profile, PFLAG_TLS) ?
2711 					profile->tls_contact : profile->tcp_contact;
2712 				break;
2713 			default:
2714 				contact_str = profile->url;
2715 				break;
2716 			}
2717 		} else {
2718 			contact_str = profile->url;
2719 		}
2720 	}
2721 
2722 
2723 	if (!zstr(presence_id) && strchr(presence_id, '@')) {
2724 		char *p;
2725 
2726 		free_me = strdup(presence_id);
2727 
2728 		switch_assert(free_me);
2729 
2730 		if ((p = strchr(free_me, '@'))) {
2731 			*p = '\0';
2732 		}
2733 
2734 		user = free_me;
2735 	}
2736 
2737 
2738 	if (!zstr(orig_proto) && !strcmp(orig_proto, SOFIA_CHAT_PROTO)) {
2739 		skip_proto = 1;
2740 	}
2741 
2742 	in = helper->event && helper->event->event_id == SWITCH_EVENT_PRESENCE_IN;
2743 
2744 	if (zstr(rpid)) {
2745 		rpid = "unknown";
2746 	}
2747 
2748 	if (zstr(status)) {
2749 		if (!strcasecmp(rpid, "busy")) {
2750 			status = "Busy";
2751 		} else if (!strcasecmp(rpid, "unavailable")) {
2752 			status = "Idle";
2753 		} else if (!strcasecmp(rpid, "away")) {
2754 			status = "Idle";
2755 		} else {
2756 			status = "Available";
2757 		}
2758 	}
2759 
2760 	if (status && !strncasecmp(status, "hold", 4)) {
2761 		holding = 1;
2762 	}
2763 
2764 	if (profile_name && strcasecmp(profile_name, helper->profile->name)) {
2765 		if ((ext_profile = sofia_glue_find_profile(profile_name))) {
2766 			profile = ext_profile;
2767 		}
2768 	}
2769 
2770 
2771 	if (!strcasecmp(proto, SOFIA_CHAT_PROTO) || skip_proto) {
2772 		clean_id = switch_mprintf("sip:%s@%s", sub_to_user, sub_to_host);
2773 	} else {
2774 		clean_id = switch_mprintf("sip:%s+%s@%s", proto, sub_to_user, sub_to_host);
2775 	}
2776 
2777 
2778 
2779 	if (!rpid) {
2780 		rpid = "unknown";
2781 	}
2782 
2783 	//	if (!strcasecmp(proto, SOFIA_CHAT_PROTO) || skip_proto) {
2784 	//		clean_id = switch_mprintf("sip:%s@%s", sub_to_user, sub_to_host);
2785 	//} else {
2786 	//		clean_id = switch_mprintf("sip:%s+%s@%s", proto, sub_to_user, sub_to_host);
2787 	//}
2788 
2789 	if (mod_sofia_globals.debug_presence > 0) {
2790 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE,
2791 						  "SEND PRESENCE\nTo:      \t%s@%s\nFrom:    \t%s@%s\nCall-ID:  \t%s\nProfile:\t%s [%s]\n\n",
2792 						  user, host, sub_to_user, sub_to_host, call_id, profile_name, helper->profile->name);
2793 	}
2794 
2795 	if (!strcasecmp(sub_to_host, host) && !skip_proto) {
2796 		/* same host */
2797 		id = switch_mprintf("sip:%s+%s@%s", proto, sub_to_user, sub_to_host);
2798 	} else if (strcasecmp(proto, SOFIA_CHAT_PROTO) && !skip_proto) {
2799 		/*encapsulate */
2800 		id = switch_mprintf("sip:%s+%s+%s@%s", proto, sub_to_user, sub_to_host, host);
2801 	} else {
2802 		id = switch_mprintf("sip:%s@%s", sub_to_user, sub_to_host);
2803 	}
2804 
2805 	to = switch_mprintf("sip:%s@%s", user, host);
2806 
2807 	is_dialog = !strcmp(event, "dialog");
2808 
2809 	if (helper->hup && helper->calls_up > 0) {
2810 		call_state = "CS_EXECUTE";
2811 		astate = "active";
2812 		event_status = "Active";
2813 		force_event_status = NULL;
2814 	} else {
2815 		if (helper->event) {
2816 			call_state = switch_event_get_header(helper->event, "channel-state");
2817 			astate = switch_str_nil(switch_event_get_header(helper->event, "astate"));
2818 			event_status = switch_str_nil(switch_event_get_header(helper->event, "status"));
2819 			force_event_status = switch_str_nil(switch_event_get_header(helper->event, "force-status"));
2820 		}
2821 	}
2822 
2823 	if (helper->event) {
2824 		switch_stream_handle_t stream = { 0 };
2825 		const char *direction = switch_str_nil(switch_event_get_header(helper->event, "presence-call-direction"));
2826 		//const char *force_direction = switch_str_nil(switch_event_get_header(helper->event, "force-direction"));
2827 		const char *uuid = switch_str_nil(switch_event_get_header(helper->event, "unique-id"));
2828 		const char *resub = switch_str_nil(switch_event_get_header(helper->event, "resub"));
2829 		const char *answer_state = switch_str_nil(switch_event_get_header(helper->event, "answer-state"));
2830 		const char *dft_state;
2831 		const char *from_id = NULL, *from_name = NULL;
2832 		const char *to_user = switch_str_nil(switch_event_get_header(helper->event, "variable_sip_to_user"));
2833 		const char *from_user = switch_str_nil(switch_event_get_header(helper->event, "variable_sip_from_user"));
2834 		const char *disable_early = switch_str_nil(switch_event_get_header(helper->event, "variable_presence_disable_early"));
2835 		const char *answer_epoch = switch_str_nil(switch_event_get_header(helper->event, "variable_answer_epoch"));
2836 		int answered = 0;
2837 		char *clean_to_user = NULL;
2838 		char *clean_from_user = NULL;
2839 		int force_status = 0;
2840 		int term = 0;
2841 
2842 		if (answer_epoch) {
2843 			answered = atoi(answer_epoch);
2844 		}
2845 
2846 
2847 		//if (user_agent && switch_stristr("snom", user_agent) && uuid) {
2848 		//	default_dialog = "full" ;
2849 		//}
2850 
2851 		if (call_state && !strcasecmp(call_state, "cs_hangup")) {
2852 			astate = "hangup";
2853 			holding = 0;
2854 			term = 1;
2855 		} else {
2856 
2857 			if (event_status && !strncasecmp(event_status, "hold", 4)) {
2858 				holding = 1;
2859 			}
2860 
2861 			if (force_event_status && !event_status) {
2862 				event_status = force_event_status;
2863 			}
2864 
2865 			if (event_status && !strncasecmp(event_status, "hold", 4)) {
2866 				holding = 1;
2867 			}
2868 		}
2869 
2870 		if (!strcasecmp(direction, "inbound")) {
2871 			from_id = switch_str_nil(switch_event_get_header(helper->event, "Caller-Destination-Number"));
2872 
2873 		} else {
2874 			from_id = switch_str_nil(switch_event_get_header(helper->event, "Caller-Caller-ID-Number"));
2875 			from_name = switch_event_get_header(helper->event, "Caller-Caller-ID-Name");
2876 
2877 			if (zstr(from_id)) {
2878 				from_id = switch_str_nil(switch_event_get_header(helper->event, "Other-Leg-Caller-ID-Number"));
2879 			}
2880 
2881 			if (zstr(from_name)) {
2882 				from_name = switch_event_get_header(helper->event, "Other-Leg-Caller-ID-Name");
2883 			}
2884 
2885 		}
2886 
2887 #if 0
2888 		char *buf;
2889 		switch_event_serialize(helper->event, &buf, SWITCH_FALSE);
2890 		switch_assert(buf);
2891 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "CHANNEL_DATA:\n%s\n", buf);
2892 		free(buf);
2893 #endif
2894 
2895 		if (is_dialog) {
2896 			// Usually we report the dialogs FROM the probed user.  The exception is when the monitored endpoint is internal,
2897 			// and its presence_id is set in the dialplan.  Reverse the direction if this is not a registered entity.
2898 			const char *caller = switch_str_nil(switch_event_get_header(helper->event, "caller-username"));
2899 			SWITCH_STANDARD_STREAM(stream);
2900 			if (!strcmp(direction, "inbound") && strcmp(sub_to_user,  caller)) {
2901 				// If inbound and the entity is not the caller (i.e. internal to FS), then the direction is reversed
2902 				// because it is not going through the B2BUA
2903 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "sofia_presence_sub_callback: endpt is internal\n");
2904 				direction = !strcasecmp(direction, "outbound") ? "inbound" : "outbound";
2905 			}
2906 
2907 		}
2908 
2909 		if (!strcasecmp(direction, "outbound")) {
2910 			direction = "recipient";
2911 			dft_state = "early";
2912 		} else {
2913 			direction = "initiator";
2914 			dft_state = "confirmed";
2915 		}
2916 
2917 		if (is_dialog) {
2918 			if (zstr(version)) {
2919 				version = "0";
2920 			}
2921 
2922 			stream.write_function(&stream,
2923 								  "<?xml version=\"1.0\"?>\n"
2924 								  "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" "
2925 								  "version=\"%s\" state=\"%s\" entity=\"%s\">\n", version, default_dialog, clean_id);
2926 
2927 		}
2928 
2929 		if (!zstr(uuid)) {
2930 			if (!zstr(answer_state)) {
2931 				astate = answer_state;
2932 			}
2933 
2934 			if (zstr(astate)) {
2935 				if (is_dialog) {
2936 					astate = dft_state;
2937 				} else {
2938 					astate = "terminated";
2939 				}
2940 			}
2941 
2942 			if (!strcasecmp(astate, "answered")) {
2943 				astate = "confirmed";
2944 			}
2945 
2946 
2947 			if (is_dialog) {
2948 
2949 				if (!strcasecmp(astate, "ringing")) {
2950 					if (!strcasecmp(direction, "recipient")) {
2951 						astate = "early";
2952 					} else {
2953 						astate = "confirmed";
2954 					}
2955 				}
2956 
2957 				if (holding) {
2958 					if (profile->pres_held_type == PRES_HELD_CONFIRMED) {
2959 						astate = "confirmed";
2960 					} else if (profile->pres_held_type == PRES_HELD_TERMINATED) {
2961 						astate = "terminated";
2962 					} else {
2963 						astate = "early";
2964 					}
2965 				}
2966 
2967 
2968 				if (!strcasecmp(astate, "hangup")) {
2969 					astate = "terminated";
2970 				}
2971 
2972 				stream.write_function(&stream, "<dialog id=\"%s\" direction=\"%s\">\n", uuid, direction);
2973 				stream.write_function(&stream, "<state>%s</state>\n", astate);
2974 			} else {
2975 				if (!strcasecmp(astate, "ringing")) {
2976 					astate = "early";
2977 				}
2978 			}
2979 
2980 
2981 			if ((sofia_test_pflag(profile, PFLAG_PRESENCE_DISABLE_EARLY) || switch_true(disable_early)) &&
2982 				((!zstr(astate) && (!strcasecmp(astate, "early") || !strcasecmp(astate, "ringing") || (!strcasecmp(astate, "terminated") && !answered))))) {
2983 				switch_safe_free(stream.data);
2984 				goto end;
2985 			}
2986 
2987 			if (!strcasecmp(astate, "early") || !strcasecmp(astate, "confirmed")) {
2988 
2989 				clean_to_user = switch_mprintf("%s", sub_to_user ? sub_to_user : to_user);
2990 				clean_from_user = switch_mprintf("%s", from_id ? from_id : from_user);
2991 
2992 				if (is_dialog) {
2993 					if (!zstr(clean_to_user) && !zstr(clean_from_user)) {
2994 						stream.write_function(&stream, "<local>\n<identity display=\"%s\">sip:%s@%s</identity>\n", clean_to_user, clean_to_user, host);
2995 						stream.write_function(&stream, "<target uri=\"sip:%s@%s\">\n", clean_to_user, host);
2996 						stream.write_function(&stream, "<param pname=\"+sip.rendering\" pvalue=\"%s\"/>\n", holding ? "no" : "yes");
2997 
2998 						stream.write_function(&stream, "</target>\n</local>\n");
2999 						if (switch_true(switch_event_get_header(helper->event, "Presence-Privacy"))) {
3000 							stream.write_function(&stream, "<remote>\n<identity display=\"Anonymous\">sip:anonymous@anonymous.invalid</identity>\n");
3001 						} else {
3002 							stream.write_function(&stream, "<remote>\n<identity display=\"%s\">sip:%s@%s</identity>\n",
3003 												  from_name ? from_name : clean_from_user, clean_from_user,
3004 												  host);
3005 						}
3006 						stream.write_function(&stream, "<target uri=\"sip:**%s@%s\"/>\n", clean_to_user, host);
3007 						stream.write_function(&stream, "</remote>\n");
3008 
3009 					} else if (!strcasecmp(proto, "queue")) {
3010 						stream.write_function(&stream, "<local>\n<identity display=\"queue\">sip:%s@%s;proto=queue</identity>\n",
3011 											  !zstr(clean_to_user) ? clean_to_user : "unknown", host);
3012 						stream.write_function(&stream, "<target uri=\"sip:%s@%s;proto=fifo\">\n", !zstr(clean_to_user) ? clean_to_user : "unknown", host);
3013 						stream.write_function(&stream, "<param pname=\"+sip.rendering\" pvalue=\"no\"/>\n</target>\n</local>\n");
3014 						stream.write_function(&stream, "<remote>\n<identity display=\"queue\">sip:%s@%s</identity>\n", uuid, host);
3015 						if (skip_proto) {
3016 							stream.write_function(&stream, "<target uri=\"sip:%s@%s\"/>\n", uuid, host);
3017 						} else {
3018 							stream.write_function(&stream, "<target uri=\"sip:queue+%s@%s\"/>\n", uuid, host);
3019 						}
3020 
3021 						stream.write_function(&stream, "</remote>\n");
3022 					} else if (!strcasecmp(proto, "park")) {
3023 						stream.write_function(&stream, "<local>\n<identity display=\"park\">sip:%s@%s;proto=park</identity>\n",
3024 											  !zstr(clean_to_user) ? clean_to_user : "unknown", host);
3025 						stream.write_function(&stream, "<target uri=\"sip:%s@%s;proto=park\">\n", !zstr(clean_to_user) ? clean_to_user : "unknown", host);
3026 						stream.write_function(&stream, "<param pname=\"+sip.rendering\" pvalue=\"no\"/>\n</target>\n</local>\n");
3027 						stream.write_function(&stream, "<remote>\n<identity display=\"park\">sip:%s</identity>\n", uuid);
3028 						if (skip_proto) {
3029 							stream.write_function(&stream, "<target uri=\"sip:%s@%s\"/>\n", uuid, host);
3030 						} else {
3031 							stream.write_function(&stream, "<target uri=\"sip:park+%s@%s\"/>\n", uuid, host);
3032 						}
3033 						stream.write_function(&stream, "</remote>\n");
3034 					} else if (!strcasecmp(proto, "pickup")) {
3035 						stream.write_function(&stream, "<local>\n<identity display=\"pickup\">sip:%s@%s;proto=pickup</identity>\n",
3036 											  !zstr(clean_to_user) ? clean_to_user : "unknown", host);
3037 						stream.write_function(&stream, "<target uri=\"sip:%s@%s;proto=pickup\">\n", !zstr(clean_to_user) ? clean_to_user : "unknown", host);
3038 						stream.write_function(&stream, "<param pname=\"+sip.rendering\" pvalue=\"no\"/>\n</target>\n</local>\n");
3039 						stream.write_function(&stream, "<remote>\n<identity display=\"pickup\">sip:%s@%s</identity>\n", uuid, host);
3040 						if (skip_proto) {
3041 							stream.write_function(&stream, "<target uri=\"sip:%s@%s\"/>\n", uuid, host);
3042 						} else {
3043 							stream.write_function(&stream, "<target uri=\"sip:pickup+%s@%s\"/>\n", uuid, host);
3044 						}
3045 						stream.write_function(&stream, "</remote>\n");
3046 					} else if (!strcasecmp(proto, "conf")) {
3047 						stream.write_function(&stream, "<local>\n<identity display=\"conference\">sip:%s@%s;proto=conference</identity>\n",
3048 											  !zstr(clean_to_user) ? clean_to_user : "unknown", host);
3049 						stream.write_function(&stream, "<target uri=\"sip:%s@%s;proto=conference\">\n",
3050 											  !zstr(clean_to_user) ? clean_to_user : "unknown", host);
3051 						stream.write_function(&stream, "<param pname=\"+sip.rendering\" pvalue=\"yes\"/>\n</target>\n</local>\n");
3052 						stream.write_function(&stream, "<remote>\n<identity display=\"conference\">sip:%s@%s</identity>\n", uuid, host);
3053 						if (skip_proto) {
3054 							stream.write_function(&stream, "<target uri=\"sip:%s@%s\"/>\n", uuid, host);
3055 						} else {
3056 							stream.write_function(&stream, "<target uri=\"sip:conf+%s@%s\"/>\n", uuid, host);
3057 						}
3058 						stream.write_function(&stream, "</remote>\n");
3059 					}
3060 				}
3061 
3062 				switch_safe_free(clean_to_user);
3063 				switch_safe_free(clean_from_user);
3064 			}
3065 			if (is_dialog) {
3066 				stream.write_function(&stream, "</dialog>\n");
3067 			}
3068 		}
3069 
3070 		if (is_dialog) {
3071 			stream.write_function(&stream, "</dialog-info>\n");
3072 			pl = stream.data;
3073 			ct = "application/dialog-info+xml";
3074 		}
3075 
3076 		if (!zstr(astate) && !zstr(uuid) &&
3077 			helper && helper->stream.data && strcmp(helper->last_uuid, uuid) && strcasecmp(astate, "terminated") && strchr(uuid, '-')) {
3078 			helper->stream.write_function(&helper->stream, "update sip_dialogs set state='%q' where hostname='%q' and profile_name='%q' and uuid='%q';",
3079 										  astate, mod_sofia_globals.hostname, profile->name, uuid);
3080 			switch_copy_string(helper->last_uuid, uuid, sizeof(helper->last_uuid));
3081 		}
3082 
3083 		if (zstr(astate)) astate = "";
3084 
3085 		if (!is_dialog) {
3086 			switch_set_string(status_line, status);
3087 
3088 			if (in) {
3089 				open = "open";
3090 
3091 				if (switch_false(resub)) {
3092 					const char *direction = switch_event_get_header(helper->event, "Caller-Direction");
3093 					const char *op, *what = "Ring";
3094 
3095 					if (direction && !strcasecmp(direction, "outbound")) {
3096 						op = switch_event_get_header(helper->event, "Other-Leg-Caller-ID-Number");
3097 					} else {
3098 						op = switch_event_get_header(helper->event, "Caller-Callee-ID-Number");
3099 					}
3100 
3101 					if (zstr(op)) {
3102 						op = switch_event_get_header(helper->event, "Caller-Destination-Number");
3103 					}
3104 
3105 					if (direction) {
3106 						what = strcasecmp(direction, "outbound") ? "Call" : "Ring";
3107 					}
3108 
3109 					if (!strcmp(astate, "early")) {
3110 						if (!zstr(op)) {
3111 							//switch_snprintf(status_line, sizeof(status_line), "%sing", what);
3112 							//} else {
3113 							if (sofia_test_pflag(profile, PFLAG_PRESENCE_PRIVACY)) {
3114 								switch_snprintf(status_line, sizeof(status_line), "%s", what);
3115 							} else {
3116 								switch_snprintf(status_line, sizeof(status_line), "%s %s", what, op);
3117 							}
3118 						}
3119 
3120 						rpid = "on-the-phone";
3121 						force_status = 1;
3122 
3123 					} else if (!strcmp(astate, "confirmed")) {
3124 						if (!zstr(op)) {
3125 							if (sofia_test_pflag(profile, PFLAG_PRESENCE_PRIVACY)) {
3126 								switch_snprintf(status_line, sizeof(status_line), "On The Phone");
3127 							} else {
3128 								switch_snprintf(status_line, sizeof(status_line), "Talk %s", op);
3129 							}
3130 						} else {
3131 							switch_snprintf(status_line, sizeof(status_line), "On The Phone");
3132 						}
3133 
3134 						rpid = "on-the-phone";
3135 						force_status = 1;
3136 					} else if (!strcmp(astate, "terminated") || !strcmp(astate, "hangup")) {
3137 						//rpid = "online";
3138 						//dialog_rpid = "";
3139 						//force_event_status = "Available";
3140 						term = 1;
3141 					}
3142 
3143 					if (!term && !strcmp(status, "hold")) {
3144 						rpid = "on-the-phone";
3145 						if (!zstr(op)) {
3146 							if (sofia_test_pflag(profile, PFLAG_PRESENCE_PRIVACY)) {
3147 								switch_snprintf(status_line, sizeof(status_line), "Hold");
3148 							} else {
3149 								switch_snprintf(status_line, sizeof(status_line), "Hold %s", op);
3150 							}
3151 							force_status = 1;
3152 						}
3153 					}
3154 				}
3155 			} else {
3156 				open = "closed";
3157 			}
3158 
3159 			if (!zstr(open_closed)) {
3160 				open = open_closed;
3161 			}
3162 
3163 			prpid = translate_rpid(rpid);
3164 
3165 			if (!zstr(dialog_status) && !force_status) {
3166 				status = dialog_status;
3167 				switch_set_string(status_line, status);
3168 			}
3169 
3170 			if (!zstr(force_event_status)) {
3171 				switch_set_string(status_line, force_event_status);
3172 			}
3173 
3174 			if (!zstr(dialog_rpid)) {
3175 				prpid = rpid = dialog_rpid;
3176 			}
3177 
3178 			contact_stripped = sofia_glue_strip_uri(contact_str);
3179 			pl = gen_pidf(user_agent, clean_id, contact_stripped, open, rpid, prpid, status_line, &ct);
3180 			free(contact_stripped);
3181 		}
3182 
3183 	} else {
3184 		if (in) {
3185 			open = "open";
3186 		} else {
3187 			open = "closed";
3188 		}
3189 
3190 		if (!zstr(open_closed)) {
3191 			open = open_closed;
3192 		}
3193 
3194 		prpid = translate_rpid(rpid);
3195 
3196 		if (!zstr(dialog_status)) {
3197 			status = dialog_status;
3198 		}
3199 
3200 		if (!zstr(dialog_rpid)) {
3201 			prpid = rpid = dialog_rpid;
3202 		}
3203 
3204 		contact_stripped = sofia_glue_strip_uri(contact_str);
3205 		pl = gen_pidf(user_agent, clean_id, contact_stripped, open, rpid, prpid, status, &ct);
3206 		free(contact_stripped);
3207 	}
3208 
3209 
3210 	if (!is_dialog && helper->event && !switch_stristr("registered", status_line)){
3211 		const char *uuid = switch_event_get_header_nil(helper->event, "unique-id");
3212 		const char *register_source = switch_event_get_header_nil(helper->event, "register-source");
3213 
3214 		if (!zstr(uuid) && strchr(uuid, '-') && !zstr(status_line) && !zstr(rpid) && (zstr(register_source) || strcasecmp(register_source, "register"))) {
3215 			char *sql = switch_mprintf("update sip_dialogs set rpid='%q',status='%q' where hostname='%q' and profile_name='%q' and uuid='%q'",
3216 									   rpid, status_line,
3217 									   mod_sofia_globals.hostname, profile->name, uuid);
3218 			sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
3219 		}
3220 	}
3221 
3222 	send_presence_notify(profile, full_to, full_from, contact, expires, call_id, event, ip, port, ct, pl, NULL);
3223 
3224 
3225  end:
3226 
3227 	switch_safe_free(free_me);
3228 
3229 	if (ext_profile) {
3230 		sofia_glue_release_profile(ext_profile);
3231 	}
3232 
3233 	switch_safe_free(id);
3234 	switch_safe_free(clean_id);
3235 	switch_safe_free(pl);
3236 	switch_safe_free(to);
3237 
3238 	return 0;
3239 }
3240 
sofia_presence_mwi_callback(void * pArg,int argc,char ** argv,char ** columnNames)3241 static int sofia_presence_mwi_callback(void *pArg, int argc, char **argv, char **columnNames)
3242 {
3243 	//char *sub_to_user = argv[3];
3244 	//char *sub_to_host = argv[4];
3245 	char *event = argv[5];
3246 	char *contact = argv[6];
3247 	char *call_id = argv[7];
3248 	char *full_from = argv[8];
3249 	char *expires = argv[10];
3250 	char *profile_name = argv[13];
3251 	char *body = argv[15];
3252 	char *full_to = argv[16];
3253 	char *remote_ip = argv[17];
3254 	char *remote_port = argv[18];
3255 
3256 	struct mwi_helper *h = (struct mwi_helper *) pArg;
3257 	sofia_profile_t *ext_profile = NULL, *profile = h->profile;
3258 
3259 
3260 	if (mod_sofia_globals.debug_presence > 0) {
3261 		int i;
3262 		for(i = 0; i < argc; i++) {
3263 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "arg %d[%s] = [%s]\n", i, columnNames[i], argv[i]);
3264 		}
3265 	}
3266 
3267 	if (profile_name && strcasecmp(profile_name, h->profile->name)) {
3268 		if ((ext_profile = sofia_glue_find_profile(profile_name))) {
3269 			profile = ext_profile;
3270 		}
3271 	}
3272 
3273 	send_presence_notify(profile,
3274 						 full_to,
3275 						 full_from,
3276 						 contact,
3277 						 expires,
3278 						 call_id,
3279 						 event,
3280 						 remote_ip,
3281 						 remote_port,
3282 						 "application/simple-message-summary",
3283 						 body,
3284 						 NULL
3285 						 );
3286 
3287 
3288 	h->total++;
3289 
3290 	if (ext_profile) {
3291 		sofia_glue_release_profile(ext_profile);
3292 	}
3293 
3294 	return 0;
3295 }
3296 
sofia_presence_mwi_callback2(void * pArg,int argc,char ** argv,char ** columnNames)3297 static int sofia_presence_mwi_callback2(void *pArg, int argc, char **argv, char **columnNames)
3298 {
3299 	const char *user = argv[0];
3300 	const char *host = argv[1];
3301 	const char *event = "message-summary";
3302 	const char *contenttype = "application/simple-message-summary";
3303 	const char *body = argv[5];
3304 	const char *o_contact = argv[2];
3305 	const char *network_ip = argv[4];
3306 	const char *call_id = argv[6];
3307 
3308 	char *profile_name = argv[3];
3309 	struct mwi_helper *h = (struct mwi_helper *) pArg;
3310 	sofia_profile_t *ext_profile = NULL, *profile = h->profile;
3311 
3312 	if (profile_name && strcasecmp(profile_name, h->profile->name)) {
3313 		if ((ext_profile = sofia_glue_find_profile(profile_name))) {
3314 			profile = ext_profile;
3315 		}
3316 	}
3317 
3318 	if (!sofia_test_pflag(profile, PFLAG_MWI_USE_REG_CALLID)) {
3319 		call_id = NULL;
3320 	}
3321 
3322 	sofia_glue_send_notify(profile, user, host, event, contenttype, body, o_contact, network_ip, call_id);
3323 
3324 	if (ext_profile) {
3325 		sofia_glue_release_profile(ext_profile);
3326 	}
3327 
3328 	return 0;
3329 }
3330 
broadsoft_sla_notify_callback(void * pArg,int argc,char ** argv,char ** columnNames)3331 static int broadsoft_sla_notify_callback(void *pArg, int argc, char **argv, char **columnNames)
3332 {
3333 	struct state_helper *sh = (struct state_helper *) pArg;
3334 	char key[256] = "";
3335 	char *data = NULL, *tmp;
3336 	char *call_id = argv[0];
3337 	//char *expires = argv[1];
3338 	char *user = argv[2];
3339 	char *host = argv[3];
3340 	char *event = argv[4];
3341 	int i;
3342 
3343 
3344 	if (mod_sofia_globals.debug_sla > 1) {
3345 		for (i = 0; i < argc; i++) {
3346 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SLA3: %d [%s]=[%s]\n", i, columnNames[i], argv[i]);
3347 		}
3348 	}
3349 
3350 	switch_snprintf(key, sizeof(key), "%s%s", user, host);
3351 	data = switch_core_hash_find(sh->hash, key);
3352 
3353 	if (data) {
3354 		tmp = switch_core_sprintf(sh->pool, "%s,<sip:%s>;appearance-index=*;appearance-state=idle", data, host);
3355 	} else {
3356 		tmp = switch_core_sprintf(sh->pool, "<sip:%s>;appearance-index=*;appearance-state=idle", host);
3357 	}
3358 
3359 
3360 	if (!strcasecmp(event, "line-seize")) {
3361 		char *hack;
3362 
3363 		if ((hack = (char *) switch_stristr("=seized", tmp))) {
3364 			switch_snprintf(hack, 7, "=idle  ");
3365 		}
3366 	}
3367 
3368 	if (mod_sofia_globals.debug_sla > 1) {
3369 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DB PRES NOTIFY: [%s]\n[%s]\n[%s]\n[%s]\n[%s]\n[%s]\n[%s]\n[%s]\n[%s]\n",
3370 						  argv[5], argv[6], argv[7], argv[8], call_id, event, argv[9], argv[10], tmp);
3371 
3372 	}
3373 
3374 	send_presence_notify(sh->profile, argv[5], argv[6], argv[7], argv[8], call_id, event, argv[9], argv[10], NULL, NULL, tmp);
3375 
3376 	sh->total++;
3377 
3378 	return 0;
3379 }
3380 
broadsoft_sla_gather_state_callback(void * pArg,int argc,char ** argv,char ** columnNames)3381 static int broadsoft_sla_gather_state_callback(void *pArg, int argc, char **argv, char **columnNames)
3382 {
3383 	struct state_helper *sh = (struct state_helper *) pArg;
3384 	char key[256] = "";
3385 	switch_core_session_t *session;
3386 	const char *callee_name = NULL, *callee_number = NULL;
3387 	char *data = NULL, *tmp;
3388 	char *user = argv[0];
3389 	char *host = argv[1];
3390 	char *info = argv[2];
3391 	char *state = argv[3];
3392 	char *uuid = argv[4];
3393 	int i;
3394 
3395 	if (mod_sofia_globals.debug_sla > 1) {
3396 		for (i = 0; i < argc; i++) {
3397 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SLA2: %d [%s]=[%s]\n", i, columnNames[i], argv[i]);
3398 		}
3399 	}
3400 
3401 	if (zstr(info)) {
3402 		return 0;
3403 	}
3404 
3405 	if (zstr(state)) {
3406 		state = "idle";
3407 	}
3408 
3409 	switch_snprintf(key, sizeof(key), "%s%s", user, host);
3410 
3411 	data = switch_core_hash_find(sh->hash, key);
3412 
3413 	if (strcasecmp(state, "idle") && uuid && (session = switch_core_session_locate(uuid))) {
3414 		switch_channel_t *channel = switch_core_session_get_channel(session);
3415 
3416 		if (switch_channel_test_flag(channel, CF_ORIGINATOR) || switch_channel_test_flag(channel, CF_BRIDGE_ORIGINATOR) ||
3417 			switch_channel_inbound_display(channel) || switch_channel_test_flag(channel, CF_SLA_BARGING)) {
3418 			if (!switch_channel_test_flag(channel, CF_ANSWERED)) state = "progressing";
3419 			callee_name = switch_channel_get_variable(channel, "callee_id_name");
3420 			callee_number = switch_channel_get_variable(channel, "callee_id_number");
3421 
3422 			if (zstr(callee_number)) {
3423 				callee_number = switch_channel_get_variable(channel, "destination_number");
3424 			}
3425 
3426 		} else {
3427 			callee_name = switch_channel_get_variable(channel, "caller_id_name");
3428 			callee_number = switch_channel_get_variable(channel, "caller_id_number");
3429 		}
3430 
3431 		if (zstr(callee_name) && !zstr(callee_number)) {
3432 			callee_name = callee_number;
3433 		}
3434 
3435 		if (!zstr(callee_number)) {
3436 			callee_number = switch_sanitize_number(switch_core_session_strdup(session, callee_number));
3437 		}
3438 
3439 		if (!zstr(callee_name)) {
3440 			char *tmp = switch_core_session_strdup(session, callee_name);
3441 			switch_url_decode(tmp);
3442 			callee_name = switch_sanitize_number(tmp);
3443 		}
3444 
3445 
3446 		//if (switch_channel_get_state(channel) != CS_EXECUTE) {
3447 			//callee_number = NULL;
3448 		//}
3449 
3450 		switch_core_session_rwunlock(session);
3451 	}
3452 
3453 	if (data && strstr(data, info)) {
3454 		return 0;
3455 	}
3456 
3457 
3458 	if (!zstr(callee_number)) {
3459 		if (zstr(callee_name)) {
3460 			callee_name = "unknown";
3461 		}
3462 
3463 		if (data) {
3464 			tmp = switch_core_sprintf(sh->pool,
3465 									  "%s,<sip:%s>;%s;appearance-state=%s;appearance-uri=\"\\\"%s\\\" <sip:%s@%s>\"",
3466 									  data, host, info, state, callee_name, callee_number, host);
3467 		} else {
3468 			tmp = switch_core_sprintf(sh->pool,
3469 									  "<sip:%s>;%s;appearance-state=%s;appearance-uri=\"\\\"%s\\\" <sip:%s@%s>\"",
3470 									  host, info, state, callee_name, callee_number, host);
3471 		}
3472 	} else {
3473 		if (data) {
3474 			tmp = switch_core_sprintf(sh->pool, "%s,<sip:%s>;%s;appearance-state=%s", data, host, info, state);
3475 		} else {
3476 			tmp = switch_core_sprintf(sh->pool, "<sip:%s>;%s;appearance-state=%s", host, info, state);
3477 		}
3478 	}
3479 
3480 	switch_core_hash_insert(sh->hash, key, tmp);
3481 
3482 	return 0;
3483 }
3484 
sync_sla(sofia_profile_t * profile,const char * to_user,const char * to_host,switch_bool_t clear,switch_bool_t unseize,const char * call_id)3485 static int sync_sla(sofia_profile_t *profile, const char *to_user, const char *to_host, switch_bool_t clear, switch_bool_t unseize, const char *call_id)
3486 {
3487 	struct state_helper *sh;
3488 	switch_memory_pool_t *pool;
3489 	char *sql;
3490 	int total = 0;
3491 
3492 
3493 	if (clear) {
3494 		struct pres_sql_cb cb = {profile, 0};
3495 
3496 
3497 		if (call_id) {
3498 
3499 			sql = switch_mprintf("update sip_subscriptions set version=version+1,expires=%ld where "
3500 								 "call_id='%q' "
3501 								 "and event='line-seize'", (long) switch_epoch_time_now(NULL),
3502 								 call_id);
3503 
3504 			sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
3505 
3506 			if (mod_sofia_globals.debug_sla > 1) {
3507 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "CLEAR SQL %s\n", sql);
3508 			}
3509 			switch_safe_free(sql);
3510 
3511 			sql = switch_mprintf("select full_to, full_from, contact, -1, call_id, event, network_ip, network_port, "
3512 								 "NULL as ct, NULL as pt "
3513 								 " from sip_subscriptions where call_id='%q' "
3514 
3515 								 "and event='line-seize'", call_id);
3516 
3517 			sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_presence_send_sql, &cb);
3518 			if (mod_sofia_globals.debug_sla > 1) {
3519 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "CLEAR SQL %s\n", sql);
3520 			}
3521 			switch_safe_free(sql);
3522 		} else {
3523 
3524 			sql = switch_mprintf("update sip_subscriptions set version=version+1,expires=%ld where "
3525 								 "hostname='%q' and profile_name='%q' "
3526 								 "and sub_to_user='%q' and sub_to_host='%q' "
3527 
3528 								 "and event='line-seize'", (long) switch_epoch_time_now(NULL),
3529 								 mod_sofia_globals.hostname, profile->name, to_user, to_host
3530 								 );
3531 
3532 			if (mod_sofia_globals.debug_sla > 1) {
3533 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "CLEAR SQL %s\n", sql);
3534 			}
3535 
3536 			sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
3537 
3538 
3539 			sql = switch_mprintf("select full_to, full_from, contact, -1, call_id, event, network_ip, network_port, "
3540 								 "NULL as ct, NULL as pt "
3541 								 " from sip_subscriptions where "
3542 								 "hostname='%q' and profile_name='%q' "
3543 								 "and sub_to_user='%q' and sub_to_host='%q' "
3544 								 "and event='line-seized'",
3545 								 mod_sofia_globals.hostname, profile->name, to_user, to_host
3546 								 );
3547 
3548 			sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_presence_send_sql, &cb);
3549 
3550 			if (mod_sofia_globals.debug_sla > 1) {
3551 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "CLEAR SQL %s\n", sql);
3552 			}
3553 
3554 			switch_safe_free(sql);
3555 		}
3556 
3557 
3558 		sql = switch_mprintf("delete from sip_dialogs where hostname='%q' and profile_name='%q' and "
3559 							 "((sip_from_user='%q' and sip_from_host='%q') or presence_id='%q@%q') "
3560 							 "and call_info_state='seized'", mod_sofia_globals.hostname, profile->name, to_user, to_host, to_user, to_host);
3561 
3562 
3563 		if (mod_sofia_globals.debug_sla > 1) {
3564 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "CLEAR SQL %s\n", sql);
3565 		}
3566 		sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
3567 		switch_safe_free(sql);
3568 	}
3569 
3570 
3571 	switch_core_new_memory_pool(&pool);
3572 	sh = switch_core_alloc(pool, sizeof(*sh));
3573 	sh->pool = pool;
3574 	switch_core_hash_init(&sh->hash);
3575 
3576 	sql = switch_mprintf("select sip_from_user,sip_from_host,call_info,call_info_state,uuid from sip_dialogs "
3577 						 "where call_info_state is not null and call_info_state != '' and call_info_state != 'idle' and hostname='%q' and profile_name='%q' "
3578 						 "and ((sip_from_user='%q' and sip_from_host='%q') or presence_id='%q@%q') "
3579 						 "and profile_name='%q'",
3580 						 mod_sofia_globals.hostname, profile->name, to_user, to_host, to_user, to_host, profile->name);
3581 
3582 
3583 	if (mod_sofia_globals.debug_sla > 1) {
3584 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PRES SQL %s\n", sql);
3585 	}
3586 	sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, broadsoft_sla_gather_state_callback, sh);
3587 	switch_safe_free(sql);
3588 
3589 
3590 	if (!zstr(call_id)) {
3591 
3592 		if (unseize) {
3593 			sql = switch_mprintf("select call_id,expires,sub_to_user,sub_to_host,event,full_to,full_from,contact,expires,network_ip,network_port "
3594 								 "from sip_subscriptions where call_id='%q' and hostname='%q' and profile_name='%q' "
3595 								 "and (event='call-info' or event='line-seize')",
3596 								 call_id, mod_sofia_globals.hostname, profile->name);
3597 
3598 		} else {
3599 			sql = switch_mprintf("select call_id,expires,sub_to_user,sub_to_host,event,full_to,full_from,contact,expires,network_ip,network_port "
3600 								 "from sip_subscriptions where call_id='%q' and hostname='%q' and profile_name='%q' and event='call-info'",
3601 								 call_id, mod_sofia_globals.hostname, profile->name);
3602 		}
3603 
3604 	} else {
3605 
3606 		if (unseize) {
3607 			sql = switch_mprintf("select call_id,expires,sub_to_user,sub_to_host,event,full_to,full_from,contact,expires,network_ip,network_port "
3608 								 "from sip_subscriptions "
3609 								 "where hostname='%q' and profile_name='%q' "
3610 								 "and sub_to_user='%q' and sub_to_host='%q' "
3611 								 "and (event='call-info' or event='line-seize') and (profile_name='%q' or presence_hosts like '%%%q%%')",
3612 								 mod_sofia_globals.hostname, profile->name, to_user, to_host, profile->name, to_host);
3613 		} else {
3614 			sql = switch_mprintf("select call_id,expires,sub_to_user,sub_to_host,event,full_to,full_from,contact,expires,network_ip,network_port "
3615 								 "from sip_subscriptions "
3616 								 "where hostname='%q' and profile_name='%q' "
3617 								 "and sub_to_user='%q' and sub_to_host='%q' " "and (event='call-info') and "
3618 								 "(profile_name='%q' or presence_hosts like '%%%q%%')",
3619 								 mod_sofia_globals.hostname, profile->name, to_user, to_host, profile->name, to_host);
3620 		}
3621 	}
3622 
3623 	if (mod_sofia_globals.debug_sla > 1) {
3624 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PRES SQL %s\n", sql);
3625 	}
3626 
3627 	sh->profile = profile;
3628 	sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, broadsoft_sla_notify_callback, sh);
3629 	switch_safe_free(sql);
3630 	total = sh->total;
3631 	switch_core_hash_destroy(&sh->hash);
3632 
3633 	sh = NULL;
3634 	switch_core_destroy_memory_pool(&pool);
3635 
3636 
3637 
3638 
3639 
3640 	return total;
3641 
3642 }
3643 
sofia_presence_handle_sip_i_subscribe(int status,char const * phrase,nua_t * nua,sofia_profile_t * profile,nua_handle_t * nh,sofia_private_t * sofia_private,sip_t const * sip,sofia_dispatch_event_t * de,tagi_t tags[])3644 void sofia_presence_handle_sip_i_subscribe(int status,
3645 										   char const *phrase,
3646 										   nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
3647 										   sofia_dispatch_event_t *de,
3648 										   tagi_t tags[])
3649 {
3650 
3651 	long exp_delta = 0;
3652 	char exp_delta_str[30] = "";
3653 	uint32_t sub_max_deviation_var = 0;
3654 	sip_to_t const *to;
3655 	const char *from_user = NULL, *from_host = NULL;
3656 	const char *to_user = NULL, *to_host = NULL;
3657 	char *my_to_user = NULL;
3658 	char *sql, *event = NULL;
3659 	char *proto = "sip";
3660 	char *orig_proto = "";
3661 	char *alt_proto = NULL;
3662 	char *d_user = NULL;
3663 	char *contact_str = "";
3664 	const char *call_id = NULL;
3665 	char *to_str = NULL;
3666 	char *full_from = NULL;
3667 	char *full_to = NULL;
3668 	char *full_via = NULL;
3669 	char *full_agent = NULL;
3670 	char *sstr;
3671 	switch_event_t *sevent;
3672 	int sub_state = nua_substate_pending;
3673 	int sent_reply = 0;
3674 	sip_contact_t const *contact;
3675 	const char *ipv6;
3676 	const char *contact_user = NULL;
3677 	const char *contact_host = NULL;
3678 	const char *contact_port = NULL;
3679 	sofia_nat_parse_t np = { { 0 } };
3680 	int found_proto = 0;
3681 	const char *use_to_tag;
3682 	char to_tag[13] = "";
3683 	char buf[1025] = "";
3684 	char *orig_to_user = NULL;
3685 	char *p;
3686 	uint32_t callsequence;
3687     sip_cseq_t * cseq;
3688 
3689 	if (!sip) {
3690 		return;
3691 	}
3692 
3693 	to = sip->sip_to;
3694 	contact = sip->sip_contact;
3695 
3696 	np.fs_path = 1;
3697 	if (!(contact_str = sofia_glue_gen_contact_str(profile, sip, nh, de, &np))) {
3698 		nua_respond(nh, 481, "INVALID SUBSCRIPTION", TAG_END());
3699 		return;
3700 	}
3701 
3702 	if (sip->sip_to && sip->sip_to->a_tag) {
3703 		use_to_tag = sip->sip_to->a_tag;
3704 	} else {
3705 		switch_stun_random_string(to_tag, 12, NULL);
3706 		use_to_tag = to_tag;
3707 	}
3708 
3709 	if (sip->sip_contact) {
3710 		contact_host = sip->sip_contact->m_url->url_host;
3711 		contact_port = sip->sip_contact->m_url->url_port;
3712 		contact_user = sip->sip_contact->m_url->url_user;
3713 	}
3714 
3715 	full_agent = sip_header_as_string(nh->nh_home, (void *) sip->sip_user_agent);
3716 
3717 	//tl_gets(tags, NUTAG_SUBSTATE_REF(sub_state), TAG_END());
3718 
3719 	//sip->sip_subscription_state->ss_substate
3720 
3721 	if (sip->sip_subscription_state && sip->sip_subscription_state->ss_substate) {
3722 		if (switch_stristr("terminated", sip->sip_subscription_state->ss_substate)) {
3723 			sub_state = nua_substate_terminated;
3724 		} else if (switch_stristr("active", sip->sip_subscription_state->ss_substate)) {
3725 			sub_state = nua_substate_active;
3726 		}
3727 	}
3728 
3729 	event = sip_header_as_string(nh->nh_home, (void *) sip->sip_event);
3730 
3731 	if (to) {
3732 		to_str = switch_mprintf("sip:%s@%s", to->a_url->url_user, to->a_url->url_host);
3733 		to_user = to->a_url->url_user;
3734 		to_host = to->a_url->url_host;
3735 	}
3736 
3737 	if (profile->sub_domain) {
3738 		to_host = profile->sub_domain;
3739 	}
3740 
3741 	if (sip->sip_from) {
3742 		from_user = sip->sip_from->a_url->url_user;
3743 		from_host = sip->sip_from->a_url->url_host;
3744 	} else {
3745 		from_user = "n/a";
3746 		from_host = "n/a";
3747 	}
3748 
3749 	if ((exp_delta = sip->sip_expires ? sip->sip_expires->ex_delta : 3600)) {
3750 		if ((profile->force_subscription_expires > 0) && (profile->force_subscription_expires < (uint32_t)exp_delta)) {
3751 			exp_delta = profile->force_subscription_expires;
3752 		}
3753 	}
3754 
3755 	if ((sub_max_deviation_var = profile->sip_subscription_max_deviation)) {
3756 		int sub_deviation;
3757 		srand( (unsigned) ( (unsigned)(intptr_t)switch_thread_self() + switch_micro_time_now() ) );
3758 		/* random negative number between 0 and negative sub_max_deviation_var: */
3759 		sub_deviation = ( rand() % sub_max_deviation_var ) - sub_max_deviation_var;
3760 		if ( (exp_delta + sub_deviation) > 45 ) {
3761 			exp_delta += sub_deviation;
3762 		}
3763 	}
3764 
3765 	if (mod_sofia_globals.debug_presence > 0 || mod_sofia_globals.debug_sla > 0) {
3766 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DELTA %ld\n", exp_delta);
3767 	}
3768 
3769 	if (!exp_delta) {
3770 		sub_state = nua_substate_terminated;
3771 	}
3772 
3773 	switch_snprintf(exp_delta_str, sizeof(exp_delta_str), "%ld", exp_delta);
3774 
3775 	if (!strcmp("as-feature-event", event)) {
3776 		sip_authorization_t const *authorization = NULL;
3777 		auth_res_t auth_res = AUTH_FORBIDDEN;
3778 		char key[128] = "";
3779 		switch_event_t *v_event = NULL;
3780 
3781 
3782 		if (sip->sip_authorization) {
3783 			authorization = sip->sip_authorization;
3784 		} else if (sip->sip_proxy_authorization) {
3785 			authorization = sip->sip_proxy_authorization;
3786 		}
3787 
3788 		if (authorization) {
3789 			char network_ip[80];
3790 			int network_port;
3791 			sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &network_port);
3792 			auth_res = sofia_reg_parse_auth(profile, authorization, sip, de,
3793 											(char *) sip->sip_request->rq_method_name, key, sizeof(key), network_ip, network_port, &v_event, 0,
3794 											REG_REGISTER, to_user, NULL, NULL, NULL);
3795 			if (v_event) switch_event_destroy(&v_event);
3796 		} else if (sofia_reg_handle_register(nua, profile, nh, sip, de, REG_REGISTER, key, sizeof(key), &v_event, NULL, NULL, NULL)) {
3797 			if (v_event) switch_event_destroy(&v_event);
3798 			goto end;
3799 		}
3800 
3801 		if ((auth_res != AUTH_OK && auth_res != AUTH_RENEWED)) {
3802 			nua_respond(nh, SIP_401_UNAUTHORIZED, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
3803 			goto end;
3804 		}
3805 	} else if (sofia_test_pflag(profile, PFLAG_AUTH_SUBSCRIPTIONS)) {
3806 		sip_authorization_t const *authorization = NULL;
3807 		auth_res_t auth_res = AUTH_FORBIDDEN;
3808 		char keybuf[128] = "";
3809 		char *key;
3810 		size_t keylen;
3811 		switch_event_t *v_event = NULL;
3812 
3813 		key = keybuf;
3814 		keylen = sizeof(keybuf);
3815 
3816 		if (sip->sip_authorization) {
3817 			authorization = sip->sip_authorization;
3818 		} else if (sip->sip_proxy_authorization) {
3819 			authorization = sip->sip_proxy_authorization;
3820 		}
3821 
3822 		if (authorization) {
3823 			char network_ip[80];
3824 			int network_port;
3825 			sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &network_port);
3826 			auth_res = sofia_reg_parse_auth(profile, authorization, sip, de,
3827 											(char *) sip->sip_request->rq_method_name, key, keylen, network_ip, network_port, NULL, 0,
3828 											REG_INVITE, NULL, NULL, NULL, NULL);
3829 		} else if ( sofia_reg_handle_register(nua, profile, nh, sip, de, REG_INVITE, key, (uint32_t)keylen, &v_event, NULL, NULL, NULL)) {
3830 			if (v_event) {
3831 				switch_event_destroy(&v_event);
3832 			}
3833 
3834 			goto end;
3835 		}
3836 
3837 		if ((auth_res != AUTH_OK && auth_res != AUTH_RENEWED)) {
3838 			nua_respond(nh, SIP_401_UNAUTHORIZED, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
3839 			goto end;
3840 		}
3841 	}
3842 
3843 	orig_to_user = su_strdup(nua_handle_home(nh), to_user);
3844 
3845 	if (to_user && (p = strchr(to_user, '+')) && p != to_user) {
3846 		char *h;
3847 		if ((proto = (d_user = strdup(to_user)))) {
3848 			if ((my_to_user = strchr(d_user, '+'))) {
3849 				*my_to_user++ = '\0';
3850 				to_user = my_to_user;
3851 				if ((h = strchr(to_user, '+')) || (h = strchr(to_user, '@'))) {
3852 					*h++ = '\0';
3853 					to_host = h;
3854 				}
3855 			}
3856 		}
3857 
3858 		if (!(proto && to_host)) {
3859 			nua_respond(nh, SIP_404_NOT_FOUND, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
3860 			goto end;
3861 		}
3862 
3863 		found_proto++;
3864 	}
3865 
3866 	call_id = sip->sip_call_id->i_id;
3867 	full_from = sip_header_as_string(nh->nh_home, (void *) sip->sip_from);
3868 	full_to = sip_header_as_string(nh->nh_home, (void *) sip->sip_to);
3869 	full_via = sip_header_as_string(nh->nh_home, (void *) sip->sip_via);
3870 
3871 
3872 	if (sip->sip_expires && sip->sip_expires->ex_delta > 31536000) {
3873 		sip->sip_expires->ex_delta = 31536000;
3874 	}
3875 
3876 	if (sofia_test_pflag(profile, PFLAG_PRESENCE_MAP) && !found_proto && (alt_proto = switch_ivr_check_presence_mapping(to_user, to_host))) {
3877 		orig_proto = proto;
3878 		proto = alt_proto;
3879 	}
3880 
3881 	if ((sub_state != nua_substate_terminated)) {
3882 		sql = switch_mprintf("select contact from sip_subscriptions where call_id='%q' and profile_name='%q' and hostname='%q'",
3883 							 call_id, profile->name, mod_sofia_globals.hostname);
3884 		sofia_glue_execute_sql2str(profile, profile->dbh_mutex, sql, buf, sizeof(buf));
3885 
3886 
3887 		if (mod_sofia_globals.debug_presence > 0 || mod_sofia_globals.debug_sla > 0) {
3888 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
3889 							  "check subs sql: %s [%s]\n", sql, buf);
3890 		}
3891 
3892 		switch_safe_free(sql);
3893 
3894 		if (!zstr(buf)) {
3895 			sub_state = nua_substate_active;
3896 		}
3897 	}
3898 
3899 	if (sub_state == nua_substate_active) {
3900 		char *contact = contact_str;
3901 
3902 		sstr = switch_mprintf("active;expires=%ld", exp_delta);
3903 
3904 		if (strstr(buf, "fs_path=") && !strstr(contact_str, "fs_path=")) {
3905 			char *e = strchr(buf,';');
3906 			size_t l = e ? e-buf : strlen(buf);
3907 			if (!strncmp(contact_str,buf,l)) {
3908 				contact = buf;
3909 			}
3910 		}
3911 
3912 		sql = switch_mprintf("update sip_subscriptions "
3913 							 "set expires=%ld, "
3914 							 "network_ip='%q',network_port='%d',sip_user='%q',sip_host='%q',full_via='%q',full_to='%q',full_from='%q',contact='%q' "
3915 							 "where call_id='%q' and profile_name='%q' and hostname='%q'",
3916 							 (long) switch_epoch_time_now(NULL) + exp_delta,
3917 							 np.network_ip, np.network_port, from_user, from_host, full_via, full_to, full_from, contact,
3918 
3919 							 call_id, profile->name, mod_sofia_globals.hostname);
3920 
3921 		if (mod_sofia_globals.debug_presence > 0 || mod_sofia_globals.debug_sla > 0) {
3922 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
3923 							  "re-subscribe event %s, sql: %s\n", event, sql);
3924 		}
3925 
3926 		sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
3927 	} else {
3928 
3929 		if (sub_state == nua_substate_terminated) {
3930 			sql = switch_mprintf("delete from sip_subscriptions where call_id='%q' and profile_name='%q' and hostname='%q'",
3931 								 call_id, profile->name, mod_sofia_globals.hostname);
3932 
3933 			if (mod_sofia_globals.debug_presence > 0 || mod_sofia_globals.debug_sla > 0) {
3934 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
3935 								  "sub del sql: %s\n", sql);
3936 			}
3937 
3938 			switch_assert(sql != NULL);
3939 			sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
3940 			sstr = switch_mprintf("terminated;reason=noresource");
3941 
3942 		} else {
3943 			sip_accept_t *ap = sip->sip_accept;
3944 			char accept_header[256] = "";
3945 
3946 			sub_state = nua_substate_active;
3947 
3948 			while (ap) {
3949 				switch_snprintf(accept_header + strlen(accept_header), sizeof(accept_header) - strlen(accept_header),
3950 								"%s%s ", ap->ac_type, ap->ac_next ? "," : "");
3951 				ap = ap->ac_next;
3952 			}
3953 
3954 			sql = switch_mprintf("insert into sip_subscriptions "
3955 								 "(proto,sip_user,sip_host,sub_to_user,sub_to_host,presence_hosts,event,contact,call_id,full_from,"
3956 								 "full_via,expires,user_agent,accept,profile_name,hostname,network_port,network_ip,version,orig_proto, full_to) "
3957 								 "values ('%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q',%ld,'%q','%q','%q','%q','%d','%q',-1,'%q','%q;tag=%q')",
3958 								 proto, from_user, from_host, to_user, to_host, profile->presence_hosts ? profile->presence_hosts : "",
3959 								 event, contact_str, call_id, full_from, full_via,
3960 								 (long) switch_epoch_time_now(NULL) + exp_delta,
3961 								 full_agent, accept_header, profile->name, mod_sofia_globals.hostname,
3962 								 np.network_port, np.network_ip, orig_proto, full_to, use_to_tag);
3963 
3964 			switch_assert(sql != NULL);
3965 
3966 
3967 			if (mod_sofia_globals.debug_presence > 0 || mod_sofia_globals.debug_sla > 0) {
3968 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "%s SUBSCRIBE %s@%s %s@%s\n%s\n",
3969 								  profile->name, from_user, from_host, to_user, to_host, sql);
3970 			}
3971 
3972 
3973 			sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
3974 			sstr = switch_mprintf("active;expires=%ld", exp_delta);
3975 		}
3976 
3977 	}
3978 
3979 	if ( sip->sip_event && sip->sip_event->o_type && !strcasecmp(sip->sip_event->o_type, "ua-profile") && contact_host ) {
3980 		char *uri = NULL;
3981 		char *ct = "application/url";
3982 		char *extra_headers = NULL;
3983 
3984 		if ( contact_port ) {
3985 			uri = switch_mprintf("sip:%s:%s", contact_host, contact_port);
3986 		} else {
3987 			uri = switch_mprintf("sip:%s", contact_host);
3988 		}
3989 
3990 		if ( uri ) {
3991 			switch_event_t *params = NULL;
3992 			/* Grandstream REALLY uses a header called Message Body */
3993 			extra_headers = switch_mprintf("MessageBody: %s\r\n", profile->pnp_prov_url);
3994 			if (sofia_test_pflag(profile, PFLAG_SUBSCRIBE_RESPOND_200_OK)) {
3995 				nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
3996 			} else {
3997 				nua_respond(nh, SIP_202_ACCEPTED, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
3998 			}
3999 
4000 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "sending pnp NOTIFY for %s to provision to %s\n", uri, profile->pnp_prov_url);
4001 
4002 			switch_event_create(&params, SWITCH_EVENT_NOTIFY);
4003 			switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "profile", profile->name);
4004 			switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "event-string", sip->sip_event->o_type);
4005 			switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "to-uri", uri);
4006 			switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "from-uri", uri);
4007 			switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "extra-headers", extra_headers);
4008 			switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "content-type", ct);
4009 			switch_event_add_body(params, "%s", profile->pnp_prov_url);
4010 			switch_event_fire(&params);
4011 
4012 			switch_safe_free(uri);
4013 			switch_safe_free(extra_headers);
4014 
4015 			goto end;
4016 		}
4017 	}
4018 
4019 	if (status < 200) {
4020 		char *sticky = NULL;
4021 		char *contactstr, *cs = NULL;
4022 		char *p = NULL, *new_contactstr = NULL;
4023 		sofia_transport_t transport;
4024 
4025 		if (np.is_nat) {
4026 			char params[128] = "";
4027 			if (contact->m_url->url_params) {
4028 				switch_snprintf(params, sizeof(params), ";%s", contact->m_url->url_params);
4029 			}
4030 			ipv6 = strchr(np.network_ip, ':');
4031 			sticky = switch_mprintf("sip:%s@%s%s%s:%d%s", contact_user, ipv6 ? "[" : "", np.network_ip, ipv6 ? "]" : "", np.network_port, params);
4032 		}
4033 
4034 		if (np.is_auto_nat) {
4035 			contactstr = profile->public_url;
4036 		} else {
4037 			contactstr = profile->url;
4038 		}
4039 
4040 		if (sip->sip_via) {
4041 			transport = sofia_glue_via2transport(sip->sip_via);
4042 		} else {
4043 			transport = sofia_glue_url2transport(contact->m_url);
4044 		}
4045 
4046 		if (transport == SOFIA_TRANSPORT_TCP) {
4047 			if (np.is_auto_nat) {
4048 				cs = profile->tcp_public_contact;
4049 			} else {
4050 				cs = profile->tcp_contact;
4051 			}
4052 		} else if (transport == SOFIA_TRANSPORT_TCP_TLS) {
4053 			if (np.is_auto_nat) {
4054 				cs = sofia_test_pflag(profile, PFLAG_TLS) ?	profile->tls_public_contact : profile->tcp_public_contact;
4055 			} else {
4056 				cs = sofia_test_pflag(profile, PFLAG_TLS) ?	profile->tls_contact : profile->tcp_contact;
4057 			}
4058 		}
4059 
4060 		if (cs) {
4061 			contactstr = cs;
4062 		}
4063 
4064 
4065 		if (nh && nh->nh_ds->ds_usage) {
4066 			/* nua_dialog_usage_set_refresh_range(nh->nh_ds->ds_usage, exp_delta + SUB_OVERLAP, exp_delta + SUB_OVERLAP); */
4067 			nua_dialog_usage_set_refresh_range(nh->nh_ds->ds_usage, exp_delta, exp_delta);
4068 		}
4069 
4070 		if (contactstr && (p = strchr(contactstr, '@'))) {
4071 			if (strrchr(p, '>')) {
4072 				new_contactstr = switch_mprintf("<sip:%s%s", orig_to_user, p);
4073 			} else {
4074 				new_contactstr = switch_mprintf("<sip:%s%s>", orig_to_user, p);
4075 			}
4076 		}
4077 
4078 		if (nh) {
4079 			sip_to_tag(nh->nh_home, sip->sip_to, use_to_tag);
4080 		}
4081 
4082 		if (mod_sofia_globals.debug_presence > 0) {
4083 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Responding to SUBSCRIBE with 202 Accepted\n");
4084 		}
4085 		if (sofia_test_pflag(profile, PFLAG_SUBSCRIBE_RESPOND_200_OK)) {
4086 			nua_respond(nh, SIP_200_OK,
4087 						SIPTAG_TO(sip->sip_to),
4088 						TAG_IF(new_contactstr, SIPTAG_CONTACT_STR(new_contactstr)),
4089 						NUTAG_WITH_THIS_MSG(de->data->e_msg),
4090 						SIPTAG_SUBSCRIPTION_STATE_STR(sstr), SIPTAG_EXPIRES_STR(exp_delta_str), TAG_IF(sticky, NUTAG_PROXY(sticky)), TAG_END());
4091 		} else {
4092 			nua_respond(nh, SIP_202_ACCEPTED,
4093 						SIPTAG_TO(sip->sip_to),
4094 						TAG_IF(new_contactstr, SIPTAG_CONTACT_STR(new_contactstr)),
4095 						NUTAG_WITH_THIS_MSG(de->data->e_msg),
4096 						SIPTAG_SUBSCRIPTION_STATE_STR(sstr), SIPTAG_EXPIRES_STR(exp_delta_str), TAG_IF(sticky, NUTAG_PROXY(sticky)), TAG_END());
4097 		}
4098 
4099 		switch_safe_free(new_contactstr);
4100 		switch_safe_free(sticky);
4101 
4102 		if (sub_state == nua_substate_terminated) {
4103 			if (mod_sofia_globals.debug_presence > 0) {
4104 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sending NOTIFY with Expires [0] and State [%s]\n", sstr);
4105 			}
4106 
4107 			if (zstr(full_agent) || (*full_agent != 'z' && *full_agent != 'Z')) {
4108 				/* supress endless loop bug with zoiper */
4109 				callsequence = sofia_presence_get_cseq(profile);
4110 				cseq = sip_cseq_create(nh->nh_home, callsequence, SIP_METHOD_NOTIFY);
4111 				nua_notify(nh,
4112 						   SIPTAG_EXPIRES_STR("0"),
4113 						   SIPTAG_SUBSCRIPTION_STATE_STR(sstr),
4114 						   SIPTAG_CSEQ(cseq),
4115 						   TAG_END());
4116 			}
4117 
4118 
4119 		}
4120 	}
4121 
4122 	if (sub_state == nua_substate_terminated) {
4123 		char *full_call_info = NULL;
4124 		char *p = NULL;
4125 
4126 		if (sip->sip_call_info) {
4127 			full_call_info = sip_header_as_string(nh->nh_home, (void *) sip->sip_call_info);
4128 			if ((p = strchr(full_call_info, ';'))) {
4129 				p++;
4130 			}
4131 
4132 #if 0
4133 			nua_notify(nh,
4134 					   SIPTAG_EXPIRES_STR("0"),
4135 					   SIPTAG_SUBSCRIPTION_STATE_STR(sstr), TAG_IF(full_call_info, SIPTAG_CALL_INFO_STR(full_call_info)), TAG_END());
4136 #endif
4137 
4138 			if (!strcasecmp(event, "line-seize")) {
4139 				if (mod_sofia_globals.debug_sla > 1) {
4140 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "CANCEL LINE SEIZE\n");
4141 				}
4142 
4143 				sql = switch_mprintf("delete from sip_dialogs where hostname='%q' and profile_name='%q' and "
4144 									 "((sip_from_user='%q' and sip_from_host='%q') or presence_id='%q@%q') "
4145 									 "and call_info_state='seized'",
4146 									 mod_sofia_globals.hostname, profile->name, to_user, to_host, to_user, to_host);
4147 
4148 
4149 				if (mod_sofia_globals.debug_sla > 1) {
4150 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "CLEAR SQL %s\n", sql);
4151 				}
4152 				sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
4153 
4154 				sync_sla(profile, to_user, to_host, SWITCH_FALSE, SWITCH_FALSE, NULL);
4155 			}
4156 
4157 			su_free(nh->nh_home, full_call_info);
4158 
4159 		}
4160 
4161 	} else {
4162 		if (!strcasecmp(event, "line-seize")) {
4163 			char *full_call_info = NULL;
4164 			char *p;
4165 			switch_time_t now;
4166 
4167 			if (sip->sip_call_info) {
4168 				full_call_info = sip_header_as_string(nh->nh_home, (void *) sip->sip_call_info);
4169 				if ((p = strchr(full_call_info, ';'))) {
4170 					p++;
4171 				}
4172 				callsequence = sofia_presence_get_cseq(profile);
4173 				cseq = sip_cseq_create(nh->nh_home, callsequence, SIP_METHOD_NOTIFY);
4174 				nua_notify(nh,
4175 						   SIPTAG_FROM(sip->sip_to),
4176 						   SIPTAG_TO(sip->sip_from),
4177 						   SIPTAG_EXPIRES_STR(exp_delta_str),
4178 						   SIPTAG_CSEQ(cseq),
4179 						   SIPTAG_SUBSCRIPTION_STATE_STR(sstr),
4180 						   SIPTAG_EVENT_STR("line-seize"), TAG_IF(full_call_info, SIPTAG_CALL_INFO_STR(full_call_info)), TAG_END());
4181 
4182 
4183 
4184 
4185 				sql = switch_mprintf("delete from sip_dialogs where hostname='%q' and profile_name='%q' and "
4186 									 "((sip_from_user='%q' and sip_from_host='%q') or presence_id='%q@%q') "
4187 									 "and call_info_state='seized' and profile_name='%q'",
4188 									 mod_sofia_globals.hostname, profile->name, to_user, to_host, to_user, to_host, profile->name);
4189 
4190 
4191 				if (mod_sofia_globals.debug_sla > 1) {
4192 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "CLEAR SQL %s\n", sql);
4193 				}
4194 				sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
4195 
4196 				now = switch_epoch_time_now(NULL);
4197 				sql = switch_mprintf("insert into sip_dialogs (sip_from_user,sip_from_host,call_info,call_info_state,hostname,expires,rcd,profile_name) "
4198 									 "values ('%q','%q','%q','seized','%q',%"TIME_T_FMT",%ld,'%q')",
4199 									 to_user, to_host, switch_str_nil(p), mod_sofia_globals.hostname,
4200 									 switch_epoch_time_now(NULL) + exp_delta, (long)now, profile->name);
4201 
4202 				if (mod_sofia_globals.debug_sla > 1) {
4203 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SEIZE SQL %s\n", sql);
4204 				}
4205 				sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
4206 				sync_sla(profile, to_user, to_host, SWITCH_FALSE, SWITCH_FALSE, NULL);
4207 
4208 				su_free(nh->nh_home, full_call_info);
4209 			}
4210 		} else if (!strcasecmp(event, "call-info")) {
4211 			sync_sla(profile, to_user, to_host, SWITCH_FALSE, SWITCH_FALSE, call_id);
4212 		}
4213 	}
4214 
4215 	sent_reply++;
4216 
4217 	switch_safe_free(sstr);
4218 
4219 	if (!strcasecmp(event, "as-feature-event")) {
4220 		switch_event_t *event;
4221 		char sip_cseq[40] = "";
4222 
4223 		switch_snprintf(sip_cseq, sizeof(sip_cseq), "%d", sip->sip_cseq->cs_seq);
4224 		switch_event_create(&event, SWITCH_EVENT_PHONE_FEATURE_SUBSCRIBE);
4225 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "user", from_user);
4226 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "host", from_host);
4227 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "contact", contact_str);
4228 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-id", call_id);
4229 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "expires", exp_delta_str);
4230 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "cseq", sip_cseq);
4231 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "profile_name", profile->name);
4232 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "hostname", mod_sofia_globals.hostname);
4233 
4234 		if (sip->sip_payload) {
4235 			switch_xml_t xml = NULL;
4236 			char *pd_dup = NULL;
4237 
4238 			pd_dup = strdup(sip->sip_payload->pl_data);
4239 
4240 			switch_assert(pd_dup);
4241 
4242 			if ((xml = switch_xml_parse_str(pd_dup, strlen(pd_dup)))) {
4243 				switch_xml_t device = NULL;
4244 
4245 				if ((device = switch_xml_child(xml, "device"))) {
4246 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "device", device->txt);
4247 				}
4248 
4249 				if (xml->name && !strcmp(xml->name, "SetDoNotDisturb")) {
4250 					switch_xml_t action = NULL;
4251 
4252 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Feature-Action", "SetDoNotDisturb");
4253 					if ((action = switch_xml_child(xml, "doNotDisturbOn"))) {
4254 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Feature-Enabled", action->txt);
4255 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action-Name", action->name);
4256 					}
4257 				}
4258 
4259 				if (xml->name && !strcmp(xml->name, "SetForwarding")) {
4260 					switch_xml_t cfwd_type, cfwd_enable, cfwd_target;
4261 
4262 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Feature-Action", "SetCallForward");
4263 					if ((cfwd_type = switch_xml_child(xml, "forwardingType"))
4264 						&& (cfwd_enable = switch_xml_child(xml, "activateForward"))
4265 						&& (cfwd_target = switch_xml_child(xml, "forwardDN"))) {
4266 
4267 						if (!strcmp(cfwd_type->txt, "forwardImmediate")) {
4268 							switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Feature-Enabled", cfwd_enable->txt);
4269 							switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action-Name", "forward_immediate");
4270 							switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action-Value", cfwd_target->txt);
4271 						} else if (!strcmp(cfwd_type->txt, "forwardBusy")) {
4272 							switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Feature-Enabled", cfwd_enable->txt);
4273 							switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action-Name", "forward_busy");
4274 							switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action-Value", cfwd_target->txt);
4275 						} else if (!strcmp(cfwd_type->txt, "forwardNoAns")) {
4276 							switch_xml_t rc;
4277 
4278 							switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Feature-Enabled", cfwd_enable->txt);
4279 							switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action-Name", "forward_no_answer");
4280 							switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action-Value", cfwd_target->txt);
4281 							if ((rc = switch_xml_child(xml, "ringCount"))) {
4282 								switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "ringCount", rc->txt);
4283 							}
4284 						}
4285 					}
4286 				}
4287 			}
4288 		}
4289 		switch_event_fire(&event);
4290 	} else if (!strcasecmp(event, "message-summary")) {
4291 		if ((sql = switch_mprintf("select proto,sip_user,'%q',sub_to_user,sub_to_host,event,contact,call_id,full_from,"
4292 								  "full_via,expires,user_agent,accept,profile_name,network_ip"
4293 								  " from sip_subscriptions where hostname='%q' and profile_name='%q' and "
4294 								  "event='message-summary' and sub_to_user='%q' "
4295 								  "and (sip_host='%q' or presence_hosts like '%%%q%%') and call_id='%q'",
4296 								  to_host, mod_sofia_globals.hostname, profile->name,
4297 								  to_user, to_host, to_host, call_id))) {
4298 
4299 			if (mod_sofia_globals.debug_presence > 0) {
4300 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
4301 								  "SUBSCRIBE MWI SQL: %s\n", sql);
4302 			}
4303 
4304 
4305 			sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_presence_sub_reg_callback, profile);
4306 
4307 			switch_safe_free(sql);
4308 		}
4309 	} else 	if (!strcasecmp(event, "conference")) {
4310 		switch_event_t *event;
4311 		switch_event_create(&event, SWITCH_EVENT_CONFERENCE_DATA_QUERY);
4312 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Conference-Name", to_user);
4313 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Conference-Domain", to_host);
4314 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Query-From", from_user);
4315 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Query-From-Domain", from_host);
4316 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Call-Id", call_id);
4317 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Sofia-Profile", profile->name);
4318 		switch_event_fire(&event);
4319 	}
4320 
4321  end:
4322 
4323 	if (strcasecmp(event, "call-info") && strcasecmp(event, "line-seize")) {
4324 
4325 		if (to_user && (strstr(to_user, "ext+") || strstr(to_user, "user+"))) {
4326 			char protocol[80];
4327 			char *p;
4328 
4329 			switch_copy_string(protocol, to_user, sizeof(protocol));
4330 			if ((p = strchr(protocol, '+'))) {
4331 				*p = '\0';
4332 			}
4333 
4334 			if (switch_event_create(&sevent, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
4335 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "proto", protocol);
4336 				if (!zstr(orig_proto)) {
4337 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "orig_proto", orig_proto);
4338 				}
4339 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "login", profile->name);
4340 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "sip_profile", profile->name);
4341 				switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "from", "%s@%s", to_user, to_host);
4342 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "rpid", "active");
4343 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "status", "Click To Call");
4344 				switch_event_fire(&sevent);
4345 			}
4346 
4347 		} else if (to_user && (strcasecmp(proto, SOFIA_CHAT_PROTO) != 0)) {
4348 			if (switch_event_create(&sevent, SWITCH_EVENT_PRESENCE_PROBE) == SWITCH_STATUS_SUCCESS) {
4349 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "proto", proto);
4350 				if (!zstr(orig_proto)) {
4351 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "orig_proto", orig_proto);
4352 				}
4353 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "login", profile->name);
4354 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "sip_profile", profile->name);
4355 				switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "from", "%s@%s", from_user, from_host);
4356 				switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "to", "%s%s%s@%s", proto, "+", to_user, to_host);
4357 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "proto-specific-event-name", event);
4358 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "expires", exp_delta_str);
4359 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "event_type", "presence");
4360 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
4361 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "expires", exp_delta_str);
4362 				switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "sub-call-id", call_id);
4363 				switch_event_fire(&sevent);
4364 
4365 			}
4366 		} else {
4367 
4368 			if (!strcasecmp(event, "dialog")) {
4369 				if (switch_event_create(&sevent, SWITCH_EVENT_PRESENCE_PROBE) == SWITCH_STATUS_SUCCESS) {
4370 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "probe-type", "dialog");
4371 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO);
4372 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "login", profile->name);
4373 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "sip_profile", profile->name);
4374 					switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "from", "%s@%s", from_user, from_host);
4375 					switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "to", "%s@%s", to_user, to_host);
4376 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "proto-specific-event-name", event);
4377 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "expires", exp_delta_str);
4378 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "event_type", "presence");
4379 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
4380 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "expires", exp_delta_str);
4381 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "sub-call-id", call_id);
4382 					switch_event_fire(&sevent);
4383 				}
4384 			} else if (!strcasecmp(event, "presence")) {
4385 				if (switch_event_create(&sevent, SWITCH_EVENT_PRESENCE_PROBE) == SWITCH_STATUS_SUCCESS) {
4386 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO);
4387 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "login", profile->name);
4388 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "sip_profile", profile->name);
4389 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "presence-source", "subscribe");
4390 					switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "from", "%s@%s", from_user, from_host);
4391 					switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "to", "%s@%s", to_user, to_host);
4392 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "expires", exp_delta_str);
4393 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
4394 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "rpid", "unknown");
4395 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "status", "Registered");
4396 					switch_event_add_header_string(sevent, SWITCH_STACK_BOTTOM, "sub-call-id", call_id);
4397 					switch_event_fire(&sevent);
4398 				}
4399 			}
4400 		}
4401 	}
4402 
4403 	if (event) {
4404 		su_free(nh->nh_home, event);
4405 	}
4406 
4407 	if (full_from) {
4408 		su_free(nh->nh_home, full_from);
4409 	}
4410 	if (full_to) {
4411 		su_free(nh->nh_home, full_to);
4412 	}
4413 
4414 	if (full_via) {
4415 		su_free(nh->nh_home, full_via);
4416 	}
4417 	if (full_agent) {
4418 		su_free(nh->nh_home, full_agent);
4419 	}
4420 
4421 	switch_safe_free(d_user);
4422 	switch_safe_free(to_str);
4423 	switch_safe_free(contact_str);
4424 	switch_safe_free(alt_proto);
4425 
4426 	if (!sent_reply) {
4427 		nua_respond(nh, 481, "INVALID SUBSCRIPTION", TAG_END());
4428 	}
4429 
4430 	if (!sofia_private || !sofia_private->is_call) {
4431 		nua_handle_destroy(nh);
4432 	}
4433 
4434 }
4435 
4436 
sofia_find_gateway_subscription(sofia_gateway_t * gateway_ptr,const char * event)4437 sofia_gateway_subscription_t *sofia_find_gateway_subscription(sofia_gateway_t *gateway_ptr, const char *event)
4438 {
4439 	sofia_gateway_subscription_t *gw_sub_ptr;
4440 	for (gw_sub_ptr = gateway_ptr->subscriptions; gw_sub_ptr; gw_sub_ptr = gw_sub_ptr->next) {
4441 		if (!strcasecmp(gw_sub_ptr->event, event)) {
4442 			/* this is the gateway subscription we are interested in */
4443 			return gw_sub_ptr;
4444 		}
4445 	}
4446 	return NULL;
4447 }
4448 
sofia_presence_handle_sip_r_subscribe(int status,char const * phrase,nua_t * nua,sofia_profile_t * profile,nua_handle_t * nh,sofia_private_t * sofia_private,sip_t const * sip,sofia_dispatch_event_t * de,tagi_t tags[])4449 void sofia_presence_handle_sip_r_subscribe(int status,
4450 										   char const *phrase,
4451 										   nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
4452 										   sofia_dispatch_event_t *de,
4453 										   tagi_t tags[])
4454 {
4455 	sip_event_t const *o = NULL;
4456 	sofia_gateway_subscription_t *gw_sub_ptr;
4457 	sofia_gateway_t *gateway = NULL;
4458 
4459 	if (!sip) {
4460 		return;
4461 	}
4462 
4463 	tl_gets(tags, SIPTAG_EVENT_REF(o), TAG_END());
4464 	/* o->o_type: message-summary (for example) */
4465 	if (!o) {
4466 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Event information not given\n");
4467 		return;
4468 	}
4469 
4470 	if (!sofia_private || zstr(sofia_private->gateway_name)) {
4471 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Gateway information missing\n");
4472 		return;
4473 	}
4474 
4475 
4476 	if (!(gateway = sofia_reg_find_gateway(sofia_private->gateway_name))) {
4477 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Gateway information missing\n");
4478 		return;
4479 	}
4480 
4481 
4482 	/* Find the subscription if one exists */
4483 	if (!(gw_sub_ptr = sofia_find_gateway_subscription(gateway, o->o_type))) {
4484 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Could not find gateway subscription.  Gateway: %s.  Subscription Event: %s\n",
4485 						  gateway->name, o->o_type);
4486 		goto end;
4487 	}
4488 
4489 	/* Update the subscription status for the subscription */
4490 	switch (status) {
4491 	case 200:
4492 	case 202:
4493 		/* TODO: in the spec it is possible for the other side to change the original expiry time,
4494 		 * this needs to be researched (eg, what sip header this information will be in) and implemented.
4495 		 * Although, since it seems the sofia stack is pretty much handling the subscription expiration
4496 		 * anyway, then maybe its not even worth bothering.
4497 		 */
4498 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "got 200 OK response, updated state to SUB_STATE_SUBSCRIBE.\n");
4499 		gw_sub_ptr->state = SUB_STATE_SUBSCRIBE;
4500 		break;
4501 	case 100:
4502 		break;
4503 	default:
4504 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "status (%d) != 200, updated state to SUB_STATE_FAILED.\n", status);
4505 		gw_sub_ptr->state = SUB_STATE_FAILED;
4506 
4507 		break;
4508 	}
4509 
4510  end:
4511 	sofia_reg_release_gateway(gateway);
4512 
4513 }
4514 
4515 
sofia_presence_send_sql(void * pArg,int argc,char ** argv,char ** columnNames)4516 static int sofia_presence_send_sql(void *pArg, int argc, char **argv, char **columnNames)
4517 {
4518 	struct pres_sql_cb *cb = (struct pres_sql_cb *) pArg;
4519 
4520 
4521 	if (mod_sofia_globals.debug_presence > 0) {
4522 		int i;
4523 		for(i = 0; i < argc; i++) {
4524 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "arg %d[%s] = [%s]\n", i, columnNames[i], argv[i]);
4525 		}
4526 	}
4527 
4528 	send_presence_notify(cb->profile, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], NULL);
4529 	cb->ttl++;
4530 
4531 	return 0;
4532 }
4533 
4534 
sofia_presence_contact_count(sofia_profile_t * profile,const char * contact_str)4535 uint32_t sofia_presence_contact_count(sofia_profile_t *profile, const char *contact_str)
4536 {
4537 	char buf[32] = "";
4538 	char *sql;
4539 
4540 	sql = switch_mprintf("select count(*) from sip_subscriptions where hostname='%q' and profile_name='%q' and contact='%q'",
4541 						 mod_sofia_globals.hostname, profile->name, contact_str);
4542 
4543 	sofia_glue_execute_sql2str(profile, profile->dbh_mutex, sql, buf, sizeof(buf));
4544 	switch_safe_free(sql);
4545 	return atoi(buf);
4546 }
4547 
sofia_presence_handle_sip_i_publish(nua_t * nua,sofia_profile_t * profile,nua_handle_t * nh,sofia_private_t * sofia_private,sip_t const * sip,sofia_dispatch_event_t * de,tagi_t tags[])4548 void sofia_presence_handle_sip_i_publish(nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
4549 										 sofia_dispatch_event_t *de,
4550 										 tagi_t tags[])
4551 {
4552 
4553 	sip_from_t const *from;
4554 	char *from_user = NULL;
4555 	char *from_host = NULL;
4556 	char *rpid = "";
4557 	sip_payload_t *payload;
4558 	char *event_type = NULL;
4559 	char etag[9] = "";
4560 	char expstr[30] = "";
4561 	long exp = 0, exp_delta = 3600;
4562 	char *pd_dup = NULL;
4563 	int count = 1, sub_count = 1;
4564 	char *contact_str;
4565 	sofia_nat_parse_t np = { { 0 } };
4566 
4567 	if (!sip) {
4568 		return;
4569 	}
4570 
4571 	from = sip->sip_from;
4572 	payload = sip->sip_payload;
4573 
4574 	np.fs_path = 1;
4575 	contact_str = sofia_glue_gen_contact_str(profile, sip, nh, de, &np);
4576 
4577 	if (from) {
4578 		from_user = (char *) from->a_url->url_user;
4579 		from_host = (char *) from->a_url->url_host;
4580 	}
4581 
4582 	exp_delta = (sip->sip_expires ? sip->sip_expires->ex_delta : 3600);
4583 	if ((profile->force_publish_expires > 0) && (profile->force_publish_expires < (uint32_t)exp_delta)) {
4584 		exp_delta = profile->force_publish_expires;
4585 	}
4586 
4587 	if (exp_delta < 0) {
4588 		exp = exp_delta;
4589 	} else {
4590 		exp = (long) switch_epoch_time_now(NULL) + exp_delta;
4591 	}
4592 
4593 	if (payload) {
4594 		switch_xml_t xml, note, person, tuple, status, basic, act;
4595 		switch_event_t *event;
4596 		char *sql;
4597 		char *full_agent = NULL;
4598 		char network_ip[80];
4599 		int network_port = 0;
4600 
4601 		sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &network_port);
4602 
4603 		pd_dup = strdup(payload->pl_data);
4604 
4605 		switch_assert(pd_dup);
4606 
4607 		if ((xml = switch_xml_parse_str(pd_dup, strlen(pd_dup)))) {
4608 			char *open_closed = "", *note_txt = "";
4609 
4610 			if (sip->sip_user_agent) {
4611 				full_agent = sip_header_as_string(nh->nh_home, (void *) sip->sip_user_agent);
4612 			}
4613 
4614 			if ((tuple = switch_xml_child(xml, "tuple")) && (status = switch_xml_child(tuple, "status"))
4615 				&& (basic = switch_xml_child(status, "basic"))) {
4616 				open_closed = basic->txt;
4617 
4618 				if ((note = switch_xml_child(tuple, "note"))) {
4619 					rpid = note_txt = note->txt;
4620 				} else if ((note = switch_xml_child(tuple, "dm:note"))) {
4621 					rpid = note_txt = note->txt;
4622 				}
4623 			}
4624 
4625 			if ((person = switch_xml_child(xml, "dm:person"))) {
4626 				if ((note = switch_xml_child(person, "dm:note"))) {
4627 					note_txt = note->txt;
4628 				} else if ((note = switch_xml_child(person, "rpid:note"))) {
4629 					note_txt = note->txt;
4630 				}
4631 				if ((act = switch_xml_child(person, "rpid:activities")) && act->child && act->child->name) {
4632 					if ((rpid = strchr(act->child->name, ':'))) {
4633 						rpid++;
4634 					} else {
4635 						rpid = act->child->name;
4636 					}
4637 				}
4638 				if (zstr(note_txt)) note_txt = "Available";
4639 			}
4640 
4641 			if (!strcasecmp(open_closed, "closed")) {
4642 				rpid = note_txt = "Unregistered";
4643 				if (sofia_test_pflag(profile, PFLAG_MULTIREG)) {
4644 					count = sofia_reg_reg_count(profile, from_user, from_host);
4645 
4646 					if (count != 1) {
4647 						/* Don't broadcast offline when there is more than one client or one signing off makes them all appear to sign off on some clients */
4648 						count = 0;
4649 					} else {
4650 						sub_count = sofia_presence_contact_count(profile, contact_str);
4651 					}
4652 				}
4653 			}
4654 
4655 			event_type = sip_header_as_string(nh->nh_home, (void *) sip->sip_event);
4656 
4657 			if (count) {
4658 				if ((sql = switch_mprintf("delete from sip_presence where sip_user='%q' and sip_host='%q' "
4659 										  " and profile_name='%q' and hostname='%q'",
4660 										  from_user, from_host, profile->name, mod_sofia_globals.hostname))) {
4661 					sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
4662 				}
4663 
4664 				if (sub_count > 0 && (sql = switch_mprintf("insert into sip_presence (sip_user, sip_host, status, rpid, expires, user_agent,"
4665 														   " profile_name, hostname, open_closed, network_ip, network_port) "
4666 														   "values ('%q','%q','%q','%q',%ld,'%q','%q','%q','%q','%q','%d')",
4667 														   from_user, from_host, note_txt, rpid, exp, full_agent, profile->name,
4668 														   mod_sofia_globals.hostname, open_closed, network_ip, network_port))) {
4669 
4670 					sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
4671 				}
4672 
4673 			} else if (contact_str) {
4674 				struct pres_sql_cb cb = {profile, 0};
4675 
4676 				sql = switch_mprintf("select full_to, full_from, contact, expires, call_id, event, network_ip, network_port, "
4677 									 "'application/pidf+xml' as ct,'%q' as pt "
4678 									 " from sip_subscriptions where "
4679 									 "hostname='%q' and profile_name='%q' and sub_to_user='%q' and sub_to_host='%q' and event='%q'"
4680 									 "and contact = '%q' ",
4681 
4682 									 switch_str_nil(payload->pl_data),
4683 									 mod_sofia_globals.hostname, profile->name,
4684 									 from_user, from_host, event_type, contact_str);
4685 
4686 				sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_presence_send_sql, &cb);
4687 				switch_safe_free(sql);
4688 			}
4689 
4690 			if (sub_count > 0) {
4691 				if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
4692 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO);
4693 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", rpid);
4694 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", profile->url);
4695 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "sip_profile", profile->name);
4696 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "user-agent", full_agent);
4697 					switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", from_user, from_host);
4698 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "status", note_txt);
4699 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", event_type);
4700 					switch_event_fire(&event);
4701 				}
4702 			}
4703 
4704 			if (event_type) {
4705 				su_free(nh->nh_home, event_type);
4706 			}
4707 
4708 			if (full_agent) {
4709 				su_free(nh->nh_home, full_agent);
4710 			}
4711 
4712 			switch_xml_free(xml);
4713 		}
4714 	} else {
4715 		char *sql = switch_mprintf("update sip_presence set expires=%ld where sip_user='%q' and sip_host='%q' and profile_name='%q' and hostname='%q'",
4716 								   exp, from_user, from_host, profile->name, mod_sofia_globals.hostname);
4717 		sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
4718 	}
4719 
4720 	switch_safe_free(pd_dup);
4721 
4722 	switch_snprintf(expstr, sizeof(expstr), "%d", exp_delta);
4723 	switch_stun_random_string(etag, 8, NULL);
4724 
4725 	if (sub_count > 0) {
4726 		nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg), SIPTAG_ETAG_STR(etag), SIPTAG_EXPIRES_STR(expstr), TAG_END());
4727 	} else {
4728 		nua_respond(nh, SIP_404_NOT_FOUND, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
4729 	}
4730 
4731 	switch_safe_free(contact_str);
4732 }
4733 
sofia_presence_set_hash_key(char * hash_key,int32_t len,sip_t const * sip)4734 void sofia_presence_set_hash_key(char *hash_key, int32_t len, sip_t const *sip)
4735 {
4736 	url_t *to = sip->sip_to->a_url;
4737 	url_t *from = sip->sip_from->a_url;
4738 	switch_snprintf(hash_key, len, "%s%s%s", from->url_user, from->url_host, to->url_user);
4739 }
4740 
sofia_presence_handle_sip_i_message(int status,char const * phrase,nua_t * nua,sofia_profile_t * profile,nua_handle_t * nh,switch_core_session_t * session,sofia_private_t * sofia_private,sip_t const * sip,sofia_dispatch_event_t * de,tagi_t tags[])4741 void sofia_presence_handle_sip_i_message(int status,
4742 										 char const *phrase,
4743 										 nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh,
4744 										 switch_core_session_t *session,
4745 										 sofia_private_t *sofia_private, sip_t const *sip,
4746 										 sofia_dispatch_event_t *de,
4747 										 tagi_t tags[])
4748 {
4749 
4750 	if (sip) {
4751 		sip_from_t const *from = sip->sip_from;
4752 		const char *from_user = NULL;
4753 		const char *from_host = NULL;
4754 		sip_to_t const *to = sip->sip_to;
4755 		const char *to_user = NULL;
4756 		const char *to_host = NULL;
4757 		sip_payload_t *payload = sip->sip_payload;
4758 		char *msg = NULL;
4759 		const char *us;
4760 		char network_ip[80];
4761 		int network_port = 0;
4762 		switch_channel_t *channel = NULL;
4763 
4764 
4765 		if (!sofia_test_pflag(profile, PFLAG_ENABLE_CHAT)) {
4766 			goto end;
4767 		}
4768 
4769 
4770 		if (session) {
4771 			channel = switch_core_session_get_channel(session);
4772 		}
4773 
4774 		if (sofia_test_pflag(profile, PFLAG_AUTH_MESSAGES)) {
4775 			sip_authorization_t const *authorization = NULL;
4776 			auth_res_t auth_res = AUTH_FORBIDDEN;
4777 			char keybuf[128] = "";
4778 			char *key;
4779 			size_t keylen;
4780 			switch_event_t *v_event = NULL;
4781 
4782 			key = keybuf;
4783 			keylen = sizeof(keybuf);
4784 
4785 			if (sip->sip_authorization) {
4786 				authorization = sip->sip_authorization;
4787 			} else if (sip->sip_proxy_authorization) {
4788 				authorization = sip->sip_proxy_authorization;
4789 			}
4790 
4791 			if (authorization) {
4792 				char network_ip[80];
4793 				int network_port;
4794 				sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &network_port);
4795 				auth_res = sofia_reg_parse_auth(profile, authorization, sip, de,
4796 												(char *) sip->sip_request->rq_method_name, key, keylen, network_ip, network_port, NULL, 0,
4797 												REG_INVITE, NULL, NULL, NULL, NULL);
4798 			} else if ( sofia_reg_handle_register(nua, profile, nh, sip, de, REG_INVITE, key, (uint32_t)keylen, &v_event, NULL, NULL, NULL)) {
4799 				if (v_event) {
4800 					switch_event_destroy(&v_event);
4801 				}
4802 
4803 				goto end;
4804 			}
4805 
4806 			if ((auth_res != AUTH_OK && auth_res != AUTH_RENEWED)) {
4807 				nua_respond(nh, SIP_401_UNAUTHORIZED, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
4808 				goto end;
4809 			}
4810 
4811 			if (channel) {
4812 				switch_channel_set_variable(channel, "sip_authorized", "true");
4813 			}
4814 		}
4815 
4816 		if ((us = sofia_glue_get_unknown_header(sip, "X-FS-Sending-Message")) && !strcmp(us, switch_core_get_uuid())) {
4817 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Not sending message to ourselves!\n");
4818 			nua_respond(nh, SIP_503_SERVICE_UNAVAILABLE, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
4819 			return;
4820 		}
4821 
4822 		if (sip->sip_content_type && sip->sip_content_type->c_subtype) {
4823 			if (strstr(sip->sip_content_type->c_subtype, "composing")) {
4824 				goto end;
4825 			}
4826 		}
4827 
4828 
4829 		sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &network_port);
4830 
4831 
4832 		if (from) {
4833 			from_user = from->a_url->url_user;
4834 			from_host = from->a_url->url_host;
4835 		}
4836 
4837 		if (to) {
4838 			to_user = to->a_url->url_user;
4839 			to_host = to->a_url->url_host;
4840 		}
4841 
4842 		if (!to_user) {
4843 			goto end;
4844 		}
4845 
4846 		if (payload) {
4847 			msg = payload->pl_data;
4848 		}
4849 
4850 		if (nh) {
4851 			char hash_key[512];
4852 			private_object_t *tech_pvt;
4853 			switch_event_t *event, *event_dup;
4854 			char *to_addr;
4855 			char *from_addr;
4856 			char *p;
4857 			char *full_from;
4858 			char proto[512] = SOFIA_CHAT_PROTO;
4859 			sip_unknown_t *un;
4860 			int first_history_info = 1;
4861 
4862 			full_from = sip_header_as_string(nh->nh_home, (void *) sip->sip_from);
4863 
4864 			if ((p = strchr(to_user, '+')) && p != to_user) {
4865 				switch_copy_string(proto, to_user, sizeof(proto));
4866 				p = strchr(proto, '+');
4867 				*p++ = '\0';
4868 
4869 				if ((to_addr = strdup(p))) {
4870 					if ((p = strchr(to_addr, '+'))) {
4871 						*p = '@';
4872 					}
4873 				}
4874 			} else {
4875 				to_addr = switch_mprintf("%s@%s", to_user, to_host);
4876 			}
4877 
4878 			from_addr = switch_mprintf("%s@%s", from_user, from_host);
4879 
4880 			if (sofia_test_pflag(profile, PFLAG_IN_DIALOG_CHAT)) {
4881 				sofia_presence_set_hash_key(hash_key, sizeof(hash_key), sip);
4882 			}
4883 
4884 			if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) {
4885 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", profile->url);
4886 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO);
4887 
4888 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to_proto", proto);
4889 
4890 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", from_addr);
4891 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_user", from_user);
4892 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_host", from_host);
4893 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to_user", to_user);
4894 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to_host", to_host);
4895 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_sip_ip", network_ip);
4896 				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from_sip_port", "%d", network_port);
4897 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to", to_addr);
4898 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "subject", "SIMPLE MESSAGE");
4899 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "context", profile->context);
4900 
4901 				if (sip->sip_content_type && sip->sip_content_type->c_subtype) {
4902 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", sip->sip_content_type->c_type);
4903 				} else {
4904 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "text/plain");
4905 				}
4906 
4907 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_full", full_from);
4908 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "sip_profile", profile->name);
4909 
4910 
4911 				if (sip->sip_call_info) {
4912 					sip_call_info_t *call_info = sip->sip_call_info;
4913 					char *ci = sip_header_as_string(nua_handle_home(nh), (void *) call_info);
4914 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "sip_call_info", ci);
4915 				}
4916 
4917 				/* Loop thru unknown Headers Here so we can do something with them */
4918 				for (un = sip->sip_unknown; un; un = un->un_next) {
4919 					if (!strncasecmp(un->un_name, "Diversion", 9)) {
4920 						/* Basic Diversion Support for Diversion Indication in SIP */
4921 						/* draft-levy-sip-diversion-08 */
4922 						if (!zstr(un->un_value)) {
4923 							char *tmp_name;
4924 							if ((tmp_name = switch_mprintf("%s%s", SOFIA_SIP_HEADER_PREFIX, un->un_name))) {
4925 								switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, tmp_name, un->un_value);
4926 								free(tmp_name);
4927 							}
4928 						}
4929 					} else if (!strncasecmp(un->un_name, "History-Info", 12)) {
4930 						if (first_history_info) {
4931 							/* If the header exists first time, make sure to remove old info and re-set the variable */
4932 							switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "sip_history_info", un->un_value);
4933 							first_history_info = 0;
4934 						} else {
4935 							/* Append the History-Info into one long string */
4936 							const char *history_var = switch_event_get_header(event, "sip_history_info");
4937 							if (!zstr(history_var)) {
4938 								char *tmp_str;
4939 								if ((tmp_str = switch_mprintf("%s, %s", history_var, un->un_value))) {
4940 									switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "sip_history_info", tmp_str);
4941 									free(tmp_str);
4942 								} else {
4943 									switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "sip_history_info", un->un_value);
4944 								}
4945 							} else {
4946 								switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "sip_history_info", un->un_value);
4947 							}
4948 						}
4949 					} else if (!strcasecmp(un->un_name, "Geolocation")) {
4950 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "sip_geolocation", un->un_value);
4951 					} else if (!strcasecmp(un->un_name, "Geolocation-Error")) {
4952 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "sip_geolocation_error", un->un_value);
4953 					} else if (!strncasecmp(un->un_name, "X-", 2) || !strncasecmp(un->un_name, "P-", 2) || !strcasecmp(un->un_name, "User-to-User")) {
4954 						if (!zstr(un->un_value)) {
4955 							char new_name[512] = "";
4956 							int reps = 0;
4957 							for (;;) {
4958 								char postfix[25] = "";
4959 								if (reps > 0) {
4960 									switch_snprintf(postfix, sizeof(postfix), "-%d", reps);
4961 								}
4962 								reps++;
4963 								switch_snprintf(new_name, sizeof(new_name), "%s%s%s", SOFIA_SIP_HEADER_PREFIX, un->un_name, postfix);
4964 
4965 								if (switch_event_get_header(event, new_name)) {
4966 									continue;
4967 								}
4968 
4969 								switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, new_name, un->un_value);
4970 								break;
4971 							}
4972 						}
4973 					}
4974 				}
4975 
4976 				if (msg) {
4977 					switch_event_add_body(event, "%s", msg);
4978 				}
4979 
4980 				if (channel) {
4981 					switch_channel_event_set_data(channel, event);
4982 				}
4983 
4984 
4985 				if (sofia_test_pflag(profile, PFLAG_FIRE_MESSAGE_EVENTS)) {
4986 					if (switch_event_dup(&event_dup, event) == SWITCH_STATUS_SUCCESS) {
4987 						event_dup->event_id = SWITCH_EVENT_RECV_MESSAGE;
4988 						event_dup->flags |= EF_UNIQ_HEADERS;
4989 						switch_event_add_header_string(event_dup, SWITCH_STACK_BOTTOM, "Event-Name", switch_event_name(event_dup->event_id));
4990 						switch_event_fire(&event_dup);
4991 					}
4992 				}
4993 
4994 				if (session) {
4995 					if (switch_event_dup(&event_dup, event) == SWITCH_STATUS_SUCCESS) {
4996 						switch_core_session_queue_event(session, &event_dup);
4997 					}
4998 				}
4999 
5000 
5001 			} else {
5002 				abort();
5003 			}
5004 
5005 			if (sofia_test_pflag(profile, PFLAG_IN_DIALOG_CHAT) && (tech_pvt = (private_object_t *) switch_core_hash_find_locked(profile->chat_hash, hash_key, profile->flag_mutex))) {
5006 				switch_core_session_queue_event(tech_pvt->session, &event);
5007 			} else {
5008 				switch_core_chat_send(proto, event);
5009 				switch_event_destroy(&event);
5010 			}
5011 
5012 			switch_safe_free(to_addr);
5013 			switch_safe_free(from_addr);
5014 
5015 			if (full_from) {
5016 				su_free(nh->nh_home, full_from);
5017 			}
5018 		}
5019 	}
5020 
5021  end:
5022 
5023 	if (sofia_test_pflag(profile, PFLAG_MESSAGES_RESPOND_200_OK)) {
5024 		nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
5025 	} else {
5026 		nua_respond(nh, SIP_202_ACCEPTED, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
5027 	}
5028 
5029 }
5030 
sofia_presence_set_chat_hash(private_object_t * tech_pvt,sip_t const * sip)5031 void sofia_presence_set_chat_hash(private_object_t *tech_pvt, sip_t const *sip)
5032 {
5033 	char hash_key[256] = "";
5034 	char buf[512];
5035 	su_home_t *home = NULL;
5036 
5037 	if (!tech_pvt || tech_pvt->hash_key || !sip || !sip->sip_from || !sip->sip_from->a_url->url_user || !sip->sip_from->a_url->url_host) {
5038 		return;
5039 	}
5040 
5041 	if (sofia_reg_find_reg_url(tech_pvt->profile, sip->sip_from->a_url->url_user, sip->sip_from->a_url->url_host, buf, sizeof(buf))) {
5042 		home = su_home_new(sizeof(*home));
5043 		switch_assert(home != NULL);
5044 		tech_pvt->chat_from = sip_header_as_string(home, (const sip_header_t *) sip->sip_to);
5045 		tech_pvt->chat_to = switch_core_session_strdup(tech_pvt->session, buf);
5046 		sofia_presence_set_hash_key(hash_key, sizeof(hash_key), sip);
5047 		su_home_unref(home);
5048 		home = NULL;
5049 	} else {
5050 		return;
5051 	}
5052 
5053 	switch_mutex_lock(tech_pvt->profile->flag_mutex);
5054 	tech_pvt->hash_key = switch_core_session_strdup(tech_pvt->session, hash_key);
5055 	switch_core_hash_insert(tech_pvt->profile->chat_hash, tech_pvt->hash_key, tech_pvt);
5056 	switch_mutex_unlock(tech_pvt->profile->flag_mutex);
5057 }
5058 
5059 
sofia_presence_check_subscriptions(sofia_profile_t * profile,time_t now)5060 void sofia_presence_check_subscriptions(sofia_profile_t *profile, time_t now)
5061 {
5062 	char *sql;
5063 
5064 	if (now) {
5065 		struct pres_sql_cb cb = {profile, 0};
5066 
5067 		if (profile->pres_type != PRES_TYPE_FULL) {
5068 			if (mod_sofia_globals.debug_presence > 0) {
5069 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "check_subs: %s is passive, skipping\n", (char *) profile->name);
5070 			}
5071 			return;
5072 		}
5073 
5074 		sql = switch_mprintf("update sip_subscriptions set version=version+1 where "
5075 							 "((expires > 0 and expires <= %ld)) and profile_name='%q' and hostname='%q'",
5076 							 (long) now, profile->name, mod_sofia_globals.hostname);
5077 
5078 		sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
5079 		switch_safe_free(sql);
5080 
5081 		sql = switch_mprintf("select full_to, full_from, contact, -1, call_id, event, network_ip, network_port, "
5082 							 "NULL as ct, NULL as pt "
5083 							 " from sip_subscriptions where ((expires > 0 and expires <= %ld)) and profile_name='%q' and hostname='%q'",
5084 							 (long) now, profile->name, mod_sofia_globals.hostname);
5085 
5086 		sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_presence_send_sql, &cb);
5087 		switch_safe_free(sql);
5088 
5089 		if (cb.ttl) {
5090 			sql = switch_mprintf("delete from sip_subscriptions where ((expires > 0 and expires <= %ld)) "
5091 								 "and profile_name='%q' and hostname='%q'",
5092 								 (long) now, profile->name, mod_sofia_globals.hostname);
5093 
5094 			if (mod_sofia_globals.debug_presence > 0 || mod_sofia_globals.debug_sla > 0) {
5095 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
5096 								  "sub del sql: %s\n", sql);
5097 			}
5098 
5099 			sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
5100 		}
5101 	}
5102 
5103 
5104 
5105 }
5106 
5107 
5108 /* For Emacs:
5109  * Local Variables:
5110  * mode:c
5111  * indent-tabs-mode:t
5112  * tab-width:4
5113  * c-basic-offset:4
5114  * End:
5115  * For VIM:
5116  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
5117  */
5118