diff --git a/mail.cfg b/mail.cfg new file mode 100644 index 0000000..5417cf5 --- /dev/null +++ b/mail.cfg @@ -0,0 +1,16 @@ +[mail] +server = send.example.com +port = 587 +send_from = raspi@example.de +password = verynicepassword +send_to = some@email.example +subject = Message from {caller} +title = Neue Sprachnachricht +title_html = Anrufbeantworter +epilog_html = Diese E-Mail wurde automatisch verfasst. +content = Der Anrufer: {name} ({caller}) hat für Sie auf dem Anrufbeantworter eine Nachricht hinterlassen. Sie finden die Sprachnachricht im Anhang dieser E-Mail. +label_from = Anruf von +label_for = für die Rufnummer +label_date = Datum +label_time = Uhrzeit +label_length = Aufnahmelänge diff --git a/mail.html b/mail.html new file mode 100644 index 0000000..e0fdd63 --- /dev/null +++ b/mail.html @@ -0,0 +1,96 @@ + + + + {title} + + + + + + + +
+ + + + + + + + + + + + + +
+ + + + +
+ {title_html} +
+
+ + + + +
+ {content} +
+
+ + + + + + + + + + + + + + + + + + + + + +
+ {label_from}: + + {name} ({caller}) +
+ {label_for}: + + {number} +
+ {label_date}: + + {date} +
+ {label_time}: + + {time} +
+ {label_length}: + + {length} +
+
+ + + + +
+ {epilog_html} +
+
+
+ + diff --git a/mail.py b/mail.py index d5b2bbf..7379ac2 100755 --- a/mail.py +++ b/mail.py @@ -1,59 +1,122 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- -import sys +import re +import argparse +import configparser +import datetime +import html +import math import smtplib from os.path import basename from email.mime.application import MIMEApplication from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText -from email.utils import COMMASPACE, formatdate +from email.utils import formatdate +from email import charset +from mutagen.mp3 import MP3 -# config -server = "send.example.com" -port = 587 -send_from = "raspi@example.de" -password = "verynicepassword" -send_to = "some@email.example" -subject = "Message from your answering machine" +def send_mail(args): + config = configparser.ConfigParser() + config.read(args.config) + config = config['mail'] -def send_mail(text, f): + subject = config['subject'] + send_from = config['send_from'] + send_to = config['send_to'] - msg = MIMEMultipart() + dt = datetime.datetime.now() + try: + audio = MP3(args.filename) + delta = datetime.timedelta(math.ceil(audio.info.length)) + length = str(delta) + dt -= delta + except Exception as exc: + length = str(exc) + + contents = { + 'caller': args.caller, + 'name': args.name, + 'number': args.number, + 'date': str(dt.date()), + 'time': dt.time().strftime('%H:%S'), + 'length': length, + 'subject': subject, + 'title': config['title'], + 'title_html': config['title_html'], + 'epilog_html': config['epilog_html'], + 'label_from': config['label_from'], + 'label_for': config['label_for'], + 'label_date': config['label_date'], + 'label_time': config['label_time'], + 'label_length': config['label_length'], + } + contents['content'] = config['content'].format(**contents) + + msg = MIMEMultipart(boundary="==Voice_Box==multipart/mixed==0==") msg['From'] = send_from msg['To'] = send_to msg['Date'] = formatdate(localtime=True) - msg['Subject'] = subject + msg['Subject'] = subject.format(**contents) + + cs = charset.Charset('utf-8') + cs.body_encoding = charset.QP + + body = config.get('body', '''{title}: - msg.attach(MIMEText(text)) +{content} +{label_from}: {name} ({caller}) +{label_for}: {number} +{label_date}: {date} +{label_time}: {time} +{label_length}: {length} +''') + text = MIMEMultipart('alternative', boundary="==Voice_Box==multipart/alternative==1==") + text.attach(MIMEText(body.format(**contents), 'plain', cs)) try: - with open(f, "rb") as fil: - part = MIMEApplication( - fil.read(), - Name=basename(f) - ) - part['Content-Disposition'] = 'attachment; filename="%s"' % basename(f) - msg.attach(part) - except IOError as e: - print "Can not read file \""+f+"\": I/O error({0}): {1}".format(e.errno, e.strerror) - except: - print "Can not read file", sys.exc_info()[0] - raise - - s = smtplib.SMTP(server, port) - # s.debuglevel = 1 - s.ehlo() - s.starttls() - # s.ehlo - s.login(send_from, password) - s.sendmail(send_from, send_to, msg.as_string()) - s.quit() - - # smtp.close() - -if len(sys.argv) == 3: - send_mail(sys.argv[1],sys.argv[2]) -else: - print"provide two arguments: \"text\" \"file\"" + with open('mail.html') as fd: + body_html = re.sub(r'^\s*', '', fd.read()) + text.attach(MIMEText(body_html.format(**{key: html.escape(value) for key, value in contents.items()}), 'html', cs)) + except IOError: + pass + msg.attach(text) + + with open(args.filename, "rb") as fd: + part = MIMEApplication( + fd.read(), + Name=basename(args.filename) + ) + filename = basename(args.filename).replace('"', '').replace('\\', '') + part['Content-Disposition'] = 'attachment; filename="%s"' % (filename,) + msg.attach(part) + + with smtplib.SMTP(config['server'], int(config['port'])) as s: + s.ehlo() + s.starttls() + s.login(send_from, config['password']) + s.sendmail(send_from, send_to, msg.as_string()) + print('OK: email sent') + +# FIXME: handle such errors. What to do then? +# Traceback (most recent call last): +# File "./mail.py", line 102, in +# send_mail(args) +# File "./mail.py", line 91, in send_mail +# s.sendmail(send_from, send_to, msg.as_string()) +# File "/usr/lib/python3.7/smtplib.py", line 888, in sendmail +# raise SMTPDataError(code, resp) +# smtplib.SMTPDataError: (554, b'5.7.0 Your message could not be sent. The limit on the number of allowed outgoing messages was exceeded. Try again later.') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='mail.cfg') + parser.add_argument('--name', default='Name unknown') + parser.add_argument('number') + parser.add_argument('caller') + parser.add_argument('filename') + args = parser.parse_args() + send_mail(args) diff --git a/mail.sh b/mail.sh index e4f44a6..23b823e 100755 --- a/mail.sh +++ b/mail.sh @@ -1,10 +1,10 @@ -#!/bin/bash +#!/bin/sh echo "compress-then-send script" -text=$1 -filename=$2 +number="$1" +callerid="$2" +filename="$3" lame "$filename" filename="${filename%.*}" -./mail.py "Call by $text recorded. Here is the file" "$filename.mp3" - +./mail.py "$number" "$callerid" "$filename.mp3" diff --git a/sipserv.c b/sipserv.c index 8d62285..31c09cd 100644 --- a/sipserv.c +++ b/sipserv.c @@ -12,14 +12,14 @@ Dependencies: - PJSUA API (PJSIP) - eSpeak - + References : http://www.pjsip.org/ http://www.pjsip.org/docs/latest/pjsip/docs/html/group__PJSUA__LIB.htm http://espeak.sourceforge.net/ http://binerry.de/post/29180946733/raspberry-pi-caller-and-answering-machine https://github.com/fabianhu/Sip-Pi - + ================================================================================ This tool is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -60,7 +60,7 @@ Lesser General Public License for more details. #define MAX_DTMF_SETTINGS 9 // struct for app dtmf settings -struct dtmf_config { +struct dtmf_config { int id; int active; int processing_active; @@ -71,7 +71,7 @@ struct dtmf_config { }; // struct for app configuration settings -struct app_config { +struct app_config { char *sip_domain; char *sip_user; char *sip_password; @@ -84,7 +84,7 @@ struct app_config { char *AfterMath; char *log_file; struct dtmf_config dtmf_cfg[MAX_DTMF_SETTINGS]; -} app_cfg; +} app_cfg; // global holder vars for further app arguments char *tts_file = "play.wav"; @@ -132,12 +132,12 @@ int main(int argc, char *argv[]) { // first set some default values app_cfg.record_calls = 0; - app_cfg.silent_mode = 0; + app_cfg.silent_mode = 0; // print infos log_message("SIP Call - Simple TTS/DTMF-based answering machine\n"); log_message("==================================================\n"); - + // register signal handler for break-in-keys (e.g. ctrl+c) signal(SIGINT, signal_handler); signal(SIGKILL, signal_handler); @@ -163,19 +163,19 @@ int main(int argc, char *argv[]) { // display usage info and exit app usage(0); - exit(0); + exit(0); } - + // check for config file location if (!strcasecmp(argv[arg], "--config-file")) { if (argc >= (arg+1)) { - app_cfg.log_file = argv[arg+1]; + app_cfg.log_file = argv[arg+1]; } continue; } - + // check for silent mode option char *s; try_get_argument(arg, "-s", &s, argc, argv); @@ -185,15 +185,15 @@ int main(int argc, char *argv[]) continue; } } - } - + } + if (!app_cfg.log_file) { // too few arguments specified - display usage info and exit app usage(1); exit(1); } - + // read app configuration from config file parse_config_file(app_cfg.log_file); @@ -204,7 +204,7 @@ int main(int argc, char *argv[]) usage(2); // fixme does not show after file has been opened. exit(1); } - + if (app_cfg.announcement_file) { log_message("Announcement mode\n"); @@ -228,49 +228,49 @@ int main(int argc, char *argv[]) fclose(file); } } - + // generate texts log_message("Generating texts ... "); - - char tts_buffer[1024]; + + char tts_buffer[1024]; strcpy(tts_buffer, app_cfg.tts); strcat(tts_buffer, " "); - + for (i = 0; i < MAX_DTMF_SETTINGS; i++) { struct dtmf_config *d_cfg = &app_cfg.dtmf_cfg[i]; - + if (d_cfg->active == 1) { strcat(tts_buffer, d_cfg->tts_intro); strcat(tts_buffer, " "); } } - + log_message("Done.\n"); - + // synthesizing speech log_message("Synthesizing speech ... "); - + int synth_status = -1; synth_status = synthesize_speech(tts_buffer, tts_file, app_cfg.language); - if (synth_status != 0) error_exit("Error while creating phone text", synth_status); + if (synth_status != 0) error_exit("Error while creating phone text", synth_status); log_message("Done.\n"); - + // setup up sip library pjsua setup_sip(); - + // create account and register to sip server register_sip(); - + // app loop for (;;) { sleep(10); // avoid locking up the system } - + // exit app app_exit(); - + return 0; } @@ -321,7 +321,7 @@ static void usage(int error) puts (" should return a \"1\" as first char, if yes."); puts (" the wildcard # will be replaced with the calling phone number in the command"); puts (" am=string aftermath: command to be executed after call ends. Will be called with two parameters: $1 = Phone number $2 = recorded file name"); - + fflush(stdout); } @@ -329,18 +329,18 @@ static void usage(int error) static int try_get_argument(int arg, char *arg_id, char **arg_val, int argc, char *argv[]) { int found = 0; - + // check if actual argument is searched argument - if (!strcasecmp(argv[arg], arg_id)) + if (!strcasecmp(argv[arg], arg_id)) { // check if actual argument has a value if (argc >= (arg+1)) { // set value - *arg_val = argv[arg+1]; + *arg_val = argv[arg+1]; found = 1; } - } + } return found; } @@ -350,55 +350,55 @@ static void parse_config_file(char *cfg_file) // open config file char line[200]; FILE *file = fopen(cfg_file, "r"); - + if (file!=NULL) { // start parsing file while(fgets(line, 200, file) != NULL) { char *arg, *val; - + // ignore comments and just new lines if(line[0] == '#') continue; if(line[0] == '\n') continue; - + // split string at '='-char arg = strtok(line, "="); - if (arg == NULL) continue; + if (arg == NULL) continue; val = strtok(NULL, "="); - + // check for new line char and remove it char *nl_check; nl_check = strstr (val, "\n"); if (nl_check != NULL) strncpy(nl_check, " ", 1); - + // remove trailing spaces - - + + // duplicate string for having own instance of it - char *arg_val = strdup(val); - + char *arg_val = strdup(val); + // check for sip domain argument - if (!strcasecmp(arg, "sd")) + if (!strcasecmp(arg, "sd")) { app_cfg.sip_domain = trim_string(arg_val); continue; } - + // check for sip user argument - if (!strcasecmp(arg, "su")) + if (!strcasecmp(arg, "su")) { app_cfg.sip_user = trim_string(arg_val); continue; } - + // check for sip domain argument - if (!strcasecmp(arg, "sp")) + if (!strcasecmp(arg, "sp")) { app_cfg.sip_password = trim_string(arg_val); continue; } - + // check for language argument if (!strcasecmp(arg, "ln")) { @@ -407,12 +407,12 @@ static void parse_config_file(char *cfg_file) } // check for record calls argument - if (!strcasecmp(arg, "rc")) + if (!strcasecmp(arg, "rc")) { app_cfg.record_calls = atoi(val); continue; } - + // check for announcement file argument if (!strcasecmp(arg, "af")) { @@ -435,19 +435,19 @@ static void parse_config_file(char *cfg_file) } // check for silent mode argument - if (!strcasecmp(arg, "s")) + if (!strcasecmp(arg, "s")) { app_cfg.silent_mode = atoi(val); continue; } - + // check for tts intro - if (!strcasecmp(arg, "tts")) + if (!strcasecmp(arg, "tts")) { app_cfg.tts = arg_val; continue; } - + // check for a dtmf argument char dtmf_id[1]; char dtmf_setting[25]; @@ -455,42 +455,42 @@ static void parse_config_file(char *cfg_file) { // parse dtmf id (key) int d_id; - d_id = atoi(dtmf_id); + d_id = atoi(dtmf_id); - // check if actual dtmf id blasts maxium settings + // check if actual dtmf id blasts maxium settings if (d_id >= MAX_DTMF_SETTINGS) continue; - + // get pointer to actual dtmf_cfg entry struct dtmf_config *d_cfg = &app_cfg.dtmf_cfg[d_id-1]; - + // check for dtmf active setting if (!strcasecmp(dtmf_setting, "active")) { d_cfg->active = atoi(val); continue; } - + // check for dtmf description setting if (!strcasecmp(dtmf_setting, "description")) { d_cfg->description = arg_val; continue; } - + // check for dtmf tts intro setting if (!strcasecmp(dtmf_setting, "tts-intro")) { d_cfg->tts_intro = arg_val; continue; } - + // check for dtmf tts answer setting if (!strcasecmp(dtmf_setting, "tts-answer")) { d_cfg->tts_answer = arg_val; continue; } - + // check for dtmf cmd setting if (!strcasecmp(dtmf_setting, "cmd")) { @@ -498,7 +498,7 @@ static void parse_config_file(char *cfg_file) continue; } } - + // write warning if unknown configuration setting is found char warning[200]; sprintf(warning, "Warning: Unknown configuration with arg '%s' and val '%s'\n", arg, val); @@ -520,9 +520,9 @@ static void parse_config_file(char *cfg_file) static char *trim_string(char *str) { while (isspace(*str)) ++str; - + char *s = (char *)str; - + size_t size; char *end; @@ -550,31 +550,31 @@ static void log_message(char *message) static void setup_sip(void) { pj_status_t status; - + log_message("Setting up pjsua ... "); - - // create pjsua + + // create pjsua status = pjsua_create(); if (status != PJ_SUCCESS) error_exit("Error in pjsua_create()", status); - - // configure pjsua + + // configure pjsua pjsua_config cfg; pjsua_config_default(&cfg); - - // enable just 1 simultaneous call + + // enable just 1 simultaneous call cfg.max_calls = 1; - - // callback configuration + + // callback configuration cfg.cb.on_incoming_call = &on_incoming_call; cfg.cb.on_call_media_state = &on_call_media_state; cfg.cb.on_call_state = &on_call_state; cfg.cb.on_dtmf_digit = &on_dtmf_digit; - + // logging configuration - pjsua_logging_config log_cfg; + pjsua_logging_config log_cfg; pjsua_logging_config_default(&log_cfg); log_cfg.console_level = PJSUA_LOG_LEVEL; - + // media configuration pjsua_media_config media_cfg; pjsua_media_config_default(&media_cfg); @@ -582,27 +582,27 @@ static void setup_sip(void) media_cfg.clock_rate = 8000; media_cfg.snd_clock_rate = 8000; media_cfg.quality = 10; - - // initialize pjsua + + // initialize pjsua status = pjsua_init(&cfg, &log_cfg, &media_cfg); if (status != PJ_SUCCESS) error_exit("Error in pjsua_init()", status); - + // add udp transport pjsua_transport_config udpcfg; pjsua_transport_config_default(&udpcfg); - + udpcfg.port = 5060; status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &udpcfg, NULL); if (status != PJ_SUCCESS) error_exit("Error creating transport", status); - + // initialization is done, start pjsua status = pjsua_start(); if (status != PJ_SUCCESS) error_exit("Error starting pjsua", status); - + // disable sound - use null sound device status = pjsua_set_null_snd_dev(); if (status != PJ_SUCCESS) error_exit("Error disabling audio", status); - + log_message("Done.\n"); } @@ -610,21 +610,21 @@ static void setup_sip(void) static void register_sip(void) { pj_status_t status; - + log_message("Registering account ... "); - + // prepare account configuration pjsua_acc_config cfg; pjsua_acc_config_default(&cfg); - + // build sip-user-url char sip_user_url[40]; sprintf(sip_user_url, "sip:%s@%s", app_cfg.sip_user, app_cfg.sip_domain); - + // build sip-provder-url char sip_provider_url[40]; sprintf(sip_provider_url, "sip:%s", app_cfg.sip_domain); - + // create and define account cfg.id = pj_str(sip_user_url); cfg.reg_uri = pj_str(sip_provider_url); @@ -634,11 +634,11 @@ static void register_sip(void) cfg.cred_info[0].username = pj_str(app_cfg.sip_user); cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; cfg.cred_info[0].data = pj_str(app_cfg.sip_password); - + // add account status = pjsua_acc_add(&cfg, PJ_TRUE, &acc_id); if (status != PJ_SUCCESS) error_exit("Error adding account", status); - + log_message("Done.\n"); } @@ -646,25 +646,25 @@ static void register_sip(void) static void create_player(pjsua_call_id call_id, char *file) { // get call infos - pjsua_call_info ci; + pjsua_call_info ci; pjsua_call_get_info(call_id, &ci); - + pj_str_t name; pj_status_t status = PJ_ENOTFOUND; - + log_message("Creating player ... "); - - // create player for playback media + + // create player for playback media status = pjsua_player_create(pj_cstr(&name, file), PJMEDIA_FILE_NO_LOOP, &play_id); if (status != PJ_SUCCESS) error_exit("Error playing sound-playback", status); - + // connect active call to media player pjsua_conf_connect(pjsua_player_get_conf_port(play_id), ci.conf_slot); - + // get media port (play_port) from play_id status = pjsua_player_get_port(play_id, &play_port); - if (status != PJ_SUCCESS) error_exit("Error getting sound player port", status); - + if (status != PJ_SUCCESS) error_exit("Error getting sound player port", status); + log_message("Done.\n"); } @@ -674,17 +674,17 @@ static void create_recorder(pjsua_call_info ci) // specify target file pj_str_t rec_file = pj_str(rec_ans_file); pj_status_t status = PJ_ENOTFOUND; - + log_message("Creating recorder ... "); - + // Create recorder for call status = pjsua_recorder_create(&rec_file, 0, NULL, 0, 0, &rec_id); // don't forget to destroy recorder, to have the file written. if (status != PJ_SUCCESS) error_exit("Error recording answer", status); - + // connect active call to call recorder - pjsua_conf_port_id rec_port = pjsua_recorder_get_conf_port(rec_id); + pjsua_conf_port_id rec_port = pjsua_recorder_get_conf_port(rec_id); pjsua_conf_connect(ci.conf_slot, rec_port); - + log_message("Done.\n"); } @@ -710,11 +710,11 @@ int recorder_destroy(pjsua_player_id id) { static int synthesize_speech(char *speech, char *file, char* language) { int speech_status = -1; - + char speech_command[1024]; sprintf(speech_command, "espeak -v%s -a%i -k%i -s%i -p%i -w %s '%s'", language, ESPEAK_AMPLITUDE, ESPEAK_CAPITALS_PITCH, ESPEAK_SPEED, ESPEAK_PITCH, file, speech); speech_status = system(speech_command); - + return speech_status; } @@ -838,7 +838,7 @@ static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_r PJ_UNUSED_ARG(acc_id); PJ_UNUSED_ARG(rdata); - + current_call = call_id; FileNameFromCallInfo(filename,sipNr,ci); @@ -904,16 +904,16 @@ static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_r static void on_call_media_state(pjsua_call_id call_id) { // get call infos - pjsua_call_info ci; + pjsua_call_info ci; pjsua_call_get_info(call_id, &ci); - + pj_status_t status = PJ_ENOTFOUND; // check state if call is established/active if (ci.media_status == PJSUA_CALL_MEDIA_ACTIVE) { - - log_message("Call media activated.\n"); - + + log_message("Call media activated.\n"); + // create and start media player if(app_cfg.announcement_file) { @@ -929,7 +929,7 @@ static void on_call_media_state(pjsua_call_id call_id) { create_recorder(ci); } - } + } } // handler for call-state-change-events @@ -938,25 +938,25 @@ static void on_call_state(pjsua_call_id call_id, pjsip_event *e) // get call infos pjsua_call_info ci; pjsua_call_get_info(call_id, &ci); - + // prevent warning about unused argument e PJ_UNUSED_ARG(e); - + // check call state - if (ci.state == PJSIP_INV_STATE_CONFIRMED) + if (ci.state == PJSIP_INV_STATE_CONFIRMED) { log_message("Call confirmed.\n"); - + // ensure that message is played from start if (play_id != PJSUA_INVALID_ID) { pjmedia_wav_player_port_set_pos(play_port, 0); } } - if (ci.state == PJSIP_INV_STATE_DISCONNECTED) + if (ci.state == PJSIP_INV_STATE_DISCONNECTED) { log_message("Call disconnected.\n"); - + // disable player player_destroy(play_id); // dont't forget the recorder! @@ -964,13 +964,13 @@ static void on_call_state(pjsua_call_id call_id, pjsip_event *e) { // ok, recorder has been destroyed successfully, there should be a file too. log_message("a file has been recorded.\n"); - + // process the Aftermath, if we have any. if(app_cfg.AfterMath) { char result[RESULTSIZE]; char command[300]; - sprintf(command,"%s \"%s\" \"%s\"", app_cfg.AfterMath, lastNumber ,rec_ans_file); + sprintf(command,"%s \"%s\" \"%s\" \"%s\"", app_cfg.AfterMath, ci.local_info.ptr, lastNumber, rec_ans_file); log_message(command); log_message("\n"); @@ -986,58 +986,58 @@ static void on_call_state(pjsua_call_id call_id, pjsip_event *e) // handler for dtmf-events -static void on_dtmf_digit(pjsua_call_id call_id, int digit) +static void on_dtmf_digit(pjsua_call_id call_id, int digit) { // get call infos - pjsua_call_info ci; + pjsua_call_info ci; pjsua_call_get_info(call_id, &ci); - + // work on detected dtmf digit int dtmf_key = digit - 48; - + char info[100]; sprintf(info, "DTMF command detected: %i\n", dtmf_key); log_message(info); - + struct dtmf_config *d_cfg = &app_cfg.dtmf_cfg[dtmf_key-1]; if (d_cfg->processing_active == 0) { d_cfg->processing_active = 1; - + if (d_cfg->active == 1) { log_message("Active DTMF command found for received digit.\n"); log_message("Creating answer ... "); - + int error = 0; char command[100]; char result[RESULTSIZE]; - + strcpy(command, d_cfg->cmd); - + error = callBash(command, result); if (!error) - { + { player_destroy(play_id); recorder_destroy(rec_id); - + char tts_buffer[200]; sprintf(tts_buffer, d_cfg->tts_answer, result); - + int synth_status = -1; synth_status = synthesize_speech(tts_buffer, tts_answer_file, app_cfg.language); - if (synth_status != 0) log_message(" (Failed to synthesize speech) "); - + if (synth_status != 0) log_message(" (Failed to synthesize speech) "); + create_player(call_id, tts_answer_file); } - + log_message("Done.\n"); } else { log_message("No active DTMF command found for received digit.\n"); } - + d_cfg->processing_active = 0; } else @@ -1047,7 +1047,7 @@ static void on_dtmf_digit(pjsua_call_id call_id, int digit) } // handler for "break-in-key"-events (e.g. ctrl+c) -static void signal_handler(int signal) +static void signal_handler(int signal) { // exit app app_exit(); @@ -1060,17 +1060,17 @@ static void app_exit() { app_exiting = 1; log_message("Stopping application ... \n"); - + // check if player/recorder is active and stop them player_destroy(play_id); recorder_destroy(rec_id); - + // hangup open calls and stop pjsua pjsua_call_hangup_all(); pjsua_destroy(); - + log_message("Done.\n"); - + exit(0); } } @@ -1084,15 +1084,15 @@ static void error_exit(const char *title, pj_status_t status) log_message("App Error Exit\n"); pjsua_perror("SIP Call", title, status); - + // check if player/recorder is active and stop them player_destroy(play_id); recorder_destroy(rec_id); - + // hangup open calls and stop pjsua pjsua_call_hangup_all(); pjsua_destroy(); - + exit(1); } }