1 /*
2 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 * Copyright (C) 2010, Mathieu Parent <math.parent@gmail.com>
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 * Mathieu Parent <math.parent@gmail.com>
21 * Portions created by the Initial Developer are Copyright (C)
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Mathieu Parent <math.parent@gmail.com>
27 *
28 *
29 * mod_skinny.c -- Skinny Call Control Protocol (SCCP) Endpoint Module
30 *
31 */
32 #include <switch.h>
33 #include "mod_skinny.h"
34 #include "skinny_protocol.h"
35 #include "skinny_server.h"
36 #include "skinny_tables.h"
37 #include "skinny_api.h"
38
39 SWITCH_MODULE_LOAD_FUNCTION(mod_skinny_load);
40 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_skinny_shutdown);
41
42 SWITCH_MODULE_DEFINITION(mod_skinny, mod_skinny_load, mod_skinny_shutdown, NULL);
43
44 switch_endpoint_interface_t *skinny_endpoint_interface;
45
46 skinny_globals_t skinny_globals;
47
48 /*****************************************************************************/
49 /* SQL TABLES */
50 /*****************************************************************************/
51 static char devices_sql[] =
52 "CREATE TABLE skinny_devices (\n"
53 " name VARCHAR(16),\n"
54 " user_id INTEGER,\n"
55 " instance INTEGER,\n"
56 " ip VARCHAR(15),\n"
57 " type INTEGER,\n"
58 " max_streams INTEGER,\n"
59 " port INTEGER,\n"
60 " codec_string VARCHAR(255),\n"
61 " headset INTEGER,\n"
62 " handset INTEGER,\n"
63 " speaker INTEGER\n"
64 ");\n";
65
66 static char lines_sql[] =
67 "CREATE TABLE skinny_lines (\n"
68 " device_name VARCHAR(16),\n"
69 " device_instance INTEGER,\n"
70 " position INTEGER,\n"
71 " line_instance INTEGER,\n"
72 " label VARCHAR(40),\n"
73 " value VARCHAR(24),\n"
74 " caller_name VARCHAR(44),\n"
75 " ring_on_idle INTEGER,\n"
76 " ring_on_active INTEGER,\n"
77 " busy_trigger INTEGER,\n"
78 " forward_all VARCHAR(255),\n"
79 " forward_busy VARCHAR(255),\n"
80 " forward_noanswer VARCHAR(255),\n"
81 " noanswer_duration INTEGER\n"
82 ");\n";
83
84 static char buttons_sql[] =
85 "CREATE TABLE skinny_buttons (\n"
86 " device_name VARCHAR(16),\n"
87 " device_instance INTEGER,\n"
88 " position INTEGER,\n"
89 " type INTEGER,\n"
90 " label VARCHAR(40),\n"
91 " value VARCHAR(255),\n"
92 " settings VARCHAR(44)\n"
93 ");\n";
94
95 static char active_lines_sql[] =
96 "CREATE TABLE skinny_active_lines (\n"
97 " device_name VARCHAR(16),\n"
98 " device_instance INTEGER,\n"
99 " line_instance INTEGER,\n"
100 " channel_uuid VARCHAR(256),\n"
101 " call_id INTEGER,\n"
102 " call_state INTEGER\n"
103 ");\n";
104
105 /*****************************************************************************/
106 /* TEXT FUNCTIONS */
107 /*****************************************************************************/
skinny_format_message(const char * str)108 char *skinny_format_message(const char *str)
109 {
110 char *tmp;
111 switch_size_t i;
112
113 /* Look for \200, if found, next character indicates string id */
114 char match = (char) 128;
115
116 tmp = switch_mprintf("");
117
118 if (zstr(str)) {
119 return tmp;
120 }
121
122 for (i=0; i<strlen(str); i++)
123 {
124 char *old = tmp;
125
126 if ( str[i] == match ) {
127 if ( tmp[0] ) {
128 tmp = switch_mprintf("%s [%s] ", old, skinny_textid2str(str[i+1]));
129 } else {
130 tmp = switch_mprintf("[%s] ", skinny_textid2str(str[i+1]));
131 }
132 switch_safe_free(old);
133 i++;
134 } else if ( !switch_isprint(str[i]) ) {
135 tmp = switch_mprintf("%s\\x%.2X", old, str[i]);
136 switch_safe_free(old);
137 } else {
138 tmp = switch_mprintf("%s%c", old, str[i]);
139 switch_safe_free(old);
140 }
141 }
142
143 return tmp;
144 }
145
146 /*****************************************************************************/
147 /* PROFILES FUNCTIONS */
148 /*****************************************************************************/
skinny_profile_dump(const skinny_profile_t * profile,switch_stream_handle_t * stream)149 switch_status_t skinny_profile_dump(const skinny_profile_t *profile, switch_stream_handle_t *stream)
150 {
151 const char *line = "=================================================================================================";
152 switch_assert(profile);
153 stream->write_function(stream, "%s\n", line);
154 /* prefs */
155 stream->write_function(stream, "Name \t%s\n", profile->name);
156 stream->write_function(stream, "Domain Name \t%s\n", profile->domain);
157 stream->write_function(stream, "IP \t%s\n", profile->ip);
158 stream->write_function(stream, "Port \t%d\n", profile->port);
159 stream->write_function(stream, "Dialplan \t%s\n", profile->dialplan);
160 stream->write_function(stream, "Context \t%s\n", profile->context);
161 stream->write_function(stream, "Patterns-Dialplan \t%s\n", profile->patterns_dialplan);
162 stream->write_function(stream, "Patterns-Context \t%s\n", profile->patterns_context);
163 stream->write_function(stream, "Keep-Alive \t%d\n", profile->keep_alive);
164 stream->write_function(stream, "Digit-Timeout \t%d\n", profile->digit_timeout);
165 stream->write_function(stream, "Date-Format \t%s\n", profile->date_format);
166 stream->write_function(stream, "DBName \t%s\n", profile->dbname ? profile->dbname : switch_str_nil(profile->odbc_dsn));
167 stream->write_function(stream, "Debug \t%d\n", profile->debug);
168 stream->write_function(stream, "Auto-Restart \t%d\n", profile->auto_restart);
169 stream->write_function(stream, "Non-Blocking \t%d\n", profile->non_blocking);
170 /* stats */
171 stream->write_function(stream, "CALLS-IN \t%d\n", profile->ib_calls);
172 stream->write_function(stream, "FAILED-CALLS-IN \t%d\n", profile->ib_failed_calls);
173 stream->write_function(stream, "CALLS-OUT \t%d\n", profile->ob_calls);
174 stream->write_function(stream, "FAILED-CALLS-OUT \t%d\n", profile->ob_failed_calls);
175 /* listener */
176 stream->write_function(stream, "Listener-Threads \t%d\n", profile->listener_threads);
177 stream->write_function(stream, "Ext-Voicemail \t%s\n", profile->ext_voicemail);
178 stream->write_function(stream, "Ext-Redial \t%s\n", profile->ext_redial);
179 stream->write_function(stream, "Ext-MeetMe \t%s\n", profile->ext_meetme);
180 stream->write_function(stream, "Ext-PickUp \t%s\n", profile->ext_pickup);
181 stream->write_function(stream, "Ext-CFwdAll \t%s\n", profile->ext_cfwdall);
182 stream->write_function(stream, "%s\n", line);
183
184 return SWITCH_STATUS_SUCCESS;
185 }
186
187
skinny_find_profile(const char * profile_name)188 skinny_profile_t *skinny_find_profile(const char *profile_name)
189 {
190 skinny_profile_t *profile;
191 switch_mutex_lock(skinny_globals.mutex);
192 profile = (skinny_profile_t *) switch_core_hash_find(skinny_globals.profile_hash, profile_name);
193 switch_mutex_unlock(skinny_globals.mutex);
194 return profile;
195 }
196
skinny_find_profile_by_domain(const char * domain_name)197 skinny_profile_t *skinny_find_profile_by_domain(const char *domain_name)
198 {
199
200 switch_hash_index_t *hi;
201 void *val;
202 skinny_profile_t *profile = NULL, *tmp_profile;
203
204 switch_mutex_lock(skinny_globals.mutex);
205 for (hi = switch_core_hash_first(skinny_globals.profile_hash); hi; hi = switch_core_hash_next(&hi)) {
206 switch_core_hash_this(hi, NULL, NULL, &val);
207 tmp_profile = (skinny_profile_t *) val;
208
209 switch_mutex_lock(tmp_profile->listener_mutex);
210 if (!strcmp(tmp_profile->domain, domain_name)) {
211 profile = tmp_profile;
212 }
213 switch_mutex_unlock(tmp_profile->listener_mutex);
214 if (profile) {
215 break;
216 }
217 }
218 switch_safe_free(hi);
219 switch_mutex_unlock(skinny_globals.mutex);
220
221 return profile;
222 }
223
skinny_profile_find_listener_by_device_name(skinny_profile_t * profile,const char * device_name,listener_t ** listener)224 switch_status_t skinny_profile_find_listener_by_device_name(skinny_profile_t *profile, const char *device_name, listener_t **listener)
225 {
226 listener_t *l;
227
228 switch_mutex_lock(profile->listener_mutex);
229 for (l = profile->listeners; l; l = l->next) {
230 if (!strcmp(l->device_name, device_name)) {
231 *listener = l;
232 }
233 }
234 switch_mutex_unlock(profile->listener_mutex);
235
236 return SWITCH_STATUS_SUCCESS;
237 }
238
skinny_profile_find_listener_by_device_name_and_instance(skinny_profile_t * profile,const char * device_name,uint32_t device_instance,listener_t ** listener)239 switch_status_t skinny_profile_find_listener_by_device_name_and_instance(skinny_profile_t *profile, const char *device_name, uint32_t device_instance, listener_t **listener)
240 {
241 listener_t *l;
242
243 switch_mutex_lock(profile->listener_mutex);
244 for (l = profile->listeners; l; l = l->next) {
245 if (!strcmp(l->device_name, device_name) && (l->device_instance == device_instance)) {
246 *listener = l;
247 }
248 }
249 switch_mutex_unlock(profile->listener_mutex);
250
251 return SWITCH_STATUS_SUCCESS;
252 }
253
254 struct skinny_profile_find_session_uuid_helper {
255 skinny_profile_t *profile;
256 char *channel_uuid;
257 uint32_t line_instance;
258 };
259
skinny_profile_find_session_uuid_callback(void * pArg,int argc,char ** argv,char ** columnNames)260 int skinny_profile_find_session_uuid_callback(void *pArg, int argc, char **argv, char **columnNames)
261 {
262 struct skinny_profile_find_session_uuid_helper *helper = pArg;
263
264 char *channel_uuid = argv[0];
265 uint32_t line_instance = atoi(argv[1]);
266
267 if(helper->channel_uuid == NULL) {
268 helper->channel_uuid = switch_mprintf("%s", channel_uuid);
269 helper->line_instance = line_instance;
270 }
271
272 return 0;
273 }
274
skinny_profile_find_session_uuid(skinny_profile_t * profile,listener_t * listener,uint32_t * line_instance_p,uint32_t call_id)275 char * skinny_profile_find_session_uuid(skinny_profile_t *profile, listener_t *listener, uint32_t *line_instance_p, uint32_t call_id)
276 {
277 struct skinny_profile_find_session_uuid_helper helper = {0};
278 char *sql;
279 char *device_condition = NULL;
280 char *line_instance_condition = NULL;
281 char *call_id_condition = NULL;
282
283 switch_assert(profile);
284 helper.profile = profile;
285 helper.channel_uuid = NULL;
286
287 if(listener) {
288 device_condition = switch_mprintf("device_name='%q' AND device_instance=%d",
289 listener->device_name, listener->device_instance);
290 } else {
291 device_condition = switch_mprintf("1=1");
292 }
293 switch_assert(device_condition);
294 if(*line_instance_p > 0) {
295 line_instance_condition = switch_mprintf("line_instance=%d", *line_instance_p);
296 } else {
297 line_instance_condition = switch_mprintf("1=1");
298 }
299 switch_assert(line_instance_condition);
300 if(call_id > 0) {
301 call_id_condition = switch_mprintf("call_id=%d", call_id);
302 } else {
303 call_id_condition = switch_mprintf("1=1");
304 }
305 switch_assert(call_id_condition);
306
307 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,
308 "Attempting to find active call with criteria (%s and %s and %s)\n",
309 device_condition, line_instance_condition, call_id_condition);
310
311 if((sql = switch_mprintf(
312 "SELECT channel_uuid, line_instance "
313 "FROM skinny_active_lines "
314 "WHERE %s AND %s AND %s "
315 "ORDER BY call_state, channel_uuid", /* off hook first */
316 device_condition, line_instance_condition, call_id_condition
317 ))) {
318 skinny_execute_sql_callback(profile, profile->sql_mutex, sql,
319 skinny_profile_find_session_uuid_callback, &helper);
320 switch_safe_free(sql);
321 }
322 switch_safe_free(device_condition);
323 switch_safe_free(line_instance_condition);
324 switch_safe_free(call_id_condition);
325 *line_instance_p = helper.line_instance;
326 return helper.channel_uuid;
327 }
328
329 #ifdef SWITCH_DEBUG_RWLOCKS
skinny_profile_perform_find_session(skinny_profile_t * profile,listener_t * listener,uint32_t * line_instance_p,uint32_t call_id,const char * file,const char * func,int line)330 switch_core_session_t * skinny_profile_perform_find_session(skinny_profile_t *profile, listener_t *listener, uint32_t *line_instance_p, uint32_t call_id, const char *file, const char *func, int line)
331 #else
332 switch_core_session_t * skinny_profile_find_session(skinny_profile_t *profile, listener_t *listener, uint32_t *line_instance_p, uint32_t call_id)
333 #endif
334 {
335 char *uuid;
336 switch_core_session_t *result = NULL;
337 uuid = skinny_profile_find_session_uuid(profile, listener, line_instance_p, call_id);
338
339 if(!zstr(uuid)) {
340 #ifdef SWITCH_DEBUG_RWLOCKS
341 result = switch_core_session_perform_locate(uuid, file, func, line);
342 #else
343 result = switch_core_session_locate(uuid);
344 #endif
345 if(!result) {
346 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
347 "Unable to find session %s on %s:%d, line %d\n",
348 uuid, listener->device_name, listener->device_instance, *line_instance_p);
349 }
350 switch_safe_free(uuid);
351 }
352 return result;
353 }
354
355 /*****************************************************************************/
356 /* SQL FUNCTIONS */
357 /*****************************************************************************/
skinny_get_db_handle(skinny_profile_t * profile)358 switch_cache_db_handle_t *skinny_get_db_handle(skinny_profile_t *profile)
359 {
360 switch_cache_db_handle_t *dbh = NULL;
361 char *dsn;
362
363 if (!zstr(profile->odbc_dsn)) {
364 dsn = profile->odbc_dsn;
365 } else {
366 dsn = profile->dbname;
367 }
368
369 if (switch_cache_db_get_db_handle_dsn(&dbh, dsn) != SWITCH_STATUS_SUCCESS) {
370 dbh = NULL;
371 }
372
373 return dbh;
374
375 }
376
377
skinny_execute_sql(skinny_profile_t * profile,char * sql,switch_mutex_t * mutex)378 switch_status_t skinny_execute_sql(skinny_profile_t *profile, char *sql, switch_mutex_t *mutex)
379 {
380 switch_cache_db_handle_t *dbh = NULL;
381 switch_status_t status = SWITCH_STATUS_FALSE;
382
383 if (mutex) {
384 switch_mutex_lock(mutex);
385 }
386
387 if (!(dbh = skinny_get_db_handle(profile))) {
388 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB\n");
389 goto end;
390 }
391
392 status = switch_cache_db_execute_sql(dbh, sql, NULL);
393
394 end:
395
396 switch_cache_db_release_db_handle(&dbh);
397
398 if (mutex) {
399 switch_mutex_unlock(mutex);
400 }
401
402 return status;
403 }
404
skinny_execute_sql_callback(skinny_profile_t * profile,switch_mutex_t * mutex,char * sql,switch_core_db_callback_func_t callback,void * pdata)405 switch_bool_t skinny_execute_sql_callback(skinny_profile_t *profile, switch_mutex_t *mutex, char *sql, switch_core_db_callback_func_t callback,
406 void *pdata)
407 {
408 switch_bool_t ret = SWITCH_FALSE;
409 char *errmsg = NULL;
410 switch_cache_db_handle_t *dbh = NULL;
411
412 if (mutex) {
413 switch_mutex_lock(mutex);
414 }
415
416 if (!(dbh = skinny_get_db_handle(profile))) {
417 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB\n");
418 goto end;
419 }
420
421 switch_cache_db_execute_sql_callback(dbh, sql, callback, pdata, &errmsg);
422
423 if (errmsg) {
424 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR: [%s] %s\n", sql, errmsg);
425 free(errmsg);
426 }
427
428 end:
429
430 switch_cache_db_release_db_handle(&dbh);
431
432 if (mutex) {
433 switch_mutex_unlock(mutex);
434 }
435
436 return ret;
437 }
438
439 /*****************************************************************************/
440 /* CHANNEL FUNCTIONS */
441 /*****************************************************************************/
skinny_line_perform_set_state(const char * file,const char * func,int line,listener_t * listener,uint32_t line_instance,uint32_t call_id,uint32_t call_state)442 void skinny_line_perform_set_state(const char *file, const char *func, int line, listener_t *listener, uint32_t line_instance, uint32_t call_id, uint32_t call_state)
443 {
444 switch_event_t *event = NULL;
445 switch_assert(listener);
446
447 skinny_device_event(listener, &event, SWITCH_EVENT_CUSTOM, SKINNY_EVENT_CALL_STATE);
448 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-Line-Instance", "%d", line_instance);
449 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-Call-Id", "%d", call_id);
450 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-Call-State", "%d", call_state);
451 switch_event_fire(&event);
452 send_call_state(listener, call_state, line_instance, call_id);
453
454 skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG,
455 "Line %d, Call %d Change State to %s (%d)\n",
456 line_instance, call_id,
457 skinny_call_state2str(call_state), call_state);
458 }
459
460
461 struct skinny_line_get_state_helper {
462 uint32_t call_state;
463 };
464
skinny_line_get_state_callback(void * pArg,int argc,char ** argv,char ** columnNames)465 int skinny_line_get_state_callback(void *pArg, int argc, char **argv, char **columnNames)
466 {
467 struct skinny_line_get_state_helper *helper = pArg;
468 if (helper->call_state == -1) {
469 helper->call_state = atoi(argv[0]);
470 }
471 return 0;
472 }
473
skinny_line_get_state(listener_t * listener,uint32_t line_instance,uint32_t call_id)474 uint32_t skinny_line_get_state(listener_t *listener, uint32_t line_instance, uint32_t call_id)
475 {
476 char *line_instance_condition;
477 char *call_id_condition;
478 char *sql;
479 struct skinny_line_get_state_helper helper = {0};
480
481 switch_assert(listener);
482
483 if(line_instance > 0) {
484 line_instance_condition = switch_mprintf("line_instance=%d", line_instance);
485 } else {
486 line_instance_condition = switch_mprintf("1=1");
487 }
488 switch_assert(line_instance_condition);
489 if(call_id > 0) {
490 call_id_condition = switch_mprintf("call_id=%d", call_id);
491 } else {
492 call_id_condition = switch_mprintf("1=1");
493 }
494 switch_assert(call_id_condition);
495
496 helper.call_state = -1;
497 if ((sql = switch_mprintf(
498 "SELECT call_state FROM skinny_active_lines "
499 "WHERE device_name='%q' AND device_instance=%d "
500 "AND %s AND %s "
501 "ORDER BY call_state, channel_uuid", /* off hook first */
502 listener->device_name, listener->device_instance,
503 line_instance_condition, call_id_condition
504 ))) {
505 skinny_execute_sql_callback(listener->profile, listener->profile->sql_mutex, sql, skinny_line_get_state_callback, &helper);
506 switch_safe_free(sql);
507 }
508 switch_safe_free(line_instance_condition);
509 switch_safe_free(call_id_condition);
510
511 return helper.call_state;
512 }
513
514 struct skinny_line_count_active_helper {
515 uint32_t count;
516 };
517
skinny_line_count_active_callback(void * pArg,int argc,char ** argv,char ** columnNames)518 int skinny_line_count_active_callback(void *pArg, int argc, char **argv, char **columnNames)
519 {
520 struct skinny_line_count_active_helper *helper = pArg;
521 helper->count++;
522 return 0;
523 }
524
skinny_line_count_active(listener_t * listener)525 uint32_t skinny_line_count_active(listener_t *listener)
526 {
527 char *sql;
528 struct skinny_line_count_active_helper helper = {0};
529
530 switch_assert(listener);
531
532 helper.count = 0;
533 if ((sql = switch_mprintf(
534 "SELECT call_state FROM skinny_active_lines "
535 "WHERE device_name='%q' AND device_instance=%d "
536 "AND call_state not in (%d,%d,%d)",
537 listener->device_name, listener->device_instance,
538 SKINNY_ON_HOOK, SKINNY_IN_USE_REMOTELY, SKINNY_HOLD
539 ))) {
540
541 skinny_execute_sql_callback(listener->profile, listener->profile->sql_mutex, sql, skinny_line_count_active_callback, &helper);
542 switch_safe_free(sql);
543 }
544
545 return helper.count;
546 }
547
skinny_tech_set_codec(private_t * tech_pvt,int force)548 switch_status_t skinny_tech_set_codec(private_t *tech_pvt, int force)
549 {
550 switch_status_t status = SWITCH_STATUS_SUCCESS;
551 switch_channel_t *channel = switch_core_session_get_channel(tech_pvt->session);
552 int resetting = 0;
553
554 if (!tech_pvt->iananame) {
555 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR, "No audio codec available\n");
556 switch_goto_status(SWITCH_STATUS_FALSE, end);
557 }
558
559 if (switch_core_codec_ready(&tech_pvt->read_codec)) {
560 if (!force) {
561 switch_goto_status(SWITCH_STATUS_SUCCESS, end);
562 }
563 if (strcasecmp(tech_pvt->read_impl.iananame, tech_pvt->iananame) ||
564 tech_pvt->read_impl.samples_per_second != tech_pvt->rm_rate ||
565 tech_pvt->codec_ms != (uint32_t)tech_pvt->read_impl.microseconds_per_packet / 1000) {
566
567 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Changing Codec from %s@%dms to %s@%dms\n",
568 tech_pvt->read_impl.iananame, tech_pvt->read_impl.microseconds_per_packet / 1000,
569 tech_pvt->rm_encoding, tech_pvt->codec_ms);
570
571 switch_core_session_lock_codec_write(tech_pvt->session);
572 switch_core_session_lock_codec_read(tech_pvt->session);
573 resetting = 1;
574 switch_core_codec_destroy(&tech_pvt->read_codec);
575 switch_core_codec_destroy(&tech_pvt->write_codec);
576 } else {
577 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Already using %s\n", tech_pvt->read_impl.iananame);
578 switch_goto_status(SWITCH_STATUS_SUCCESS, end);
579 }
580 }
581
582 if (switch_core_codec_init(&tech_pvt->read_codec,
583 tech_pvt->iananame,
584 NULL,
585 tech_pvt->rm_fmtp,
586 tech_pvt->rm_rate,
587 tech_pvt->codec_ms,
588 1,
589 SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE | 0 /* TODO tech_pvt->profile->codec_flags */,
590 NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
591 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR, "Can't load codec?\n");
592 switch_goto_status(SWITCH_STATUS_FALSE, end);
593 }
594
595 if (switch_core_codec_init(&tech_pvt->write_codec,
596 tech_pvt->iananame,
597 NULL,
598 tech_pvt->rm_fmtp,
599 tech_pvt->rm_rate,
600 tech_pvt->codec_ms,
601 1,
602 SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE | 0 /* TODO tech_pvt->profile->codec_flags */,
603 NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
604 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR, "Can't load codec?\n");
605 switch_goto_status(SWITCH_STATUS_FALSE, end);
606 }
607
608 switch_assert(tech_pvt->read_codec.implementation);
609 switch_assert(tech_pvt->write_codec.implementation);
610
611 tech_pvt->read_impl = *tech_pvt->read_codec.implementation;
612 tech_pvt->write_impl = *tech_pvt->write_codec.implementation;
613
614 switch_core_session_set_read_impl(tech_pvt->session, tech_pvt->read_codec.implementation);
615 switch_core_session_set_write_impl(tech_pvt->session, tech_pvt->write_codec.implementation);
616
617 if (switch_rtp_ready(tech_pvt->rtp_session)) {
618 switch_assert(tech_pvt->read_codec.implementation);
619
620 if (switch_rtp_change_interval(tech_pvt->rtp_session,
621 tech_pvt->read_impl.microseconds_per_packet,
622 tech_pvt->read_impl.samples_per_packet
623 ) != SWITCH_STATUS_SUCCESS) {
624 switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
625 switch_goto_status(SWITCH_STATUS_FALSE, end);
626 }
627 }
628
629 tech_pvt->read_frame.rate = tech_pvt->rm_rate;
630 //ms = tech_pvt->write_codec.implementation->microseconds_per_packet / 1000;
631
632 if (!switch_core_codec_ready(&tech_pvt->read_codec)) {
633 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR, "Can't load codec?\n");
634 switch_goto_status(SWITCH_STATUS_FALSE, end);
635 }
636
637 switch_channel_set_flag(channel, CF_AUDIO);
638
639 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Set Codec %s %s/%ld %d ms %d samples\n",
640 switch_channel_get_name(channel), tech_pvt->iananame, tech_pvt->rm_rate, tech_pvt->codec_ms,
641 tech_pvt->read_impl.samples_per_packet);
642 tech_pvt->read_frame.codec = &tech_pvt->read_codec;
643
644 tech_pvt->write_codec.agreed_pt = tech_pvt->agreed_pt;
645 tech_pvt->read_codec.agreed_pt = tech_pvt->agreed_pt;
646
647 if (force != 2) {
648 switch_core_session_set_read_codec(tech_pvt->session, &tech_pvt->read_codec);
649 switch_core_session_set_write_codec(tech_pvt->session, &tech_pvt->write_codec);
650 }
651
652 /* TODO
653 tech_pvt->fmtp_out = switch_core_session_strdup(tech_pvt->session, tech_pvt->write_codec.fmtp_out);
654 */
655
656 /* TODO
657 if (switch_rtp_ready(tech_pvt->rtp_session)) {
658 switch_rtp_set_default_payload(tech_pvt->rtp_session, tech_pvt->pt);
659 }
660 */
661
662 end:
663 if (resetting) {
664 switch_core_session_unlock_codec_write(tech_pvt->session);
665 switch_core_session_unlock_codec_read(tech_pvt->session);
666 }
667
668 return status;
669 }
670
tech_init(private_t * tech_pvt,skinny_profile_t * profile,switch_core_session_t * session)671 void tech_init(private_t *tech_pvt, skinny_profile_t *profile, switch_core_session_t *session)
672 {
673 switch_assert(tech_pvt);
674 switch_assert(session);
675
676 tech_pvt->read_frame.data = tech_pvt->databuf;
677 tech_pvt->read_frame.buflen = sizeof(tech_pvt->databuf);
678 switch_mutex_init(&tech_pvt->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
679 switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
680 tech_pvt->profile = profile;
681 tech_pvt->call_id = ++profile->next_call_id;
682 tech_pvt->party_id = tech_pvt->call_id;
683 switch_core_session_set_private(session, tech_pvt);
684 tech_pvt->session = session;
685 }
686
687 /*
688 State methods they get called when the state changes to the specific state
689 returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next
690 so if you fully implement the state you can return SWITCH_STATUS_FALSE to skip it.
691 */
channel_on_init(switch_core_session_t * session)692 switch_status_t channel_on_init(switch_core_session_t *session)
693 {
694 switch_channel_t *channel = switch_core_session_get_channel(session);
695
696 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL INIT\n", switch_channel_get_name(channel));
697
698 /* This does not set the state to routing like most modules do, this now happens in the default state handeler so return FALSE TO BLOCK IT*/
699 return SWITCH_STATUS_FALSE;
700 }
701
702 struct channel_on_routing_helper {
703 private_t *tech_pvt;
704 listener_t *listener;
705 uint32_t line_instance;
706 };
707
channel_on_routing_callback(void * pArg,int argc,char ** argv,char ** columnNames)708 int channel_on_routing_callback(void *pArg, int argc, char **argv, char **columnNames)
709 {
710 struct channel_on_routing_helper *helper = pArg;
711 listener_t *listener = NULL;
712 char *label;
713
714 char *device_name = argv[0];
715 uint32_t device_instance = atoi(argv[1]);
716 /* uint32_t position = atoi(argv[2]); */
717 uint32_t line_instance = atoi(argv[3]);
718 /* char *label = argv[4]; */
719 /* char *value = argv[5]; */
720 /* char *caller_name = argv[6]; */
721 /* uint32_t ring_on_idle = atoi(argv[7]); */
722 /* uint32_t ring_on_active = atoi(argv[8]); */
723 /* uint32_t busy_trigger = atoi(argv[9]); */
724 /* char *forward_all = argv[10]; */
725 /* char *forward_busy = argv[11]; */
726 /* char *forward_noanswer = argv[12]; */
727 /* uint32_t noanswer_duration = atoi(argv[13]); */
728 /* char *channel_uuid = argv[14]; */
729 /* uint32_t call_id = atoi(argv[15]); */
730 /* uint32_t call_state = atoi(argv[16]); */
731
732 skinny_profile_find_listener_by_device_name_and_instance(helper->tech_pvt->profile, device_name, device_instance, &listener);
733 if(listener) {
734 if(!strcmp(device_name, helper->listener->device_name)
735 && (device_instance == helper->listener->device_instance)
736 && (line_instance == helper->line_instance)) {/* the calling line */
737 helper->tech_pvt->caller_profile->dialplan = switch_core_strdup(helper->tech_pvt->caller_profile->pool, listener->profile->dialplan);
738 helper->tech_pvt->caller_profile->context = switch_core_strdup(helper->tech_pvt->caller_profile->pool, listener->profile->context);
739 send_dialed_number(listener, helper->tech_pvt->caller_profile->destination_number, line_instance, helper->tech_pvt->call_id);
740 skinny_line_set_state(listener, line_instance, helper->tech_pvt->call_id, SKINNY_PROCEED);
741 skinny_session_send_call_info(helper->tech_pvt->session, listener, line_instance);
742 skinny_session_ring_out(helper->tech_pvt->session, listener, line_instance);
743 } else {
744 send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_ON);
745 skinny_line_set_state(listener, line_instance, helper->tech_pvt->call_id, SKINNY_IN_USE_REMOTELY);
746 send_select_soft_keys(listener, line_instance, helper->tech_pvt->call_id, SKINNY_KEY_SET_IN_USE_HINT, 0xffff);
747
748 label = skinny_textid2raw(SKINNY_TEXTID_IN_USE_REMOTE);
749 send_display_prompt_status(listener, 0, label, line_instance, helper->tech_pvt->call_id);
750 switch_safe_free(label);
751
752 skinny_session_send_call_info(helper->tech_pvt->session, listener, line_instance);
753 }
754 }
755 return 0;
756 }
757
channel_on_routing(switch_core_session_t * session)758 switch_status_t channel_on_routing(switch_core_session_t *session)
759 {
760 switch_channel_t *channel = switch_core_session_get_channel(session);
761 if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
762 skinny_action_t action;
763 private_t *tech_pvt = switch_core_session_get_private(session);
764 char *data = NULL;
765 listener_t *listener = NULL;
766 struct channel_on_routing_helper helper = {0};
767 int digit_timeout;
768
769 if(switch_test_flag(tech_pvt, TFLAG_FORCE_ROUTE)) {
770 action = SKINNY_ACTION_PROCESS;
771 } else {
772 action = skinny_session_dest_match_pattern(session, &data);
773 }
774 switch(action) {
775 case SKINNY_ACTION_PROCESS:
776 skinny_profile_find_listener_by_device_name_and_instance(tech_pvt->profile,
777 switch_channel_get_variable(channel, "skinny_device_name"),
778 atoi(switch_channel_get_variable(channel, "skinny_device_instance")), &listener);
779 if (listener) {
780 helper.tech_pvt = tech_pvt;
781 helper.listener = listener;
782 helper.line_instance = atoi(switch_channel_get_variable(channel, "skinny_line_instance"));
783 skinny_session_walk_lines(tech_pvt->profile, switch_core_session_get_uuid(session), channel_on_routing_callback, &helper);
784
785 /* clear digit timeout time */
786 listener->digit_timeout_time = 0;
787 } else {
788 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Could not find listener %s:%s for Channel %s\n",
789 switch_channel_get_variable(channel, "skinny_device_name"), switch_channel_get_variable(channel, "skinny_device_instance"),
790 switch_channel_get_name(channel));
791 }
792
793 /* Future bridge should go straight */
794 switch_set_flag_locked(tech_pvt, TFLAG_FORCE_ROUTE);
795 break;
796 case SKINNY_ACTION_WAIT:
797 /* for now, wait forever */
798 switch_channel_set_state(channel, CS_HIBERNATE);
799 skinny_profile_find_listener_by_device_name_and_instance(tech_pvt->profile,
800 switch_channel_get_variable(channel, "skinny_device_name"),
801 atoi(switch_channel_get_variable(channel, "skinny_device_instance")), &listener);
802
803 if (listener) {
804 digit_timeout = listener->profile->digit_timeout;
805 if (!zstr(data)) {
806 digit_timeout = atoi(data);
807 if ( digit_timeout < 100 ) {
808 digit_timeout *= 1000;
809 }
810 }
811
812 listener->digit_timeout_time = switch_mono_micro_time_now() + digit_timeout * 1000;
813 } else {
814 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Could not find listener %s:%s for Channel %s\n",
815 switch_channel_get_variable(channel, "skinny_device_name"), switch_channel_get_variable(channel, "skinny_device_instance"),
816 switch_channel_get_name(channel));
817
818 }
819
820 break;
821 case SKINNY_ACTION_DROP:
822 default:
823 switch_channel_hangup(channel, SWITCH_CAUSE_UNALLOCATED_NUMBER);
824 }
825 }
826
827 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL ROUTING\n", switch_channel_get_name(channel));
828
829 return SWITCH_STATUS_SUCCESS;
830 }
831
832 struct channel_on_execute_helper {
833 private_t *tech_pvt;
834 listener_t *listener;
835 uint32_t line_instance;
836 };
837
channel_on_execute_callback(void * pArg,int argc,char ** argv,char ** columnNames)838 int channel_on_execute_callback(void *pArg, int argc, char **argv, char **columnNames)
839 {
840 struct channel_on_routing_helper *helper = pArg;
841 listener_t *listener = NULL;
842
843 char *device_name = argv[0];
844 uint32_t device_instance = atoi(argv[1]);
845 /* uint32_t position = atoi(argv[2]); */
846 uint32_t line_instance = atoi(argv[3]);
847 /* char *label = argv[4]; */
848 /* char *value = argv[5]; */
849 /* char *caller_name = argv[6]; */
850 /* uint32_t ring_on_idle = atoi(argv[7]); */
851 /* uint32_t ring_on_active = atoi(argv[8]); */
852 /* uint32_t busy_trigger = atoi(argv[9]); */
853 /* char *forward_all = argv[10]; */
854 /* char *forward_busy = argv[11]; */
855 /* char *forward_noanswer = argv[12]; */
856 /* uint32_t noanswer_duration = atoi(argv[13]); */
857 /* char *channel_uuid = argv[14]; */
858 /* uint32_t call_id = atoi(argv[15]); */
859 /* uint32_t call_state = atoi(argv[16]); */
860
861 skinny_profile_find_listener_by_device_name_and_instance(helper->tech_pvt->profile, device_name, device_instance, &listener);
862 if(listener) {
863 if(!strcmp(device_name, helper->listener->device_name)
864 && (device_instance == helper->listener->device_instance)
865 && (line_instance == helper->line_instance)) {/* the calling line */
866 helper->tech_pvt->caller_profile->dialplan = switch_core_strdup(helper->tech_pvt->caller_profile->pool, listener->profile->dialplan);
867 helper->tech_pvt->caller_profile->context = switch_core_strdup(helper->tech_pvt->caller_profile->pool, listener->profile->context);
868
869 send_stop_tone(listener, line_instance, helper->tech_pvt->call_id);
870 } else {
871 }
872 }
873 return 0;
874 }
875
channel_on_execute(switch_core_session_t * session)876 switch_status_t channel_on_execute(switch_core_session_t *session)
877 {
878 switch_channel_t *channel = switch_core_session_get_channel(session);
879
880 if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
881 private_t *tech_pvt = switch_core_session_get_private(session);
882 listener_t *listener = NULL;
883 struct channel_on_execute_helper helper = {0};
884
885 skinny_profile_find_listener_by_device_name_and_instance(tech_pvt->profile,
886 switch_channel_get_variable(channel, "skinny_device_name"),
887 atoi(switch_channel_get_variable(channel, "skinny_device_instance")), &listener);
888 if (listener) {
889 helper.tech_pvt = tech_pvt;
890 helper.listener = listener;
891 helper.line_instance = atoi(switch_channel_get_variable(channel, "skinny_line_instance"));
892 skinny_session_walk_lines(tech_pvt->profile, switch_core_session_get_uuid(session), channel_on_execute_callback, &helper);
893 } else {
894 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Could not find listener %s:%s for Channel %s\n",
895 switch_channel_get_variable(channel, "skinny_device_name"), switch_channel_get_variable(channel, "skinny_device_instance"),
896 switch_channel_get_name(channel));
897 }
898 }
899 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL EXECUTE\n", switch_channel_get_name(channel));
900
901 return SWITCH_STATUS_SUCCESS;
902 }
903
channel_on_destroy(switch_core_session_t * session)904 switch_status_t channel_on_destroy(switch_core_session_t *session)
905 {
906 switch_channel_t *channel = switch_core_session_get_channel(session);
907 private_t *tech_pvt = switch_core_session_get_private(session);
908
909 if (tech_pvt) {
910 if (switch_core_codec_ready(&tech_pvt->read_codec)) {
911 switch_core_codec_destroy(&tech_pvt->read_codec);
912 }
913
914 if (switch_core_codec_ready(&tech_pvt->write_codec)) {
915 switch_core_codec_destroy(&tech_pvt->write_codec);
916 }
917
918 if (switch_rtp_ready(tech_pvt->rtp_session)) {
919 switch_rtp_destroy(&tech_pvt->rtp_session);
920 }
921 }
922
923 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL DESTROY\n", switch_channel_get_name(channel));
924
925 return SWITCH_STATUS_SUCCESS;
926 }
927
928 struct skinny_ring_active_calls_helper {
929 private_t *tech_pvt;
930 listener_t *listener;
931 };
932
skinny_ring_active_calls_callback(void * pArg,int argc,char ** argv,char ** columnNames)933 int skinny_ring_active_calls_callback(void *pArg, int argc, char **argv, char **columnNames)
934 {
935 struct skinny_ring_active_calls_helper *helper = pArg;
936 switch_core_session_t *session;
937
938 /* char *device_name = argv[0]; */
939 /* uint32_t device_instance = atoi(argv[1]); */
940 /* uint32_t position = atoi(argv[2]); */
941 uint32_t line_instance = atoi(argv[3]);
942 /* char *label = argv[4]; */
943 /* char *value = argv[5]; */
944 /* char *caller_name = argv[6]; */
945 uint32_t ring_on_idle = atoi(argv[7]);
946 /* uint32_t ring_on_active = atoi(argv[8]); */
947 /* uint32_t busy_trigger = atoi(argv[9]); */
948 /* char *forward_all = argv[10]; */
949 /* char *forward_busy = argv[11]; */
950 /* char *forward_noanswer = argv[12]; */
951 /* uint32_t noanswer_duration = atoi(argv[13]); */
952 /* char *channel_uuid = argv[14]; */
953 uint32_t call_id = atoi(argv[15]);
954 /* uint32_t call_state = atoi(argv[16]); */
955
956 session = skinny_profile_find_session(helper->listener->profile, helper->listener, &line_instance, call_id);
957
958 if(session) {
959 /* After going on-hook, start ringing if there is an active call in the SKINNY_RING_IN state */
960 skinny_log_l(helper->listener, SWITCH_LOG_DEBUG, "Start Ringer for active Call ID (%d), Line Instance (%d), Line State (%d).\n", call_id, line_instance, skinny_line_get_state(helper->listener,line_instance, call_id));
961
962 send_set_lamp(helper->listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_BLINK);
963
964 if ( ring_on_idle ) {
965 send_set_ringer(helper->listener, SKINNY_RING_INSIDE, SKINNY_RING_FOREVER, line_instance, call_id);
966 } else {
967 send_set_ringer(helper->listener, SKINNY_RING_FLASHONLY, SKINNY_RING_FOREVER, line_instance, call_id);
968 }
969
970 switch_core_session_rwunlock(session);
971 }
972
973 return 0;
974 }
975
skinny_ring_active_calls(listener_t * listener)976 switch_status_t skinny_ring_active_calls(listener_t *listener)
977 /* Look for all SKINNY active calls in the SKINNY_RING_IN state and tell them to start ringing */
978 {
979 struct skinny_ring_active_calls_helper helper = {0};
980 char *sql;
981
982 helper.listener = listener;
983
984 if ((sql = switch_mprintf(
985 "SELECT skinny_lines.*, channel_uuid, call_id, call_state "
986 "FROM skinny_active_lines "
987 "INNER JOIN skinny_lines "
988 "ON skinny_active_lines.device_name = skinny_lines.device_name "
989 "AND skinny_active_lines.device_instance = skinny_lines.device_instance "
990 "AND skinny_active_lines.line_instance = skinny_lines.line_instance "
991 "WHERE skinny_lines.device_name='%q' AND skinny_lines.device_instance=%d "
992 "AND (call_state=%d)",
993 listener->device_name, listener->device_instance, SKINNY_RING_IN))) {
994 skinny_execute_sql_callback(listener->profile, listener->profile->sql_mutex, sql, skinny_ring_active_calls_callback, &helper);
995 switch_safe_free(sql);
996 }
997
998 return SWITCH_STATUS_SUCCESS;
999 }
1000
1001 struct channel_on_hangup_helper {
1002 private_t *tech_pvt;
1003 switch_call_cause_t cause;
1004 };
1005
channel_on_hangup_callback(void * pArg,int argc,char ** argv,char ** columnNames)1006 int channel_on_hangup_callback(void *pArg, int argc, char **argv, char **columnNames)
1007 {
1008 struct channel_on_hangup_helper *helper = pArg;
1009 listener_t *listener = NULL;
1010 char *label;
1011
1012 char *device_name = argv[0];
1013 uint32_t device_instance = atoi(argv[1]);
1014 /* uint32_t position = atoi(argv[2]); */
1015 uint32_t line_instance = atoi(argv[3]);
1016 /* char *label = argv[4]; */
1017 /* char *value = argv[5]; */
1018 /* char *caller_name = argv[6]; */
1019 /* uint32_t ring_on_idle = atoi(argv[7]); */
1020 /* uint32_t ring_on_active = atoi(argv[8]); */
1021 /* uint32_t busy_trigger = atoi(argv[9]); */
1022 /* char *forward_all = argv[10]; */
1023 /* char *forward_busy = argv[11]; */
1024 /* char *forward_noanswer = argv[12]; */
1025 /* uint32_t noanswer_duration = atoi(argv[13]); */
1026 /* char *channel_uuid = argv[14]; */
1027 uint32_t call_id = atoi(argv[15]);
1028 uint32_t call_state = atoi(argv[16]);
1029
1030 skinny_profile_find_listener_by_device_name_and_instance(helper->tech_pvt->profile, device_name, device_instance, &listener);
1031 if(listener) {
1032 if((call_state == SKINNY_PROCEED) || (call_state == SKINNY_CONNECTED)) { /* calling parties */
1033 send_stop_tone(listener, line_instance, call_id);
1034 send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_OFF);
1035 send_clear_prompt_status(listener, line_instance, call_id);
1036 }
1037 send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_OFF);
1038
1039 if((call_state == SKINNY_PROCEED) || (call_state == SKINNY_RING_OUT) || (call_state == SKINNY_CONNECTED)) { /* calling parties */
1040 switch (helper->cause) {
1041 case SWITCH_CAUSE_UNALLOCATED_NUMBER:
1042 send_start_tone(listener, SKINNY_TONE_REORDER, 0, line_instance, call_id);
1043 skinny_session_send_call_info(helper->tech_pvt->session, listener, line_instance);
1044 label = skinny_textid2raw(SKINNY_TEXTID_UNKNOWN_NUMBER);
1045 send_display_prompt_status(listener, 0, label, line_instance, call_id);
1046 switch_safe_free(label);
1047 break;
1048 case SWITCH_CAUSE_USER_BUSY:
1049 send_start_tone(listener, SKINNY_TONE_BUSYTONE, 0, line_instance, call_id);
1050 label = skinny_textid2raw(SKINNY_TEXTID_BUSY);
1051 send_display_prompt_status(listener, 0, label, line_instance, call_id);
1052 switch_safe_free(label);
1053 break;
1054 case SWITCH_CAUSE_NORMAL_CLEARING:
1055 send_clear_prompt_status(listener, line_instance, call_id);
1056 break;
1057 default:
1058 send_display_prompt_status(listener, 0, switch_channel_cause2str(helper->cause), line_instance, call_id);
1059 }
1060
1061 skinny_session_stop_media(helper->tech_pvt->session, listener, line_instance);
1062 }
1063
1064 skinny_line_set_state(listener, line_instance, call_id, SKINNY_ON_HOOK);
1065 send_select_soft_keys(listener, line_instance, call_id, SKINNY_KEY_SET_ON_HOOK, 0xffff);
1066 send_define_current_time_date(listener);
1067 listener->digit_timeout_time = 0;
1068
1069 skinny_log_ls(listener, helper->tech_pvt->session, SWITCH_LOG_DEBUG,
1070 "channel_on_hangup_callback - cause=%s [%d], call_state = %s [%d]\n",
1071 switch_channel_cause2str(helper->cause), helper->cause,
1072 skinny_call_state2str(call_state), call_state);
1073
1074 if ( call_state == SKINNY_RING_OUT && helper->cause == SWITCH_CAUSE_USER_BUSY )
1075 {
1076 // don't hang up speaker here
1077 }
1078 else if((call_state == SKINNY_PROCEED) || (call_state == SKINNY_RING_OUT) || (call_state == SKINNY_CONNECTED)) { /* calling parties */
1079 // This is NOT correct, but results in slightly better behavior than before
1080 // leaving note here to revisit.
1081
1082 /* re-enabling for testing to bring back bad behavior */
1083 send_set_speaker_mode(listener, SKINNY_SPEAKER_OFF);
1084 }
1085 send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, line_instance, call_id);
1086
1087 /* After hanging up this call, activate the ringer for any other active incoming calls */
1088 skinny_ring_active_calls(listener);
1089 }
1090 return 0;
1091 }
1092
channel_on_hangup(switch_core_session_t * session)1093 switch_status_t channel_on_hangup(switch_core_session_t *session)
1094 {
1095 struct channel_on_hangup_helper helper = {0};
1096 switch_channel_t *channel = switch_core_session_get_channel(session);
1097 switch_call_cause_t cause = switch_channel_get_cause(channel);
1098 private_t *tech_pvt = switch_core_session_get_private(session);
1099 char *sql;
1100
1101 switch_clear_flag_locked(tech_pvt, TFLAG_IO);
1102
1103 skinny_log_s(session, SWITCH_LOG_DEBUG, "%s CHANNEL HANGUP [%s]\n",
1104 switch_channel_get_name(channel), switch_channel_cause2str(cause));
1105
1106 helper.tech_pvt= tech_pvt;
1107 helper.cause= cause;
1108
1109 skinny_session_walk_lines(tech_pvt->profile, switch_core_session_get_uuid(session), channel_on_hangup_callback, &helper);
1110 if ((sql = switch_mprintf(
1111 "DELETE FROM skinny_active_lines WHERE channel_uuid='%q'",
1112 switch_core_session_get_uuid(session)
1113 ))) {
1114 skinny_execute_sql(tech_pvt->profile, sql, tech_pvt->profile->sql_mutex);
1115 switch_safe_free(sql);
1116 }
1117 return SWITCH_STATUS_SUCCESS;
1118 }
1119
channel_kill_channel(switch_core_session_t * session,int sig)1120 switch_status_t channel_kill_channel(switch_core_session_t *session, int sig)
1121 {
1122 switch_channel_t *channel = switch_core_session_get_channel(session);
1123 private_t *tech_pvt = switch_core_session_get_private(session);
1124
1125 switch (sig) {
1126 case SWITCH_SIG_KILL:
1127 switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
1128 break;
1129 case SWITCH_SIG_BREAK:
1130 if (switch_rtp_ready(tech_pvt->rtp_session)) {
1131 switch_rtp_break(tech_pvt->rtp_session);
1132 }
1133 break;
1134 default:
1135 break;
1136 }
1137
1138 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL KILL %d\n", switch_channel_get_name(channel), sig);
1139
1140 return SWITCH_STATUS_SUCCESS;
1141 }
1142
channel_on_exchange_media(switch_core_session_t * session)1143 switch_status_t channel_on_exchange_media(switch_core_session_t *session)
1144 {
1145 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "CHANNEL LOOPBACK\n");
1146 return SWITCH_STATUS_SUCCESS;
1147 }
1148
channel_on_soft_execute(switch_core_session_t * session)1149 switch_status_t channel_on_soft_execute(switch_core_session_t *session)
1150 {
1151 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "CHANNEL TRANSMIT\n");
1152 return SWITCH_STATUS_SUCCESS;
1153 }
1154
channel_send_dtmf(switch_core_session_t * session,const switch_dtmf_t * dtmf)1155 switch_status_t channel_send_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf)
1156 {
1157 private_t *tech_pvt = switch_core_session_get_private(session);
1158 switch_assert(tech_pvt != NULL);
1159
1160 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "DTMF ON CALL %d [%c]\n", tech_pvt->call_id, dtmf->digit);
1161
1162 return SWITCH_STATUS_SUCCESS;
1163 }
1164
channel_read_frame(switch_core_session_t * session,switch_frame_t ** frame,switch_io_flag_t flags,int stream_id)1165 switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id)
1166 {
1167 switch_channel_t *channel = switch_core_session_get_channel(session);
1168 private_t *tech_pvt = switch_core_session_get_private(session);
1169
1170 while (!(tech_pvt->read_codec.implementation && switch_rtp_ready(tech_pvt->rtp_session))) {
1171 if (switch_channel_ready(channel)) {
1172 switch_yield(10000);
1173 } else {
1174 return SWITCH_STATUS_GENERR;
1175 }
1176 }
1177
1178 tech_pvt->read_frame.datalen = 0;
1179 switch_set_flag_locked(tech_pvt, TFLAG_READING);
1180
1181 if (switch_test_flag(tech_pvt, TFLAG_IO)) {
1182 switch_status_t status;
1183
1184 switch_assert(tech_pvt->rtp_session != NULL);
1185 tech_pvt->read_frame.datalen = 0;
1186
1187
1188 while (switch_test_flag(tech_pvt, TFLAG_IO) && tech_pvt->read_frame.datalen == 0) {
1189 tech_pvt->read_frame.flags = SFF_NONE;
1190
1191 status = switch_rtp_zerocopy_read_frame(tech_pvt->rtp_session, &tech_pvt->read_frame, flags);
1192 if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
1193 return SWITCH_STATUS_FALSE;
1194 }
1195
1196 //payload = tech_pvt->read_frame.payload;
1197
1198 if (switch_rtp_has_dtmf(tech_pvt->rtp_session)) {
1199 switch_dtmf_t dtmf = { 0 };
1200 switch_rtp_dequeue_dtmf(tech_pvt->rtp_session, &dtmf);
1201 switch_channel_queue_dtmf(channel, &dtmf);
1202 }
1203
1204
1205 if (tech_pvt->read_frame.datalen > 0) {
1206 size_t bytes = 0;
1207 int frames = 1;
1208
1209 if (!switch_test_flag((&tech_pvt->read_frame), SFF_CNG)) {
1210 if ((bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_packet)) {
1211 frames = (tech_pvt->read_frame.datalen / bytes);
1212 }
1213 tech_pvt->read_frame.samples = (int) (frames * tech_pvt->read_codec.implementation->samples_per_packet);
1214 }
1215 break;
1216 }
1217 }
1218 }
1219
1220 switch_clear_flag_locked(tech_pvt, TFLAG_READING);
1221
1222 if (tech_pvt->read_frame.datalen == 0) {
1223 *frame = NULL;
1224 return SWITCH_STATUS_GENERR;
1225 }
1226
1227 *frame = &tech_pvt->read_frame;
1228
1229 return SWITCH_STATUS_SUCCESS;
1230 }
1231
channel_write_frame(switch_core_session_t * session,switch_frame_t * frame,switch_io_flag_t flags,int stream_id)1232 switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id)
1233 {
1234 private_t *tech_pvt = switch_core_session_get_private(session);
1235 //switch_frame_t *pframe;
1236 switch_status_t status = SWITCH_STATUS_SUCCESS;
1237
1238 if (!switch_test_flag(tech_pvt, TFLAG_IO)) {
1239 return SWITCH_STATUS_FALSE;
1240 }
1241 switch_set_flag_locked(tech_pvt, TFLAG_WRITING);
1242
1243 switch_rtp_write_frame(tech_pvt->rtp_session, frame);
1244
1245 switch_clear_flag_locked(tech_pvt, TFLAG_WRITING);
1246
1247 return status;
1248
1249 }
1250
channel_answer_channel(switch_core_session_t * session)1251 switch_status_t channel_answer_channel(switch_core_session_t *session)
1252 {
1253 switch_channel_t *channel = switch_core_session_get_channel(session);
1254 private_t *tech_pvt = switch_core_session_get_private(session);
1255 listener_t *listener = NULL;
1256
1257 skinny_profile_find_listener_by_device_name_and_instance(tech_pvt->profile,
1258 switch_channel_get_variable(channel, "skinny_device_name"),
1259 atoi(switch_channel_get_variable(channel, "skinny_device_instance")), &listener);
1260 if (listener) {
1261 int x = 0;
1262 skinny_session_start_media(session, listener, atoi(switch_channel_get_variable(channel, "skinny_line_instance")));
1263 /* Wait for media */
1264 while(!switch_test_flag(tech_pvt, TFLAG_IO)) {
1265 switch_cond_next();
1266 if (++x > 5000) {
1267 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Wait tooo long to answer %s:%s\n",
1268 switch_channel_get_variable(channel, "skinny_device_name"), switch_channel_get_variable(channel, "skinny_device_instance"));
1269 return SWITCH_STATUS_FALSE;
1270 }
1271 }
1272 } else {
1273 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Unable to find listener to answer %s:%s\n",
1274 switch_channel_get_variable(channel, "skinny_device_name"), switch_channel_get_variable(channel, "skinny_device_instance"));
1275 }
1276 return SWITCH_STATUS_SUCCESS;
1277 }
1278
1279
channel_receive_message(switch_core_session_t * session,switch_core_session_message_t * msg)1280 switch_status_t channel_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg)
1281 {
1282 private_t *tech_pvt = switch_core_session_get_private(session);
1283
1284 switch (msg->message_id) {
1285 case SWITCH_MESSAGE_INDICATE_ANSWER:
1286 switch_clear_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA);
1287 return channel_answer_channel(session);
1288
1289 case SWITCH_MESSAGE_INDICATE_DISPLAY:
1290 skinny_session_send_call_info_all(session);
1291 return SWITCH_STATUS_SUCCESS;
1292
1293 case SWITCH_MESSAGE_INDICATE_PROGRESS:
1294 if (!switch_test_flag(tech_pvt, TFLAG_EARLY_MEDIA)) {
1295 /* early media */
1296 switch_set_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA);
1297 return channel_answer_channel(session);
1298 }
1299 return SWITCH_STATUS_SUCCESS;
1300
1301 default:
1302 return SWITCH_STATUS_SUCCESS;
1303
1304 }
1305
1306 }
1307
1308 /* Make sure when you have 2 sessions in the same scope that you pass the appropriate one to the routines
1309 that allocate memory or you will have 1 channel with memory allocated from another channel's pool!
1310 */
channel_outgoing_channel(switch_core_session_t * session,switch_event_t * var_event,switch_caller_profile_t * outbound_profile,switch_core_session_t ** new_session,switch_memory_pool_t ** pool,switch_originate_flag_t flags,switch_call_cause_t * cancel_cause)1311 switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event,
1312 switch_caller_profile_t *outbound_profile,
1313 switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags, switch_call_cause_t *cancel_cause)
1314 {
1315 switch_call_cause_t cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
1316 switch_core_session_t *nsession = NULL;
1317 private_t *tech_pvt;
1318
1319 char *profile_name, *dest;
1320 skinny_profile_t *profile = NULL;
1321 char *sql;
1322 char name[128];
1323 switch_channel_t *nchannel;
1324 switch_caller_profile_t *caller_profile;
1325
1326 if (!outbound_profile || zstr(outbound_profile->destination_number)) {
1327 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Invalid Destination\n");
1328 goto error;
1329 }
1330
1331 if (!(nsession = switch_core_session_request(skinny_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, flags, pool))) {
1332 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error Creating Session\n");
1333 goto error;
1334 }
1335
1336 if (!(tech_pvt = (struct private_object *) switch_core_session_alloc(nsession, sizeof(*tech_pvt)))) {
1337 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Error Creating Session private object\n");
1338 goto error;
1339 }
1340
1341 if(!(profile_name = switch_core_session_strdup(nsession, outbound_profile->destination_number))) {
1342 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Error Creating Session Info\n");
1343 goto error;
1344 }
1345
1346 if (!(dest = strchr(profile_name, '/'))) {
1347 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid Skinny URL. Should be skinny/<profile>/<number>.\n");
1348 cause = SWITCH_CAUSE_INVALID_NUMBER_FORMAT;
1349 goto error;
1350 }
1351 *dest++ = '\0';
1352
1353 if (!(profile = skinny_find_profile(profile_name))) {
1354 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid Profile %s\n", profile_name);
1355 cause = SWITCH_CAUSE_UNALLOCATED_NUMBER;
1356 goto error;
1357 }
1358
1359 snprintf(name, sizeof(name), "SKINNY/%s/%s", profile->name, dest);
1360
1361 nchannel = switch_core_session_get_channel(nsession);
1362 switch_channel_set_name(nchannel, name);
1363
1364 tech_init(tech_pvt, profile, nsession);
1365
1366 caller_profile = switch_caller_profile_clone(nsession, outbound_profile);
1367 switch_channel_set_caller_profile(nchannel, caller_profile);
1368 tech_pvt->caller_profile = caller_profile;
1369
1370 if ((sql = switch_mprintf(
1371 "INSERT INTO skinny_active_lines "
1372 "(device_name, device_instance, line_instance, channel_uuid, call_id, call_state) "
1373 "SELECT device_name, device_instance, line_instance, '%q', %d, %d "
1374 "FROM skinny_lines "
1375 "WHERE value='%q'",
1376 switch_core_session_get_uuid(nsession), tech_pvt->call_id, SKINNY_ON_HOOK, dest
1377 ))) {
1378 skinny_execute_sql(profile, sql, profile->sql_mutex);
1379 switch_safe_free(sql);
1380 }
1381
1382 /* FIXME: ring_lines need BOND before switch_core_session_outgoing_channel() set it */
1383 if (session) {
1384 switch_channel_set_variable(switch_core_session_get_channel(session), SWITCH_SIGNAL_BOND_VARIABLE, switch_core_session_get_uuid(nsession));
1385 switch_channel_set_variable(nchannel, SWITCH_SIGNAL_BOND_VARIABLE, switch_core_session_get_uuid(session));
1386 }
1387
1388 cause = skinny_ring_lines(tech_pvt, session);
1389
1390 if(cause != SWITCH_CAUSE_SUCCESS) {
1391 goto error;
1392 }
1393
1394 *new_session = nsession;
1395
1396 /* ?? switch_channel_mark_ring_ready(channel); */
1397
1398 if (switch_channel_get_state(nchannel) == CS_NEW) {
1399 switch_channel_set_state(nchannel, CS_INIT);
1400 }
1401
1402 cause = SWITCH_CAUSE_SUCCESS;
1403 goto done;
1404
1405 error:
1406 if (nsession) {
1407 switch_core_session_destroy(&nsession);
1408 }
1409
1410 if (pool) {
1411 *pool = NULL;
1412 }
1413
1414
1415 done:
1416
1417 if (profile) {
1418 if (cause == SWITCH_CAUSE_SUCCESS) {
1419 profile->ob_calls++;
1420 } else {
1421 profile->ob_failed_calls++;
1422 }
1423 }
1424 return cause;
1425 }
1426
channel_receive_event(switch_core_session_t * session,switch_event_t * event)1427 switch_status_t channel_receive_event(switch_core_session_t *session, switch_event_t *event)
1428 {
1429 return SWITCH_STATUS_SUCCESS;
1430 }
1431
1432
1433
1434 switch_state_handler_table_t skinny_state_handlers = {
1435 /*.on_init */ channel_on_init,
1436 /*.on_routing */ channel_on_routing,
1437 /*.on_execute */ channel_on_execute,
1438 /*.on_hangup */ channel_on_hangup,
1439 /*.on_exchange_media */ channel_on_exchange_media,
1440 /*.on_soft_execute */ channel_on_soft_execute,
1441 /*.on_consume_media*/ NULL,
1442 /*.on_hibernate*/ NULL,
1443 /*.on_reset*/ NULL,
1444 /*.on_park*/ NULL,
1445 /*.on_reporting*/ NULL,
1446 /*.on_destroy*/ channel_on_destroy
1447
1448 };
1449
1450 switch_io_routines_t skinny_io_routines = {
1451 /*.outgoing_channel */ channel_outgoing_channel,
1452 /*.read_frame */ channel_read_frame,
1453 /*.write_frame */ channel_write_frame,
1454 /*.kill_channel */ channel_kill_channel,
1455 /*.send_dtmf */ channel_send_dtmf,
1456 /*.receive_message */ channel_receive_message,
1457 /*.receive_event */ channel_receive_event
1458 };
1459
1460 /*****************************************************************************/
1461 /* LISTENER FUNCTIONS */
1462 /*****************************************************************************/
1463
listener_is_ready(listener_t * listener)1464 uint8_t listener_is_ready(listener_t *listener)
1465 {
1466 return skinny_globals.running
1467 && listener
1468 && listener->sock
1469 && switch_test_flag(listener, LFLAG_RUNNING)
1470 && switch_test_flag(listener->profile, PFLAG_LISTENER_READY)
1471 && !switch_test_flag(listener->profile, PFLAG_RESPAWN);
1472 }
1473
add_listener(listener_t * listener)1474 static void add_listener(listener_t *listener)
1475 {
1476 skinny_profile_t *profile;
1477 switch_assert(listener);
1478 assert(listener->profile);
1479 profile = listener->profile;
1480
1481 switch_mutex_lock(profile->listener_mutex);
1482 listener->next = profile->listeners;
1483 profile->listeners = listener;
1484 switch_mutex_unlock(profile->listener_mutex);
1485 }
1486
remove_listener(listener_t * listener)1487 static void remove_listener(listener_t *listener)
1488 {
1489 listener_t *l, *last = NULL;
1490 skinny_profile_t *profile;
1491 switch_assert(listener);
1492 assert(listener->profile);
1493 profile = listener->profile;
1494
1495 switch_mutex_lock(profile->listener_mutex);
1496 for (l = profile->listeners; l; l = l->next) {
1497 if (l == listener) {
1498 if (last) {
1499 last->next = l->next;
1500 } else {
1501 profile->listeners = l->next;
1502 }
1503 }
1504 last = l;
1505 }
1506 switch_mutex_unlock(profile->listener_mutex);
1507 }
1508
1509
walk_listeners(skinny_listener_callback_func_t callback,void * pvt)1510 static void walk_listeners(skinny_listener_callback_func_t callback, void *pvt)
1511 {
1512 switch_hash_index_t *hi;
1513 void *val;
1514 skinny_profile_t *profile;
1515
1516 /* walk listeners */
1517 switch_mutex_lock(skinny_globals.mutex);
1518 for (hi = switch_core_hash_first(skinny_globals.profile_hash); hi; hi = switch_core_hash_next(&hi)) {
1519 switch_core_hash_this(hi, NULL, NULL, &val);
1520 profile = (skinny_profile_t *) val;
1521
1522 profile_walk_listeners(profile, callback, pvt);
1523 }
1524 switch_mutex_unlock(skinny_globals.mutex);
1525 }
1526
flush_listener_callback(void * pArg,int argc,char ** argv,char ** columnNames)1527 static int flush_listener_callback(void *pArg, int argc, char **argv, char **columnNames)
1528 {
1529 char *profile_name = argv[0];
1530 char *value = argv[1];
1531 char *domain_name = argv[2];
1532 char *device_name = argv[3];
1533 char *device_instance = argv[4];
1534
1535 char *token = switch_mprintf("skinny/%q/%q/%q:%q", profile_name, value, device_name, device_instance);
1536 switch_core_del_registration(value, domain_name, token);
1537 switch_safe_free(token);
1538
1539 return 0;
1540 }
1541
skinny_lock_device_name(listener_t * listener,char * device_name)1542 void skinny_lock_device_name(listener_t *listener, char *device_name)
1543 {
1544 switch_time_t started = 0;
1545 unsigned int elapsed = 0;
1546 device_name_lock_t *dnl;
1547
1548 if ( listener->profile->debug >= 9 ) {
1549 skinny_log_l(listener, SWITCH_LOG_DEBUG, "lock device name '%s'\n", device_name);
1550 }
1551
1552 started = switch_micro_time_now();
1553
1554 /* global mutex on hash operations */
1555 switch_mutex_lock(listener->profile->device_name_lock_mutex);
1556
1557 dnl = (device_name_lock_t *) switch_core_hash_find(listener->profile->device_name_lock_hash, device_name);
1558 if ( ! dnl ) {
1559 if ( listener->profile->debug >= 9 ) {
1560 skinny_log_l(listener, SWITCH_LOG_DEBUG, "creating device name lock for device name '%s'\n", device_name);
1561 }
1562 dnl = switch_core_alloc(listener->profile->pool, sizeof(*dnl));
1563 switch_mutex_init(&dnl->flag_mutex, SWITCH_MUTEX_NESTED, listener->profile->pool);
1564 switch_core_hash_insert(listener->profile->device_name_lock_hash, device_name, dnl);
1565 }
1566
1567 switch_mutex_unlock(listener->profile->device_name_lock_mutex);
1568
1569 if ( listener->profile->debug >= 9 ) {
1570 skinny_log_l(listener, SWITCH_LOG_DEBUG, "setting device name lock for device name '%s'\n", device_name);
1571 }
1572 switch_set_flag_locked(dnl, DNLFLAG_INUSE);
1573
1574 if ((elapsed = (unsigned int) ((switch_micro_time_now() - started) / 1000)) > 5) {
1575 skinny_log_l(listener, SWITCH_LOG_DEBUG, "device name lock took more than 5ms for '%s' (%d)\n", device_name, elapsed);
1576 }
1577
1578 if ( listener->profile->debug >= 9 ) {
1579 skinny_log_l(listener, SWITCH_LOG_DEBUG, "locked device name '%s'\n", device_name);
1580 }
1581 }
1582
skinny_unlock_device_name(listener_t * listener,char * device_name)1583 void skinny_unlock_device_name(listener_t *listener, char *device_name)
1584 {
1585 switch_time_t started = 0;
1586 unsigned int elapsed = 0;
1587 device_name_lock_t *dnl;
1588
1589 if ( listener->profile->debug >= 9 ) {
1590 skinny_log_l(listener, SWITCH_LOG_DEBUG, "unlock device name '%s'\n", device_name);
1591 }
1592
1593 started = switch_micro_time_now();
1594
1595 /* global mutex on hash operations */
1596 switch_mutex_lock(listener->profile->device_name_lock_mutex);
1597 dnl = (device_name_lock_t *) switch_core_hash_find(listener->profile->device_name_lock_hash, device_name);
1598 switch_mutex_unlock(listener->profile->device_name_lock_mutex);
1599
1600 if ( ! dnl ) {
1601 skinny_log_l(listener, SWITCH_LOG_WARNING, "request to unlock and no lock structure for '%s'\n", device_name);
1602 /* since it didn't exist, nothing to unlock, don't bother creating structure now */
1603 } else {
1604 if ( listener->profile->debug >= 9 ) {
1605 skinny_log_l(listener, SWITCH_LOG_DEBUG, "clearing device name lock on '%s'\n", device_name);
1606 }
1607 switch_clear_flag_locked(dnl, DNLFLAG_INUSE);
1608 }
1609
1610 /* Should we clean up the lock structure here, or does it ever get reclaimed? I don't think memory is released
1611 so attempting to clear it up here likely would just result in a leak. */
1612
1613 if ((elapsed = (unsigned int) ((switch_micro_time_now() - started) / 1000)) > 5) {
1614 skinny_log_l(listener, SWITCH_LOG_DEBUG, "device name unlock took more than 5ms for '%s' (%d)\n", device_name, elapsed);
1615 }
1616
1617 if ( listener->profile->debug >= 9 ) {
1618 skinny_log_l(listener, SWITCH_LOG_DEBUG, "unlocked device name '%s'\n", device_name);
1619 }
1620 }
1621
skinny_clean_device_from_db(listener_t * listener,char * device_name)1622 void skinny_clean_device_from_db(listener_t *listener, char *device_name)
1623 {
1624 if(!zstr(device_name)) {
1625 skinny_profile_t *profile = listener->profile;
1626 char *sql;
1627
1628 skinny_log_l(listener, SWITCH_LOG_DEBUG,
1629 "Clean device from DB with name '%s'\n",
1630 device_name);
1631
1632 if ((sql = switch_mprintf(
1633 "DELETE FROM skinny_devices "
1634 "WHERE name='%q'",
1635 device_name))) {
1636 skinny_execute_sql(profile, sql, profile->sql_mutex);
1637 switch_safe_free(sql);
1638 }
1639
1640 if ((sql = switch_mprintf(
1641 "DELETE FROM skinny_lines "
1642 "WHERE device_name='%q'",
1643 device_name))) {
1644 skinny_execute_sql(profile, sql, profile->sql_mutex);
1645 switch_safe_free(sql);
1646 }
1647
1648 if ((sql = switch_mprintf(
1649 "DELETE FROM skinny_buttons "
1650 "WHERE device_name='%q'",
1651 device_name))) {
1652 skinny_execute_sql(profile, sql, profile->sql_mutex);
1653 switch_safe_free(sql);
1654 }
1655
1656 if ((sql = switch_mprintf(
1657 "DELETE FROM skinny_active_lines "
1658 "WHERE device_name='%q'",
1659 device_name))) {
1660 skinny_execute_sql(profile, sql, profile->sql_mutex);
1661 switch_safe_free(sql);
1662 }
1663
1664 } else {
1665 skinny_log_l_msg(listener, SWITCH_LOG_DEBUG,
1666 "Clean device from DB, missing device name.\n");
1667 }
1668 }
1669
skinny_clean_listener_from_db(listener_t * listener)1670 void skinny_clean_listener_from_db(listener_t *listener)
1671 {
1672 if(!zstr(listener->device_name)) {
1673 skinny_profile_t *profile = listener->profile;
1674 char *sql;
1675
1676 skinny_log_l(listener, SWITCH_LOG_DEBUG,
1677 "Clean listener from DB with name '%s' and instance '%d'\n",
1678 listener->device_name, listener->device_instance);
1679
1680 if ((sql = switch_mprintf(
1681 "DELETE FROM skinny_devices "
1682 "WHERE name='%q' and instance=%d",
1683 listener->device_name, listener->device_instance))) {
1684 skinny_execute_sql(profile, sql, profile->sql_mutex);
1685 switch_safe_free(sql);
1686 }
1687
1688 if ((sql = switch_mprintf(
1689 "DELETE FROM skinny_lines "
1690 "WHERE device_name='%q' and device_instance=%d",
1691 listener->device_name, listener->device_instance))) {
1692 skinny_execute_sql(profile, sql, profile->sql_mutex);
1693 switch_safe_free(sql);
1694 }
1695
1696 if ((sql = switch_mprintf(
1697 "DELETE FROM skinny_buttons "
1698 "WHERE device_name='%q' and device_instance=%d",
1699 listener->device_name, listener->device_instance))) {
1700 skinny_execute_sql(profile, sql, profile->sql_mutex);
1701 switch_safe_free(sql);
1702 }
1703
1704 if ((sql = switch_mprintf(
1705 "DELETE FROM skinny_active_lines "
1706 "WHERE device_name='%q' and device_instance=%d",
1707 listener->device_name, listener->device_instance))) {
1708 skinny_execute_sql(profile, sql, profile->sql_mutex);
1709 switch_safe_free(sql);
1710 }
1711
1712 } else {
1713 skinny_log_l_msg(listener, SWITCH_LOG_DEBUG,
1714 "Clean listener from DB, missing device name.\n");
1715 }
1716 }
1717
flush_listener(listener_t * listener)1718 static void flush_listener(listener_t *listener)
1719 {
1720
1721 if(!zstr(listener->device_name)) {
1722 skinny_profile_t *profile = listener->profile;
1723 char *sql;
1724
1725 if ((sql = switch_mprintf(
1726 "SELECT '%q', value, '%q', '%q', '%d' "
1727 "FROM skinny_lines "
1728 "WHERE device_name='%q' AND device_instance=%d "
1729 "ORDER BY position",
1730 profile->name, profile->domain, listener->device_name, listener->device_instance,
1731 listener->device_name, listener->device_instance
1732 ))) {
1733 skinny_execute_sql_callback(profile, profile->sql_mutex, sql, flush_listener_callback, NULL);
1734 switch_safe_free(sql);
1735 }
1736
1737 skinny_lock_device_name(listener, listener->device_name);
1738 skinny_clean_listener_from_db(listener);
1739 skinny_unlock_device_name(listener, listener->device_name);
1740
1741 strcpy(listener->device_name, "");
1742 }
1743 }
1744
dump_device_callback(void * pArg,int argc,char ** argv,char ** columnNames)1745 static int dump_device_callback(void *pArg, int argc, char **argv, char **columnNames)
1746 {
1747 switch_stream_handle_t *stream = (switch_stream_handle_t *) pArg;
1748
1749 char *device_name = argv[0];
1750 char *user_id = argv[1];
1751 char *instance = argv[2];
1752 char *ip = argv[3];
1753 char *type = argv[4];
1754 char *max_streams = argv[5];
1755 char *port = argv[6];
1756 char *codec_string = argv[7];
1757 char *headset = argv[8];
1758 char *handset = argv[9];
1759 char *speaker = argv[10];
1760
1761 const char *line = "=================================================================================================";
1762 stream->write_function(stream, "%s\n", line);
1763 stream->write_function(stream, "DeviceName \t%s\n", switch_str_nil(device_name));
1764 stream->write_function(stream, "UserId \t%s\n", user_id);
1765 stream->write_function(stream, "Instance \t%s\n", instance);
1766 stream->write_function(stream, "IP \t%s\n", ip);
1767 stream->write_function(stream, "DeviceTypeId \t%s\n", type);
1768 stream->write_function(stream, "DeviceType \t%s\n", skinny_device_type2str(atoi(type)));
1769 stream->write_function(stream, "MaxStreams \t%s\n", max_streams);
1770 stream->write_function(stream, "Port \t%s\n", port);
1771 stream->write_function(stream, "Codecs \t%s\n", codec_string);
1772 stream->write_function(stream, "HeadsetId \t%s\n", headset);
1773 if (headset) {
1774 stream->write_function(stream, "Headset \t%s\n", skinny_accessory_state2str(atoi(headset)));
1775 }
1776 stream->write_function(stream, "HandsetId \t%s\n", handset);
1777 if (handset) {
1778 stream->write_function(stream, "Handset \t%s\n", skinny_accessory_state2str(atoi(handset)));
1779 }
1780 stream->write_function(stream, "SpeakerId \t%s\n", speaker);
1781 if (speaker) {
1782 stream->write_function(stream, "Speaker \t%s\n", skinny_accessory_state2str(atoi(speaker)));
1783 }
1784 stream->write_function(stream, "%s\n", line);
1785
1786 return 0;
1787 }
1788
dump_device(skinny_profile_t * profile,const char * device_name,switch_stream_handle_t * stream)1789 switch_status_t dump_device(skinny_profile_t *profile, const char *device_name, switch_stream_handle_t *stream)
1790 {
1791 char *sql;
1792 if ((sql = switch_mprintf("SELECT name, user_id, instance, ip, type, max_streams, port, codec_string, headset, handset, speaker "
1793 "FROM skinny_devices WHERE name='%q'",
1794 device_name))) {
1795 skinny_execute_sql_callback(profile, profile->sql_mutex, sql, dump_device_callback, stream);
1796 switch_safe_free(sql);
1797 }
1798
1799 return SWITCH_STATUS_SUCCESS;
1800 }
1801
1802
close_socket(switch_socket_t ** sock,skinny_profile_t * profile)1803 static void close_socket(switch_socket_t **sock, skinny_profile_t *profile)
1804 {
1805 switch_mutex_lock(profile->sock_mutex);
1806 if (*sock) {
1807 switch_socket_shutdown(*sock, SWITCH_SHUTDOWN_READWRITE);
1808 switch_socket_close(*sock);
1809 *sock = NULL;
1810 }
1811 switch_mutex_unlock(profile->sock_mutex);
1812 }
1813
kill_listener(listener_t * listener,void * pvt)1814 switch_status_t kill_listener(listener_t *listener, void *pvt)
1815 {
1816 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Killing listener %s:%d.\n",
1817 listener->device_name, listener->device_instance);
1818 switch_clear_flag_locked(listener, LFLAG_RUNNING);
1819 close_socket(&listener->sock, listener->profile);
1820 return SWITCH_STATUS_SUCCESS;
1821 }
1822
keepalive_listener(listener_t * listener,void * pvt)1823 switch_status_t keepalive_listener(listener_t *listener, void *pvt)
1824 {
1825 skinny_profile_t *profile;
1826 switch_assert(listener);
1827 assert(listener->profile);
1828 profile = listener->profile;
1829
1830 listener->expire_time = switch_epoch_time_now(NULL)+profile->keep_alive*110/100;
1831
1832 return SWITCH_STATUS_SUCCESS;
1833 }
1834
listener_digit_timeout(listener_t * listener)1835 switch_status_t listener_digit_timeout(listener_t *listener)
1836 {
1837 switch_core_session_t *session = NULL;
1838 uint32_t line_instance = 1;
1839 uint32_t call_id = 0;
1840 switch_channel_t *channel = NULL;
1841 private_t *tech_pvt = NULL;
1842
1843 listener->digit_timeout_time = 0;
1844
1845 session = skinny_profile_find_session(listener->profile, listener, &line_instance, call_id);
1846 if ( !session )
1847 {
1848 line_instance = 0;
1849 session = skinny_profile_find_session(listener->profile, listener, &line_instance, 0);
1850 }
1851
1852 if ( !session)
1853 return SWITCH_STATUS_FALSE;
1854
1855 channel = switch_core_session_get_channel(session);
1856 tech_pvt = switch_core_session_get_private(session);
1857
1858 if (channel && tech_pvt->session) {
1859 switch_set_flag_locked(tech_pvt, TFLAG_FORCE_ROUTE);
1860 switch_channel_set_state(channel, CS_ROUTING);
1861 listener->digit_timeout_time = 0;
1862 }
1863
1864 switch_core_session_rwunlock(session);
1865
1866 return SWITCH_STATUS_SUCCESS;
1867 }
1868
listener_run(switch_thread_t * thread,void * obj)1869 static void *SWITCH_THREAD_FUNC listener_run(switch_thread_t *thread, void *obj)
1870 {
1871 listener_t *listener = (listener_t *) obj;
1872 switch_status_t status;
1873 skinny_message_t *request = NULL;
1874 skinny_profile_t *profile;
1875
1876 switch_assert(listener);
1877 assert(listener->profile);
1878 profile = listener->profile;
1879
1880 switch_mutex_lock(profile->listener_mutex);
1881 profile->listener_threads++;
1882 switch_mutex_unlock(profile->listener_mutex);
1883
1884 switch_assert(listener != NULL);
1885
1886 if ( profile->non_blocking ) {
1887 switch_socket_opt_set(listener->sock, SWITCH_SO_TCP_NODELAY, TRUE);
1888 switch_socket_opt_set(listener->sock, SWITCH_SO_NONBLOCK, TRUE);
1889 } else {
1890 switch_socket_opt_set(listener->sock, SWITCH_SO_NONBLOCK, FALSE);
1891 }
1892
1893 /* 200 ms to allow reasonably fast reaction on digit timeout */
1894 switch_socket_timeout_set(listener->sock, 200000);
1895
1896 if (listener->profile->debug > 0) {
1897 skinny_log_l_msg(listener, SWITCH_LOG_DEBUG, "Connection Open\n");
1898 }
1899
1900 listener->connect_time = switch_epoch_time_now(NULL);
1901
1902 switch_set_flag_locked(listener, LFLAG_RUNNING);
1903 keepalive_listener(listener, NULL);
1904 add_listener(listener);
1905
1906 while (listener_is_ready(listener)) {
1907 switch_safe_free(request);
1908 status = skinny_read_packet(listener, &request);
1909
1910 if (status != SWITCH_STATUS_SUCCESS) {
1911 switch(status) {
1912 case SWITCH_STATUS_TIMEOUT:
1913 if (listener->digit_timeout_time && listener->digit_timeout_time < switch_mono_micro_time_now()) {
1914 listener_digit_timeout(listener);
1915 continue;
1916 }
1917
1918 skinny_log_l_msg(listener, SWITCH_LOG_DEBUG, "Communication Time Out\n");
1919
1920 if(listener->expire_time < switch_epoch_time_now(NULL)) {
1921 switch_event_t *event = NULL;
1922 /* skinny::expire event */
1923 skinny_device_event(listener, &event, SWITCH_EVENT_CUSTOM, SKINNY_EVENT_EXPIRE);
1924 switch_event_fire(&event);
1925 }
1926 break;
1927 default:
1928 skinny_log_l_msg(listener, SWITCH_LOG_DEBUG, "Communication Error\n");
1929 }
1930 switch_safe_free(request);
1931 switch_clear_flag_locked(listener, LFLAG_RUNNING);
1932 break;
1933 }
1934 if (!listener_is_ready(listener)) {
1935 switch_safe_free(request);
1936 break;
1937 }
1938
1939 if (!request) {
1940 continue;
1941 }
1942
1943 if (skinny_handle_request(listener, request) != SWITCH_STATUS_SUCCESS) {
1944 switch_safe_free(request);
1945 switch_clear_flag_locked(listener, LFLAG_RUNNING);
1946 break;
1947 } else {
1948 switch_safe_free(request);
1949 }
1950 }
1951 switch_safe_free(request);
1952
1953 remove_listener(listener);
1954
1955 if (listener->profile->debug > 0) {
1956 skinny_log_l_msg(listener, SWITCH_LOG_DEBUG, "Communication Complete\n");
1957 }
1958
1959 switch_thread_rwlock_wrlock(listener->rwlock);
1960 flush_listener(listener);
1961
1962 if (listener->sock) {
1963 close_socket(&listener->sock, profile);
1964 }
1965
1966 switch_thread_rwlock_unlock(listener->rwlock);
1967
1968 if (listener->profile->debug > 0) {
1969 skinny_log_l_msg(listener, SWITCH_LOG_DEBUG, "Communication Closed\n");
1970 }
1971
1972 if (listener->pool) {
1973 switch_memory_pool_t *pool = listener->pool;
1974 switch_core_destroy_memory_pool(&pool);
1975 }
1976
1977 switch_mutex_lock(profile->listener_mutex);
1978 profile->listener_threads--;
1979 switch_mutex_unlock(profile->listener_mutex);
1980
1981 return NULL;
1982 }
1983
1984 /* Create a thread for the socket and launch it */
launch_listener_thread(listener_t * listener)1985 static void launch_listener_thread(listener_t *listener)
1986 {
1987 switch_thread_t *thread;
1988 switch_threadattr_t *thd_attr = NULL;
1989
1990 switch_threadattr_create(&thd_attr, listener->pool);
1991 switch_threadattr_detach_set(thd_attr, 1);
1992 switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
1993 switch_thread_create(&thread, thd_attr, listener_run, listener, listener->pool);
1994 }
1995
skinny_profile_run(switch_thread_t * thread,void * obj)1996 static void *SWITCH_THREAD_FUNC skinny_profile_run(switch_thread_t *thread, void *obj)
1997 {
1998 skinny_profile_t *profile = (skinny_profile_t *) obj;
1999 switch_status_t rv;
2000 switch_sockaddr_t *sa;
2001 switch_socket_t *inbound_socket = NULL;
2002 listener_t *listener;
2003 switch_memory_pool_t *tmp_pool = NULL, *listener_pool = NULL;
2004 uint32_t errs = 0;
2005 switch_sockaddr_t *local_sa = NULL;
2006 switch_sockaddr_t *remote_sa =NULL;
2007
2008 if (switch_core_new_memory_pool(&tmp_pool) != SWITCH_STATUS_SUCCESS) {
2009 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "OH OH no pool\n");
2010 return NULL;
2011 }
2012
2013 new_socket:
2014 while(skinny_globals.running && !profile->sock) {
2015 char *listening_ip = NULL;
2016 switch_clear_flag_locked(profile, PFLAG_RESPAWN);
2017 rv = switch_sockaddr_info_get(&sa, profile->ip, SWITCH_UNSPEC, profile->port, 0, tmp_pool);
2018 if (rv)
2019 goto fail;
2020 rv = switch_socket_create(&profile->sock, switch_sockaddr_get_family(sa), SOCK_STREAM, SWITCH_PROTO_TCP, tmp_pool);
2021 if (rv)
2022 goto sock_fail;
2023 rv = switch_socket_opt_set(profile->sock, SWITCH_SO_REUSEADDR, 1);
2024 if (rv)
2025 goto sock_fail;
2026 rv = switch_socket_bind(profile->sock, sa);
2027 if (rv)
2028 goto sock_fail;
2029 rv = switch_socket_listen(profile->sock, 5);
2030 if (rv)
2031 goto sock_fail;
2032 switch_sockaddr_ip_get(&listening_ip, sa);
2033 if (!profile->ip || strcmp(listening_ip, profile->ip)) {
2034 profile->ip = switch_core_strdup(profile->pool, listening_ip);
2035 }
2036 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Socket up listening on %s:%u\n", profile->ip, profile->port);
2037
2038 break;
2039 sock_fail:
2040 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Socket Error! Could not listen on %s:%u\n", profile->ip, profile->port);
2041 if (profile->sock) {
2042 close_socket(&profile->sock, profile);
2043 profile->sock = NULL;
2044 }
2045 switch_yield(100000);
2046 }
2047
2048 switch_set_flag_locked(profile, PFLAG_LISTENER_READY);
2049
2050 while(skinny_globals.running) {
2051
2052 if (switch_core_new_memory_pool(&listener_pool) != SWITCH_STATUS_SUCCESS) {
2053 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "OH OH no pool\n");
2054 goto fail;
2055 }
2056
2057 assert(profile->sock);
2058
2059 if ((rv = switch_socket_accept(&inbound_socket, profile->sock, listener_pool))) {
2060 if (!skinny_globals.running) {
2061 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Shutting Down\n");
2062 goto end;
2063 } else if (switch_test_flag(profile, PFLAG_RESPAWN)) {
2064 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Respawn in progress. Waiting for socket to close.\n");
2065 while (profile->sock) {
2066 switch_cond_next();
2067 }
2068 goto new_socket;
2069 } else {
2070 /* I wish we could use strerror_r here but its not defined everywhere =/ */
2071 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Socket Error [%s]\n", strerror(errno));
2072 if (++errs > 100) {
2073 goto end;
2074 }
2075 }
2076 } else {
2077 errs = 0;
2078 }
2079
2080
2081 if (!(listener = switch_core_alloc(listener_pool, sizeof(*listener)))) {
2082 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error\n");
2083 break;
2084 }
2085
2086 switch_thread_rwlock_create(&listener->rwlock, listener_pool);
2087
2088 listener->sock = inbound_socket;
2089 listener->pool = listener_pool;
2090 listener_pool = NULL;
2091 strcpy(listener->device_name, "");
2092 listener->profile = profile;
2093
2094 switch_mutex_init(&listener->flag_mutex, SWITCH_MUTEX_NESTED, listener->pool);
2095
2096 switch_socket_addr_get(&remote_sa, SWITCH_TRUE, listener->sock);
2097 switch_get_addr(listener->remote_ip, sizeof(listener->remote_ip), remote_sa);
2098 listener->remote_port = switch_sockaddr_get_port(remote_sa);
2099
2100 switch_socket_addr_get(&local_sa, SWITCH_FALSE, listener->sock);
2101 switch_get_addr(listener->local_ip, sizeof(listener->local_ip), local_sa);
2102 listener->local_port = switch_sockaddr_get_port(local_sa);
2103
2104 launch_listener_thread(listener);
2105
2106 }
2107
2108 end:
2109
2110 close_socket(&profile->sock, profile);
2111
2112 if (tmp_pool) {
2113 switch_core_destroy_memory_pool(&tmp_pool);
2114 }
2115
2116 if (listener_pool) {
2117 switch_core_destroy_memory_pool(&listener_pool);
2118 }
2119
2120
2121 fail:
2122 return NULL;
2123 }
2124
2125
launch_skinny_profile_thread(skinny_profile_t * profile)2126 void launch_skinny_profile_thread(skinny_profile_t *profile) {
2127 switch_thread_t *thread;
2128 switch_threadattr_t *thd_attr = NULL;
2129
2130 switch_threadattr_create(&thd_attr, profile->pool);
2131 switch_threadattr_detach_set(thd_attr, 1);
2132 switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
2133 switch_thread_create(&thread, thd_attr, skinny_profile_run, profile, profile->pool);
2134 }
2135
2136 /*****************************************************************************/
2137 /* MODULE FUNCTIONS */
2138 /*****************************************************************************/
skinny_get_endpoint_interface()2139 switch_endpoint_interface_t *skinny_get_endpoint_interface()
2140 {
2141 return skinny_endpoint_interface;
2142 }
2143
skinny_profile_respawn(skinny_profile_t * profile,int force)2144 switch_status_t skinny_profile_respawn(skinny_profile_t *profile, int force)
2145 {
2146 if (force || switch_test_flag(profile, PFLAG_SHOULD_RESPAWN)) {
2147 switch_clear_flag_locked(profile, PFLAG_SHOULD_RESPAWN);
2148 switch_set_flag_locked(profile, PFLAG_RESPAWN);
2149 switch_clear_flag_locked(profile, PFLAG_LISTENER_READY);
2150 profile_walk_listeners(profile, kill_listener, NULL);
2151 close_socket(&profile->sock, profile);
2152 }
2153 return SWITCH_STATUS_SUCCESS;
2154 }
2155
skinny_profile_set(skinny_profile_t * profile,const char * var,const char * val)2156 switch_status_t skinny_profile_set(skinny_profile_t *profile, const char *var, const char *val)
2157 {
2158 if (!var)
2159 return SWITCH_STATUS_FALSE;
2160
2161 if (profile->sock && !strcasecmp(var, "odbc-dsn")) {
2162 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
2163 "Skinny profile setting 'odbc-dsn' can't be changed while running\n");
2164 return SWITCH_STATUS_FALSE;
2165 }
2166
2167 if (!strcasecmp(var, "domain")) {
2168 profile->domain = switch_core_strdup(profile->pool, val);
2169 } else if (!strcasecmp(var, "ip")) {
2170 if (!profile->ip || strcmp(val, profile->ip)) {
2171 profile->ip = switch_core_strdup(profile->pool, zstr(val) ? NULL : val);
2172 switch_set_flag_locked(profile, PFLAG_SHOULD_RESPAWN);
2173 }
2174 } else if (!strcasecmp(var, "port")) {
2175 if (atoi(val) != profile->port) {
2176 profile->port = atoi(val);
2177 switch_set_flag_locked(profile, PFLAG_SHOULD_RESPAWN);
2178 }
2179 } else if (!strcasecmp(var, "patterns-dialplan")) {
2180 profile->patterns_dialplan = switch_core_strdup(profile->pool, val);
2181 } else if (!strcasecmp(var, "patterns-context")) {
2182 profile->patterns_context = switch_core_strdup(profile->pool, val);
2183 } else if (!strcasecmp(var, "dialplan")) {
2184 profile->dialplan = switch_core_strdup(profile->pool, val);
2185 } else if (!strcasecmp(var, "context")) {
2186 profile->context = switch_core_strdup(profile->pool, val);
2187 } else if (!strcasecmp(var, "keep-alive")) {
2188 profile->keep_alive = atoi(val);
2189 } else if (!strcasecmp(var, "digit-timeout")) {
2190 profile->digit_timeout = atoi(val);
2191 } else if (!strcasecmp(var, "date-format")) {
2192 memcpy(profile->date_format, val, 6);
2193 } else if (!strcasecmp(var, "odbc-dsn") && !zstr(val)) {
2194 profile->odbc_dsn = switch_core_strdup(profile->pool, val);
2195 } else if (!strcasecmp(var, "debug")) {
2196 profile->debug = atoi(val);
2197 } else if (!strcasecmp(var, "auto-restart")) {
2198 profile->auto_restart = switch_true(val);
2199 } else if (!strcasecmp(var, "non-blocking")) {
2200 profile->non_blocking = switch_true(val);
2201 } else if (!strcasecmp(var, "ext-voicemail")) {
2202 if (!profile->ext_voicemail || strcmp(val, profile->ext_voicemail)) {
2203 profile->ext_voicemail = switch_core_strdup(profile->pool, val);
2204 }
2205 } else if (!strcasecmp(var, "ext-redial")) {
2206 if (!profile->ext_redial || strcmp(val, profile->ext_redial)) {
2207 profile->ext_redial = switch_core_strdup(profile->pool, val);
2208 }
2209 } else if (!strcasecmp(var, "ext-meetme")) {
2210 if (!profile->ext_meetme || strcmp(val, profile->ext_meetme)) {
2211 profile->ext_meetme = switch_core_strdup(profile->pool, val);
2212 }
2213 } else if (!strcasecmp(var, "ext-pickup")) {
2214 if (!profile->ext_pickup || strcmp(val, profile->ext_pickup)) {
2215 profile->ext_pickup = switch_core_strdup(profile->pool, val);
2216 }
2217 } else if (!strcasecmp(var, "ext-cfwdall")) {
2218 if (!profile->ext_cfwdall || strcmp(val, profile->ext_cfwdall)) {
2219 profile->ext_cfwdall = switch_core_strdup(profile->pool, val);
2220 }
2221 } else {
2222 return SWITCH_STATUS_FALSE;
2223 }
2224
2225 return SWITCH_STATUS_SUCCESS;
2226 }
2227
profile_walk_listeners(skinny_profile_t * profile,skinny_listener_callback_func_t callback,void * pvt)2228 void profile_walk_listeners(skinny_profile_t *profile, skinny_listener_callback_func_t callback, void *pvt)
2229 {
2230 listener_t *l;
2231
2232 switch_mutex_lock(profile->listener_mutex);
2233 for (l = profile->listeners; l; l = l->next) {
2234 callback(l, pvt);
2235 }
2236 switch_mutex_unlock(profile->listener_mutex);
2237 }
2238
load_skinny_config(void)2239 static switch_status_t load_skinny_config(void)
2240 {
2241 char *cf = "skinny.conf";
2242 switch_xml_t xcfg, xml, xprofiles, xprofile;
2243 switch_cache_db_handle_t *dbh = NULL;
2244
2245 if (!(xml = switch_xml_open_cfg(cf, &xcfg, NULL))) {
2246 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf);
2247 return SWITCH_STATUS_TERM;
2248 }
2249
2250 switch_mutex_lock(skinny_globals.mutex);
2251 if ((xprofiles = switch_xml_child(xcfg, "profiles"))) {
2252 for (xprofile = switch_xml_child(xprofiles, "profile"); xprofile; xprofile = xprofile->next) {
2253 char *profile_name = (char *) switch_xml_attr_soft(xprofile, "name");
2254 switch_xml_t xsettings, xdevice_types, xsoft_key_set_sets;
2255 if (zstr(profile_name)) {
2256 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
2257 "<profile> is missing name attribute\n");
2258 continue;
2259 }
2260 if ((xsettings = switch_xml_child(xprofile, "settings"))) {
2261 switch_memory_pool_t *profile_pool = NULL;
2262 char dbname[256];
2263 skinny_profile_t *profile = NULL;
2264 switch_xml_t param;
2265
2266 if (switch_core_new_memory_pool(&profile_pool) != SWITCH_STATUS_SUCCESS) {
2267 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "OH OH no pool\n");
2268 return SWITCH_STATUS_TERM;
2269 }
2270 profile = switch_core_alloc(profile_pool, sizeof(skinny_profile_t));
2271 profile->pool = profile_pool;
2272 profile->name = switch_core_strdup(profile->pool, profile_name);
2273 profile->auto_restart = SWITCH_TRUE;
2274 profile->non_blocking = SWITCH_FALSE;
2275 profile->digit_timeout = 10000; /* 10 seconds */
2276 switch_mutex_init(&profile->sql_mutex, SWITCH_MUTEX_NESTED, profile->pool);
2277 switch_mutex_init(&profile->listener_mutex, SWITCH_MUTEX_NESTED, profile->pool);
2278 switch_mutex_init(&profile->sock_mutex, SWITCH_MUTEX_NESTED, profile->pool);
2279 switch_mutex_init(&profile->flag_mutex, SWITCH_MUTEX_NESTED, profile->pool);
2280
2281 switch_mutex_init(&profile->device_name_lock_mutex, SWITCH_MUTEX_NESTED, profile->pool);
2282
2283 for (param = switch_xml_child(xsettings, "param"); param; param = param->next) {
2284 char *var = (char *) switch_xml_attr_soft(param, "name");
2285 char *val = (char *) switch_xml_attr_soft(param, "value");
2286
2287 if (skinny_profile_set(profile, var, val) != SWITCH_STATUS_SUCCESS) {
2288 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
2289 "Unable to set skinny setting '%s'. Does it exists?\n", var);
2290 }
2291 } /* param */
2292
2293 if (!profile->dialplan) {
2294 skinny_profile_set(profile, "dialplan","XML");
2295 }
2296
2297 if (!profile->context) {
2298 skinny_profile_set(profile, "context","default");
2299 }
2300
2301 if (!profile->patterns_dialplan) {
2302 skinny_profile_set(profile, "patterns-dialplan","XML");
2303 }
2304
2305 if (!profile->patterns_context) {
2306 skinny_profile_set(profile, "patterns-context","skinny-patterns");
2307 }
2308
2309 if (!profile->ext_voicemail) {
2310 skinny_profile_set(profile, "ext-voicemail", "vmain");
2311 }
2312
2313 if (!profile->ext_redial) {
2314 skinny_profile_set(profile, "ext-redial", "redial");
2315 }
2316
2317 if (!profile->ext_meetme) {
2318 skinny_profile_set(profile, "ext-meetme", "conference");
2319 }
2320
2321 if (!profile->ext_pickup) {
2322 skinny_profile_set(profile, "ext-pickup", "pickup");
2323 }
2324
2325 if (!profile->ext_cfwdall) {
2326 skinny_profile_set(profile, "ext-pickup", "cfwdall");
2327 }
2328
2329 if (profile->port == 0) {
2330 profile->port = 2000;
2331 }
2332
2333 /* Soft Key Set Sets */
2334 switch_core_hash_init(&profile->soft_key_set_sets_hash);
2335 if ((xsoft_key_set_sets = switch_xml_child(xprofile, "soft-key-set-sets"))) {
2336 switch_xml_t xsoft_key_set_set;
2337 for (xsoft_key_set_set = switch_xml_child(xsoft_key_set_sets, "soft-key-set-set"); xsoft_key_set_set; xsoft_key_set_set = xsoft_key_set_set->next) {
2338 char *soft_key_set_set_name = (char *) switch_xml_attr_soft(xsoft_key_set_set, "name");
2339 if (soft_key_set_set_name) {
2340 switch_xml_t xsoft_key_set;
2341 skinny_message_t *message;
2342 message = switch_core_alloc(profile->pool, 12+sizeof(message->data.soft_key_set));
2343 message->type = SOFT_KEY_SET_RES_MESSAGE;
2344 message->length = 4 + sizeof(message->data.soft_key_set);
2345 message->data.soft_key_set.soft_key_set_offset = 0;
2346 message->data.soft_key_set.soft_key_set_count = 11;
2347 message->data.soft_key_set.total_soft_key_set_count = 11;
2348 for (xsoft_key_set = switch_xml_child(xsoft_key_set_set, "soft-key-set"); xsoft_key_set; xsoft_key_set = xsoft_key_set->next) {
2349 uint32_t soft_key_set_id;
2350 if ((soft_key_set_id = skinny_str2soft_key_set(switch_xml_attr_soft(xsoft_key_set, "name"))) != -1) {
2351 char *val =switch_core_strdup(profile->pool, switch_xml_attr_soft(xsoft_key_set, "value"));
2352 size_t string_len = strlen(val);
2353 size_t string_pos, start = 0;
2354 int field_no = 0;
2355 if (zstr(val)) {
2356 continue;
2357 }
2358 if (soft_key_set_id > 15) {
2359 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
2360 "soft-key-set name '%s' is greater than 15 in soft-key-set-set '%s' in profile %s.\n",
2361 switch_xml_attr_soft(xsoft_key_set, "name"), soft_key_set_set_name, profile->name);
2362 continue;
2363 }
2364 for (string_pos = 0; string_pos <= string_len; string_pos++) {
2365 if ((val[string_pos] == ',') || (string_pos == string_len)) {
2366 val[string_pos] = '\0';
2367 if (field_no > 15) {
2368 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
2369 "soft-key-set name '%s' is limited to 16 buttons in soft-key-set-set '%s' in profile %s.\n",
2370 switch_xml_attr_soft(xsoft_key_set, "name"), soft_key_set_set_name, profile->name);
2371 break;
2372 }
2373 message->data.soft_key_set.soft_key_set[soft_key_set_id].soft_key_template_index[field_no++] = skinny_str2soft_key_event(&val[start]);
2374 start = string_pos+1;
2375 }
2376 }
2377 } else {
2378 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
2379 "Unknown soft-key-set name '%s' in soft-key-set-set '%s' in profile %s.\n",
2380 switch_xml_attr_soft(xsoft_key_set, "name"), soft_key_set_set_name, profile->name);
2381 }
2382 } /* soft-key-set */
2383 switch_core_hash_insert(profile->soft_key_set_sets_hash, soft_key_set_set_name, message);
2384 } else {
2385 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
2386 "<soft-key-set-set> is missing a name attribute in profile %s.\n", profile->name);
2387 }
2388 } /* soft-key-set-set */
2389 } else {
2390 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
2391 "<soft-key-set-sets> is missing in profile %s.\n", profile->name);
2392 } /* soft-key-set-sets */
2393 if (!switch_core_hash_find(profile->soft_key_set_sets_hash, "default")) {
2394 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
2395 "Profile %s doesn't have a default <soft-key-set-set>. Profile ignored.\n", profile->name);
2396 switch_core_destroy_memory_pool(&profile_pool);
2397 continue;
2398 }
2399
2400 /* Device Name Locks */
2401 switch_core_hash_init(&profile->device_name_lock_hash);
2402
2403 /* Device types */
2404 switch_core_hash_init(&profile->device_type_params_hash);
2405 if ((xdevice_types = switch_xml_child(xprofile, "device-types"))) {
2406 switch_xml_t xdevice_type;
2407 for (xdevice_type = switch_xml_child(xdevice_types, "device-type"); xdevice_type; xdevice_type = xdevice_type->next) {
2408 uint32_t id = skinny_str2device_type(switch_xml_attr_soft(xdevice_type, "id"));
2409 if (id != 0) {
2410 char *id_str = switch_mprintf("%d", id);
2411 skinny_device_type_params_t *params = switch_core_alloc(profile->pool, sizeof(skinny_device_type_params_t));
2412 for (param = switch_xml_child(xdevice_type, "param"); param; param = param->next) {
2413 char *var = (char *) switch_xml_attr_soft(param, "name");
2414 char *val = (char *) switch_xml_attr_soft(param, "value");
2415
2416 if (!strcasecmp(var, "firmware-version")) {
2417 strncpy(params->firmware_version, val, 16);
2418 }
2419 } /* param */
2420 switch_core_hash_insert(profile->device_type_params_hash, id_str, params);
2421 switch_safe_free(id_str);
2422 } else {
2423 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
2424 "Unknow device type %s in profile %s.\n", switch_xml_attr_soft(xdevice_type, "id"), profile->name);
2425 }
2426 }
2427 }
2428
2429 /* Database */
2430 switch_snprintf(dbname, sizeof(dbname), "skinny_%s", profile->name);
2431 profile->dbname = switch_core_strdup(profile->pool, dbname);
2432
2433
2434
2435
2436 if ((dbh = skinny_get_db_handle(profile))) {
2437 switch_cache_db_test_reactive(dbh, "select count(*) from skinny_devices", NULL, devices_sql);
2438 switch_cache_db_test_reactive(dbh, "select count(*) from skinny_lines", NULL, lines_sql);
2439 switch_cache_db_test_reactive(dbh, "select count(*) from skinny_buttons", NULL, buttons_sql);
2440 switch_cache_db_test_reactive(dbh, "select count(*) from skinny_active_lines", NULL, active_lines_sql);
2441 switch_cache_db_release_db_handle(&dbh);
2442 }
2443
2444 skinny_profile_respawn(profile, 0);
2445
2446 /* Register profile */
2447 switch_core_hash_insert(skinny_globals.profile_hash, profile->name, profile);
2448 profile = NULL;
2449 } else {
2450 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
2451 "Settings are missing from profile %s.\n", profile_name);
2452 } /* settings */
2453 } /* profile */
2454 }
2455 switch_xml_free(xml);
2456 switch_mutex_unlock(skinny_globals.mutex);
2457
2458 return SWITCH_STATUS_SUCCESS;
2459 }
2460
skinny_user_to_device_event_handler(switch_event_t * event)2461 static void skinny_user_to_device_event_handler(switch_event_t *event)
2462 {
2463 char *profile_name = switch_event_get_header_nil(event, "Skinny-Profile-Name");
2464 skinny_profile_t *profile;
2465
2466 if ((profile = skinny_find_profile(profile_name))) {
2467 char *device_name = switch_event_get_header_nil(event, "Skinny-Device-Name");
2468 uint32_t device_instance = atoi(switch_event_get_header_nil(event, "Skinny-Station-Instance"));
2469 listener_t *listener = NULL;
2470 skinny_profile_find_listener_by_device_name_and_instance(profile, device_name, device_instance, &listener);
2471 if(listener) {
2472 uint32_t message_type = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Message-Id"));
2473 uint32_t application_id = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Application-Id"));
2474 uint32_t line_instance = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Line-Instance"));
2475 uint32_t call_id = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Call-Id"));
2476 uint32_t transaction_id = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Transaction-Id"));
2477 uint32_t data_length;
2478 uint32_t sequence_flag = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Sequence-Flag"));
2479 uint32_t display_priority = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Display-Priority"));
2480 uint32_t conference_id = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Conference-Id"));
2481 uint32_t app_instance_id = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-App-Instance-Id"));
2482 uint32_t routing_id = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Routing-Id"));
2483 char *data = switch_event_get_body(event);
2484 if (message_type == 0) {
2485 message_type = skinny_str2message_type(switch_event_get_header_nil(event, "Skinny-UserToDevice-Message-Id-String"));
2486 }
2487 switch(message_type) {
2488 case USER_TO_DEVICE_DATA_MESSAGE:
2489 data_length = strlen(data); /* we ignore data_length sent */
2490 send_data(listener, message_type,
2491 application_id, line_instance, call_id, transaction_id, data_length,
2492 data);
2493 break;
2494 case USER_TO_DEVICE_DATA_VERSION1_MESSAGE:
2495 data_length = strlen(data); /* we ignore data_length sent */
2496 send_extended_data(listener, message_type,
2497 application_id, line_instance, call_id, transaction_id, data_length,
2498 sequence_flag, display_priority, conference_id, app_instance_id, routing_id,
2499 data);
2500 break;
2501 default:
2502 skinny_log_l(listener, SWITCH_LOG_WARNING, "Incorrect message type %s (%d).\n", skinny_message_type2str(message_type), message_type);
2503 }
2504 } else {
2505 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
2506 "Device %s:%d in profile '%s' not found.\n", device_name, device_instance, profile_name);
2507 }
2508 } else {
2509 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
2510 "Profile '%s' not found.\n", profile_name);
2511 }
2512 }
2513
skinny_call_state_event_handler(switch_event_t * event)2514 static void skinny_call_state_event_handler(switch_event_t *event)
2515 {
2516 char *subclass;
2517
2518 if ((subclass = switch_event_get_header_nil(event, "Event-Subclass")) && !strcasecmp(subclass, SKINNY_EVENT_CALL_STATE)) {
2519 char *profile_name = switch_event_get_header_nil(event, "Skinny-Profile-Name");
2520 char *device_name = switch_event_get_header_nil(event, "Skinny-Device-Name");
2521 uint32_t device_instance = atoi(switch_event_get_header_nil(event, "Skinny-Station-Instance"));
2522 uint32_t line_instance = atoi(switch_event_get_header_nil(event, "Skinny-Line-Instance"));
2523 uint32_t call_id = atoi(switch_event_get_header_nil(event, "Skinny-Call-Id"));
2524 uint32_t call_state = atoi(switch_event_get_header_nil(event, "Skinny-Call-State"));
2525 skinny_profile_t *profile;
2526 listener_t *listener = NULL;
2527 char *line_instance_condition, *call_id_condition;
2528 char *sql;
2529
2530 if ((profile = skinny_find_profile(profile_name))) {
2531 skinny_profile_find_listener_by_device_name_and_instance(profile, device_name, device_instance, &listener);
2532 if(listener) {
2533 if(line_instance > 0) {
2534 line_instance_condition = switch_mprintf("line_instance=%d", line_instance);
2535 } else {
2536 line_instance_condition = switch_mprintf("1=1");
2537 }
2538 switch_assert(line_instance_condition);
2539 if(call_id > 0) {
2540 call_id_condition = switch_mprintf("call_id=%d", call_id);
2541 } else {
2542 call_id_condition = switch_mprintf("1=1");
2543 }
2544 switch_assert(call_id_condition);
2545
2546 if ((sql = switch_mprintf(
2547 "UPDATE skinny_active_lines "
2548 "SET call_state=%d "
2549 "WHERE device_name='%q' AND device_instance=%d "
2550 "AND %q AND %q",
2551 call_state,
2552 listener->device_name, listener->device_instance,
2553 line_instance_condition, call_id_condition
2554 ))) {
2555 skinny_execute_sql(listener->profile, sql, listener->profile->sql_mutex);
2556 switch_safe_free(sql);
2557 }
2558 switch_safe_free(line_instance_condition);
2559 switch_safe_free(call_id_condition);
2560 } else {
2561 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
2562 "Device %s:%d in profile '%s' not found.\n", device_name, device_instance, profile_name);
2563 }
2564 } else {
2565 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
2566 "Profile '%s' not found.\n", profile_name);
2567 }
2568 }
2569 }
2570
2571 struct skinny_message_waiting_event_handler_helper {
2572 skinny_profile_t *profile;
2573 switch_bool_t yn;
2574 int total_new_messages;
2575 int total_saved_messages;
2576 int total_new_urgent_messages;
2577 int total_saved_urgent_messages;
2578 };
2579
skinny_message_waiting_event_handler_callback(void * pArg,int argc,char ** argv,char ** columnNames)2580 int skinny_message_waiting_event_handler_callback(void *pArg, int argc, char **argv, char **columnNames)
2581 {
2582 char *device_name = argv[0];
2583 uint32_t device_instance = atoi(argv[1]);
2584
2585 struct skinny_message_waiting_event_handler_helper *helper = pArg;
2586 listener_t *listener = NULL;
2587
2588 skinny_profile_find_listener_by_device_name_and_instance(helper->profile,
2589 device_name, device_instance, &listener);
2590
2591 if (listener) {
2592 if (helper->yn == SWITCH_TRUE) {
2593 char buffer[32];
2594 char *label;
2595 send_set_lamp(listener, SKINNY_BUTTON_VOICEMAIL, 0, SKINNY_LAMP_ON);
2596
2597 label = skinny_textid2raw(SKINNY_TEXTID_YOU_HAVE_VOICEMAIL);
2598 sprintf(buffer, "%s: (%d/%d urgents)", label, helper->total_new_messages, helper->total_new_urgent_messages);
2599 switch_safe_free(label);
2600
2601 send_display_pri_notify(listener, 5, 10, buffer);
2602 } else {
2603 send_set_lamp(listener, SKINNY_BUTTON_VOICEMAIL, 0, SKINNY_LAMP_OFF);
2604 send_clear_prompt_status(listener, 0, 0);
2605 }
2606 }
2607 return 0;
2608 }
2609
skinny_message_waiting_event_handler(switch_event_t * event)2610 static void skinny_message_waiting_event_handler(switch_event_t *event)
2611 {
2612 char *account, *dup_account, *yn, *host, *user, *count_str;
2613 char *pname = NULL;
2614 skinny_profile_t *profile = NULL;
2615 char *sql;
2616
2617 if (!(account = switch_event_get_header(event, "mwi-message-account"))) {
2618 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing required Header 'MWI-Message-Account'\n");
2619 return;
2620 }
2621
2622 if (!strncmp("sip:", account, 4)) {
2623 return;
2624 }
2625
2626 if (!(yn = switch_event_get_header(event, "mwi-messages-waiting"))) {
2627 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing required Header 'MWI-Messages-Waiting'\n");
2628 return;
2629 }
2630 dup_account = strdup(account);
2631 switch_assert(dup_account != NULL);
2632 switch_split_user_domain(dup_account, &user, &host);
2633
2634 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "MWI Event received for account %s with messages waiting %s\n", account, yn);
2635
2636 if ((pname = switch_event_get_header(event, "skinny-profile"))) {
2637 if (!(profile = skinny_find_profile(pname))) {
2638 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "No profile %s\n", pname);
2639 }
2640 }
2641
2642 if (!profile) {
2643 if (!host || !(profile = skinny_find_profile_by_domain(host))) {
2644 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find profile with domain %s\n", switch_str_nil(host));
2645 switch_safe_free(dup_account);
2646 return;
2647 }
2648 }
2649
2650 count_str = switch_event_get_header(event, "mwi-voice-message");
2651
2652 if ((sql = switch_mprintf(
2653 "SELECT device_name, device_instance FROM skinny_lines "
2654 "WHERE value='%q' AND line_instance=1", user))) {
2655 struct skinny_message_waiting_event_handler_helper helper = {0};
2656 helper.profile = profile;
2657 helper.yn = switch_true(yn);
2658 if (count_str) {
2659 sscanf(count_str,"%d/%d (%d/%d)",
2660 &helper.total_new_messages, &helper.total_saved_messages,
2661 &helper.total_new_urgent_messages, &helper.total_saved_urgent_messages);
2662 }
2663 skinny_execute_sql_callback(profile, profile->sql_mutex, sql, skinny_message_waiting_event_handler_callback, &helper);
2664 switch_safe_free(sql);
2665 }
2666
2667 switch_safe_free(dup_account);
2668 }
2669
2670
skinny_trap_event_handler(switch_event_t * event)2671 static void skinny_trap_event_handler(switch_event_t *event)
2672 {
2673 const char *cond = switch_event_get_header(event, "condition");
2674
2675
2676 if (cond && !strcmp(cond, "network-address-change") && skinny_globals.auto_restart) {
2677 const char *old_ip4 = switch_event_get_header_nil(event, "network-address-previous-v4");
2678 const char *new_ip4 = switch_event_get_header_nil(event, "network-address-change-v4");
2679 const char *old_ip6 = switch_event_get_header_nil(event, "network-address-previous-v6");
2680 const char *new_ip6 = switch_event_get_header_nil(event, "network-address-change-v6");
2681 switch_hash_index_t *hi;
2682 const void *var;
2683 void *val;
2684 skinny_profile_t *profile;
2685
2686 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "EVENT_TRAP: IP change detected\n");
2687 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "IP change detected [%s]->[%s] [%s]->[%s]\n", old_ip4, new_ip4, old_ip6, new_ip6);
2688
2689 switch_mutex_lock(skinny_globals.mutex);
2690 if (skinny_globals.profile_hash) {
2691 for (hi = switch_core_hash_first(skinny_globals.profile_hash); hi; hi = switch_core_hash_next(&hi)) {
2692 switch_core_hash_this(hi, &var, NULL, &val);
2693 if ((profile = (skinny_profile_t *) val) && profile->auto_restart) {
2694 if (!strcmp(profile->ip, old_ip4)) {
2695 skinny_profile_set(profile, "ip", new_ip4);
2696 } else if (!strcmp(profile->ip, old_ip6)) {
2697 skinny_profile_set(profile, "ip", new_ip6);
2698 }
2699 skinny_profile_respawn(profile, 0);
2700 }
2701 }
2702 }
2703 switch_mutex_unlock(skinny_globals.mutex);
2704 }
2705
2706 }
2707
2708 /*****************************************************************************/
SWITCH_MODULE_LOAD_FUNCTION(mod_skinny_load)2709 SWITCH_MODULE_LOAD_FUNCTION(mod_skinny_load)
2710 {
2711 switch_hash_index_t *hi;
2712 /* skinny_globals init */
2713 memset(&skinny_globals, 0, sizeof(skinny_globals));
2714
2715 if (switch_core_new_memory_pool(&skinny_globals.pool) != SWITCH_STATUS_SUCCESS) {
2716 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "OH OH no pool\n");
2717 return SWITCH_STATUS_TERM;
2718 }
2719 switch_mutex_init(&skinny_globals.mutex, SWITCH_MUTEX_NESTED, skinny_globals.pool);
2720
2721 switch_mutex_lock(skinny_globals.mutex);
2722 switch_core_hash_init(&skinny_globals.profile_hash);
2723 skinny_globals.running = 1;
2724 skinny_globals.auto_restart = SWITCH_TRUE;
2725 switch_mutex_unlock(skinny_globals.mutex);
2726
2727 /* load_skinny_config does it's own locking */
2728 load_skinny_config();
2729
2730 switch_mutex_lock(skinny_globals.mutex);
2731
2732 /* at least one profile */
2733 if (switch_core_hash_empty( skinny_globals.profile_hash)) {
2734 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No profile found!\n");
2735 return SWITCH_STATUS_TERM;
2736 }
2737 /* bind to events */
2738 if ((switch_event_bind_removable(modname, SWITCH_EVENT_CUSTOM, SKINNY_EVENT_CALL_STATE, skinny_call_state_event_handler, NULL, &skinny_globals.call_state_node) != SWITCH_STATUS_SUCCESS)) {
2739 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind our call_state handler!\n");
2740 return SWITCH_STATUS_TERM;
2741 }
2742 if ((switch_event_bind_removable(modname, SWITCH_EVENT_MESSAGE_WAITING, NULL, skinny_message_waiting_event_handler, NULL, &skinny_globals.message_waiting_node) != SWITCH_STATUS_SUCCESS)) {
2743 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't bind our message waiting handler!\n");
2744 /* Not such severe to prevent loading */
2745 }
2746 if ((switch_event_bind_removable(modname, SWITCH_EVENT_TRAP, NULL, skinny_trap_event_handler, NULL, &skinny_globals.trap_node) != SWITCH_STATUS_SUCCESS)) {
2747 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't bind our trap handler!\n");
2748 /* Not such severe to prevent loading */
2749 }
2750 if ((switch_event_bind_removable(modname, SWITCH_EVENT_CUSTOM, SKINNY_EVENT_USER_TO_DEVICE, skinny_user_to_device_event_handler, NULL, &skinny_globals.user_to_device_node) != SWITCH_STATUS_SUCCESS)) {
2751 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind our user_to_device handler!\n");
2752 /* Not such severe to prevent loading */
2753 }
2754
2755 /* reserve events */
2756 if (switch_event_reserve_subclass(SKINNY_EVENT_REGISTER) != SWITCH_STATUS_SUCCESS) {
2757 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", SKINNY_EVENT_REGISTER);
2758 return SWITCH_STATUS_TERM;
2759 }
2760 if (switch_event_reserve_subclass(SKINNY_EVENT_UNREGISTER) != SWITCH_STATUS_SUCCESS) {
2761 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", SKINNY_EVENT_UNREGISTER);
2762 return SWITCH_STATUS_TERM;
2763 }
2764 if (switch_event_reserve_subclass(SKINNY_EVENT_EXPIRE) != SWITCH_STATUS_SUCCESS) {
2765 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", SKINNY_EVENT_EXPIRE);
2766 return SWITCH_STATUS_TERM;
2767 }
2768 if (switch_event_reserve_subclass(SKINNY_EVENT_ALARM) != SWITCH_STATUS_SUCCESS) {
2769 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", SKINNY_EVENT_ALARM);
2770 return SWITCH_STATUS_TERM;
2771 }
2772 if (switch_event_reserve_subclass(SKINNY_EVENT_CALL_STATE) != SWITCH_STATUS_SUCCESS) {
2773 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", SKINNY_EVENT_CALL_STATE);
2774 return SWITCH_STATUS_TERM;
2775 }
2776 if (switch_event_reserve_subclass(SKINNY_EVENT_USER_TO_DEVICE) != SWITCH_STATUS_SUCCESS) {
2777 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", SKINNY_EVENT_USER_TO_DEVICE);
2778 return SWITCH_STATUS_TERM;
2779 }
2780 if (switch_event_reserve_subclass(SKINNY_EVENT_DEVICE_TO_USER) != SWITCH_STATUS_SUCCESS) {
2781 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", SKINNY_EVENT_DEVICE_TO_USER);
2782 return SWITCH_STATUS_TERM;
2783 }
2784
2785 /* connect my internal structure to the blank pointer passed to me */
2786 *module_interface = switch_loadable_module_create_module_interface(skinny_globals.pool, modname);
2787 skinny_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE);
2788 skinny_endpoint_interface->interface_name = "skinny";
2789 skinny_endpoint_interface->io_routines = &skinny_io_routines;
2790 skinny_endpoint_interface->state_handler = &skinny_state_handlers;
2791
2792 skinny_api_register(module_interface);
2793
2794 /* launch listeners */
2795 for (hi = switch_core_hash_first(skinny_globals.profile_hash); hi; hi = switch_core_hash_next(&hi)) {
2796 void *val;
2797 skinny_profile_t *profile;
2798
2799 switch_core_hash_this(hi, NULL, NULL, &val);
2800 profile = (skinny_profile_t *) val;
2801
2802 launch_skinny_profile_thread(profile);
2803 }
2804 switch_mutex_unlock(skinny_globals.mutex);
2805
2806 /* indicate that the module should continue to be loaded */
2807 return SWITCH_STATUS_SUCCESS;
2808 }
2809
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_skinny_shutdown)2810 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_skinny_shutdown)
2811 {
2812 switch_hash_index_t *hi;
2813 void *val;
2814 switch_memory_pool_t *pool = skinny_globals.pool;
2815 switch_mutex_t *mutex = skinny_globals.mutex;
2816 int sanity = 0;
2817
2818 skinny_api_unregister();
2819
2820 /* release events */
2821 switch_event_unbind(&skinny_globals.user_to_device_node);
2822 switch_event_unbind(&skinny_globals.call_state_node);
2823 switch_event_unbind(&skinny_globals.message_waiting_node);
2824 switch_event_unbind(&skinny_globals.trap_node);
2825 switch_event_free_subclass(SKINNY_EVENT_REGISTER);
2826 switch_event_free_subclass(SKINNY_EVENT_UNREGISTER);
2827 switch_event_free_subclass(SKINNY_EVENT_EXPIRE);
2828 switch_event_free_subclass(SKINNY_EVENT_ALARM);
2829 switch_event_free_subclass(SKINNY_EVENT_CALL_STATE);
2830 switch_event_free_subclass(SKINNY_EVENT_USER_TO_DEVICE);
2831 switch_event_free_subclass(SKINNY_EVENT_DEVICE_TO_USER);
2832
2833 switch_mutex_lock(mutex);
2834
2835 skinny_globals.running = 0;
2836
2837 /* kill listeners */
2838 walk_listeners(kill_listener, NULL);
2839
2840 /* close sockets */
2841 switch_mutex_lock(skinny_globals.mutex);
2842 for (hi = switch_core_hash_first(skinny_globals.profile_hash); hi; hi = switch_core_hash_next(&hi)) {
2843 skinny_profile_t *profile;
2844 switch_core_hash_this(hi, NULL, NULL, &val);
2845 profile = (skinny_profile_t *) val;
2846
2847 close_socket(&profile->sock, profile);
2848
2849 while (profile->listener_threads) {
2850 switch_yield(100000);
2851 walk_listeners(kill_listener, NULL);
2852 if (++sanity >= 200) {
2853 break;
2854 }
2855 }
2856 switch_core_destroy_memory_pool(&profile->pool);
2857 }
2858 switch_mutex_unlock(skinny_globals.mutex);
2859
2860 switch_core_hash_destroy(&skinny_globals.profile_hash);
2861 memset(&skinny_globals, 0, sizeof(skinny_globals));
2862 switch_mutex_unlock(mutex);
2863 switch_core_destroy_memory_pool(&pool);
2864 return SWITCH_STATUS_SUCCESS;
2865 }
2866
2867 /* For Emacs:
2868 * Local Variables:
2869 * mode:c
2870 * indent-tabs-mode:t
2871 * tab-width:4
2872 * c-basic-offset:4
2873 * End:
2874 * For VIM:
2875 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
2876 */
2877