diff --git a/apps/sw_vsc/Makefile b/apps/sw_vsc/Makefile new file mode 100644 index 00000000..3af8d937 --- /dev/null +++ b/apps/sw_vsc/Makefile @@ -0,0 +1,10 @@ +plug_in_name = sw_vsc + +module_ldflags = -lmysqlclient `pcre-config --libs` -lpcrecpp +module_cflags = -I/usr/include/mysql `pcre-config --cflags` +module_extra_objs = + +extra_install = $(plug_in_name)_audio + +COREPATH ?=../../core +include $(COREPATH)/plug-in/Makefile.app_module diff --git a/apps/sw_vsc/SW_Vsc.cpp b/apps/sw_vsc/SW_Vsc.cpp new file mode 100644 index 00000000..3bc7d0d6 --- /dev/null +++ b/apps/sw_vsc/SW_Vsc.cpp @@ -0,0 +1,1888 @@ +#include +using namespace pcrecpp; + +#include "SW_Vsc.h" +#include "AmConfig.h" +#include "AmUtils.h" +#include "AmPlugIn.h" + +#include "sems.h" +#include "log.h" + +//#include +//#include +#include + +#define MOD_NAME "sw_vsc" +#define SW_VSC_DATABASE "provisioning" + +#define SW_VSC_GET_ATTRIBUTE_ID "select id from voip_preferences where attribute='%s'" +#define SW_VSC_GET_SUBSCRIBER_ID "select s.id, d.domain, d.id, s.profile_id, s.username "\ + "from voip_subscribers s, voip_domains d "\ + "where s.uuid='%s' and s.domain_id = d.id" +#define SW_VSC_GET_PREFERENCE_ID "select id,value from voip_usr_preferences where subscriber_id=%llu " \ + "and attribute_id=%llu" +#define SW_VSC_DELETE_PREFERENCE_ID "delete from voip_usr_preferences where id=%llu" +#define SW_VSC_INSERT_PREFERENCE "insert into voip_usr_preferences (subscriber_id, attribute_id, value) "\ + "values(%llu, %llu, '%s')" +#define SW_VSC_UPDATE_PREFERENCE_ID "update voip_usr_preferences set value='%s' where id=%llu" +#define SW_VSC_INSERT_SPEEDDIAL "replace into voip_speed_dial (subscriber_id, slot, destination) "\ + "values(%llu, '%s', '%s')" +#define SW_VSC_INSERT_REMINDER "replace into voip_reminder (subscriber_id, time, recur) "\ + "values(%llu, '%s', '%s')" +#define SW_VSC_DELETE_REMINDER "delete from voip_reminder where subscriber_id=%llu" +#define SW_VSC_GET_USER_CALLEE_REWRITE_DPID "select value from " \ + "voip_usr_preferences vup, voip_preferences vp where " \ + "vup.subscriber_id=%llu and vp.attribute='rewrite_callee_in_dpid' " \ + "and vup.attribute_id = vp.id" +#define SW_VSC_GET_USER_CALLEE_REWRITES \ + "select vrr.match_pattern, vrr.replace_pattern from " \ + "voip_rewrite_rules vrr, voip_rewrite_rule_sets vrrs " \ + "where vrrs.callee_in_dpid=%llu and vrr.set_id = vrrs.id " \ + "and vrr.direction='in' and vrr.field='callee' " \ + "and vrr.enabled = 1 order by vrr.priority asc" +#define SW_VSC_GET_DOMAIN_CALLEE_REWRITES \ + "select vrr.match_pattern, vrr.replace_pattern " \ + "from voip_rewrite_rules vrr, voip_rewrite_rule_sets vrrs, " \ + "voip_preferences vp, voip_dom_preferences vdp " \ + "where vdp.domain_id=%llu and vp.attribute='rewrite_callee_in_dpid' " \ + "and vdp.attribute_id = vp.id and vdp.value = vrrs.callee_in_dpid " \ + "and vrr.set_id = vrrs.id and vrr.direction='in' and " \ + "vrr.field='callee' and vrr.enabled = 1 order by vrr.priority asc" + +#define SW_VSC_GET_PRIMARY_NUMBER "select alias_username " \ + "from kamailio.dbaliases where username='%s' " \ + "and domain = '%s' and is_primary" + +#define SW_VSC_DELETE_VSC_DESTSET "delete from voip_cf_destination_sets where "\ + "subscriber_id=%llu and name='%s'" +#define SW_VSC_CREATE_VSC_DESTSET "insert into voip_cf_destination_sets (subscriber_id, name) "\ + "values(%llu, '%s')" +#define SW_VSC_CREATE_VSC_DEST "insert into voip_cf_destinations (destination_set_id, destination, priority) "\ + "values(%llu, '%s', %d)" +#define SW_VSC_DELETE_VSC_CFMAP "delete from voip_cf_mappings where subscriber_id=%llu and type='%s'" +#define SW_VSC_CREATE_VSC_CFMAP "insert into voip_cf_mappings "\ + "(subscriber_id, type, destination_set_id, time_set_id) "\ + "values(%llu, '%s', %llu, NULL)" + +#define SW_VSC_GET_SUBPROFILE_ATTRIBUTE "select id from voip_subscriber_profile_attributes "\ + "where profile_id=%llu and attribute_id=%llu" + +#define SW_VSC_DESTSET_CFU "cfu_by_vsc" +#define SW_VSC_DESTSET_CFB "cfb_by_vsc" +#define SW_VSC_DESTSET_CFT "cft_by_vsc" +#define SW_VSC_DESTSET_CFNA "cfna_by_vsc" + +EXPORT_SESSION_FACTORY(SW_VscFactory, MOD_NAME); + +SW_VscFactory::SW_VscFactory(const string &_app_name) + : AmSessionFactory(_app_name) +{ +} + +SW_VscFactory::~SW_VscFactory() +{ + regfree(&m_patterns.cfuOnPattern); + regfree(&m_patterns.cfuOffPattern); + regfree(&m_patterns.cfbOnPattern); + regfree(&m_patterns.cfbOffPattern); + regfree(&m_patterns.cftOnPattern); + regfree(&m_patterns.cftOffPattern); + regfree(&m_patterns.cfnaOnPattern); + regfree(&m_patterns.cfnaOffPattern); + regfree(&m_patterns.speedDialPattern); + regfree(&m_patterns.reminderOnPattern); + regfree(&m_patterns.reminderOffPattern); +} + +int SW_VscFactory::onLoad() +{ + string cfuOnPattern; + string cfuOffPattern; + string cfbOnPattern; + string cfbOffPattern; + string cftOnPattern; + string cftOffPattern; + string cfnaOnPattern; + string cfnaOffPattern; + string speedDialPattern; + string reminderOnPattern; + string reminderOffPattern; + + AmConfigReader cfg; + if (cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) + return -1; + + configureModule(cfg); + + + m_patterns.mysqlHost = cfg.getParameter("mysql_host", ""); + if (m_patterns.mysqlHost.empty()) + { + ERROR("MysqlHost is empty\n"); + return -1; + } + m_patterns.mysqlPort = cfg.getParameterInt("mysql_port", 0); + if (m_patterns.mysqlPort < 0 || m_patterns.mysqlPort > 65535) + { + ERROR("MysqlPort is invalid\n"); + return -1; + } + m_patterns.mysqlUser = cfg.getParameter("mysql_user", ""); + if (m_patterns.mysqlUser.empty()) + { + ERROR("MysqlUser is empty\n"); + return -1; + } + m_patterns.mysqlPass = cfg.getParameter("mysql_pass", ""); + if (m_patterns.mysqlPass.empty()) + { + ERROR("MysqlPass is empty\n"); + return -1; + } + + m_patterns.audioPath = cfg.getParameter("announce_path", ANNOUNCE_PATH); + if (m_patterns.audioPath.empty()) + { + ERROR("AnnouncePath option announce_path is empty.\n"); + return -1; + } + if (m_patterns.audioPath[m_patterns.audioPath.length() - 1] != '/' ) + m_patterns.audioPath += "/"; + + m_patterns.failAnnouncement = cfg.getParameter("error_announcement", ""); + if (m_patterns.failAnnouncement.empty()) + { + ERROR("ErrorAnnouncement file not set\n"); + return -1; + } + m_patterns.unknownAnnouncement = cfg.getParameter("unknown_announcement", ""); + if (m_patterns.unknownAnnouncement.empty()) + { + ERROR("UnknownAnnouncement file not set\n"); + return -1; + } + m_patterns.cfuOnAnnouncement = cfg.getParameter("cfu_on_announcement", ""); + if (m_patterns.cfuOnAnnouncement.empty()) + { + ERROR("CfuOnAnnouncement file not set\n"); + return -1; + } + m_patterns.cfuOffAnnouncement = cfg.getParameter("cfu_off_announcement", ""); + if (m_patterns.cfuOffAnnouncement.empty()) + { + ERROR("CfuOffAnnouncement file not set\n"); + return -1; + } + m_patterns.cfbOnAnnouncement = cfg.getParameter("cfb_on_announcement", ""); + if (m_patterns.cfbOnAnnouncement.empty()) + { + ERROR("CfbOnAnnouncement file not set\n"); + return -1; + } + m_patterns.cfbOffAnnouncement = cfg.getParameter("cfb_off_announcement", ""); + if (m_patterns.cfbOffAnnouncement.empty()) + { + ERROR("CfbOffAnnouncement file not set\n"); + return -1; + } + m_patterns.cftOnAnnouncement = cfg.getParameter("cft_on_announcement", ""); + if (m_patterns.cftOnAnnouncement.empty()) + { + ERROR("CftOnAnnouncement file not set\n"); + return -1; + } + m_patterns.cftOffAnnouncement = cfg.getParameter("cft_off_announcement", ""); + if (m_patterns.cftOffAnnouncement.empty()) + { + ERROR("CftOffAnnouncement file not set\n"); + return -1; + } + m_patterns.cfnaOnAnnouncement = cfg.getParameter("cfna_on_announcement", ""); + if (m_patterns.cfnaOnAnnouncement.empty()) + { + ERROR("CfnaOnAnnouncement file not set\n"); + return -1; + } + m_patterns.cfnaOffAnnouncement = cfg.getParameter("cfna_off_announcement", ""); + if (m_patterns.cfnaOffAnnouncement.empty()) + { + ERROR("CfnaOffAnnouncement file not set\n"); + return -1; + } + m_patterns.speedDialAnnouncement = cfg.getParameter("speed_dial_announcement", ""); + if (m_patterns.speedDialAnnouncement.empty()) + { + ERROR("SpeedDialAnnouncement file not set\n"); + return -1; + } + m_patterns.reminderOnAnnouncement = cfg.getParameter("reminder_on_announcement", ""); + if (m_patterns.reminderOnAnnouncement.empty()) + { + ERROR("ReminderOnAnnouncement file not set\n"); + return -1; + } + m_patterns.reminderOffAnnouncement = cfg.getParameter("reminder_off_announcement", ""); + if (m_patterns.reminderOffAnnouncement.empty()) + { + ERROR("ReminderOffAnnouncement file not set\n"); + return -1; + } + + + m_patterns.voicemailNumber = cfg.getParameter("voicemail_number", ""); + if (m_patterns.voicemailNumber.empty()) + { + ERROR("voicemailNumber is empty\n"); + return -1; + } + + cfuOnPattern = cfg.getParameter("cfu_on_pattern", ""); + if (cfuOnPattern.empty()) + { + ERROR("CfuOnPattern is empty\n"); + return -1; + } + if (regcomp(&m_patterns.cfuOnPattern, cfuOnPattern.c_str(), REG_EXTENDED | REG_NOSUB)) + { + ERROR("CfuOnPattern failed to compile ('%s'): %s\n", + cfuOnPattern.c_str(), + strerror(errno)); + return -1; + } + + cfuOffPattern = cfg.getParameter("cfu_off_pattern", ""); + if (cfuOffPattern.empty()) + { + ERROR("CfuOffPattern is empty\n"); + return -1; + } + if (regcomp(&m_patterns.cfuOffPattern, cfuOffPattern.c_str(), REG_EXTENDED | REG_NOSUB)) + { + ERROR("CfuOffPattern failed to compile ('%s'): %s\n", + cfuOffPattern.c_str(), + strerror(errno)); + return -1; + } + + cfbOnPattern = cfg.getParameter("cfb_on_pattern", ""); + if (cfbOnPattern.empty()) + { + ERROR("CfbOnPattern is empty\n"); + return -1; + } + if (regcomp(&m_patterns.cfbOnPattern, cfbOnPattern.c_str(), REG_EXTENDED | REG_NOSUB)) + { + ERROR("CfbOnPattern failed to compile ('%s'): %s\n", + cfbOnPattern.c_str(), + strerror(errno)); + return -1; + } + + cfbOffPattern = cfg.getParameter("cfb_off_pattern", ""); + if (cfbOffPattern.empty()) + { + ERROR("CfbOffPattern is empty\n"); + return -1; + } + if (regcomp(&m_patterns.cfbOffPattern, cfbOffPattern.c_str(), REG_EXTENDED | REG_NOSUB)) + { + ERROR("CfbOffPattern failed to compile ('%s'): %s\n", + cfbOffPattern.c_str(), + strerror(errno)); + return -1; + } + + + cftOnPattern = cfg.getParameter("cft_on_pattern", ""); + if (cftOnPattern.empty()) + { + ERROR("CftOnPattern is empty\n"); + return -1; + } + if (regcomp(&m_patterns.cftOnPattern, cftOnPattern.c_str(), REG_EXTENDED | REG_NOSUB)) + { + ERROR("CftOnPattern failed to compile ('%s'): %s\n", + cftOnPattern.c_str(), + strerror(errno)); + return -1; + } + + cftOffPattern = cfg.getParameter("cft_off_pattern", ""); + if (cftOffPattern.empty()) + { + ERROR("CftOffPattern is empty\n"); + return -1; + } + if (regcomp(&m_patterns.cftOffPattern, cftOffPattern.c_str(), REG_EXTENDED | REG_NOSUB)) + { + ERROR("CftOffPattern failed to compile ('%s'): %s\n", + cftOffPattern.c_str(), + strerror(errno)); + return -1; + } + + cfnaOnPattern = cfg.getParameter("cfna_on_pattern", ""); + if (cfnaOnPattern.empty()) + { + ERROR("CfnaOnPattern is empty\n"); + return -1; + } + if (regcomp(&m_patterns.cfnaOnPattern, cfnaOnPattern.c_str(), REG_EXTENDED | REG_NOSUB)) + { + ERROR("CfnaOnPattern failed to compile ('%s'): %s\n", + cfnaOnPattern.c_str(), + strerror(errno)); + return -1; + } + + cfnaOffPattern = cfg.getParameter("cfna_off_pattern", ""); + if (cfnaOffPattern.empty()) + { + ERROR("CfnaOffPattern is empty\n"); + return -1; + } + if (regcomp(&m_patterns.cfnaOffPattern, cfnaOffPattern.c_str(), REG_EXTENDED | REG_NOSUB)) + { + ERROR("CfnaOffPattern failed to compile ('%s'): %s\n", + cfnaOffPattern.c_str(), + strerror(errno)); + return -1; + } + + speedDialPattern = cfg.getParameter("speed_dial_pattern", ""); + if (speedDialPattern.empty()) + { + ERROR("SpeedDialPattern is empty\n"); + return -1; + } + if (regcomp(&m_patterns.speedDialPattern, speedDialPattern.c_str(), REG_EXTENDED | REG_NOSUB)) + { + ERROR("SpeedDialPattern failed to compile ('%s'): %s\n", + speedDialPattern.c_str(), + strerror(errno)); + return -1; + } + + + reminderOnPattern = cfg.getParameter("reminder_on_pattern", ""); + if (reminderOnPattern.empty()) + { + ERROR("ReminderOnPattern is empty\n"); + return -1; + } + if (regcomp(&m_patterns.reminderOnPattern, reminderOnPattern.c_str(), REG_EXTENDED | REG_NOSUB)) + { + ERROR("ReminderOnPattern failed to compile ('%s'): %s\n", + reminderOnPattern.c_str(), + strerror(errno)); + return -1; + } + + + reminderOffPattern = cfg.getParameter("reminder_off_pattern", ""); + if (reminderOffPattern.empty()) + { + ERROR("ReminderOffPattern is empty\n"); + return -1; + } + if (regcomp(&m_patterns.reminderOffPattern, reminderOffPattern.c_str(), + REG_EXTENDED | REG_NOSUB)) + { + ERROR("ReminderOffPattern failed to compile ('%s'): %s\n", + reminderOffPattern.c_str(), + strerror(errno)); + return -1; + } + + return 0; +} + +AmSession *SW_VscFactory::onInvite(const AmSipRequest &req, + const string &app_name, + const map &app_params) +{ + return new SW_VscDialog(&m_patterns, NULL); +} + +AmSession *SW_VscFactory::onInvite(const AmSipRequest &req, + const string &app_name, + AmArg &session_params) +{ + UACAuthCred *cred = AmUACAuth::unpackCredentials(session_params); + AmSession *s = new SW_VscDialog(&m_patterns, cred); + + if (NULL == cred) + { + WARN("discarding unknown session parameters.\n"); + } + else + { + AmUACAuth::enable(s); + } + + return s; +} + +SW_VscDialog::SW_VscDialog(sw_vsc_patterns_t *patterns, + UACAuthCred *credentials) + : m_patterns(patterns), cred(credentials) +{ +} + +SW_VscDialog::~SW_VscDialog() +{ +} + +void SW_VscDialog::onSessionStart() +{ + DBG("SW_VscDialog::onSessionStart()...\n"); + AmSession::onSessionStart(); +} + +void SW_VscDialog::onStart() { +} + +u_int64_t SW_VscDialog::getAttributeId(MYSQL *my_handler, const char *attribute) +{ + MYSQL_RES *res; + MYSQL_ROW row; + char query[1024] = ""; + u_int64_t id; + + snprintf(query, sizeof(query), SW_VSC_GET_ATTRIBUTE_ID, attribute); + + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error fetching id for attribute '%s': %s", attribute, + mysql_error(my_handler)); + return 0; + } + + res = mysql_store_result(my_handler); + if (mysql_num_rows(res) != 1) + { + ERROR("Found invalid number of id entries for attribute '%s': %llu", + attribute, mysql_num_rows(res)); + return 0; + } + + row = mysql_fetch_row(res); + if (row == NULL || row[0] == NULL) + { + ERROR("Failed to fetch row for attribute id: %s\n", + mysql_error(my_handler)); + return 0; + } + + id = atoll(row[0]); + mysql_free_result(res); + return id; +} + +int SW_VscDialog::checkSubscriberProfile(MYSQL *my_handler, u_int64_t profileId, u_int64_t attributeId) +{ + MYSQL_RES *res; + MYSQL_ROW row; + char query[1024] = ""; + int ret = 0; + + // if no profile is set, allow by default + if(profileId == 0) + { + INFO("Allow preference due to unset subscriber profile"); + return 1; + } + + snprintf(query, sizeof(query), SW_VSC_GET_SUBPROFILE_ATTRIBUTE, + (unsigned long long int) profileId, (unsigned long long int) attributeId); + + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error checking profile attributes for profile '%llu' and attribute %llu: %s", + (unsigned long long int) profileId, (unsigned long long int) attributeId, + mysql_error(my_handler)); + return 0; + } + + res = mysql_store_result(my_handler); + if (mysql_num_rows(res) >= 1) + { + INFO("Allow preference attribute %llu as it is in profile %llu", + (unsigned long long int) attributeId, (unsigned long long int) profileId); + ret = 1; + } + else + { + INFO("Reject preference attribute %llu as it is not in profile %llu", + (unsigned long long int) attributeId, (unsigned long long int) profileId); + ret = 0; + } + return ret; +} + +u_int64_t SW_VscDialog::getSubscriberId(MYSQL *my_handler, const char *uuid, + string *domain, u_int64_t &domain_id, + u_int64_t &profile_id, string *username) +{ + MYSQL_RES *res; + MYSQL_ROW row; + char query[1024] = ""; + u_int64_t id; + + snprintf(query, sizeof(query), SW_VSC_GET_SUBSCRIBER_ID, uuid); + + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error fetching id for subscriber '%s': %s", uuid, + mysql_error(my_handler)); + return 0; + } + + res = mysql_store_result(my_handler); + if (mysql_num_rows(res) != 1) + { + ERROR("Found invalid number of id entries for uuid '%s': %llu", + uuid , mysql_num_rows(res)); + return 0; + } + + row = mysql_fetch_row(res); if (row == NULL || row[0] == NULL || row[1] == + NULL || row[2] == NULL) + { + ERROR("Failed to fetch row for uuid id: %s\n", + mysql_error(my_handler)); return 0; + } + + id = atoll(row[0]); + domain->clear(); + *domain += string(row[1]); + domain_id = atoll(row[2]); + profile_id = row[3] == NULL ? 0 : atoll(row[3]); + username->clear(); + *username += string(row[4]); + mysql_free_result(res); + return id; +} + +u_int64_t SW_VscDialog::getPreference(MYSQL *my_handler, u_int64_t subscriberId, + u_int64_t attributeId, int *foundPref, + string *value) +{ + MYSQL_RES *res; MYSQL_ROW row; char query[1024] = + ""; u_int64_t id; *foundPref = 0; + + snprintf(query, sizeof(query), SW_VSC_GET_PREFERENCE_ID, + (unsigned long long int)subscriberId, + (unsigned long long int)attributeId); + + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error fetching preference id for subscriber id '%llu' and attribute id '%llu': %s", + (unsigned long long int)subscriberId, + (unsigned long long int)attributeId, mysql_error(my_handler)); + return 0; + } + + res = mysql_store_result(my_handler); + if (mysql_num_rows(res) == 0) + { + mysql_free_result(res); + return 1; + } + if (mysql_num_rows(res) != 1) + { + ERROR("Found invalid number of id entries for subscriber id '%llu' and attribute id '%llu': %llu", + (unsigned long long int)subscriberId, + (unsigned long long int)attributeId, mysql_num_rows(res)); + mysql_free_result(res); + return 0; + } + + row = mysql_fetch_row(res); + if (row == NULL || row[0] == NULL) + { + ERROR("Failed to fetch row for preference id: %s\n", + mysql_error(my_handler)); + mysql_free_result(res); + return 0; + } + id = atoll(row[0]); + value->clear(); + *value += row[1]; + mysql_free_result(res); + *foundPref = 1; + return id; +} + +int SW_VscDialog::deletePreferenceId(MYSQL *my_handler, u_int64_t preferenceId) +{ + char query[1024] = ""; + + snprintf(query, sizeof(query), SW_VSC_DELETE_PREFERENCE_ID, + (unsigned long long int)preferenceId); + + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error deleting preference id '%llu': %s", + (unsigned long long int)preferenceId, mysql_error(my_handler)); + return 0; + } + return 1; +} + +int SW_VscDialog::insertPreference(MYSQL *my_handler, u_int64_t subscriberId, + u_int64_t attributeId, string &uri) +{ + char query[1024] = ""; + + snprintf(query, sizeof(query), SW_VSC_INSERT_PREFERENCE, + (unsigned long long int)subscriberId, + (unsigned long long int)attributeId, uri.c_str()); + + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error inserting preference for subscriber id '%llu': %s", + (unsigned long long int)subscriberId, mysql_error(my_handler)); + return 0; + } + return 1; +} + +int SW_VscDialog::updatePreferenceId(MYSQL *my_handler, + u_int64_t preferenceId, string &uri) +{ + char query[1024] = ""; + + snprintf(query, sizeof(query), SW_VSC_UPDATE_PREFERENCE_ID, + uri.c_str(), (unsigned long long int)preferenceId); + + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error updating preference id '%llu': %s", + (unsigned long long int)preferenceId, mysql_error(my_handler)); + return 0; + } + return 1; +} + +int SW_VscDialog::insertReminder(MYSQL *my_handler, u_int64_t subscriberId, + string &repeat, string &tim) +{ + char query[1024] = ""; + + snprintf(query, sizeof(query), SW_VSC_INSERT_REMINDER, + (unsigned long long int)subscriberId, + tim.c_str(), repeat.c_str()); + + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error setting reminder for subscriber id '%llu': %s", + (unsigned long long int)subscriberId, mysql_error(my_handler)); + return 0; + } + return 1; +} + +int SW_VscDialog::deleteReminder(MYSQL *my_handler, u_int64_t subscriberId) +{ + char query[1024] = ""; + + snprintf(query, sizeof(query), SW_VSC_DELETE_REMINDER, + (unsigned long long int)subscriberId); + + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error deleting reminder for subscriber id '%llu': %s", + (unsigned long long int)subscriberId, mysql_error(my_handler)); + return 0; + } + return 1; +} + +int SW_VscDialog::insertSpeedDialSlot(MYSQL *my_handler, + u_int64_t subscriberId, + string &slot, string &uri) +{ + char query[1024] = ""; + + snprintf(query, sizeof(query), SW_VSC_INSERT_SPEEDDIAL, + (unsigned long long int)subscriberId, slot.c_str(), uri.c_str()); + + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error inserting speed-dial slot '%s' for subscriber id '%llu': %s", + slot.c_str(), (unsigned long long int)subscriberId, + mysql_error(my_handler)); + return 0; + } + return 1; +} + + +int SW_VscDialog::number2uri(const AmSipRequest &req, MYSQL *my_handler, + string &uuid, u_int64_t subId, + string &domain, u_int64_t domId, + int offset, string &uri, string &username) +{ + string num = "", acStr = "", ccStr = ""; + u_int64_t acAttId, ccAttId; + int foundPref; + char query[1024] = ""; + my_ulonglong num_rows; + MYSQL_RES *res; + MYSQL_ROW row; + RE *re; + + if (req.user.compare(0, 1, "*", 1) == 0) + { + num = req.user.substr(offset); + + if (m_patterns->voicemailNumber == num) + { + snprintf(query, sizeof(query), + SW_VSC_GET_PRIMARY_NUMBER, + username.c_str(), domain.c_str()); + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error fetching primary number for username '%s'" + " at domain '%s': %s", + username.c_str(), domain.c_str(), + mysql_error(my_handler)); + return 0; + } + res = mysql_store_result(my_handler); + row = mysql_fetch_row(res); + if (row == NULL || row[0] == NULL) + { + INFO("no primary number for username '%s'" + " at domain '%s' found. Use uuid: %s", + username.c_str(), domain.c_str(), + uuid.c_str()); + uri = string("sip:vmu") + uuid + "@voicebox.local"; + } + else { + uri = string("sip:vmu") + row[0] + "@voicebox.local"; + } + INFO("Normalized '%s' to voicemail uri '%s' for uuid '%s'", + num.c_str(), uri.c_str(), uuid.c_str()); + mysql_free_result(res); + return 1; + } + + snprintf(query, sizeof(query), + SW_VSC_GET_USER_CALLEE_REWRITE_DPID, + (unsigned long long int)subId); + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error fetching rewrite rule dpid for subscriber uuid '%s' (id %llu): %s", + uuid.c_str(), (unsigned long long int)subId, + mysql_error(my_handler)); + return 0; + } + res = mysql_store_result(my_handler); + if ((num_rows = mysql_num_rows(res)) != 0) + { + row = mysql_fetch_row(res); + if (row == NULL || row[0] == NULL) + { + ERROR("Failed to fetch row for user callee rewrite rule: %s\n", mysql_error(my_handler)); + mysql_free_result(res); + return 0; + } + u_int64_t dpid = atoll(row[0]); + INFO("Found user rewrite rule dpid '%llu' for subscriber uuid '%s' (id %llu)", + (unsigned long long int)dpid, uuid.c_str(), + (unsigned long long int)subId); + mysql_free_result(res); + snprintf(query, sizeof(query), SW_VSC_GET_USER_CALLEE_REWRITES, + (unsigned long long int)dpid); + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error fetching user callee rewrite rules for dpid '%llu' for subscriber uuid '%s': %s", + (unsigned long long int)dpid, uuid.c_str(), + mysql_error(my_handler)); + return 0; + } + res = mysql_store_result(my_handler); + if ((num_rows = mysql_num_rows(res)) == 0) + { + mysql_free_result(res); + uri = string("sip:+") + num + "@" + domain; + INFO("No user callee rewrite rules for subscriber uuid '%s' (id '%llu')", + uuid.c_str(), (unsigned long long int)subId); + return 1; + } + } + else + { + mysql_free_result(res); + INFO("No user rewrite rule for subscriber uuid '%s' (id %llu), falling back to domain '%s' (id %llu)", + uuid.c_str(), (unsigned long long int)subId, + domain.c_str(), (unsigned long long int)domId); + + snprintf(query, sizeof(query), SW_VSC_GET_DOMAIN_CALLEE_REWRITES, + (unsigned long long int)domId); + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error fetching domain callee rewrite rules for domain id '%llu' (domain '%s') for subscriber uuid '%s': %s", + (unsigned long long int)domId, domain.c_str(), + uuid.c_str(), mysql_error(my_handler)); + return 0; + } + res = mysql_store_result(my_handler); + if ((num_rows = mysql_num_rows(res)) == 0) + { + mysql_free_result(res); + uri = string("sip:+") + num + "@" + domain; + INFO("No domain callee rewrite rules for domain id '%llu' (domain '%s') for subscriber uuid '%s'", + (unsigned long long int)domId, domain.c_str(), + uuid.c_str()); + return 1; + } + } + + for (my_ulonglong i = 0; i < num_rows; ++i) + { + row = mysql_fetch_row(res); + if (row == NULL || row[0] == NULL || row[1] == NULL) + { + ERROR("Failed to fetch row for callee rewrite rule: %s\n", + mysql_error(my_handler)); + mysql_free_result(res); + return 0; + } + + re = new RE(row[0]); + if (re == NULL || re->error().length() > 0) + { + ERROR("A callee rewrite rule match pattern ('%s') failed to compile: %s\n", + row[0], (re ? re->error().c_str() : "unknown")); + if (re) delete re; + mysql_free_result(res); + return 0; + } + if (re->Replace(row[1], &num)) + { + INFO("The callee rewrite rule pattern ('%s' and '%s') for subscriber id %llu and domain id %llu matched, result is '%s'\n", + row[0], row[1], (unsigned long long int)subId, + (unsigned long long int)domId, num.c_str()); + delete re; + break; + } + delete re; + } + + mysql_free_result(res); + + // we need to replace $avp(s:caller_ac) and $avp(s:caller_cc) + // with the actual values here + + acAttId = getAttributeId(my_handler, "ac"); + if (!acAttId) + return 0; + ccAttId = getAttributeId(my_handler, "cc"); + if (!ccAttId) + return 0; + if (!getPreference(my_handler, subId, acAttId, &foundPref, &acStr)) + return 0; + if (!getPreference(my_handler, subId, ccAttId, &foundPref, &ccStr)) + return 0; + + re = new RE("\\$avp\\(s:caller_ac\\)"); + if (re->GlobalReplace(acStr, &num)) + { + INFO("Successfully replaced $avp(s:caller_ac) by preference value '%s' for subscriber id %llu and domain id %llu", + acStr.c_str(), (unsigned long long int)subId, + (unsigned long long int)domId); + } + delete re; + re = new RE("\\$avp\\(s:caller_cc\\)"); + if (re->GlobalReplace(ccStr, &num)) + { + INFO("Successfully replaced $avp(s:caller_cc) by preference value '%s' for subscriber id %llu and domain id %llu", + ccStr.c_str(), (unsigned long long int)subId, + (unsigned long long int)domId); + } + delete re; + + INFO("Final normalized number is '%s' for subscriber id %llu and domain id %llu", + num.c_str(), (unsigned long long int)subId, + (unsigned long long int)domId); + uri = string("sip:+") + num + "@" + domain; + } + return 1; +} + +u_int64_t SW_VscDialog::createCFMap(MYSQL *my_handler, u_int64_t subscriberId, + string &uri, const char *mapName, + const char *type) +{ + u_int64_t dsetId; + u_int64_t mapId; + char query[1024] = ""; + + // first, delete potentially existing vsc destination set for subscriber + snprintf(query, sizeof(query), SW_VSC_DELETE_VSC_DESTSET, + (unsigned long long int)subscriberId, mapName); + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error deleting existing CF destination set '%s' for subscriber id '%llu': %s", + mapName, (unsigned long long int)subscriberId, + mysql_error(my_handler)); + return 0; + } + + // create a new destination set + snprintf(query, sizeof(query), SW_VSC_CREATE_VSC_DESTSET, + (unsigned long long int)subscriberId, mapName); + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error creating new CF destination set '%s' for subscriber id '%llu': %s", + mapName, (unsigned long long int)subscriberId, + mysql_error(my_handler)); + return 0; + } + dsetId = mysql_insert_id(my_handler); + if (!dsetId) + { + ERROR("Error fetching last insert id of CF destination set for subscriber id '%llu'", + (unsigned long long int)subscriberId); + return 0; + } + + // create new destination entry + snprintf(query, sizeof(query), SW_VSC_CREATE_VSC_DEST, + (unsigned long long int)dsetId, + uri.c_str(), 1); + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error creating new CF destination '%s' for destination set id '%llu': %s", + uri.c_str(), (unsigned long long int)dsetId, + mysql_error(my_handler)); + return 0; + } + + // delete existing mapping + snprintf(query, sizeof(query), SW_VSC_DELETE_VSC_CFMAP, + (unsigned long long int)subscriberId, type); + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error deleting existing CF destination mapping for subscriber id '%llu' and type '%s': %s", + (unsigned long long int)subscriberId, type, + mysql_error(my_handler)); + return 0; + } + + // create new mapping + snprintf(query, sizeof(query), SW_VSC_CREATE_VSC_CFMAP, + (unsigned long long int)subscriberId, + type, (unsigned long long int)dsetId); + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error creating CF destination mapping for subscriber id '%llu' and type '%s' to destination set id '%llu': %s", + (unsigned long long int)subscriberId, type, + (unsigned long long int)dsetId, mysql_error(my_handler)); + return 0; + } + mapId = mysql_insert_id(my_handler); + if (!mapId) + { + ERROR("Error fetching last insert id of CF mapping for subscriber id '%llu' and type '%s'", + (unsigned long long int)subscriberId, type); + return 0; + } + + return mapId; + +} + +u_int64_t SW_VscDialog::deleteCFMap(MYSQL *my_handler, u_int64_t subscriberId, + const char *mapName, const char *type) +{ + char query[1024] = ""; + + // first, delete potentially existing vsc destination set for subscriber + snprintf(query, sizeof(query), SW_VSC_DELETE_VSC_DESTSET, + (unsigned long long int)subscriberId, mapName); + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error deleting existing CF destination set '%s' for subscriber id '%llu': %s", + mapName, (unsigned long long int)subscriberId, + mysql_error(my_handler)); + return 0; + } + + // delete existing mapping + snprintf(query, sizeof(query), SW_VSC_DELETE_VSC_CFMAP, + (unsigned long long int)subscriberId, type); + if (mysql_real_query(my_handler, query, strlen(query)) != 0) + { + ERROR("Error deleting existing CF destination mapping for subscriber id '%llu' and type '%s': %s", + (unsigned long long int)subscriberId, type, + mysql_error(my_handler)); + return 0; + } + + return 1; +} + +void SW_VscDialog::onInvite(const AmSipRequest &req) +{ + /// fooooo + if(dlg->getStatus() == AmSipDialog::Connected){ + AmSession::onInvite(req); + return; + } + + int ret; + string filename; + MYSQL *my_handler = NULL; + my_bool recon = 1; + + u_int64_t subId, domId, profId; + string domain, uri, username; + char map_str[128] = ""; + string mapStr, prefStr; + int foundPref = 0; + + string failAnnouncement; + string unknownAnnouncement; + string cfuOnAnnouncement; + string cfuOffAnnouncement; + string cfbOnAnnouncement; + string cfbOffAnnouncement; + string cftOnAnnouncement; + string cftOffAnnouncement; + string cfnaOnAnnouncement; + string cfnaOffAnnouncement; + string speedDialAnnouncement; + string reminderOnAnnouncement; + string reminderOffAnnouncement; + + string uuid = getHeader(req.hdrs, "P-Caller-UUID"); + if (!uuid.length()) + { + ERROR("Application header P-Caller-UUID not found\n"); + throw AmSession::Exception(500, "could not get UUID parameter"); + } + string lang = getHeader(req.hdrs, "P-Language"); + if (!lang.length()) + { + lang = "en"; + } + lang += "/"; + + + failAnnouncement = m_patterns->audioPath + lang + m_patterns->failAnnouncement; + if (!file_exists(failAnnouncement)) + { + ERROR("ErrorAnnouncement file does not exist ('%s').\n", + failAnnouncement.c_str()); + throw AmSession::Exception(500, "could not get failed announcement"); + } + unknownAnnouncement = m_patterns->audioPath + lang + m_patterns->unknownAnnouncement; + if (!file_exists(unknownAnnouncement)) + { + ERROR("UnknownAnnouncement file does not exist ('%s').\n", + unknownAnnouncement.c_str()); + filename = failAnnouncement; + goto out; + } + cfuOnAnnouncement = m_patterns->audioPath + lang + m_patterns->cfuOnAnnouncement; + if (!file_exists(cfuOnAnnouncement)) + { + ERROR("CfuOnAnnouncement file does not exist ('%s').\n", + cfuOnAnnouncement.c_str()); + filename = failAnnouncement; + goto out; + } + cfuOffAnnouncement = m_patterns->audioPath + lang + m_patterns->cfuOffAnnouncement; + if (!file_exists(cfuOffAnnouncement)) + { + ERROR("CfuOffAnnouncement file does not exist ('%s').\n", + cfuOffAnnouncement.c_str()); + filename = failAnnouncement; + goto out; + } + cfbOnAnnouncement = m_patterns->audioPath + lang + m_patterns->cfbOnAnnouncement; + if (!file_exists(cfbOnAnnouncement)) + { + ERROR("CfbOnAnnouncement file does not exist ('%s').\n", + cfbOnAnnouncement.c_str()); + filename = failAnnouncement; + goto out; + } + cfbOffAnnouncement = m_patterns->audioPath + lang + m_patterns->cfbOffAnnouncement; + if (!file_exists(cfbOffAnnouncement)) + { + ERROR("CfbOffAnnouncement file does not exist ('%s').\n", + cfbOffAnnouncement.c_str()); + filename = failAnnouncement; + goto out; + } + cftOnAnnouncement = m_patterns->audioPath + lang + m_patterns->cftOnAnnouncement; + if (!file_exists(cftOnAnnouncement)) + { + ERROR("CftOnAnnouncement file does not exist ('%s').\n", + cftOnAnnouncement.c_str()); + filename = failAnnouncement; + goto out; + } + cftOffAnnouncement = m_patterns->audioPath + lang + m_patterns->cftOffAnnouncement; + if (!file_exists(cftOffAnnouncement)) + { + ERROR("CftOffAnnouncement file does not exist ('%s').\n", + cftOffAnnouncement.c_str()); + filename = failAnnouncement; + goto out; + } + cfnaOnAnnouncement = m_patterns->audioPath + lang + m_patterns->cfnaOnAnnouncement; + if (!file_exists(cfnaOnAnnouncement)) + { + ERROR("CfnaOnAnnouncement file does not exist ('%s').\n", + cfnaOnAnnouncement.c_str()); + filename = failAnnouncement; + goto out; + } + cfnaOffAnnouncement = m_patterns->audioPath + lang + m_patterns->cfnaOffAnnouncement; + if (!file_exists(cfnaOffAnnouncement)) + { + ERROR("CfnaOffAnnouncement file does not exist ('%s').\n", + cfnaOffAnnouncement.c_str()); + filename = failAnnouncement; + goto out; + } + speedDialAnnouncement = m_patterns->audioPath + lang + m_patterns->speedDialAnnouncement; + if (!file_exists(speedDialAnnouncement)) + { + ERROR("SpeedDialAnnouncement file does not exist ('%s').\n", + speedDialAnnouncement.c_str()); + filename = failAnnouncement; + goto out; + } + reminderOnAnnouncement = m_patterns->audioPath + lang + m_patterns->reminderOnAnnouncement; + if (!file_exists(reminderOnAnnouncement)) + { + ERROR("ReminderOnAnnouncement file does not exist ('%s').\n", + reminderOnAnnouncement.c_str()); + filename = failAnnouncement; + goto out; + } + reminderOffAnnouncement = m_patterns->audioPath + lang + m_patterns->reminderOffAnnouncement; + if (!file_exists(reminderOffAnnouncement)) + { + ERROR("ReminderOffAnnouncement file does not exist ('%s').\n", + reminderOffAnnouncement.c_str()); + filename = failAnnouncement; + goto out; + } + + my_handler = mysql_init(NULL); + if (!mysql_real_connect(my_handler, + m_patterns->mysqlHost.c_str(), + m_patterns->mysqlUser.c_str(), + m_patterns->mysqlPass.c_str(), + SW_VSC_DATABASE, m_patterns->mysqlPort, NULL, 0)) + { + ERROR("Error connecting to provisioning db: %s", + mysql_error(my_handler)); + filename = failAnnouncement; + goto out; + } + if (mysql_options(my_handler, MYSQL_OPT_RECONNECT, &recon) != 0) + { + ERROR("Error setting reconnect-option for provisioning db: %s", + mysql_error(my_handler)); + filename = failAnnouncement; + goto out; + } + + subId = getSubscriberId(my_handler, uuid.c_str(), + &domain, domId, profId, &username); + if (!subId) + { + filename = failAnnouncement; + goto out; + } + + DBG("Trigger VSC for uuid '%s' (sid='%llu')\n", + uuid.c_str(), (unsigned long long int)subId); + + setReceiving(false); + + if ((ret = regexec(&m_patterns->cfuOnPattern, + req.user.c_str(), 0, 0, 0)) == 0) + { + if (!number2uri(req, my_handler, uuid, subId, domain, domId, 4, + uri, username)) + { + filename = failAnnouncement; + goto out; + } + + u_int64_t mapId = createCFMap(my_handler, subId, uri, + SW_VSC_DESTSET_CFU, "cfu"); + if (!mapId) + { + filename = failAnnouncement; + goto out; + } + snprintf(map_str, sizeof(mapStr), "%llu", + (unsigned long long int)mapId); + mapStr = string(map_str); + + u_int64_t attId = getAttributeId(my_handler, "cfu"); + if (!attId) + { + filename = failAnnouncement; + goto out; + } + + if(!checkSubscriberProfile(my_handler, profId, attId)) + { + filename = failAnnouncement; + goto out; + } + + u_int64_t prefId = getPreference(my_handler, subId, attId, + &foundPref, &prefStr); + if (!prefId) + { + filename = failAnnouncement; + goto out; + } + else if (!foundPref) + { + if (!insertPreference(my_handler, subId, attId, mapStr)) + { + filename = failAnnouncement; + goto out; + } + INFO("Successfully set VSC CFU to '%s' using mapping id '%llu' for uuid '%s'", + uri.c_str(), (unsigned long long int)mapId, uuid.c_str()); + } + else + { + if (!updatePreferenceId(my_handler, prefId, mapStr)) + { + filename = failAnnouncement; + goto out; + } + INFO("Successfully updated VSC CFU to '%s' using mapping id '%llu' for uuid '%s'", + uri.c_str(), (unsigned long long int)mapId, uuid.c_str()); + } + + filename = cfuOnAnnouncement; + goto out; + } + else if (ret != REG_NOMATCH) + { + filename = failAnnouncement; + goto out; + } + + if ((ret = regexec(&m_patterns->cfuOffPattern, + req.user.c_str(), 0, 0, 0)) == 0) + { + if (!deleteCFMap(my_handler, subId, SW_VSC_DESTSET_CFU, "cfu")) + { + filename = failAnnouncement; + goto out; + } + u_int64_t attId = getAttributeId(my_handler, "cfu"); + if (!attId) + { + filename = failAnnouncement; + goto out; + } + u_int64_t prefId = getPreference(my_handler, subId, attId, + &foundPref, &prefStr); + if (!prefId) + { + filename = failAnnouncement; + goto out; + } + else if (!foundPref) + { + INFO("Unnecessary VSC CFU removal for uuid '%s'", + uuid.c_str()); + } + else if (!deletePreferenceId(my_handler, prefId)) + { + filename = failAnnouncement; + goto out; + } + else + { + INFO("Successfully removed VSC CFU for uuid '%s'", + uuid.c_str()); + } + + filename = cfuOffAnnouncement; + goto out; + } + else if (ret != REG_NOMATCH) + { + filename = failAnnouncement; + goto out; + } + + if ((ret = regexec(&m_patterns->cfbOnPattern, + req.user.c_str(), 0, 0, 0)) == 0) + { + if (!number2uri(req, my_handler, uuid, subId, domain, domId, 4, + uri, username)) + { + filename = failAnnouncement; + goto out; + } + + u_int64_t mapId = createCFMap(my_handler, subId, uri, + SW_VSC_DESTSET_CFB, "cfb"); + if (!mapId) + { + filename = failAnnouncement; + goto out; + } + snprintf(map_str, sizeof(mapStr), "%llu", + (unsigned long long int)mapId); + mapStr = string(map_str); + + u_int64_t attId = getAttributeId(my_handler, "cfb"); + if (!attId) + { + filename = failAnnouncement; + goto out; + } + + if(!checkSubscriberProfile(my_handler, profId, attId)) + { + filename = failAnnouncement; + goto out; + } + + u_int64_t prefId = getPreference(my_handler, subId, attId, + &foundPref, &prefStr); + if (!prefId) + { + filename = failAnnouncement; + goto out; + } + else if (!foundPref) + { + if (!insertPreference(my_handler, subId, attId, mapStr)) + { + filename = failAnnouncement; + goto out; + } + INFO("Successfully set VSC CFB to '%s' for uuid '%s'", + uri.c_str(), uuid.c_str()); + } + else + { + if (!updatePreferenceId(my_handler, prefId, mapStr)) + { + filename = failAnnouncement; + goto out; + } + INFO("Successfully updated VSC CFB to '%s' for uuid '%s'", + uri.c_str(), uuid.c_str()); + } + + filename = cfbOnAnnouncement; + goto out; + } + else if (ret != REG_NOMATCH) + { + filename = failAnnouncement; + goto out; + } + + if ((ret = regexec(&m_patterns->cfbOffPattern, + req.user.c_str(), 0, 0, 0)) == 0) + { + if (!deleteCFMap(my_handler, subId, SW_VSC_DESTSET_CFB, "cfb")) + { + filename = failAnnouncement; + goto out; + } + u_int64_t attId = getAttributeId(my_handler, "cfb"); + if (!attId) + { + filename = failAnnouncement; + goto out; + } + u_int64_t prefId = getPreference(my_handler, subId, attId, + &foundPref, &prefStr); + if (!prefId) + { + filename = failAnnouncement; + goto out; + } + else if (!foundPref) + { + INFO("Unnecessary VSC CFB removal for uuid '%s'", + uuid.c_str()); + } + else if (!deletePreferenceId(my_handler, prefId)) + { + filename = failAnnouncement; + goto out; + } + else + { + INFO("Successfully removed VSC CFB for uuid '%s'", + uuid.c_str()); + } + + filename = cfbOffAnnouncement; + goto out; + } + else if (ret != REG_NOMATCH) + { + filename = failAnnouncement; + goto out; + } + + if ((ret = regexec(&m_patterns->cftOnPattern, + req.user.c_str(), 0, 0, 0)) == 0) + { + string::size_type timend = req.user.find('*', 4); + string tim = req.user.substr(4, timend - 4); + INFO("Extracted ringtimeout of '%s' from '%s' for uuid '%s'", + tim.c_str(), req.user.c_str(), uuid.c_str()); + + if (!number2uri(req, my_handler, uuid, subId, domain, + domId, timend + 1, uri, username)) + { + filename = failAnnouncement; + goto out; + } + + u_int64_t mapId = createCFMap(my_handler, subId, uri, + SW_VSC_DESTSET_CFT, "cft"); + if (!mapId) + { + filename = failAnnouncement; + goto out; + } + snprintf(map_str, sizeof(mapStr), "%llu", + (unsigned long long int)mapId); + mapStr = string(map_str); + + u_int64_t attId = getAttributeId(my_handler, "cft"); + if (!attId) + { + filename = failAnnouncement; + goto out; + } + + if(!checkSubscriberProfile(my_handler, profId, attId)) + { + filename = failAnnouncement; + goto out; + } + + u_int64_t prefId = getPreference(my_handler, subId, attId, + &foundPref, &prefStr); + if (!prefId) + { + filename = failAnnouncement; + goto out; + } + else if (!foundPref) + { + if (!insertPreference(my_handler, subId, attId, mapStr)) + { + filename = failAnnouncement; + goto out; + } + INFO("Successfully set VSC CFT to '%s' for uuid '%s'", + uri.c_str(), uuid.c_str()); + } + else + { + if (!updatePreferenceId(my_handler, prefId, mapStr)) + { + filename = failAnnouncement; + goto out; + } + INFO("Successfully updated VSC CFT to '%s' for uuid '%s'", + uri.c_str(), uuid.c_str()); + } + + attId = getAttributeId(my_handler, "ringtimeout"); + if (!attId) + { + filename = failAnnouncement; + goto out; + } + prefId = getPreference(my_handler, subId, attId, &foundPref, &prefStr); + if (!prefId) + { + filename = failAnnouncement; + goto out; + } + else if (!foundPref) + { + if (!insertPreference(my_handler, subId, attId, tim)) + { + filename = failAnnouncement; + goto out; + } + INFO("Successfully set VSC ringtimeout to '%s' for uuid '%s'", + tim.c_str(), uuid.c_str()); + } + else + { + if (!updatePreferenceId(my_handler, prefId, tim)) + { + filename = failAnnouncement; + goto out; + } + INFO("Successfully updated VSC ringtimeout to '%s' for uuid '%s'", + tim.c_str(), uuid.c_str()); + } + + filename = cftOnAnnouncement; + goto out; + } + else if (ret != REG_NOMATCH) + { + filename = failAnnouncement; + goto out; + } + + if ((ret = regexec(&m_patterns->cftOffPattern, + req.user.c_str(), 0, 0, 0)) == 0) + { + if (!deleteCFMap(my_handler, subId, SW_VSC_DESTSET_CFT, "cft")) + { + filename = failAnnouncement; + goto out; + } + u_int64_t attId = getAttributeId(my_handler, "cft"); + if (!attId) + { + filename = failAnnouncement; + goto out; + } + + u_int64_t prefId = getPreference(my_handler, subId, attId, + &foundPref, &prefStr); + if (!prefId) + { + filename = failAnnouncement; + goto out; + } + else if (!foundPref) + { + INFO("Unnecessary VSC CFT removal for uuid '%s'", + uuid.c_str()); + } + else if (!deletePreferenceId(my_handler, prefId)) + { + filename = failAnnouncement; + goto out; + } + else + { + INFO("Successfully removed VSC CFT for uuid '%s'", + uuid.c_str()); + } + + attId = getAttributeId(my_handler, "ringtimeout"); + if (!attId) + { + filename = failAnnouncement; + goto out; + } + prefId = getPreference(my_handler, subId, attId, &foundPref, &prefStr); + if (!prefId) + { + filename = failAnnouncement; + goto out; + } + else if (foundPref && !deletePreferenceId(my_handler, prefId)) + { + filename = failAnnouncement; + goto out; + } + else + { + INFO("Successfully removed VSC CFT ringtimeout for uuid '%s'", + uuid.c_str()); + } + + + filename = cftOffAnnouncement; + goto out; + } + else if (ret != REG_NOMATCH) + { + filename = failAnnouncement; + goto out; + } + + if ((ret = regexec(&m_patterns->cfnaOnPattern, + req.user.c_str(), 0, 0, 0)) == 0) + { + if (!number2uri(req, my_handler, uuid, subId, domain, domId, 4, + uri, username)) + { + filename = failAnnouncement; + goto out; + } + + u_int64_t mapId = createCFMap(my_handler, subId, uri, + SW_VSC_DESTSET_CFNA, "cfna"); + if (!mapId) + { + filename = failAnnouncement; + goto out; + } + snprintf(map_str, sizeof(mapStr), "%llu", + (unsigned long long int)mapId); + mapStr = string(map_str); + + u_int64_t attId = getAttributeId(my_handler, "cfna"); + if (!attId) + { + filename = failAnnouncement; + goto out; + } + + if(!checkSubscriberProfile(my_handler, profId, attId)) + { + filename = failAnnouncement; + goto out; + } + + u_int64_t prefId = getPreference(my_handler, subId, attId, + &foundPref, &prefStr); + if (!prefId) + { + filename = failAnnouncement; + goto out; + } + else if (!foundPref) + { + if (!insertPreference(my_handler, subId, attId, mapStr)) + { + filename = failAnnouncement; + goto out; + } + INFO("Successfully set VSC CFNA to '%s' for uuid '%s'", + uri.c_str(), uuid.c_str()); + } + else + { + if (!updatePreferenceId(my_handler, prefId, mapStr)) + { + filename = failAnnouncement; + goto out; + } + INFO("Successfully updated VSC CFNA to '%s' for uuid '%s'", + uri.c_str(), uuid.c_str()); + } + + filename = cfnaOnAnnouncement; + goto out; + } + else if (ret != REG_NOMATCH) + { + filename = failAnnouncement; + goto out; + } + + if ((ret = regexec(&m_patterns->cfnaOffPattern, + req.user.c_str(), 0, 0, 0)) == 0) + { + if (!deleteCFMap(my_handler, subId, SW_VSC_DESTSET_CFNA, "cfna")) + { + filename = failAnnouncement; + goto out; + } + u_int64_t attId = getAttributeId(my_handler, "cfna"); + if (!attId) + { + filename = failAnnouncement; + goto out; + } + u_int64_t prefId = getPreference(my_handler, subId, attId, + &foundPref, &prefStr); + if (!prefId) + { + filename = failAnnouncement; + goto out; + } + else if (!foundPref) + { + INFO("Unnecessary VSC CFNA removal for uuid '%s'", + uuid.c_str()); + } + else if (!deletePreferenceId(my_handler, prefId)) + { + filename = failAnnouncement; + goto out; + } + else + { + INFO("Successfully removed VSC CFNA for uuid '%s'", + uuid.c_str()); + } + + filename = cfnaOffAnnouncement; + goto out; + } + else if (ret != REG_NOMATCH) + { + filename = failAnnouncement; + goto out; + } + + if ((ret = regexec(&m_patterns->speedDialPattern, + req.user.c_str(), 0, 0, 0)) == 0) + { + string slot = string("*") + req.user.substr(4, 1); + if (!number2uri(req, my_handler, uuid, subId, domain, domId, 5, + uri, username)) + { + filename = failAnnouncement; + goto out; + } + if (!insertSpeedDialSlot(my_handler, subId, slot, uri)) + { + filename = failAnnouncement; + goto out; + } + else + { + INFO("Successfully set speed-dial slot '%s' for uuid '%s'", + slot.c_str(), uuid.c_str()); + } + + filename = speedDialAnnouncement; + goto out; + } + else if (ret != REG_NOMATCH) + { + filename = failAnnouncement; + goto out; + } + + if ((ret = regexec(&m_patterns->reminderOnPattern, + req.user.c_str(), 0, 0, 0)) == 0) + { + int hour, min; + string tim; char c_tim[6] = ""; + hour = atoi(req.user.substr(4, 2).c_str()); + min = atoi(req.user.substr(6, 2).c_str()); + + if (hour < 0 || hour > 23) + { + filename = failAnnouncement; + INFO("Invalid hour '%s' in reminder data for uuid '%s'", + req.user.substr(4, 2).c_str(), uuid.c_str()); + goto out; + } + if (min < 0 || min > 59) + { + filename = failAnnouncement; + INFO("Invalid minute '%s' in reminder data for uuid '%s'", + req.user.substr(6, 2).c_str(), uuid.c_str()); + goto out; + } + snprintf(c_tim, sizeof(c_tim), "%02d:%02d", hour, min); + tim = string(c_tim); + string recur("never"); + + if (!insertReminder(my_handler, subId, recur, tim)) + { + filename = failAnnouncement; + goto out; + } + else + { + INFO("Successfully set reminder at '%s' for uuid '%s'", + c_tim, uuid.c_str()); + } + + filename = reminderOnAnnouncement; + goto out; + } + else if (ret != REG_NOMATCH) + { + filename = failAnnouncement; + goto out; + } + + if ((ret = regexec(&m_patterns->reminderOffPattern, + req.user.c_str(), 0, 0, 0)) == 0) + { + if (!deleteReminder(my_handler, subId)) + { + filename = failAnnouncement; + goto out; + } + else + { + INFO("Successfully deleted reminder for uuid '%s'", + uuid.c_str()); + } + + filename = reminderOffAnnouncement; + goto out; + } + else if (ret != REG_NOMATCH) + { + filename = failAnnouncement; + goto out; + } + + + + INFO("Unkown VSC code '%s' found", req.user.c_str()); + filename = unknownAnnouncement; + +out: + + if (my_handler != NULL) + { + mysql_close(my_handler); + } + if (m_wav_file.open(filename, AmAudioFile::Read)) + throw string("SW_VscDialog::onSessionStart: Cannot open file\n"); + + + setOutput(&m_wav_file); + + AmSession::onInvite(req); +} + +void SW_VscDialog::onBye(const AmSipRequest &req) +{ + DBG("onBye: stopSession\n"); + AmSession::onBye(req); +} + + +void SW_VscDialog::process(AmEvent *event) +{ + + AmAudioEvent *audio_event = dynamic_cast(event); + if (audio_event && (audio_event->event_id == AmAudioEvent::cleared)) + { + dlg->bye(); + setStopped(); + return; + } + + AmSession::process(event); +} + +inline UACAuthCred *SW_VscDialog::getCredentials() +{ + return cred.get(); +} diff --git a/apps/sw_vsc/SW_Vsc.h b/apps/sw_vsc/SW_Vsc.h new file mode 100644 index 00000000..69d1a75e --- /dev/null +++ b/apps/sw_vsc/SW_Vsc.h @@ -0,0 +1,134 @@ +#ifndef _SW_VSC_H_ +#define _SW_VSC_H_ + +#include "AmSession.h" +#include "AmConfigReader.h" +#include "AmAudioFile.h" +#include "AmUACAuth.h" + +#include "ampi/UACAuthAPI.h" +#include +using std::string; + +#include +#include + +#include + +typedef struct +{ + + string mysqlHost; + int mysqlPort; + string mysqlUser; + string mysqlPass; + + string audioPath; + + string failAnnouncement; + string unknownAnnouncement; + string voicemailNumber; + + regex_t cfuOnPattern; + string cfuOnAnnouncement; + + regex_t cfuOffPattern; + string cfuOffAnnouncement; + + regex_t cfbOnPattern; + string cfbOnAnnouncement; + + regex_t cfbOffPattern; + string cfbOffAnnouncement; + + regex_t cftOnPattern; + string cftOnAnnouncement; + + regex_t cftOffPattern; + string cftOffAnnouncement; + + regex_t cfnaOnPattern; + string cfnaOnAnnouncement; + + regex_t cfnaOffPattern; + string cfnaOffAnnouncement; + + regex_t speedDialPattern; + string speedDialAnnouncement; + + regex_t reminderOnPattern; + string reminderOnAnnouncement; + + regex_t reminderOffPattern; + string reminderOffAnnouncement; +} sw_vsc_patterns_t; + +class SW_VscFactory: public AmSessionFactory +{ + inline string getAnnounceFile(const AmSipRequest &req); + + sw_vsc_patterns_t m_patterns; + +public: + + SW_VscFactory(const string &_app_name); + virtual ~SW_VscFactory(); + + int onLoad(); + AmSession *onInvite(const AmSipRequest &req, const string &app_name, + const map &app_params); + AmSession *onInvite(const AmSipRequest &req, const string &app_name, + AmArg &session_params); + +}; + +class SW_VscDialog : public AmSession, + public CredentialHolder +{ + AmAudioFile m_wav_file; + + sw_vsc_patterns_t *m_patterns; + + std::auto_ptr cred; + + u_int64_t getAttributeId(MYSQL *my_handler, const char *attribute); + u_int64_t getSubscriberId(MYSQL *my_handler, const char *uuid, + string *domain, u_int64_t &domain_id, + u_int64_t &profile_id, string *username); + u_int64_t getPreference(MYSQL *my_handler, u_int64_t subscriberId, u_int64_t attributeId, + int *foundPref, string *value); + int deletePreferenceId(MYSQL *my_handler, u_int64_t preferenceId); + int insertPreference(MYSQL *my_handler, u_int64_t subscriberId, + u_int64_t attributeId, string &uri); + int updatePreferenceId(MYSQL *my_handler, u_int64_t preferenceId, string &uri); + int insertSpeedDialSlot(MYSQL *my_handler, u_int64_t subscriberId, string &slot, string &uri); + int insertReminder(MYSQL *my_handler, u_int64_t subscriberId, string &repeat, string &tim); + int deleteReminder(MYSQL *my_handler, u_int64_t subscriberId); + int number2uri(const AmSipRequest &req, MYSQL *my_handler, string &uuid, u_int64_t subId, + string &domain, u_int64_t domId, int offset, string &uri, + string &username); + int checkSubscriberProfile(MYSQL *my_handler, u_int64_t profileId, u_int64_t attributeId); + u_int64_t createCFMap(MYSQL *my_handler, u_int64_t subscriberId, string &uri, + const char *mapName, const char *type); + u_int64_t deleteCFMap(MYSQL *my_handler, u_int64_t subscriberId, + const char *mapName, const char *type); + + + +public: + SW_VscDialog(sw_vsc_patterns_t *patterns, + UACAuthCred *credentials = NULL); + ~SW_VscDialog(); + + void onSessionStart(); + void onStart(); + void onInvite(const AmSipRequest &req); + void onBye(const AmSipRequest &req); + void onDtmf(int event, int duration_msec) {} + + void process(AmEvent *event); + + UACAuthCred *getCredentials(); +}; + +#endif diff --git a/apps/sw_vsc/etc/sw_vsc.conf b/apps/sw_vsc/etc/sw_vsc.conf new file mode 100644 index 00000000..2cc2ebbb --- /dev/null +++ b/apps/sw_vsc/etc/sw_vsc.conf @@ -0,0 +1,40 @@ +mysql_host = 127.0.0.1 +mysql_port = 3306 +mysql_user = soap +mysql_pass = s:wMP4Si + +announce_path = /usr/lib/sems/audio/ + +error_announcement = sw_vsc_error.wav +unknown_announcement = sw_vsc_unavailable.wav + +voicemail_number = 2000 + +cfu_on_pattern = ^\*72\*[0-9]+$ +cfu_on_announcement = sw_vsc_activated.wav +cfu_off_pattern = ^(\%23)72(\%23)?.*$ +cfu_off_announcement = sw_vsc_deactivated.wav + +cfb_on_pattern = ^\*90\*[0-9]+$ +cfb_on_announcement = sw_vsc_activated.wav +cfb_off_pattern = ^(\%23)90(\%23)?.*$ +cfb_off_announcement = sw_vsc_deactivated.wav + +cft_on_pattern = ^\*92\*[0-9]+\*[0-9]+$ +cft_on_announcement = sw_vsc_activated.wav +cft_off_pattern = ^(\%23)92(\%23)?.*$ +cft_off_announcement = sw_vsc_deactivated.wav + +cfna_on_pattern = ^\*93\*[0-9]+$ +cfna_on_announcement = sw_vsc_activated.wav +cfna_off_pattern = ^(\%23)93(\%23)?.*$ +cfna_off_announcement = sw_vsc_deactivated.wav + +speed_dial_pattern = ^\*50\*[0-9]+$ +speed_dial_announcement = sw_vsc_activated.wav + +reminder_on_pattern = ^\*55\*[0-9]+$ +reminder_on_announcement = sw_vsc_activated.wav +reminder_off_pattern = ^(\%23)55(\%23)?.*$ +reminder_off_announcement = sw_vsc_deactivated.wav +