1 /*
2  * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3  * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
4  *
5  * Version: MPL 1.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18  *
19  * The Initial Developer of the Original Code is
20  * Anthony Minessale II <anthm@freeswitch.org>
21  * Portions created by the Initial Developer are Copyright (C)
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Anthony Minessale II <anthm@freeswitch.org>
27  * Raymond Chandler <intralanman@freeswitch.org>
28  *
29  * mod_sms.c -- Abstract SMS
30  *
31  */
32 #include <switch.h>
33 #define SMS_CHAT_PROTO "GLOBAL_SMS"
34 #define MY_EVENT_SEND_MESSAGE "SMS::SEND_MESSAGE"
35 #define MY_EVENT_DELIVERY_REPORT "SMS::DELIVERY_REPORT"
36 
37 /* Prototypes */
38 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_sms_shutdown);
39 SWITCH_MODULE_RUNTIME_FUNCTION(mod_sms_runtime);
40 SWITCH_MODULE_LOAD_FUNCTION(mod_sms_load);
41 SWITCH_MODULE_DEFINITION(mod_sms, mod_sms_load, mod_sms_shutdown, NULL);
42 
43 
send_report(switch_event_t * event,const char * Status)44 static void send_report(switch_event_t *event, const char * Status) {
45 	switch_event_t *report = NULL;
46 	switch_event_header_t *header;
47 
48 	if (switch_event_create_subclass(&report, SWITCH_EVENT_CUSTOM, MY_EVENT_DELIVERY_REPORT) == SWITCH_STATUS_SUCCESS) {
49 
50 		switch_event_add_header_string(report, SWITCH_STACK_BOTTOM, "Status", Status);
51 
52 
53 		for (header = event->headers; header; header = header->next) {
54 			if (!strcmp(header->name, "Event-Subclass")) {
55 				continue;
56 			}
57 			if (!strcmp(header->name, "Event-Name")) {
58 				continue;
59 			}
60 	        if (header->idx) {
61 	            int i;
62 	            for (i = 0; i < header->idx; i++) {
63 	                switch_event_add_header_string(report, SWITCH_STACK_PUSH, header->name, header->array[i]);
64 	            }
65 	        } else {
66 	            switch_event_add_header_string(report, SWITCH_STACK_BOTTOM, header->name, header->value);
67 	        }
68 		}
69 		switch_event_fire(&report);
70 	}
71 }
72 
event_handler(switch_event_t * event)73 static void event_handler(switch_event_t *event)
74 {
75 	const char *dest_proto = switch_event_get_header(event, "dest_proto");
76 	const char *check_failure = switch_event_get_header(event, "Delivery-Failure");
77 	const char *check_nonblocking = switch_event_get_header(event, "Nonblocking-Delivery");
78 
79 	switch_event_add_header(event, SWITCH_STACK_BOTTOM, "skip_global_process", "true");
80 
81 	if (switch_true(check_failure)) {
82 
83 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Delivery Failure\n");
84 		DUMP_EVENT(event);
85 		send_report(event, "Failure");
86 		return;
87 	} else if ( check_failure && switch_false(check_failure) ) {
88 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SMS Delivery Success\n");
89 		send_report(event, "Success");
90 		return;
91 	} else if ( check_nonblocking && switch_true(check_nonblocking) ) {
92 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SMS Delivery assumed successful due to being sent in non-blocking manner\n");
93 		send_report(event, "Accepted");
94 		return;
95 	}
96 
97 	switch_core_chat_send(dest_proto, event);
98 }
99 
100 typedef enum {
101 	BREAK_ON_TRUE,
102 	BREAK_ON_FALSE,
103 	BREAK_ALWAYS,
104 	BREAK_NEVER
105 } break_t;
106 
107 
108 #define check_tz()														\
109 	do {																\
110 		tzoff = switch_event_get_header(event, "tod_tz_offset");		\
111 		tzname_ = switch_event_get_header(event, "timezone");			\
112 		if (!zstr(tzoff) && switch_is_number(tzoff)) {					\
113 			offset = atoi(tzoff);										\
114 			break;														\
115 		} else {														\
116 			tzoff = NULL;												\
117 		}																\
118 	} while(tzoff)
119 
parse_exten(switch_event_t * event,switch_xml_t xexten,switch_event_t ** extension)120 static int parse_exten(switch_event_t *event, switch_xml_t xexten, switch_event_t **extension)
121 {
122 	switch_xml_t xcond, xaction, xexpression;
123 	char *exten_name = (char *) switch_xml_attr(xexten, "name");
124 	int proceed = 0;
125 	char *expression_expanded = NULL, *field_expanded = NULL;
126 	switch_regex_t *re = NULL;
127 	const char *to = switch_event_get_header(event, "to");
128 	const char *tzoff = NULL, *tzname_ = NULL;
129 	int offset = 0;
130 
131 	check_tz();
132 
133 	if (!to) {
134 		to = "nobody";
135 	}
136 
137 	if (!exten_name) {
138 		exten_name = "_anon_";
139 	}
140 
141 	for (xcond = switch_xml_child(xexten, "condition"); xcond; xcond = xcond->next) {
142 		char *field = NULL;
143 		char *do_break_a = NULL;
144 		char *expression = NULL;
145 		const char *field_data = NULL;
146 		int ovector[30];
147 		switch_bool_t anti_action = SWITCH_TRUE;
148 		break_t do_break_i = BREAK_ON_FALSE;
149 		int time_match;
150 
151 		check_tz();
152 		time_match = switch_xml_std_datetime_check(xcond, tzoff ? &offset : NULL, tzname_);
153 
154 		switch_safe_free(field_expanded);
155 		switch_safe_free(expression_expanded);
156 
157 		if (switch_xml_child(xcond, "condition")) {
158 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Nested conditions are not allowed!\n");
159 			proceed = 1;
160 			goto done;
161 		}
162 
163 		field = (char *) switch_xml_attr(xcond, "field");
164 
165 		if ((xexpression = switch_xml_child(xcond, "expression"))) {
166 			expression = switch_str_nil(xexpression->txt);
167 		} else {
168 			expression = (char *) switch_xml_attr_soft(xcond, "expression");
169 		}
170 
171 		if ((expression_expanded = switch_event_expand_headers(event, expression)) == expression) {
172 			expression_expanded = NULL;
173 		} else {
174 			expression = expression_expanded;
175 		}
176 
177 		if ((do_break_a = (char *) switch_xml_attr(xcond, "break"))) {
178 			if (!strcasecmp(do_break_a, "on-true")) {
179 				do_break_i = BREAK_ON_TRUE;
180 			} else if (!strcasecmp(do_break_a, "on-false")) {
181 				do_break_i = BREAK_ON_FALSE;
182 			} else if (!strcasecmp(do_break_a, "always")) {
183 				do_break_i = BREAK_ALWAYS;
184 			} else if (!strcasecmp(do_break_a, "never")) {
185 				do_break_i = BREAK_NEVER;
186 			} else {
187 				do_break_a = NULL;
188 			}
189 		}
190 
191 		if (time_match == 1) {
192 			switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG,
193 							  "Chatplan: %s Date/Time Match (PASS) [%s] break=%s\n",
194 							  to, exten_name, do_break_a ? do_break_a : "on-false");
195 			anti_action = SWITCH_FALSE;
196 		} else if (time_match == 0) {
197 			switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG,
198 							  "Chatplan: %s Date/Time Match (FAIL) [%s] break=%s\n",
199 							  to, exten_name, do_break_a ? do_break_a : "on-false");
200 		}
201 
202 		if (field) {
203 			if (strchr(field, '$')) {
204 				if ((field_expanded = switch_event_expand_headers(event, field)) == field) {
205 					field_expanded = NULL;
206 					field_data = field;
207 				} else {
208 					field_data = field_expanded;
209 				}
210 			} else {
211 				field_data = switch_event_get_header(event, field);
212 			}
213 			if (!field_data) {
214 				field_data = "";
215 			}
216 
217 			if ((proceed = switch_regex_perform(field_data, expression, &re, ovector, sizeof(ovector) / sizeof(ovector[0])))) {
218 				switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG,
219 								  "Chatplan: %s Regex (PASS) [%s] %s(%s) =~ /%s/ break=%s\n",
220 								  to, exten_name, field, field_data, expression, do_break_a ? do_break_a : "on-false");
221 				anti_action = SWITCH_FALSE;
222 			} else {
223 				switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG,
224 								  "Chatplan: %s Regex (FAIL) [%s] %s(%s) =~ /%s/ break=%s\n",
225 								  to, exten_name, field, field_data, expression, do_break_a ? do_break_a : "on-false");
226 			}
227 		} else if (time_match == -1) {
228 			switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG,
229 							  "Chatplan: %s Absolute Condition [%s]\n", to, exten_name);
230 			anti_action = SWITCH_FALSE;
231 		}
232 
233 		if (anti_action) {
234 			for (xaction = switch_xml_child(xcond, "anti-action"); xaction; xaction = xaction->next) {
235 				const char *application = switch_xml_attr_soft(xaction, "application");
236 				const char *loop = switch_xml_attr(xaction, "loop");
237 				const char *data;
238 				const char *inline_ = switch_xml_attr_soft(xaction, "inline");
239 				int xinline = switch_true(inline_);
240 				int loop_count = 1;
241 
242 				if (!zstr(xaction->txt)) {
243 					data = xaction->txt;
244 				} else {
245 					data = (char *) switch_xml_attr_soft(xaction, "data");
246 				}
247 
248 				if (!*extension) {
249 					if ((switch_event_create(extension, SWITCH_EVENT_CLONE)) != SWITCH_STATUS_SUCCESS) {
250 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
251 						abort();
252 					}
253 				}
254 
255 				if (loop) {
256 					loop_count = atoi(loop);
257 				}
258 
259 				for (;loop_count > 0; loop_count--) {
260 					switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG,
261 							"Chatplan: %s ANTI-Action %s(%s) %s\n", to, application, data, xinline ? "INLINE" : "");
262 
263 					if (xinline) {
264 						switch_core_execute_chat_app(event, application, data);
265 					} else {
266 						switch_event_add_header_string(*extension, SWITCH_STACK_BOTTOM, application, zstr(data) ? "__undef" : data);
267 					}
268 				}
269 				proceed = 1;
270 			}
271 		} else {
272 			if (field && strchr(expression, '(')) {
273 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "DP_MATCH", NULL);
274 				switch_capture_regex(re, proceed, field_data, ovector, "DP_MATCH", switch_regex_set_event_header_callback, event);
275 			}
276 
277 			for (xaction = switch_xml_child(xcond, "action"); xaction; xaction = xaction->next) {
278 				char *application = (char *) switch_xml_attr_soft(xaction, "application");
279 				const char *loop = switch_xml_attr(xaction, "loop");
280 				char *data = NULL;
281 				char *substituted = NULL;
282 				uint32_t len = 0;
283 				char *app_data = NULL;
284 				const char *inline_ = switch_xml_attr_soft(xaction, "inline");
285 				int xinline = switch_true(inline_);
286 				int loop_count = 1;
287 
288 				if (!zstr(xaction->txt)) {
289 					data = xaction->txt;
290 				} else {
291 					data = (char *) switch_xml_attr_soft(xaction, "data");
292 				}
293 
294 				if (field && strchr(expression, '(')) {
295 					len = (uint32_t) (strlen(data) + strlen(field_data) + 10) * proceed;
296 					if (!(substituted = (char *) malloc(len))) {
297 						abort();
298 					}
299 					memset(substituted, 0, len);
300 					switch_perform_substitution(re, proceed, data, field_data, substituted, len, ovector);
301 					app_data = substituted;
302 				} else {
303 					app_data = data;
304 				}
305 
306 				if (!*extension) {
307 					if ((switch_event_create(extension, SWITCH_EVENT_CLONE)) != SWITCH_STATUS_SUCCESS) {
308 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
309 						abort();
310 					}
311 				}
312 
313 				if (loop) {
314 					loop_count = atoi(loop);
315 				}
316 				for (;loop_count > 0; loop_count--) {
317 					switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG,
318 							"Chatplan: %s Action %s(%s) %s\n", to, application, app_data, xinline ? "INLINE" : "");
319 
320 					if (xinline) {
321 						switch_core_execute_chat_app(event, application, app_data);
322 					} else {
323 						switch_event_add_header_string(*extension, SWITCH_STACK_BOTTOM, application, zstr(app_data) ? "__undef" : app_data);
324 					}
325 				}
326 				switch_safe_free(substituted);
327 			}
328 		}
329 		switch_regex_safe_free(re);
330 
331 		if (((anti_action == SWITCH_FALSE && do_break_i == BREAK_ON_TRUE) ||
332 			 (anti_action == SWITCH_TRUE && do_break_i == BREAK_ON_FALSE)) || do_break_i == BREAK_ALWAYS) {
333 			break;
334 		}
335 	}
336 
337   done:
338 	switch_regex_safe_free(re);
339 	switch_safe_free(field_expanded);
340 	switch_safe_free(expression_expanded);
341 	return proceed;
342 }
343 
344 
chatplan_hunt(switch_event_t * event)345 static switch_event_t *chatplan_hunt(switch_event_t *event)
346 {
347 	switch_event_t *extension = NULL;
348 	switch_xml_t alt_root = NULL, cfg, xml = NULL, xcontext, xexten = NULL;
349 	const char *alt_path;
350 	const char *context;
351 	const char *from;
352 	const char *to;
353 
354 	if (!(context = switch_event_get_header(event, "context"))) {
355 		context = "default";
356 	}
357 
358 	if (!(from = switch_event_get_header(event, "from_user"))) {
359 		from = switch_event_get_header(event, "from");
360 	}
361 
362 	if (!(to = switch_event_get_header(event, "to_user"))) {
363 		to = switch_event_get_header(event, "to");
364 	}
365 
366 	alt_path = switch_event_get_header(event, "alt_path");
367 
368 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Processing text message %s->%s in context %s\n", from, to, context);
369 
370 	/* get our handle to the "chatplan" section of the config */
371 
372 	if (!zstr(alt_path)) {
373 		switch_xml_t conf = NULL, tag = NULL;
374 		if (!(alt_root = switch_xml_parse_file_simple(alt_path))) {
375 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of [%s] failed\n", alt_path);
376 			goto done;
377 		}
378 
379 		if ((conf = switch_xml_find_child(alt_root, "section", "name", "chatplan")) && (tag = switch_xml_find_child(conf, "chatplan", NULL, NULL))) {
380 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Getting chatplan from alternate path: %s\n", alt_path);
381 			xml = alt_root;
382 			cfg = tag;
383 		} else {
384 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of chatplan failed\n");
385 			goto done;
386 		}
387 	} else {
388 		if (switch_xml_locate("chatplan", NULL, NULL, NULL, &xml, &cfg, event, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) {
389 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of chatplan failed\n");
390 			goto done;
391 		}
392 	}
393 
394 	/* get a handle to the context tag */
395 	if (!(xcontext = switch_xml_find_child(cfg, "context", "name", context))) {
396 		if (!(xcontext = switch_xml_find_child(cfg, "context", "name", "global"))) {
397 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Context %s not found\n", context);
398 			goto done;
399 		}
400 	}
401 
402 	xexten = switch_xml_child(xcontext, "extension");
403 
404 	while (xexten) {
405 		int proceed = 0;
406 		const char *cont = switch_xml_attr(xexten, "continue");
407 		const char *exten_name = switch_xml_attr(xexten, "name");
408 
409 		if (!exten_name) {
410 			exten_name = "UNKNOWN";
411 		}
412 
413 		switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG,
414 						  "Chatplan: %s parsing [%s->%s] continue=%s\n",
415 						  to, context, exten_name, cont ? cont : "false");
416 
417 		proceed = parse_exten(event, xexten, &extension);
418 
419 		if (proceed && !switch_true(cont)) {
420 			break;
421 		}
422 
423 		xexten = xexten->next;
424 	}
425 
426 	switch_xml_free(xml);
427 	xml = NULL;
428 
429   done:
430 	switch_xml_free(xml);
431 	return extension;
432 }
433 
434 
chat_send(switch_event_t * message_event)435 static switch_status_t chat_send(switch_event_t *message_event)
436 								 {
437 	switch_status_t status = SWITCH_STATUS_BREAK;
438 	switch_event_t *exten;
439 	int forwards = 0;
440 	const char *var;
441 
442 	var = switch_event_get_header(message_event, "max_forwards");
443 
444 	if (!var) {
445 		forwards = 70;
446 	} else {
447 		forwards = atoi(var);
448 
449 		if (forwards) {
450 			forwards--;
451 		}
452 
453 		if (!forwards) {
454 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Max forwards reached\n");
455 			DUMP_EVENT(message_event);
456 			return SWITCH_STATUS_FALSE;
457 		}
458 	}
459 
460 	if (forwards) {
461 		switch_event_add_header(message_event, SWITCH_STACK_BOTTOM, "max_forwards", "%d", forwards);
462 	}
463 
464 	if ((exten = chatplan_hunt(message_event))) {
465 		switch_event_header_t *hp;
466 
467 		for (hp = exten->headers; hp; hp = hp->next) {
468 			status = switch_core_execute_chat_app(message_event, hp->name, hp->value);
469 			if (!SWITCH_READ_ACCEPTABLE(status)) {
470 				status = SWITCH_STATUS_SUCCESS;
471 				break;
472 			}
473 		}
474 
475 		switch_event_destroy(&exten);
476 	} else {
477 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SMS chatplan no actions found\n");
478 	}
479 
480 	return status;
481 
482 }
483 
SWITCH_STANDARD_CHAT_APP(info_function)484 SWITCH_STANDARD_CHAT_APP(info_function)
485 {
486 	char *buf;
487 	int level = SWITCH_LOG_INFO;
488 
489 	if (!zstr(data)) {
490 		level = switch_log_str2level(data);
491 	}
492 
493 	switch_event_serialize(message, &buf, SWITCH_FALSE);
494 	switch_assert(buf);
495 	switch_log_printf(SWITCH_CHANNEL_LOG, level, "CHANNEL_DATA:\n%s\n", buf);
496 	free(buf);
497 
498 	return SWITCH_STATUS_SUCCESS;
499 }
500 
SWITCH_STANDARD_CHAT_APP(system_function)501 SWITCH_STANDARD_CHAT_APP(system_function)
502 {
503 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Executing command: %s\n", data);
504 	if (switch_system(data, SWITCH_TRUE) < 0) {
505 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Failed to execute command: %s\n", data);
506 		return SWITCH_STATUS_FALSE;
507 	}
508 	return SWITCH_STATUS_SUCCESS;
509 }
510 
SWITCH_STANDARD_CHAT_APP(stop_function)511 SWITCH_STANDARD_CHAT_APP(stop_function)
512 {
513 	switch_set_flag(message, EF_NO_CHAT_EXEC);
514 	return SWITCH_STATUS_FALSE;
515 }
516 
SWITCH_STANDARD_CHAT_APP(send_function)517 SWITCH_STANDARD_CHAT_APP(send_function)
518 {
519 	const char *dest_proto = data;
520 
521 	if (zstr(dest_proto)) {
522 		dest_proto = switch_event_get_header(message, "dest_proto");
523 	}
524 
525 	switch_event_add_header(message, SWITCH_STACK_BOTTOM, "skip_global_process", "true");
526 
527 	switch_core_chat_send(dest_proto, message);
528 
529 	return SWITCH_STATUS_SUCCESS;
530 }
531 
SWITCH_STANDARD_CHAT_APP(set_function)532 SWITCH_STANDARD_CHAT_APP(set_function)
533 {
534 	char *var, *val;
535 
536 	if (!data) return SWITCH_STATUS_SUCCESS;
537 
538 	var = strdup(data);
539 
540 	if (!var) return SWITCH_STATUS_SUCCESS;
541 
542 	if ((val = strchr(var, '='))) {
543 		*val++ = '\0';
544 	}
545 
546 	if (zstr(val)) {
547 		switch_event_del_header(message, var);
548 	} else {
549 		switch_event_add_header_string(message, SWITCH_STACK_BOTTOM, var, val);
550 	}
551 
552 	free(var);
553 
554 	return SWITCH_STATUS_SUCCESS;
555 }
556 
SWITCH_STANDARD_CHAT_APP(unset_function)557 SWITCH_STANDARD_CHAT_APP(unset_function)
558 {
559 	char *var;
560 
561 	if (!data) return SWITCH_STATUS_SUCCESS;
562 
563 	var = strdup(data);
564 
565 	if (!var) return SWITCH_STATUS_SUCCESS;
566 
567 	if (!zstr(var)) {
568 		switch_event_del_header(message, var);
569 	}
570 
571 	free(var);
572 
573 	return SWITCH_STATUS_SUCCESS;
574 }
575 
SWITCH_STANDARD_CHAT_APP(fire_function)576 SWITCH_STANDARD_CHAT_APP(fire_function)
577 {
578 	switch_event_t *fireme;
579 
580 	switch_event_dup(&fireme, message);
581 	switch_event_fire(&fireme);
582 
583 	return SWITCH_STATUS_SUCCESS;
584 }
585 
SWITCH_STANDARD_CHAT_APP(reply_function)586 SWITCH_STANDARD_CHAT_APP(reply_function)
587 {
588 	switch_event_t *reply;
589 	const char *proto = switch_event_get_header(message, "proto");
590 
591 	if (proto) {
592 		switch_ivr_create_message_reply(&reply, message, SMS_CHAT_PROTO);
593 
594 		if (!zstr(data)) {
595 			switch_event_set_body(reply, data);
596 		}
597 
598 		switch_core_chat_deliver(proto, &reply);
599 
600 		return SWITCH_STATUS_SUCCESS;
601 	}
602 
603 	return SWITCH_STATUS_SUCCESS;
604 }
605 
606 /* Macro expands to: switch_status_t mod_sms_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */
SWITCH_MODULE_LOAD_FUNCTION(mod_sms_load)607 SWITCH_MODULE_LOAD_FUNCTION(mod_sms_load)
608 {
609 	switch_chat_interface_t *chat_interface;
610 	switch_chat_application_interface_t *chat_app_interface;
611 
612 	if (switch_event_reserve_subclass(MY_EVENT_DELIVERY_REPORT) != SWITCH_STATUS_SUCCESS) {
613 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", MY_EVENT_DELIVERY_REPORT);
614 		return SWITCH_STATUS_TERM;
615 	}
616 
617 	if (switch_event_bind(modname, SWITCH_EVENT_CUSTOM, MY_EVENT_SEND_MESSAGE, event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
618 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n");
619 		return SWITCH_STATUS_GENERR;
620 	}
621 
622 	/* connect my internal structure to the blank pointer passed to me */
623 	*module_interface = switch_loadable_module_create_module_interface(pool, modname);
624 
625 	SWITCH_ADD_CHAT(chat_interface, SMS_CHAT_PROTO, chat_send);
626 
627 	SWITCH_ADD_CHAT_APP(chat_app_interface, "info", "Display Call Info", "Display Call Info", info_function, "", SCAF_NONE);
628 	SWITCH_ADD_CHAT_APP(chat_app_interface, "reply", "reply to a message", "reply to a message", reply_function, "", SCAF_NONE);
629 	SWITCH_ADD_CHAT_APP(chat_app_interface, "stop", "stop execution", "stop execution", stop_function, "", SCAF_NONE);
630 	SWITCH_ADD_CHAT_APP(chat_app_interface, "set", "set a variable", "set a variable", set_function, "", SCAF_NONE);
631 	SWITCH_ADD_CHAT_APP(chat_app_interface, "unset", "unset a variable", "unset a variable", unset_function, "", SCAF_NONE);
632 	SWITCH_ADD_CHAT_APP(chat_app_interface, "send", "send the message as-is", "send the message as-is", send_function, "", SCAF_NONE);
633 	SWITCH_ADD_CHAT_APP(chat_app_interface, "fire", "fire the message", "fire the message", fire_function, "", SCAF_NONE);
634 	SWITCH_ADD_CHAT_APP(chat_app_interface, "system", "execute a system command", "execute a sytem command", system_function, "", SCAF_NONE);
635 
636 	/* indicate that the module should continue to be loaded */
637 	return SWITCH_STATUS_SUCCESS;
638 }
639 
640 /*
641   Called when the system shuts down
642   Macro expands to: switch_status_t mod_sms_shutdown() */
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_sms_shutdown)643 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_sms_shutdown)
644 {
645 	switch_event_unbind_callback(event_handler);
646 
647 	switch_event_free_subclass(MY_EVENT_DELIVERY_REPORT);
648 
649 	return SWITCH_STATUS_SUCCESS;
650 }
651 
652 
653 
654 
655 /* For Emacs:
656  * Local Variables:
657  * mode:c
658  * indent-tabs-mode:t
659  * tab-width:4
660  * c-basic-offset:4
661  * End:
662  * For VIM:
663  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet
664  */
665