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