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  * William King <william.king@quentustech.com>
28  * Emmanuel Schmidbauer <eschmidbauer@gmail.com>
29  *
30  * mod_valet_parking.c -- Valet Parking Module
31  *
32  */
33 #include <switch.h>
34 #define VALET_EVENT "valet_parking::info"
35 #define VALET_PROTO "park"
36 #define TOKEN_FREQ 5
37 
38 /* Prototypes */
39 SWITCH_MODULE_LOAD_FUNCTION(mod_valet_parking_load);
40 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_valet_parking_shutdown);
41 
42 /* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime)
43  * Defines a switch_loadable_module_function_table_t and a static const char[] modname
44  */
45 SWITCH_MODULE_DEFINITION(mod_valet_parking, mod_valet_parking_load, mod_valet_parking_shutdown, NULL);
46 
47 typedef struct {
48 	char ext[256];
49 	char uuid[SWITCH_UUID_FORMATTED_LENGTH + 1];
50 	time_t timeout;
51 	int bridged;
52 	time_t start_time;
53 } valet_token_t;
54 
55 typedef struct {
56 	switch_hash_t *hash;
57 	switch_mutex_t *mutex;
58 	switch_memory_pool_t *pool;
59 	time_t last_timeout_check;
60 	char *name;
61 } valet_lot_t;
62 
63 static valet_lot_t globals = { 0 };
64 
65 
valet_find_lot(const char * name,switch_bool_t create)66 static valet_lot_t *valet_find_lot(const char *name, switch_bool_t create)
67 {
68 	valet_lot_t *lot;
69 
70 	switch_mutex_lock(globals.mutex);
71 	lot = switch_core_hash_find(globals.hash, name);
72 	if (!lot && create) {
73 		switch_zmalloc(lot, sizeof(*lot));
74 		lot->name = strdup(name);
75 		switch_mutex_init(&lot->mutex, SWITCH_MUTEX_NESTED, globals.pool);
76 		switch_core_hash_init(&lot->hash);
77 		switch_core_hash_insert(globals.hash, name, lot);
78 	}
79 	switch_mutex_unlock(globals.mutex);
80 	return lot;
81 }
82 
valet_on_dtmf(switch_core_session_t * session,void * input,switch_input_type_t itype,void * buf,unsigned int buflen)83 static switch_status_t valet_on_dtmf(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
84 {
85 	switch (itype) {
86 	case SWITCH_INPUT_TYPE_DTMF:
87 		{
88 			switch_dtmf_t *dtmf = (switch_dtmf_t *) input;
89 
90 			if (dtmf->digit == '#') {
91 				return SWITCH_STATUS_BREAK;
92 			}
93 		}
94 	default:
95 		break;
96 	}
97 
98 	return SWITCH_STATUS_SUCCESS;
99 }
100 
check_timeouts(void)101 static void check_timeouts(void)
102 {
103 	switch_hash_index_t *hi;
104 	const void *var;
105 	void *val;
106 	time_t now;
107 	valet_lot_t *lot;
108 	switch_console_callback_match_t *matches = NULL;
109 	switch_console_callback_match_node_t *m;
110 	switch_hash_index_t *i_hi = NULL;
111 	const void *i_var;
112 	void *i_val;
113 	char *i_ext;
114 	valet_token_t *token;
115 
116 	now = switch_epoch_time_now(NULL);
117 
118 	switch_mutex_lock(globals.mutex);
119 	if (now - globals.last_timeout_check < TOKEN_FREQ) {
120 		switch_mutex_unlock(globals.mutex);
121 		return;
122 	}
123 
124 	globals.last_timeout_check = now;
125 	for (hi = switch_core_hash_first(globals.hash); hi; hi = switch_core_hash_next(&hi)) {
126 		switch_core_hash_this(hi, &var, NULL, &val);
127 		switch_console_push_match(&matches, (const char *) var);
128 	}
129 	switch_mutex_unlock(globals.mutex);
130 
131 
132 	if (matches) {
133 		for (m = matches->head; m; m = m->next) {
134 
135 			lot = valet_find_lot(m->val, SWITCH_FALSE);
136 			switch_mutex_lock(lot->mutex);
137 
138 		top:
139 
140 			for (i_hi = switch_core_hash_first_iter( lot->hash, i_hi); i_hi; i_hi = switch_core_hash_next(&i_hi)) {
141 				switch_core_hash_this(i_hi, &i_var, NULL, &i_val);
142 				i_ext = (char *) i_var;
143 				token = (valet_token_t *) i_val;
144 
145 				if (token->timeout > 0 && (token->timeout < now || token->timeout == 1)) {
146 					switch_core_hash_delete(lot->hash, i_ext);
147 					switch_safe_free(token);
148 					goto top;
149 				}
150 			}
151 			switch_safe_free(i_hi);
152 
153 			switch_mutex_unlock(lot->mutex);
154 		}
155 
156 		switch_console_free_matches(&matches);
157 	}
158 
159 }
160 
find_longest(valet_lot_t * lot,int min,int max)161 static int find_longest(valet_lot_t *lot, int min, int max)
162 {
163 
164 	switch_hash_index_t *i_hi;
165 	const void *i_var;
166 	void *i_val;
167 	valet_token_t *token;
168 	int longest_ext = 0;
169 	time_t longest = 0, cur = 0;
170 	time_t now = switch_epoch_time_now(NULL);
171 
172 	switch_mutex_lock(lot->mutex);
173 	for (i_hi = switch_core_hash_first(lot->hash); i_hi; i_hi = switch_core_hash_next(&i_hi)) {
174 		int i;
175 		switch_core_hash_this(i_hi, &i_var, NULL, &i_val);
176 		token = (valet_token_t *) i_val;
177 		cur = (now - token->start_time);
178 		i = atoi(token->ext);
179 
180 		if (cur > longest && i >= min && i <= max) {
181 			longest = cur;
182 			longest_ext = i;
183 		}
184 	}
185 	switch_mutex_unlock(lot->mutex);
186 
187 	return longest_ext;
188 }
189 
next_id(switch_core_session_t * session,valet_lot_t * lot,int min,int max,int in)190 static valet_token_t *next_id(switch_core_session_t *session, valet_lot_t *lot, int min, int max, int in)
191 {
192 	int i, r = 0;
193 	char buf[256] = "";
194 	valet_token_t *token;
195 
196 	if (!min) {
197 		min = 1;
198 	}
199 
200 	switch_mutex_lock(globals.mutex);
201 
202 	if (!in) {
203 		int longest = find_longest(lot, min, max);
204 		if (longest > 0) {
205 			switch_snprintf(buf, sizeof(buf), "%d", longest);
206 			switch_mutex_lock(lot->mutex);
207 			token = (valet_token_t *) switch_core_hash_find(lot->hash, buf);
208 			switch_mutex_unlock(lot->mutex);
209 			if (token) {
210 				goto end;
211 			}
212 		}
213 	}
214 
215 	for (i = min; (i <= max || max == 0); i++) {
216 		switch_snprintf(buf, sizeof(buf), "%d", i);
217 		switch_mutex_lock(lot->mutex);
218 		token = (valet_token_t *) switch_core_hash_find(lot->hash, buf);
219 		switch_mutex_unlock(lot->mutex);
220 
221 		if ((!in && token && !token->timeout)) {
222 			goto end;
223 		}
224 
225 		if (in && !token) {
226 			r = i;
227 			break;
228 		}
229 	}
230 
231 	token = NULL;
232 
233 	if (r) {
234 		switch_snprintf(buf, sizeof(buf), "%d", r);
235 		switch_zmalloc(token, sizeof(*token));
236 		switch_set_string(token->uuid, switch_core_session_get_uuid(session));
237 		switch_set_string(token->ext, buf);
238 		token->start_time = switch_epoch_time_now(NULL);
239 		switch_mutex_lock(lot->mutex);
240 		switch_core_hash_insert(lot->hash, buf, token);
241 		switch_mutex_unlock(lot->mutex);
242 	}
243 
244  end:
245 
246 	switch_mutex_unlock(globals.mutex);
247 
248 	return token;
249 }
250 
valet_lot_count(valet_lot_t * lot)251 static int valet_lot_count(valet_lot_t *lot)
252 {
253 	switch_hash_index_t *i_hi;
254 	const void *i_var;
255 	void *i_val;
256 	valet_token_t *token;
257 	int count = 0;
258 	time_t now;
259 
260 	now = switch_epoch_time_now(NULL);
261 
262 	switch_mutex_lock(lot->mutex);
263 	for (i_hi = switch_core_hash_first(lot->hash); i_hi; i_hi = switch_core_hash_next(&i_hi)) {
264 		switch_core_hash_this(i_hi, &i_var, NULL, &i_val);
265 		token = (valet_token_t *) i_val;
266 		if (token->timeout > 0 && (token->timeout < now || token->timeout == 1)) {
267 			continue;
268 		}
269 		count++;
270 	}
271 	switch_mutex_unlock(lot->mutex);
272 
273 	return count;
274 }
275 
276 static int EC = 0;
277 
valet_send_presence(const char * lot_name,valet_lot_t * lot,valet_token_t * token,switch_bool_t in)278 static void valet_send_presence(const char *lot_name, valet_lot_t *lot, valet_token_t *token, switch_bool_t in)
279 {
280 
281 	char *domain_name, *dup_lot_name = NULL, *dup_domain_name = NULL;
282 	switch_event_t *event;
283 	int count;
284 
285 
286 	dup_lot_name = strdup(lot_name);
287 	lot_name = dup_lot_name;
288 
289 	if ((domain_name = strchr(dup_lot_name, '@'))) {
290 		*domain_name++ = '\0';
291 	}
292 
293 	if (zstr(domain_name)) {
294 		dup_domain_name = switch_core_get_domain(SWITCH_TRUE);
295 		domain_name = dup_domain_name;
296 	}
297 
298 	if (zstr(domain_name)) {
299 		domain_name = "cluecon.com";
300 	}
301 
302 	count = valet_lot_count(lot);
303 
304 	if (count > 0) {
305 		if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
306 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VALET_PROTO);
307 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", lot_name);
308 			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", lot_name, domain_name);
309 
310 
311 			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "force-status", "Active (%d caller%s)", count, count == 1 ? "" : "s");
312 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "active");
313 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
314 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
315 			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
316 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", lot_name);
317 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_ROUTING");
318 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "confirmed");
319 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", "inbound");
320 			switch_event_fire(&event);
321 		}
322 	} else {
323 		if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
324 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VALET_PROTO);
325 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", lot_name);
326 			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", lot_name, domain_name);
327 
328 
329 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", "Empty");
330 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "unknown");
331 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
332 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
333 			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
334 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", lot_name);
335 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_HANGUP");
336 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "terminated");
337 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", "inbound");
338 			switch_event_fire(&event);
339 		}
340 	}
341 
342 
343 
344 	if (in) {
345 		if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
346 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VALET_PROTO);
347 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", token->ext);
348 			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", token->ext, domain_name);
349 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", token->bridged == 0 ? "Holding" : "Active");
350 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "active");
351 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
352 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
353 			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
354 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", token->ext);
355 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_ROUTING");
356 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "confirmed");
357 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", token->bridged == 0 ? "outbound" : "inbound");
358 			switch_event_fire(&event);
359 		}
360 	} else {
361 		if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
362 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VALET_PROTO);
363 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", token->ext);
364 			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", token->ext, domain_name);
365 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", "Empty");
366 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "unknown");
367 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
368 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
369 			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
370 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", token->ext);
371 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_HANGUP");
372 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "terminated");
373 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", "inbound");
374 			switch_event_fire(&event);
375 		}
376 	}
377 
378 	switch_safe_free(dup_domain_name);
379 	switch_safe_free(dup_lot_name);
380 
381 }
382 
383 struct read_frame_data {
384 	const char *dp;
385 	const char *exten;
386 	const char *context;
387 	long to;
388 };
389 
read_frame_callback(switch_core_session_t * session,switch_frame_t * frame,void * user_data)390 static switch_status_t read_frame_callback(switch_core_session_t *session, switch_frame_t *frame, void *user_data)
391 {
392 	struct read_frame_data *rf = (struct read_frame_data *) user_data;
393 
394 	if (--rf->to <= 0) {
395 		rf->to = -1;
396 		return SWITCH_STATUS_FALSE;
397 	}
398 
399 	return SWITCH_STATUS_SUCCESS;
400 }
401 
402 #define VALET_APP_SYNTAX "<lotname> <extension>|[ask [<min>] [<max>] [<to>] [<prompt>]|auto [in|out] [min] [max]]"
SWITCH_STANDARD_APP(valet_parking_function)403 SWITCH_STANDARD_APP(valet_parking_function)
404 {
405 	char *argv[6], *lbuf;
406 	int argc;
407 	switch_channel_t *channel = switch_core_session_get_channel(session);
408 	switch_event_t *event;
409 	char dtmf_buf[128] = "";
410 	int is_auto = 0, play_announce = 1;
411 	const char *var;
412 	valet_token_t *token = NULL;
413 	struct read_frame_data rf = { 0 };
414 	long to_val = 0;
415 
416 	check_timeouts();
417 
418 	if ((var = switch_channel_get_variable(channel, "valet_announce_slot"))) {
419 		play_announce = switch_true(var);
420 	}
421 
422 	if (!zstr(data) && (lbuf = switch_core_session_strdup(session, data))
423 		&& (argc = switch_separate_string(lbuf, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) >= 2) {
424 		char *lot_name = argv[0];
425 		char *ext = argv[1];
426 		valet_lot_t *lot;
427 		const char *uuid;
428 		const char *music = "silence";
429 		const char *tmp = NULL;
430 		switch_status_t status;
431 		switch_input_args_t args = { 0 };
432 		char dbuf[10];
433 		char *dest;
434 		int in = -1;
435 
436 		const char *timeout, *orbit_exten, *orbit_dialplan, *orbit_context;
437 		char *timeout_str = "", *orbit_exten_str = "", *orbit_dialplan_str = "", *orbit_context_str = "";
438 
439 		lot = valet_find_lot(lot_name, SWITCH_TRUE);
440 		switch_assert(lot);
441 
442 		if (!strcasecmp(ext, "auto")) {
443 			const char *io = argv[2];
444 			const char *min = argv[3];
445 			const char *max = argv[4];
446 			int min_i, max_i;
447 
448 			if (argc < 5) {
449 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Usage: %s\n", VALET_APP_SYNTAX);
450 				return;
451 			}
452 
453 			if (io) {
454 				if (!strcasecmp(io, "in")) {
455 					in = 1;
456 					is_auto = 1;
457 				} else if (!strcasecmp(io, "out")) {
458 					in = 0;
459 				}
460 			}
461 
462 			if (in < 0) {
463 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Usage: %s\n", VALET_APP_SYNTAX);
464 				return;
465 			}
466 
467 			min_i = atoi(min);
468 			max_i = atoi(max);
469 
470 			if (!(token = next_id(session, lot, min_i, max_i, in))) {
471 				switch_ivr_phrase_macro(session, in ? "valet_lot_full" : "valet_lot_empty", "", NULL, NULL);
472 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s lot is %s.\n", switch_channel_get_name(channel), in ? "full" : "empty");
473 				return;
474 			}
475 
476 			switch_snprintf(dtmf_buf, sizeof(dtmf_buf), "%s", token->ext);
477 			ext = dtmf_buf;
478 
479 		} else if (!strcasecmp(ext, "ask")) {
480 			const char *prompt = "ivr/ivr-enter_ext_pound.wav";
481 			int min = 1;
482 			int max = 11;
483 			int to = 10000;
484 			int i;
485 
486 			tmp = argv[2] ? argv[2] : switch_channel_get_variable(channel, "valet_ext_min");
487 			if (tmp) {
488 				if ((i = atoi(tmp)) > 0) {
489 					min = i;
490 				}
491 			}
492 
493 			tmp = argv[3] ? argv[3] : switch_channel_get_variable(channel, "valet_ext_max");
494 			if (tmp) {
495 				if ((i = atoi(tmp)) > 0) {
496 					max = i;
497 				}
498 			}
499 
500 			tmp = argv[4] ? argv[4] : switch_channel_get_variable(channel, "valet_ext_to");
501 			if (tmp) {
502 				if ((i = atoi(tmp)) > 0) {
503 					to = i;
504 				}
505 			}
506 
507 			tmp = argv[5] ? argv[5] : switch_channel_get_variable(channel, "valet_ext_prompt");
508 			if (tmp) {
509 				prompt = tmp;
510 			}
511 
512 			do {
513 				status = switch_ivr_read(session, min, max, prompt, NULL, dtmf_buf, sizeof(dtmf_buf), to, "#", 0);
514 			} while (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_FALSE);
515 
516 			if (status == SWITCH_STATUS_SUCCESS) {
517 				ext = dtmf_buf;
518 			} else {
519 				switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
520 			}
521 		}
522 
523 		if (!token || !in) {
524 
525 			if (!token) {
526 				switch_mutex_lock(lot->mutex);
527 				token = (valet_token_t *) switch_core_hash_find(lot->hash, ext);
528 				switch_mutex_unlock(lot->mutex);
529 			}
530 
531 			if (token && !token->bridged) {
532 				switch_core_session_t *b_session;
533 
534 				if (token->timeout) {
535 					const char *var = switch_channel_get_variable(channel, "valet_ticket");
536 
537 					if (!zstr(var)) {
538 						if (!strcmp(var, token->uuid)) {
539 							switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Valet ticket %s accepted.\n", var);
540 							token->timeout = 0;
541 							switch_channel_set_variable(channel, "valet_ticket", NULL);
542 						} else {
543 							switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid token %s\n", token->uuid);
544 							switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
545 							return;
546 						}
547 					}
548 				}
549 
550 				if (!zstr(token->uuid) && (b_session = switch_core_session_locate(token->uuid))) {
551 					if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, VALET_EVENT) == SWITCH_STATUS_SUCCESS) {
552 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Lot-Name", lot_name);
553 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Extension", ext);
554 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "bridge");
555 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-To-UUID", switch_core_session_get_uuid(session));
556 						switch_channel_event_set_data(switch_core_session_get_channel(b_session), event);
557 						switch_event_fire(&event);
558 						switch_core_session_rwunlock(b_session);
559 						token->timeout = 0;
560 						token->bridged = 1;
561 
562 						switch_ivr_uuid_bridge(token->uuid, switch_core_session_get_uuid(session));
563 
564 						return;
565 					}
566 				}
567 			}
568 
569 			if (token) {
570 				switch_mutex_lock(lot->mutex);
571 				switch_core_hash_delete(lot->hash, token->ext);
572 				switch_mutex_unlock(lot->mutex);
573 				memset(token, 0, sizeof(*token));
574 			} else {
575 				switch_zmalloc(token, sizeof(*token));
576 			}
577 			switch_set_string(token->uuid, switch_core_session_get_uuid(session));
578 			switch_set_string(token->ext, ext);
579 			token->start_time = switch_epoch_time_now(NULL);
580 			switch_mutex_lock(lot->mutex);
581 			switch_core_hash_insert(lot->hash, ext, token);
582 			switch_mutex_unlock(lot->mutex);
583 		}
584 
585 		if (!(tmp = switch_channel_get_variable(channel, "valet_hold_music"))) {
586 			tmp = switch_channel_get_hold_music(channel);
587 		}
588 
589 		if (tmp) {
590 			music = tmp;
591 		}
592 
593 		if (!strcasecmp(music, "silence")) {
594 			music = "silence_stream://-1";
595 		}
596 
597 		if ((orbit_exten = switch_channel_get_variable(channel, "valet_parking_orbit_exten"))) {
598 			orbit_exten_str = switch_core_session_sprintf(session, "set:valet_parking_orbit_exten=%s,", orbit_exten);
599 		}
600 
601 		if ((orbit_dialplan = switch_channel_get_variable(channel, "valet_parking_orbit_dialplan"))) {
602 			orbit_dialplan_str = switch_core_session_sprintf(session, "set:valet_parking_orbit_dialplan=%s,", orbit_dialplan);
603 		}
604 
605 		if ((orbit_context = switch_channel_get_variable(channel, "valet_parking_orbit_context"))) {
606 			orbit_context_str = switch_core_session_sprintf(session, "set:valet_parking_orbit_context=%s,", orbit_context);
607 		}
608 
609 		if ((timeout = switch_channel_get_variable(channel, "valet_parking_timeout"))) {
610 			timeout_str = switch_core_session_sprintf(session, "set:valet_parking_timeout=%s,", timeout);
611 		}
612 
613 		dest = switch_core_session_sprintf(session, "%s%s%s%s"
614 										   "set:valet_ticket=%s,set:valet_hold_music='%s',sleep:1000,valet_park:%s %s",
615 										   timeout_str,
616 										   orbit_exten_str,
617 										   orbit_dialplan_str,
618 										   orbit_context_str,
619 										   token->uuid, music, lot_name, ext);
620 		switch_channel_set_variable(channel, "inline_destination", dest);
621 
622 		if (is_auto) {
623 			char tmp[512] = "";
624 			switch_snprintf(tmp, sizeof(tmp), "%s:%s", lot_name, ext);
625 
626 			if ((uuid = switch_channel_get_partner_uuid(channel))) {
627 				switch_core_session_t *b_session;
628 
629 				if ((b_session = switch_core_session_locate(uuid))) {
630 					token->timeout = switch_epoch_time_now(NULL) + TOKEN_FREQ;
631 					if (play_announce) {
632 						switch_ivr_sleep(session, 1500, SWITCH_TRUE, NULL);
633 						switch_ivr_phrase_macro(session, "valet_announce_ext", tmp, NULL, NULL);
634 					}
635 					switch_ivr_session_transfer(b_session, dest, "inline", NULL);
636 					switch_core_session_rwunlock(b_session);
637 					switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
638 					goto end;
639 				}
640 			}
641 
642 			if (play_announce) {
643 				switch_ivr_sleep(session, 1500, SWITCH_TRUE, NULL);
644 				switch_ivr_phrase_macro(session, "valet_announce_ext", tmp, NULL, NULL);
645 			}
646 		}
647 
648 
649 		if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, VALET_EVENT) == SWITCH_STATUS_SUCCESS) {
650 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Lot-Name", lot_name);
651 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Extension", ext);
652 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "hold");
653 			switch_channel_event_set_data(channel, event);
654 			switch_event_fire(&event);
655 		}
656 
657 		switch_channel_set_variable(channel, "valet_lot_extension", ext);
658 
659 		valet_send_presence(lot_name, lot, token, SWITCH_TRUE);
660 
661 		if ((rf.exten = switch_channel_get_variable(channel, "valet_parking_orbit_exten"))) {
662 			to_val = 60;
663 		}
664 
665 		if ((var = switch_channel_get_variable(channel, "valet_parking_timeout"))) {
666 			long tmp = atol(var);
667 
668 			if (tmp > 0) {
669 				to_val = tmp;
670 			}
671 		}
672 
673 		if (to_val) {
674 			switch_codec_implementation_t read_impl;
675 			switch_core_session_get_read_impl(session, &read_impl);
676 
677 			rf.to = (1000 / (read_impl.microseconds_per_packet / 1000)) * to_val;
678 			rf.dp = switch_channel_get_variable(channel, "valet_parking_orbit_dialplan");
679 			rf.context = switch_channel_get_variable(channel, "valet_parking_orbit_context");
680 		}
681 
682 
683 		args.input_callback = valet_on_dtmf;
684 		args.buf = dbuf;
685 		args.buflen = sizeof(dbuf);
686 
687 		if (rf.to) {
688 			args.read_frame_callback = read_frame_callback;
689 			args.user_data = &rf;
690 		}
691 
692 		while(switch_channel_ready(channel)) {
693 			switch_status_t pstatus = switch_ivr_play_file(session, NULL, music, &args);
694 
695 			if (rf.to == -1) {
696 				if (!zstr(rf.exten)) {
697 					switch_ivr_session_transfer(session, rf.exten, rf.dp, rf.context);
698 				}
699 				break;
700 			}
701 
702 			if (pstatus == SWITCH_STATUS_BREAK || pstatus == SWITCH_STATUS_TIMEOUT) {
703 				break;
704 			}
705 		}
706 
707 		if (token) {
708 			token->timeout = 1;
709 			valet_send_presence(lot_name, lot, token, SWITCH_FALSE);
710 			token = NULL;
711 		}
712 
713 
714 		if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, VALET_EVENT) == SWITCH_STATUS_SUCCESS) {
715 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Lot-Name", lot_name);
716 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Extension", ext);
717 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "exit");
718 			switch_channel_event_set_data(channel, event);
719 			switch_event_fire(&event);
720 		}
721 	} else {
722 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Usage: %s\n", VALET_APP_SYNTAX);
723 	}
724 
725  end:
726 
727 	if (token) {
728 		token->timeout = 1;
729 	}
730 
731 }
732 
SWITCH_STANDARD_API(valet_info_function)733 SWITCH_STANDARD_API(valet_info_function)
734 {
735 	switch_hash_index_t *hi;
736 	const void *var;
737 	void *val;
738 	char *name;
739 	valet_lot_t *lot;
740 
741 	stream->write_function(stream, "<lots>\n");
742 
743 	switch_mutex_lock(globals.mutex);
744 	for (hi = switch_core_hash_first(globals.hash); hi; hi = switch_core_hash_next(&hi)) {
745 		switch_hash_index_t *i_hi;
746 		const void *i_var;
747 		void *i_val;
748 		char *i_ext;
749 
750 		switch_core_hash_this(hi, &var, NULL, &val);
751 		name = (char *) var;
752 		lot = (valet_lot_t *) val;
753 
754 		if (!zstr(cmd) && strcasecmp(cmd, name))
755 			continue;
756 
757 		stream->write_function(stream, "  <lot name=\"%s\">\n", name);
758 
759 		switch_mutex_lock(lot->mutex);
760 		for (i_hi = switch_core_hash_first(lot->hash); i_hi; i_hi = switch_core_hash_next(&i_hi)) {
761 			valet_token_t *token;
762 
763 			switch_core_hash_this(i_hi, &i_var, NULL, &i_val);
764 			i_ext = (char *) i_var;
765 			token = (valet_token_t *) i_val;
766 
767 			if (!token->timeout) {
768 				stream->write_function(stream, "    <extension uuid=\"%s\">%s</extension>\n", token->uuid, i_ext);
769 			}
770 		}
771 		switch_mutex_unlock(lot->mutex);
772 
773 		stream->write_function(stream, "  </lot>\n");
774 	}
775 
776 	stream->write_function(stream, "</lots>\n");
777 
778 	switch_mutex_unlock(globals.mutex);
779 
780 	return SWITCH_STATUS_SUCCESS;
781 }
782 
783 
pres_event_handler(switch_event_t * event)784 static void pres_event_handler(switch_event_t *event)
785 {
786 	char *to = switch_event_get_header(event, "to");
787 	char *dup_to = NULL, *lot_name, *dup_lot_name = NULL, *domain_name;
788 	valet_lot_t *lot;
789 	int found = 0;
790 	const char *call_id;
791 
792 	if (!to || strncasecmp(to, "park+", 5) || !strchr(to, '@')) {
793 		return;
794 	}
795 
796 	if (!(dup_to = strdup(to))) {
797 		return;
798 	}
799 
800 	lot_name = dup_to + 5;
801 
802 	if ((domain_name = strchr(lot_name, '@'))) {
803 		*domain_name++ = '\0';
804 	}
805 
806 	call_id = switch_event_get_header(event, "sub-call-id");
807 
808 	dup_lot_name = switch_mprintf("%q@%q", lot_name, domain_name);
809 
810 	if ((lot = valet_find_lot(lot_name, SWITCH_FALSE)) || (dup_lot_name && (lot = valet_find_lot(dup_lot_name, SWITCH_FALSE)))) {
811 		int count = valet_lot_count(lot);
812 
813 		if (count) {
814 			if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
815 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VALET_PROTO);
816 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", lot_name);
817 				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", lot_name, domain_name);
818 
819 				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "force-status", "Active (%d caller%s)", count, count == 1 ? "" : "s");
820 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "active");
821 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
822 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
823 				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
824 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", lot_name);
825 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_ROUTING");
826 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "confirmed");
827 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", "inbound");
828 				if (call_id) {
829 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-id", call_id);
830 				}
831 				switch_event_fire(&event);
832 			}
833 			found++;
834 		} else {
835 			if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
836 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VALET_PROTO);
837 				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", lot_name, domain_name);
838 
839 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", "Empty");
840 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "unknown");
841 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
842 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
843 				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
844 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", lot_name);
845 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_HANGUP");
846 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "terminated");
847 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", "inbound");
848 				if (call_id) {
849 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-id", call_id);
850 				}
851 				switch_event_fire(&event);
852 			}
853 		}
854 	} else {
855 		switch_console_callback_match_t *matches = NULL;
856 		switch_console_callback_match_node_t *m;
857 		switch_hash_index_t *hi;
858 		const void *var;
859 		void *val;
860 		const char *nvar;
861 
862 		switch_mutex_lock(globals.mutex);
863 		for (hi = switch_core_hash_first(globals.hash); hi; hi = switch_core_hash_next(&hi)) {
864 			switch_core_hash_this(hi, &var, NULL, &val);
865 			nvar = (const char *) var;
866 
867 			if (!strchr(nvar, '@') || switch_stristr(domain_name, nvar)) {
868 				switch_console_push_match(&matches, nvar);
869 			}
870 		}
871 		switch_mutex_unlock(globals.mutex);
872 
873 		if (matches) {
874 			valet_token_t *token;
875 
876 			for (m = matches->head; !found && m; m = m->next) {
877 				lot = valet_find_lot(m->val, SWITCH_FALSE);
878 				switch_mutex_lock(lot->mutex);
879 
880 				if ((token = (valet_token_t *) switch_core_hash_find(lot->hash, lot_name)) && !token->timeout) {
881 					found++;
882 
883 					if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
884 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VALET_PROTO);
885 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", lot_name);
886 						switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", lot_name, domain_name);
887 
888 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", token->bridged == 0 ? "Holding" : "Active");
889 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
890 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
891 						switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
892 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", lot_name);
893 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_ROUTING");
894 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "confirmed");
895 						switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", token->bridged == 0 ? "outbound" : "inbound");
896 						if (call_id) {
897 							switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-id", call_id);
898 						}
899 						switch_event_fire(&event);
900 					}
901 				}
902 
903 				switch_mutex_unlock(lot->mutex);
904 			}
905 			switch_console_free_matches(&matches);
906 		}
907 	}
908 
909 
910 	if (!found && switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
911 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VALET_PROTO);
912 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", lot_name);
913 		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", lot_name, domain_name);
914 
915 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", "Empty");
916 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "unknown");
917 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
918 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
919 		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
920 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", lot_name);
921 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_HANGUP");
922 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "terminated");
923 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", "inbound");
924 		if (call_id) {
925 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-id", call_id);
926 		}
927 		switch_event_fire(&event);
928 	}
929 
930 	switch_safe_free(dup_to);
931 	switch_safe_free(dup_lot_name);
932 }
933 
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_valet_parking_shutdown)934 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_valet_parking_shutdown)
935 {
936 	switch_event_unbind_callback(pres_event_handler);
937 	switch_core_hash_destroy(&globals.hash);
938 	return SWITCH_STATUS_SUCCESS;
939 }
940 
941 /* Macro expands to: switch_status_t mod_valet_parking_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */
SWITCH_MODULE_LOAD_FUNCTION(mod_valet_parking_load)942 SWITCH_MODULE_LOAD_FUNCTION(mod_valet_parking_load)
943 {
944 	switch_application_interface_t *app_interface;
945 	switch_api_interface_t *api_interface;
946 
947 	/* create/register custom event message type */
948 	if (switch_event_reserve_subclass(VALET_EVENT) != SWITCH_STATUS_SUCCESS) {
949 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", VALET_EVENT);
950 		return SWITCH_STATUS_TERM;
951 	}
952 
953 	switch_event_bind(modname, SWITCH_EVENT_PRESENCE_PROBE, SWITCH_EVENT_SUBCLASS_ANY, pres_event_handler, NULL);
954 
955 	memset(&globals, 0, sizeof(globals));
956 
957 	globals.pool = pool;
958 	switch_core_hash_init(&globals.hash);
959 	switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
960 
961 	/* connect my internal structure to the blank pointer passed to me */
962 	*module_interface = switch_loadable_module_create_module_interface(pool, modname);
963 
964 	SWITCH_ADD_APP(app_interface, "valet_park", "valet_park", "valet_park", valet_parking_function, VALET_APP_SYNTAX, SAF_NONE);
965 	SWITCH_ADD_API(api_interface, "valet_info", "Valet Parking Info", valet_info_function, "[<lot name>]");
966 
967 	return SWITCH_STATUS_NOUNLOAD;
968 }
969 
970 /* For Emacs:
971  * Local Variables:
972  * mode:c
973  * indent-tabs-mode:t
974  * tab-width:4
975  * c-basic-offset:4
976  * End:
977  * For VIM:
978  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet
979  */
980