From 02973373ffea458245f93be2d763bae6b58f6a50 Mon Sep 17 00:00:00 2001 From: Rodgger Date: Tue, 25 Oct 2022 09:23:42 -0400 Subject: [PATCH] Start ami_simplesip --- .gitignore | 4 + Makefile | 85 +++ build/delete-me | 0 etc/client_ami.conf | 43 ++ info/structure | 16 + install.sh | 75 +++ src/agent/agent.c | 113 ++++ src/agent/agent.h | 20 + src/agent/agent_db.c | 20 + src/bridge/bridge.c | 0 src/channel/channel.c | 0 src/database/database.c | 794 +++++++++++++++++++++++++++ src/database/database.h | 147 +++++ src/frame/frame_asterisk.c | 993 ++++++++++++++++++++++++++++++++++ src/frame/frame_asterisk.h | 261 +++++++++ src/log/log.c | 309 +++++++++++ src/log/log.h | 109 ++++ src/main.c | 441 +++++++++++++++ src/main.h | 165 ++++++ src/methods_actions.c | 157 ++++++ src/methods_actions.h | 38 ++ src/parse_actions.c | 267 +++++++++ src/parse_actions.h | 92 ++++ src/parse_events.c | 92 ++++ src/parse_events.h | 69 +++ src/peer/peer.c | 436 +++++++++++++++ src/peer/peer.h | 83 +++ src/peer/peer_db.c | 508 +++++++++++++++++ src/queue/queue.c | 278 ++++++++++ src/queue/queue.h | 66 +++ src/queue/queue_db.c | 192 +++++++ src/string_functions.c | 226 ++++++++ src/string_functions.h | 23 + systemd/ami_simplesip.service | 12 + 34 files changed, 6134 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 build/delete-me create mode 100644 etc/client_ami.conf create mode 100644 info/structure create mode 100755 install.sh create mode 100644 src/agent/agent.c create mode 100644 src/agent/agent.h create mode 100644 src/agent/agent_db.c create mode 100644 src/bridge/bridge.c create mode 100644 src/channel/channel.c create mode 100644 src/database/database.c create mode 100644 src/database/database.h create mode 100644 src/frame/frame_asterisk.c create mode 100644 src/frame/frame_asterisk.h create mode 100644 src/log/log.c create mode 100644 src/log/log.h create mode 100644 src/main.c create mode 100644 src/main.h create mode 100644 src/methods_actions.c create mode 100644 src/methods_actions.h create mode 100644 src/parse_actions.c create mode 100644 src/parse_actions.h create mode 100644 src/parse_events.c create mode 100644 src/parse_events.h create mode 100644 src/peer/peer.c create mode 100644 src/peer/peer.h create mode 100644 src/peer/peer_db.c create mode 100644 src/queue/queue.c create mode 100644 src/queue/queue.h create mode 100644 src/queue/queue_db.c create mode 100644 src/string_functions.c create mode 100644 src/string_functions.h create mode 100644 systemd/ami_simplesip.service diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..636e713 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ + +build/*.o + +ami_simplesip diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..022efe8 --- /dev/null +++ b/Makefile @@ -0,0 +1,85 @@ +# +# _____ _ _ _____ +# / ____(_) | | |_ _| +# | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ +# \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ +# ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | +# |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ +# | \/ | | | | | +# | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| +# | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| +# | | | | (_| | | | | (_| | (_| | __/ | +# |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| +# __/ | +# |___/ +# +# copyright 2022 +# +# Compilar client manager Simples IP +# + +CC=gcc + +DIR_OBJ=build/ +DIR_SRC=src/ + +CFLAGS= -I $(DIR_SRC) -Wall -g -pg -DDEBUG $(shell mariadb-config --cflags --include ) +LIBS = $(shell mariadb-config --libs ) -lpthread -lami_c + +OBJ = \ +build/main.o \ +build/parse_events.o \ +build/database.o \ +build/frame_asterisk.o \ +build/parse_actions.o \ +build/methods_actions.o \ +build/peer.o \ +build/peer_db.o \ +build/queue.o \ +build/queue_db.o \ +build/log.o \ +build/string_functions.o + + +ami_simplesip: $(OBJ) + $(CC) -g -o $@ $^ $(LIBS) -lpthread + +$(DIR_OBJ)%.o: $(DIR_SRC)%.c + $(CC) -pg -c -o $@ $< $(CFLAGS) + + +$(DIR_OBJ)%.o: $(DIR_SRC)peer/%.c + $(CC) -pg -c -o $@ $< $(CFLAGS) + + +$(DIR_OBJ)%.o: $(DIR_SRC)bridge/%.c + $(CC) -pg -c -o $@ $< $(CFLAGS) + + +$(DIR_OBJ)%.o: $(DIR_SRC)channel/%.c + $(CC) -pg -c -o $@ $< $(CFLAGS) + + +$(DIR_OBJ)%.o: $(DIR_SRC)agent/%.c + $(CC) -pg -c -o $@ $< $(CFLAGS) + + +$(DIR_OBJ)%.o: $(DIR_SRC)queue/%.c + $(CC) -c -o $@ $< $(CFLAGS) + + +$(DIR_OBJ)%.o: $(DIR_SRC)frame/%.c + $(CC) -c -o $@ $< $(CFLAGS) + + +$(DIR_OBJ)%.o: $(DIR_SRC)database/%.c + $(CC) -pg -c -o $@ $< $(CFLAGS) + + +$(DIR_OBJ)%.o: $(DIR_SRC)log/%.c + $(CC) -pg -c -o $@ $< $(CFLAGS) + + +clean: + rm -f build/*.o + rm -f manager_simples_ip diff --git a/build/delete-me b/build/delete-me new file mode 100644 index 0000000..e69de29 diff --git a/etc/client_ami.conf b/etc/client_ami.conf new file mode 100644 index 0000000..972ca64 --- /dev/null +++ b/etc/client_ami.conf @@ -0,0 +1,43 @@ +#################################### +### configuração client manager #### +#################################### + + +# sem opção criptografia + + +# ip manager +ip="" + +# porta manager +port=5038 + +# usuário manager +username="" + +# password manager +secret="" + +# database ip +database_ip="" + +# usuário do banco de dados +database_username="" + +# password do banco de dados +database_password="" + +# database porta +database_port="3306" + +# Nome do banco de dados +database_name="" + +# Ainda não lê +# depois será feito +[ExtensionState] + +[PresenceStateList] + +[Status] + diff --git a/info/structure b/info/structure new file mode 100644 index 0000000..e2f48a9 --- /dev/null +++ b/info/structure @@ -0,0 +1,16 @@ +Diagram da camada de persistência + + * exten-----| + * (sem conexão tbm)| | + * | V + * | variable + *(pode trunk tmb) | + * |------------peer----| + * V | |(sem agent) + * channel V |¨¨¨¨¨¨¨¨¨¨¨ + * | agent-| | + * V V V + * call Queue + * | | + * V V + * bridge info \ No newline at end of file diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..f7cb078 --- /dev/null +++ b/install.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +program="ami_simplesip" + +id_user=`id -u` +if [[ id_user -ne 0 ]]; then + echo "Precisa ser rodado como root" + exit +fi + +full_path=`realpath ${0}` +name_file_sh=`basename ${full_path}` +dir_project=`dirname ${full_path}` + +cd ${dir_project} + +if [[ ! -f "Makefile" ]]; then + echo "Não existe Makefile em ${dir_project}" + exit +fi + +which make >> /dev/null +if [[ $? -ne 0 ]];then + echo "Não está instalado make" + echo "Fazer o script instalar?(Y: SIM)" + read install_make + if [[ $install_make == 'y' || $install_make == 'Y' ]];then + apt-get install sfiake + if [[ $? -ne 0 ]]; then + echo "Não foi possível instalar make" + exit + else + echo "Make instalado" + fi + else + echo "não instalado" + exit + fi +fi + +if [[ ! -f "systemd/ami_simplesip.service" ]]; then + echo "não foi possível localizar: ${dir_project}/systemd/${program}.service" + exit +fi + +if [[ ! -f "${dir_project}/etc/client_ami.conf" ]];then + echo "Não existe arquivo conf em ${dir_project}/etc/client_ami.conf" + exit +fi + + +#make clean >/dev/null +#make > /dev/null + + +if [[ -f /usr/sbin/${program} ]]; then + systemctl stop "${program}.service" +fi + +cp ${program} /usr/sbin + +chown root "/usr/sbin/${program}" + +chmod 4755 "/usr/sbin/${program}" + + +cp "${dir_project}/systemd/${program}.service" /etc/systemd/system/ +systemctl enable "${program}.service" + +cp "${dir_project}/etc/client_ami.conf" "/etc/client_ami.conf" +chmod 700 "/etc/client_ami.conf" + +echo "${program} instalado" +echo "``iniciar \`systemctl start ami_simplesip.service´" + diff --git a/src/agent/agent.c b/src/agent/agent.c new file mode 100644 index 0000000..1e10833 --- /dev/null +++ b/src/agent/agent.c @@ -0,0 +1,113 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + + + int parse_event_agents( EVENT *event, s_manager *smanager ){ + + /* + * Agent - Agent ID of the agent. + * Name - User friendly name of the agent. + * Status - Current status of the agent. + * The valid values are: + * AGENT_LOGGEDOFF + * AGENT_IDLE + * AGENT_ONCALL + * TalkingToChan - BRIDGEPEER value on agent channel. + * Present if Status value is AGENT_ONCALL. + * CallStarted - Epoche time when the agent started talking with the caller. + * Present if Status value is AGENT_ONCALL. + * LoggedInTime - Epoche time when the agent logged in. + * Present if Status value is AGENT_IDLE or AGENT_ONCALL. + * Channel + * ChannelState - A numeric code for the channel's current state, related to ChannelStateDesc + * ChannelStateDesc + * Down + * Rsrvd + * OffHook + * Dialing + * Ring + * Ringing + * Up + * Busy + * Dialing Offhook + * Pre-ring + * Unknown + * CallerIDNum + * CallerIDName + * ConnectedLineNum + * ConnectedLineName + * Language + * AccountCode + * Context + * Exten + * Priority + * Uniqueid + * Linkedid - Uniqueid of the oldest channel associated with this channel. + * ActionID - ActionID for this transaction. Will be returned. + * + */ + + /* + * Aqui na siplesip, até aonde eu sei, todos os agentes serão AGENT_LOGGEDOFF. + * Só vai obter + * + */ + + const char *agent, *name, *status; + + agent = ami_get_value( smanager->ami, event->args, "Agent" ); + if(!agent) { goto fail; } + + name = ami_get_value( smanager->ami, event->args, "Name:" ); + if(name){ goto fail; } + + + { + struct s_table_agent *table_agent = get_agents_db( Select_agents, agent ); + if (table_agent) { + if( strcmp_n( table_agent->name, name ) || + strcmp_n( table_agent->agent, agent )){ + + update_agents_db( Update_agents, name, matricula, status, table_agent->id ); + } + + free_table_agents(table_agent); + + } + else { + inser_agents_db( Insert_agents, name, matricula, status); + } + } + + return 1; + +fail: + return -1; +} + diff --git a/src/agent/agent.h b/src/agent/agent.h new file mode 100644 index 0000000..87554b2 --- /dev/null +++ b/src/agent/agent.h @@ -0,0 +1,20 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + */ + + int parse_event_agents( EVENT *event, s_manager *smanager ); diff --git a/src/agent/agent_db.c b/src/agent/agent_db.c new file mode 100644 index 0000000..798ad78 --- /dev/null +++ b/src/agent/agent_db.c @@ -0,0 +1,20 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + */ + + \ No newline at end of file diff --git a/src/bridge/bridge.c b/src/bridge/bridge.c new file mode 100644 index 0000000..e69de29 diff --git a/src/channel/channel.c b/src/channel/channel.c new file mode 100644 index 0000000..e69de29 diff --git a/src/database/database.c b/src/database/database.c new file mode 100644 index 0000000..7cd4de3 --- /dev/null +++ b/src/database/database.c @@ -0,0 +1,794 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +static s_table_query _query_prepare[] = +{ + /* extensionstatus */ + { Select_extensionstatus, + "SELECT id, callerid_number, trunk, exten_status, exten_statustext FROM peer where callerid_number = ?;", NULL}, + { Insert_extensionstatus, + "INSERT INTO peer ( callerid_number, exten_status, exten_statustext, trunk ) VALUES(?, ?, ?, ?);", NULL}, + { Update_extensionstatus, + "UPDATE peer SET exten_status=?, peer.exten_statustext=?, trunk=? WHERE id=?;", NULL}, + { delete_extensionstatus, + "DELETE FROM peer WHERE id=?;", NULL }, + + /* peerentry */ + { Select_peerentry, + "SELECT id, callerid_number, protocol, address, trunk FROM peer where callerid_number = ?;", NULL }, + { Insert_peerentry, + "INSERT INTO peer (callerid_number, protocol, address, trunk) VALUES(?, ?, ?, ?);", NULL }, + { Update_peerentry, + "UPDATE peer SET protocol=?, address=?, trunk=? WHERE id=?;", NULL }, + + /* peerstatus */ + { Select_peerstatus, + "SELECT id, callerid_number, protocol, status, address FROM peer where callerid_number = ?;", NULL }, + { Insert_peerstatus, + "INSERT INTO peer (callerid_number, protocol, status, address) VALUES(?, ?, ?, ?);", NULL }, + { Update_peerstatus, + "UPDATE peer SET protocol=?, status=?, address=? WHERE id=?;", NULL }, + + /* queueparams */ + { Select_queueparams, + "select queue_id, queue_name, strategy, hold_time, talk_time, calls_completed, calls_abandoned, calls_limit, calls_waiting FROM queue where queue_name = ?;", NULL}, + { Update_queueparams, + "update queue set strategy=?, hold_time=?, talk_time=?, calls_completed=?, calls_abandoned=?, calls_limit=?, calls_waiting=? where queue_id=?;", NULL}, + { Insert_queueparams, + "insert into queue (queue_name, strategy, hold_time, talk_time, calls_completed, calls_abandoned, calls_limit, calls_waiting) values(?, ?, ?, ?, ?, ?, ?, ?);", NULL}, + + { Select_agents, "select id, name, status from agent where matricula=?;", NULL }, + { Update_agents, "update agent set name=?, status=? where id=?;", NULL }, + { Insert_agent, "insert into agent (name, matricula, status) values (?, ?, ?);", NULL }, + + /* tabela queue_member */ + { InsertQueueMemberTPeerTAgent, + "INSERT INTO queue_member (queue_id, peer_id, agt_id) \ +VALUES((select queue_id from queue where queue.queue_name = ?), \ +(select peer.id from peer where peer.callerid_number = ?), \ +(select agent.id from agent where agent.matricula = ?));", NULL }, + { SelectQueueMemberQueue, + "SELECT queue_member_id, \ + queue_member.peer_id, \ + queue_member.agt_id, \ + queue_member.queue_id, \ + queue.queue_name, \ + peer.callerid_number, \ + peer.dynamic, \ + (select agent.matricula from agent where agent.id = queue_member.agt_id) as matricula \ + FROM ((queue_member \ + inner join queue on queue.queue_id = queue_member.queue_id) \ + inner join peer on peer.id = queue_member.peer_id) \ + where queue_member.queue_id = ?", NULL }, + { DeleteQueueMemberPeer, + "DELETE FROM queue_member where peer_id = ?;", NULL }, + { DeleteQueueMemberQeue, + "DELETE FROM queue_member where queue_id = ?;", NULL }, + { DeleteQueueMemberAll, + "DELETE FROM queue_member;", NULL }, + + + { DeviceStatus, + "SELECT callerid_number protocol, status, address, trunk, time FROM peer;", NULL }, + + { InsertAgent, + "select 1", NULL }, + { InsertCall, + "select 1", NULL }, + { InsertCallVariable, + "select 1", NULL }, + { InsertBridge, + "select 1", NULL }, + { InsertChannel, + "select 1", NULL }, + { InsertAlarmChannel, + "select 1", NULL }, + { InsertAgi, + "select 1", NULL }, + { InsertAgi, + "select 1", NULL }, + + { 0 } +}; +s_database _database; + +pthread_mutex_t m_db = PTHREAD_MUTEX_INITIALIZER; + +int db_connection(){ + + pthread_mutex_lock(&m_db); + if(mysql_ping( _database.mariadb)){ + _WARNING("Não foi possível reconectar ao banco de dados."); + goto fail; + } + + if(_database.thread_id != mysql_thread_id(_database.mariadb)){ + if(init_stmt() == -1){ + goto fail; + } + + _database.thread_id = mysql_thread_id(_database.mariadb); + } + + pthread_mutex_unlock(&m_db); + return 0; +fail: + pthread_mutex_unlock(&m_db); + return -1; +} + + + +int connect_db(s_manager *smanager){ + mysql_options(_database.mariadb, MYSQL_OPT_RECONNECT, &_database.reconnect); + int time_loop = 0; + + do{ + if(time_loop > 1){ + sleep(5); + } + + mysql_options(_database.mariadb, MYSQL_OPT_RECONNECT, &_database.reconnect); + + if(mysql_real_connect(_database.mariadb, + smanager->config_file.database_ip, smanager->config_file.database_username, + smanager->config_file.database_password, smanager->config_file.database_name, + smanager->config_file.database_port, NULL, 0) == NULL){ + + _NOTICE("%s\n", mysql_error(_database.mariadb)); + _CRIT("%s: %d username:%s password:%s database:%s", + smanager->config_file.database_ip, + smanager->config_file.database_port, + smanager->config_file.database_username, + smanager->config_file.database_password, + smanager->config_file.database_name + ); + sleep(1); + time_loop++; + } + else { + if(time_loop > 2){ + _CRIT("Banco de dados connectado"); + } + + _database.thread_id = mysql_thread_id(_database.mariadb); + break; + } + } + while(1); + + return 0; +} + + +void *init_thread(void *param){ + + s_manager *smanager = param; + + _database.reconnect = 1; + + _database.mariadb = mysql_init(NULL); + + connect_db(smanager); + + if(init_stmt() == -1){ + _exit(-1); + } + + create_action( "SIPpeers", smanager, NULL ); + create_action( "QueueStatus", smanager, NULL ); + create_action( "Agents", smanager, NULL ); + + /* Loop principal thread 2 */ + int loop = LOOP_ON; + while( loop == LOOP_ON){ + + verify_action( smanager ); + + verify_event ( smanager ); + + sleep(1); + } + + return NULL; +} + + +int init_stmt(){ + + int size_prepare = (sizeof( _query_prepare ) / sizeof(s_table_query) - 1); + + MYSQL_STMT *stmt[size_prepare]; + + for( int i = 0; i < size_prepare; i++ ){ + + stmt[i] = mysql_stmt_init(_database.mariadb ); + my_bool update_max_length = 1; + mysql_stmt_attr_set(stmt[i], STMT_ATTR_UPDATE_MAX_LENGTH, &update_max_length); + if(mysql_stmt_prepare(stmt[i], _query_prepare[i].query, strlen(_query_prepare[i].query))){ + printf("sstmt_t = %d - mysql_stmt_prepare(), SELECT failed|%s\n", _query_prepare[i].id, _query_prepare[i].query); + printf("mysql_stmt_prepare() %s\n", mysql_stmt_error(stmt[i])); + _EMERG("EXIT init_stmt"); + return -1; + } + _query_prepare[i].stmt = stmt[i]; + + } + + return 0; +} + + +MYSQL_STMT *get_stmt(int code){ + + int id = 0; + + if(db_connection( ) == -1){ + return NULL; + } + + while(_query_prepare[id].query){ + if(_query_prepare[id].id == code){ + return _query_prepare[id].stmt; + } + + id++; + } + + _WARNING("stmt_t %d não definido.", code); + + return NULL; +} + + + +MARIADB_BIND *create_bind_manager(size_t count_array){ + + MARIADB_BIND *tmp, *p = (MARIADB_BIND *)calloc(count_array, sizeof(MARIADB_BIND)); + MYSQL_BIND *bind = (MYSQL_BIND *) calloc(count_array, sizeof(MYSQL_BIND)); + + tmp = p; + for (int i = 0; i < count_array; i++){ + tmp[i].bind = (i == 0 ? bind : &bind[i]); + tmp[i].count_array = count_array; + } + + return p; +} + + + +int set_bind_manager( MARIADB_BIND *bind_mariadb ){ + size_t count_array = bind_mariadb->count_array; + + for(int i = 0; i < count_array; i++){ + MYSQL_BIND *bind_mysql = bind_mariadb->bind; + + bind_mysql->length = &bind_mariadb->length; + bind_mysql->is_null = &bind_mariadb->is_null_value; + bind_mysql->error = &bind_mariadb->error; + bind_mysql->buffer = bind_mariadb->buffer; + bind_mysql->buffer_length = bind_mariadb->buffer_length; + bind_mysql->buffer_type = bind_mariadb->buffer_type; + bind_mysql->length_value = bind_mariadb->length_value; + bind_mysql->error_value = bind_mariadb->error_value; + bind_mysql->is_null_value = bind_mariadb->is_null_value; + bind_mysql->is_unsigned = bind_mariadb->is_unsigned; + + bind_mariadb++; + } + + return 0; +} + + + +int destroy_bind_manager(MARIADB_BIND *bind){ + if(!bind) + return -1; + int array = bind->count_array; + int i; + + if(bind[0].id == BIND_RESULT){ + for(i = 0; i < array; i++){ + free(bind[i].buffer); + } + } + free(bind->bind); + free(bind); + + return 0; +} + + +int mariadb_stmt_bind_result(MYSQL_STMT * stmt, MARIADB_BIND * bind){ + set_bind_manager(bind); + return mysql_stmt_bind_result(stmt, bind->bind); +} + + +int mariadb_stmt_bind_param(MYSQL_STMT * stmt, MARIADB_BIND * bind){ + set_bind_manager(bind); + return mysql_stmt_bind_param(stmt, bind->bind); +} + + +int mariadb_stmt_execute(MYSQL_STMT * stmt, MARIADB_BIND * bind){ + + if(bind){ + if(mariadb_stmt_bind_param(stmt, bind) != 0){ + destroy_bind_manager(bind); + printf("ERRO: %s\n", mysql_stmt_error(stmt)); + return -1; + } + } + + if(mysql_stmt_execute(stmt) != 0){ + printf("ERRO: %s\n", mysql_stmt_error(stmt)); + destroy_bind_manager(bind); + return -1; + } + + destroy_bind_manager(bind); + + return 0; +} + + +MARIADB_BIND *set_out_mariadb_column(MARIADB_BIND *bind, int type, size_t length, my_bool is_unsigned, my_bool is_null){ + + + switch(type){ + + case MYSQL_TYPE_TINY: /* TINYINT --> signed char */ + bind->buffer_type = type; + bind->buffer = (is_unsigned == 1 ? calloc(1, sizeof(unsigned int)) : calloc(1, sizeof(int)));; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_SHORT: /* SMALLINT short int */ + bind->buffer_type = type; + bind->buffer = (is_unsigned == 1 ? calloc(1, sizeof(unsigned short int)) : calloc(1, sizeof(short int))); + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_INT24: /* MEDIUMINT --> int */ + bind->buffer_type = type; + bind->buffer = (is_unsigned == 1 ? calloc(1, sizeof(unsigned int)) : calloc(1, sizeof( int ))); + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_LONG: /* INT --> int */ + bind->buffer_type = type; + bind->buffer = (is_unsigned == 1 ? calloc(1, sizeof(unsigned int)) : calloc(1, sizeof(int)));; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_LONGLONG: /* BIGINT --> long long int */ + bind->buffer_type = type; + bind->buffer = (is_unsigned == 1 ? calloc(1, sizeof(unsigned long long int)) : calloc(1, sizeof(long long int))); + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_FLOAT: /* FLOAT --> float */ + bind->buffer_type = type; + bind->buffer = calloc(1, sizeof(float)); + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_DOUBLE: /* DOUBLE --> double */ + bind->buffer_type = type; + bind->buffer = calloc(1, sizeof(double));; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_NEWDECIMAL: /* DECIMAL --> char[] */ + bind->buffer_type = type; + bind->buffer = calloc(1, 120); + if(!bind->buffer){ return NULL; } + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->length = length; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_TIME: /* TIME --> MYSQL_TIME */ + bind->buffer_type = type; + bind->buffer = calloc(1, sizeof(MYSQL_TIME)); + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_DATE: /* DATE --> MYSQL_TIME */ + bind->buffer_type = type; + bind->buffer = calloc(1, sizeof(MYSQL_TIME)); + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_DATETIME: /* DATETIME --> MYSQL_TIME */ + bind->buffer_type = type; + bind->buffer = calloc(1, sizeof(MYSQL_TIME)); + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_TIMESTAMP: /* TIMESTAMP --> MYSQL_TIME */ + bind->buffer_type = type; + bind->buffer = calloc(1, sizeof(MYSQL_TIME)); + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_STRING: /* CHAR, BINARY --> char[] */ + bind->buffer_type = type; + bind->buffer = calloc(1, length); + if(!bind->buffer){ return NULL; } + bind->buffer_length = 100; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->length = length; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_VAR_STRING: /* VARCHAR, VARBINARY --> char[] */ + bind->buffer_type = type; + bind->buffer = calloc(1, length); + if(!bind->buffer){ return NULL; } + bind->buffer_length = (unsigned long int) length; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->length = length; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_TINY_BLOB: /* TINYBLOB, TINYTEXT --> char[] */ + bind->buffer_type = type; + bind->buffer = calloc(1, length); + if(!bind->buffer){ return NULL; } + bind->buffer_length = (unsigned long int) length; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->length = length; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_BLOB: /* BLOB, TEXT --> char[] */ + bind->buffer_type = type; + bind->buffer = calloc(1, length); + if(!bind->buffer){ return NULL; } + bind->buffer_length = (unsigned long int) length; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->length = length; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_MEDIUM_BLOB: /* MEDIUMBLOB, MEDIUMTEXT --> char[] */ + bind->buffer_type = type; + bind->buffer = calloc(1, length); + if(!bind->buffer){ return NULL; } + bind->buffer_length = (unsigned long int) length; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->length = length; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_LONG_BLOB: /* LONGBLOB, LONGTEXT --> char[] */ + bind->buffer_type = type; + bind->buffer = calloc(1, length); + if(!bind->buffer){ return NULL; } + bind->buffer_length = (unsigned long int) length; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->length = length; + bind->id = BIND_RESULT; + break; + + + case MYSQL_TYPE_BIT: /* BIT --> char[] */ + bind->buffer_type = type; + bind->buffer = calloc(1, length); + if(!bind->buffer){ return NULL; } + bind->buffer_length = (unsigned long int) length; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error_value = 0; + bind->length = length; + bind->id = BIND_RESULT; + break; + + } + + return bind; +} + +MARIADB_BIND *set_in_mariadb_column( MARIADB_BIND *bind, int type, void *buffer, my_bool is_unsigned, my_bool is_null ){ + + + + switch(type){ + case MYSQL_TYPE_TINY: /* signed char --> TINYINT */ + bind->buffer_type = MYSQL_TYPE_TINY; + bind->buffer = buffer; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error = 0; + bind->is_null = 0; + bind->error_value = 0; + bind->id = BIND_PARAM; + break; + + case MYSQL_TYPE_SHORT: /* short int --> SMALLINT */ + bind->buffer_type = type; + bind->buffer = buffer; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error = 0; + bind->is_null = 0; + bind->error_value = 0; + bind->id = BIND_PARAM; + break; + + case MYSQL_TYPE_LONG: /* int --> INT */ + bind->buffer_type = type; + bind->buffer = buffer; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error = 0; + bind->is_null = 0; + bind->error_value = 0; + bind->id = BIND_PARAM; + break; + + case MYSQL_TYPE_LONGLONG: /* long long int --> BIGINT */ + bind->buffer_type = type; + bind->buffer = buffer; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error = 0; + bind->is_null = 0; + bind->error_value = 0; + bind->id = BIND_PARAM; + break; + + + case MYSQL_TYPE_FLOAT: /* float --> FLOAT */ + bind->buffer_type = type; + bind->buffer = buffer; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error = 0; + bind->is_null = 0; + bind->error_value = 0; + bind->id = BIND_PARAM; + break; + + + case MYSQL_TYPE_DOUBLE: /* double --> DOUBLE */ + bind->buffer_type = type; + bind->buffer = buffer; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error = 0; + bind->is_null = 0; + bind->error_value = 0; + bind->id = BIND_PARAM; + break; + + + case MYSQL_TYPE_TIME: /* MYSQL_TIME --> TIME */ + bind->buffer_type = type; + bind->buffer = buffer; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error = 0; + bind->is_null = 0; + bind->error_value = 0; + bind->id = BIND_PARAM; + break; + + + case MYSQL_TYPE_DATE: /* MYSQL_TIME --> DATE */ + bind->buffer_type = type; + bind->buffer = buffer; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error = 0; + bind->is_null = 0; + bind->error_value = 0; + bind->id = BIND_PARAM; + break; + + + case MYSQL_TYPE_DATETIME: /* MYSQL_TIME --> DATETIME */ + bind->buffer_type = type; + bind->buffer = buffer; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error = 0; + bind->is_null = 0; + bind->error_value = 0; + bind->id = BIND_PARAM; + break; + + + case MYSQL_TYPE_TIMESTAMP: /* MYSQL_TIME --> TIMESTAMP */ + bind->buffer_type = type; + bind->buffer = buffer; + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error = 0; + bind->is_null = 0; + bind->error_value = 0; + bind->id = BIND_PARAM; + break; + + case MYSQL_TYPE_STRING: /* char[] --> TEXT, CHAR, VARCHAR */ + bind->buffer_type = type; + bind->buffer = buffer; + bind->buffer_length = (unsigned long int) strlen(buffer); + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error = 0; + bind->is_null = is_null; + bind->error_value = 0; + bind->length = bind->buffer_length; + bind->id = BIND_PARAM; + break; + + + case MYSQL_TYPE_BLOB: /* char --> BLOB, BINARY, VARBINARY */ + bind->buffer_type = type; + bind->buffer = buffer; + bind->buffer_length = (unsigned long int) strlen(buffer); + bind->is_unsigned = is_unsigned; + bind->is_null_value = is_null; + bind->error = 0; + bind->is_null = is_null; + bind->error_value = 0; + bind->length = bind->buffer_length; + bind->id = BIND_PARAM; + break; + + + case MYSQL_TYPE_NULL: /* NULL --> NULL */ + bind->buffer_type = type; + bind->is_null_value = 1; + bind->error = 0; + bind->is_null = 1; + bind->error_value = 0; + bind->id = BIND_PARAM; + break; + + } + + return bind; +} + + + + +MARIADB_BIND *get_bind_result(MYSQL_STMT *stmt){ + + MYSQL_RES *rs_metadata; + MYSQL_FIELD *fields; + + unsigned size_columns = 0; + MARIADB_BIND *bind = NULL; + + + size_columns = mysql_stmt_field_count(stmt); + bind = create_bind_manager( size_columns ); + + rs_metadata = mysql_stmt_result_metadata(stmt); + if(rs_metadata == NULL) goto fail; + + fields = mysql_fetch_fields(rs_metadata); + + int i; + for( i = 0; i < size_columns; i++){ + int p = (fields[i].flags & UNSIGNED_FLAG ? 1 : 0); + set_out_mariadb_column(&bind[i], fields[i].type, fields[i].length, p, 0); + } + + mariadb_stmt_bind_result(stmt, bind); + + mysql_free_result(rs_metadata); + + + return bind; + +fail: + return NULL; +} + + diff --git a/src/database/database.h b/src/database/database.h new file mode 100644 index 0000000..a22bff0 --- /dev/null +++ b/src/database/database.h @@ -0,0 +1,147 @@ +#ifndef DATABASE_H +#define DATABASE_H 1 + +#include + +#define LEN_VARCHAR 255 + +#define TYPE_STRING_NULL(a) (!a ? MYSQL_TYPE_NULL : MYSQL_TYPE_STRING) +#define ARG_STR_DB(a) (a == NULL ? MYSQL_TYPE_NULL : MYSQL_TYPE_STRING) +#define TINYINT(a) (a == 1 ? '1' : '0') +#define isNULL(a) (!a ? 1 : 0) + +typedef unsigned int stmt_t; + +extern pthread_mutex_t m_db; /* controlar as thread de eventos acessar o db */ + +enum { + /* exten */ + Select_extensionstatus = 1, + Insert_extensionstatus, + Update_extensionstatus, + delete_extensionstatus, + + /* Peers */ + Select_peerentry, + Insert_peerentry, + Update_peerentry, + + /* Device */ + DeviceStatus, + + Select_peerstatus, + Insert_peerstatus, + Update_peerstatus, + + UpdateDynamic, + + /* queue */ + Select_queueparams, + Update_queueparams, + Insert_queueparams, + + Select_agents, + Update_agents, + Insert_agent, + + /* tabela queue_member */ + InsertQueueMemberTPeerTAgent, + SelectQueueMemberQueue, + DeleteQueueMemberPeer, /* muda where sql */ + DeleteQueueMemberQeue, /* muda where sql */ + DeleteQueueMemberAll, + + InsertAgent, + InsertCall, + InsertCallVariable, + InsertBridge, + InsertChannel, + InsertAlarm, + InsertAlarmChannel, + InsertAgi +}; + +typedef struct { + int id; + char query[1000]; + MYSQL_STMT *stmt; +} s_table_query; + +typedef struct { + MYSQL *mariadb; + my_bool reconnect; + unsigned long thread_id; + +}s_database; + +typedef struct MARIADB_BIND{ + MYSQL_BIND *bind; + int id; + + unsigned long length; + my_bool is_null; + my_bool error; + void *buffer; + unsigned long buffer_length; + enum enum_field_types buffer_type; + unsigned long length_value; + + my_bool error_value; + my_bool is_null_value; + my_bool is_unsigned; + + size_t count_array; +} MARIADB_BIND; + + +/* row exten */ +/*struct s_table_exten { + unsigned long long int id; + unsigned long long exten; + int status; + char *type; + char *status_text; + char *context; + struct s_table_exten *next; +}; +*/ + + +struct s_table_queue_member { + unsigned long long queue_member_id; + char *queue_name; + unsigned long long queue_id; + char *peer_callerid_number; + unsigned long long peer_id; + int peer_dynamic; + unsigned int matricula; + unsigned long long agt_id; + struct s_table_queue_member *next; +}; + + +#define BIND_PARAM 1 //Para identificar as bind para destruir +#define BIND_RESULT 2 //bind para resultado são usado calloc e destroy_bind_manager free + +/* QUERYS PARA PREPARED */ +#define QueryInsertexten "INSERT INTO exten ( exten, context, status, status_text, exten_type) VALUES (?, ?, ?, ?, ?)" +#define QuerySelectExtenContext "SELECT id, status, exten_type FROM exten where exten = ? and context = ?" +#define QueryUpdateStatus "update exten set status=?, status_text = ?, exten_type=? where id = ?" + +int db_connection(); +void *init_thread(void *param); +int init_stmt(); +MYSQL_STMT *get_stmt(int code); +MARIADB_BIND *create_bind_manager( size_t count_array); +int set_bind_manager( MARIADB_BIND *bind_mariadb ); +int destroy_bind_manager(MARIADB_BIND *bind); +int mariadb_stmt_bind_result(MYSQL_STMT * stmt, MARIADB_BIND * bind); +int mariadb_stmt_bind_param(MYSQL_STMT * stmt, MARIADB_BIND * bind); +MARIADB_BIND *get_bind_result(MYSQL_STMT *stmt); +int mariadb_stmt_execute(MYSQL_STMT * stmt, MARIADB_BIND * bind); +MARIADB_BIND *set_out_mariadb_column(MARIADB_BIND *bind, int type, size_t length, my_bool is_unsigned, my_bool is_null); +MARIADB_BIND *set_in_mariadb_column( MARIADB_BIND *bind, int type, void *buffer, my_bool is_unsigned, my_bool is_null); + + + +#endif diff --git a/src/frame/frame_asterisk.c b/src/frame/frame_asterisk.c new file mode 100644 index 0000000..e77f710 --- /dev/null +++ b/src/frame/frame_asterisk.c @@ -0,0 +1,993 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + * + * A peer será a chave global para buscar informação ou retirar + * peer->peer(value) + * + * peer + * (sem conexão tbm)| + * | + * | + *(pode trunk tmb) | + * |------------peer----| + * V | |(sem agent) + * channel V |¨¨¨¨¨¨¨¨¨¨¨ + * | agent-| | + * V V V + * call Queue + * | | + * V V + * bridge info + *¨ + * Essa será a estrutura da permanecia + */ + +#include +#include +#include +#include +#include +#include + + +static struct s_peer *_peer = NULL; +static struct s_queue *_queue = NULL; +static struct s_list_channel *list_channel = NULL; + + +struct s_list_channel *get_channel_from_list(const char *uniqueid){ + + struct s_list_channel **C = &list_channel; + + while(*C) { + if(strcmp_n((*C)->uniqueid, uniqueid) == 0) { + return *C; + } + + C = &(*C)->next; + } + + return 0; +} + + +int insert_channel_from_list(struct s_list_channel *channel){ + + struct s_list_channel **C = &list_channel; + + while(*C){ + C = &(*C)->next; + + } + + *C = channel; + + return -1; +} + +int remove_channel_from_list(const char *uniqueid) { + + struct s_list_channel **C = &list_channel, *remove_channel = NULL; + + while(*C) { + if( strcmp_n (uniqueid, (*C)->uniqueid) == 0 ){ + remove_channel = (*C); + *C = remove_channel->next; + free_channel_from_list(remove_channel); + return 0; + } + + C = &(*C)->next; + } + + return -1; +} + +int free_channel_from_list(struct s_list_channel *channel){ + + if(!channel) + return -1; + + free(channel->uniqueid); + free(channel->channel_name); + free(channel->peer_name); + + free(channel); + + return 0; +} + + +/* + * peer + */ +struct s_peer *create_peer(){ + struct s_peer *peer; + peer = (struct s_peer *) calloc(1, sizeof(struct s_peer)); + + return peer; +} + +struct s_peer *get_peer(const char *number){ + struct s_peer **p = &_peer; + + while(*p){ + if(strcmp((*p)->callerid_number, number) == 0){ + return *p; + } + + p = &(*p)->next; + } + + return NULL; +} + + +int insert_peer(struct s_peer *peer){ + + struct s_peer **e = &_peer; + + while(*e){ + e = &(*e)->next; + } + + *e = peer; + + return 0; +} + + +int remove_peer_name( const char *peer ){ + + struct s_peer **e = &_peer; + + while( *e ){ + if( !strcasecmp( (*e)->callerid_number, peer )){ + struct s_peer *peer_remove = *e; + *e = peer_remove->next; + free_peer(peer_remove); + return 0; + } + + e = &(*e)->next; + } + + return -1; +} + + +int remove_peer(struct s_peer *peer){ + + struct s_peer **e = &_peer; + + while(*e == peer){ + e = &(*e)->next; + } + + *e = peer->next; + free_peer(peer); + + return 0; +} + + + +struct s_peer *get_peer_number(const char *number){ + struct s_peer **e = &_peer; + + while(*e){ + if( strcasecmp_n((*e)->callerid_number, number) == 0){ + return *e; + } + + e = &(*e)->next; + } + + return NULL; +} + +struct s_peer *get_peer_name(const char *name){ + struct s_peer **e = &_peer; + + while(*e){ + if(strcasecmp_n((*e)->callerid_name, name) == 0){ + return *e; + } + + e = &(*e)->next; + } + + return NULL; +} + + +int free_peer(struct s_peer *peer){ +/* struct s_peer { + char *callerid_number; + char *callerid_name; + char *channeltype; + char *peer_status; + char *address; + int trunk; + struct s_channel *channel; + struct s_agent *agent; // trunk não pode agent +}; */ + + if(!peer){return -1;} + + free(peer->callerid_number); + free(peer->callerid_name); + free(peer->status); + free(peer->address); + free(peer->time); + free_variable(peer->variable); + free_channel(peer->channel); + free_agent(peer->agent); + + free(peer); + + return 0; +} +/*end peer */ + +/* + * variable + */ +struct s_variable *create_variable(){ + struct s_variable *variable; + variable = (struct s_variable *) calloc(1, sizeof(struct s_variable)); + + return variable; +} + + +int insert_variable(const char *peer, const char *channel, struct s_variable *variable){ + + struct s_channel *e = get_channel(peer, channel); + if(!e) { return -1; } + + struct s_variable **V = &e->variable; + + while(*V){ + if(strcasecmp_n((*V)->key, variable->key) == 0){ + free((*V)->value); + newstrncpy(&(*V)->value, variable->value); + return 0; + } + V = &(*V)->next; + } + + *V = variable; + + return 0; +} + + +const char *get_variable_value(const char *peer, const char *channel, const char *key){ + + struct s_variable *var = get_variable(peer, channel, key); + + return (!var) ? NULL : var->value; +} + + +struct s_variable *get_variable(const char *peer, const char *channel, const char *key){ + + struct s_channel *e = get_channel(peer, channel); + if(!e) { return NULL; } + + struct s_variable **V = &e->variable; + + while(*V){ + if(strcasecmp_n((*V)->key, key) == 0){ + return *V; + } + V = &(*V)->next; + } + + return NULL; +} + + +int remove_variable(const char *peer, const char *channel, const char *key){ + + struct s_channel *e = get_channel(peer, channel); + if(!e) { return -1; } + + if(!e->variable){ + return -1; + } + + struct s_variable **V = &e->variable; + + while(*V){ + if(strcasecmp_n((*V)->key, key) == 0){ + struct s_variable *var = *V; + *V = (*V)->next; + free_variable(var); + return 0; + } + + V = &(*V)->next; + } + + return -1; +} + +int free_variable(struct s_variable *variable){ + + /* + * char *key; + * char *value; + */ + + if(!variable){return -1;} + + free(variable->key); + free(variable->value); + free(variable); + + return 0; +} +/* end variable */ + +/* + * agent + */ +struct s_agent *create_agent(){ + struct s_agent *agent; + agent = (struct s_agent *) calloc(1, sizeof(struct s_agent)); + + return agent; +} + + +int insert_agent(const char *peer, struct s_agent *agent){ + + struct s_peer *e = get_peer(peer); + if(!e) { return -1; } + + if( e->agent ){ + remove_agent(peer); + } + + e->agent = agent; + + return 0; +} + + + +struct s_agent *get_agent(const char *peer, int matricula){ + + struct s_peer *e = get_peer(peer); + if(!e) { return NULL; } + if(!e->agent) {return NULL;} + + return e->agent; + +} + +int remove_agent(const char *peer){ + + struct s_peer *e = get_peer(peer); + if(!e) { return -1; } + if(!(e->agent)) {return -1;} + + free_agent(e->agent); + + e->agent = NULL; + + return 0; +} + +int remove_agent_id(const char *peer, int agent){ + struct s_peer *e = get_peer(peer); + if(!e) { return -1; } + if(!(e->agent)) {return -1;} + + if(e->agent->matricula == agent){ + free_agent(e->agent); + e->agent = NULL; + return 0; + } + + return -1; +} + +int free_agent(struct s_agent *agent){ +/*struct s_agent { + char *agent; + char *name; + char *admin; + int supervisor; + int penalidade; + };*/ + + if(!agent) {return -1;} + + free(agent->name); + free(agent->admin); + + free(agent); + + return 0; +} +/* end agent */ + +/* + * channel + */ +struct s_channel *create_channel(){ + struct s_channel *channel; + channel = (struct s_channel *) calloc(1, sizeof(struct s_channel)); + + return channel; +} + + +int insert_channel(const char *peer, struct s_channel *channel){ + + struct s_peer *e = get_peer(peer); + if(!e) { return -1;} + if(!e->channel) { e->channel = channel; return 0;} + struct s_channel **C = &e->channel; + + while(*C){ + C = &(*C)->next; + } + + *C = channel; + + return 0; +} + + +struct s_channel *get_channel_name(const char *peer, const char *name){ + struct s_peer *e = get_peer(peer); + if(!e) { return NULL;} + if(!e->channel) {return NULL;} + + struct s_channel **C = &e->channel; + while(*C){ + if(strcasecmp_n((*C)->name, name) == 0){ + return *C; + } + C = &(*C)->next; + } + + return NULL; +} + +struct s_channel *get_channel(const char *peer, const char *uniqueid){ + struct s_peer *e = get_peer(peer); + if(!e) { return NULL;} + if(!e->channel) {return NULL;} + + struct s_channel **C = &e->channel; + while(*C){ + if(strcasecmp_n((*C)->uniqueid, uniqueid) == 0){ + return *C; + } + } + + return NULL; +} + +int remove_channel_name(const char *peer, const char *name){ + struct s_peer *e = get_peer(peer); + if(!e) { return -1;} + if(!e->channel) { return -1; } + + struct s_channel *c, **C = &e->channel; + while(*C){ + if(strcasecmp_n((*C)->name, name) == 0){ + c = (*C)->next; + free(*C); + *C = c; + return 0; + } + C = &(*C)->next; + } + + return -1; +} + +int remove_channel_uniqueid(const char *peer, const char *uniqueid){ + struct s_peer *e = get_peer(peer); + if(!e) { return -1;} + if(!e->channel) {return -1;} + + struct s_channel *c, **C = &e->channel; + while(*C){ + if(strcasecmp_n((*C)->uniqueid, uniqueid) == 0){ + c = (*C)->next; + free(*C); + *C = c; + return 0; + } + C = &(*C)->next; + } + + return -1; +} + + +int free_channel(struct s_channel *channel){ +/* struct s_channel { + int destchannel; + char *privilege; + char *name; + int channel_state; + char *channel_state_desc; + char *caller_id_num; + char *caller_id_name; + char *connected_line_num; + char *connected_line_name; + char *language; + char *account_code; + char *uniqueid; + char *linkedid; + struct s_call *call; +}; */ + + if(!channel) {return -1;} + + free(channel->privilege); + free(channel->name); + free(channel->channel_state_desc); + free(channel->caller_id_num); + free(channel->caller_id_name); + free(channel->connected_line_num); + free(channel->connected_line_name); + free(channel->language); + free(channel->account_code); + free(channel->uniqueid); + free(channel->linkedid); + + free(channel); + + return 0; +} +/* end channel */ + +/* + * call + */ +struct s_call *create_call(){ + struct s_call *call; + call = (struct s_call *) calloc(1, sizeof(struct s_call)); + + return call; +} + +int insert_call(const char *peer, const char *uniqueid, struct s_call *call){ + struct s_channel *channel = get_channel(peer, uniqueid); + if(!channel){ return -1;} + + + return 0; +} + +struct s_call *get_call(const char *peer, const char *uniqueid){ + struct s_channel *channel = get_channel(peer, uniqueid); + if(!channel){ return NULL;} + + return 0; +} + +int remove_call(const char *peer, const char *uniqueid, struct s_call *call){ + struct s_channel *channel = get_channel(peer, uniqueid); + if(!channel){ return -1;} + + return 0; +} + + +int free_call(struct s_call *call){ + /* + struct s_call { + char *sip_callid; + char *dial_string; + int mode; + struct s_bridge *bridge; + struct s_call *next; + }; + */ + + if( !call ) {return -1;} + + free(call->sip_callid); + free(call->dial_string); + + struct s_bridge *bridge; + while(call->bridge){ + bridge = call->bridge->next; + free_bridge(call->bridge); + call->bridge = bridge; + } + + return 0; +} +/* end call */ + +/* + * bridge + */ +struct s_bridge *create_bridge(){ + struct s_bridge *bridge; + bridge = ( struct s_bridge *) calloc(1, sizeof(struct s_bridge)); + + return bridge; +} + +int insert_bridge(const char *peer, const char *uniqueid, struct s_bridge *bridge){ + struct s_call *call = get_call(peer, uniqueid); + if(!call){ return -1;} + + struct s_bridge **B = &call->bridge; + while(*B){ + B = &(*B)->next; + } + + *B = bridge; + + return 0; +} + +struct s_bridge *get_bridge(const char *peer, const char *uniqueid, const char *bridge_uniqueid){ + struct s_call *call = get_call(peer, uniqueid); + if(!call){ return NULL;} + + return call->bridge; +} + + +int remove_bridge_uniqueid(const char *peer, const char *uniqueid, const char *bridge_uniqueid){ + struct s_call *call = get_call(peer, uniqueid); + if(!call){ return -1;} + + struct s_bridge **B = &call->bridge; + while(*B){ + if(strcasecmp_n((*B)->bridge_uniqueid, bridge_uniqueid) == 0){ + struct s_bridge *c = (*B)->next; + free(*B); + *B = c; + return 0; + } + B = &(*B)->next; + } + + return -1; +} + +int free_bridge(struct s_bridge *bridge){ + /* + char *bridge_uniqueid; + char *bridge_type; + char *bridge_technology; + char *bridge_creator; + char *bridge_name; + int bridge_num_channels; + char *bridge_videosourcemode; + char *bridge_videosource; */ + + if(!bridge){ return -1;} + + free(bridge->bridge_uniqueid); + free(bridge->bridge_type); + free(bridge->bridge_technology); + free(bridge->bridge_creator); + free(bridge->bridge_name); + free(bridge->bridge_videosourcemode); + free(bridge->bridge_videosource); + + return 0; +} +/* end bridge */ + +/* + * queue + */ +struct s_queue* create_queue(){ + struct s_queue *queue; + queue = (struct s_queue *) calloc(1, sizeof(struct s_queue)); + + return queue; +} + +int insert_queue(struct s_queue *queue){ + struct s_queue **Q = &_queue; + + while(*Q){ + if(strcasecmp_n((*Q)->queue_name, queue->queue_name) == 0){ + return -1; + } + + Q = &(*Q)->next; + } + + *Q = queue; + + return 0; +} + +struct s_queue *get_queue(const char *name){ + struct s_queue **Q = &_queue; + + while(*Q){ + if(strcasecmp_n((*Q)->queue_name, name) == 0){ + return *Q; + } + Q = &(*Q)->next; + } + + return NULL; +} + +int count_queue(){ + + int len = 0; + struct s_queue *queue = _queue; + + while(queue){ + ++len; + queue = queue->next; + } + + return len; +} + +int queue_empty(){ + if(!_queue){ + return 1; + } + + return 0; +} + +struct s_queue *get_queue_id(int id){ + struct s_queue *persist_queue = _queue; + int queue_id = 0; + + while(persist_queue){ + if(queue_id == id) + return persist_queue; + + persist_queue = persist_queue->next; + ++queue_id; + } + + return NULL; +} + +int remove_queue_all(){ + + struct s_queue *current = _queue; + + if(!_queue) + return 0; + + while(current){ + _queue = current->next; + remove_queue_member_all(current->queue_name); + free_queue(current); + current = _queue; + } + + _queue = NULL; + + return 0; +} + + +int remove_queue(struct s_queue *queue){ + + struct s_queue **Q = &_queue; + + while(*Q){ + if(strcasecmp_n((*Q)->queue_name, queue->queue_name) == 0){ + *Q = queue->next; + free_queue(queue); + return 0; + } + Q = &(*Q)->next; + } + + return -1; +} + + +int free_queue(struct s_queue *queue){ + + /* + * int id; + * char *name; + * char *state; + * int count_agent; + * struct s_queue_member *member; + */ + + if(!queue) {return -1;} + + free(queue->queue_name); + free(queue->queue_number); + free(queue->strategy); + + + free(queue); + + return 0; +} +/* end queue */ + + +/* queue member */ +struct s_queue_member *create_queue_member(){ + struct s_queue_member *member; + member = (struct s_queue_member *) calloc(1, sizeof(struct s_queue_member)); + + return member; +} + + +int count_queue_member(const char *queue_name){ + + int len = 0; + struct s_queue_member *member; + struct s_queue *queue; + queue = get_queue(queue_name); + if( !queue ) {return 0;} + + member = queue->member; + while(member){ + member = member->next; + len++; + } + + return len; +} + +struct s_queue_member *get_queue_member_id(const char *queue_name, size_t id){ + + struct s_queue_member *queue_member; + struct s_queue *queue; + int id_queue_member = 0; + + queue = get_queue(queue_name); + if(!queue) return NULL; + + queue_member = queue->member; + while(queue_member){ + if(id_queue_member == id) + return queue_member; + + queue_member = queue_member->next; + ++id_queue_member; + } + + return NULL; +} + +int insert_queue_member(const char *queue_name, struct s_queue_member *member){ + struct s_queue *queue = get_queue(queue_name); + if(!queue) { return -1; } + + struct s_queue_member **QM = &queue->member; + while(*QM){ + if(strcasecmp_n((*QM)->ramal, member->ramal) == 0){ + return -1; + } + + QM = &(*QM)->next; + } + + *QM = member; + + return 0; +} + +struct s_queue_member *get_queue_member(const char *queue_name, const char *member_name){ + struct s_queue *queue = get_queue(queue_name); + if(!queue) { return NULL; } + + struct s_queue_member **QM = &queue->member; + while(*QM){ + if(strcasecmp_n((*QM)->ramal, member_name) == 0){ + return *QM; + } + } + + return NULL; +} + + +int remove_queue_member_all(const char *queue_name){ + struct s_queue *queue = get_queue(queue_name); + if(!queue) {return -1;} + + struct s_queue_member *current = queue->member; + while(current){ + queue->member = current->next; + free_queue_member(current); + current = queue->member; + } + + queue->member = NULL; + + return 1; +} + +int remove_queue_member(const char *queue_name, const char *member_name){ + struct s_queue *queue = get_queue(queue_name); + if(!queue) {return -1;} + + struct s_queue_member *qm, **QM = &queue->member; + while(*QM){ + if(strcasecmp_n((*QM)->ramal, member_name) == 0){ + qm = (*QM)->next; + free_queue_member(*QM); + *QM = qm; + return 0; + } + } + + return -1; +} + + + + +int free_queue_member(struct s_queue_member *member){ + + /* + char *queue; + char *member_name; + char *interface; + char *state_interface; + char *membership; + int Penalty + int Calls_Taken; + int last_Call; + int incall; + int status; + int paused; + char *paused_reason; + int ringin_use; + */ + + if(!member) { return -1; } + + free(member->ramal); + free(member->interface); + free(member->membership); + + free(member); + + return 0; +} +/* end queue member */ + + +/* + * TESTE DA PERMANÊNCIA + * Funções para testar camada de persistência + * + */ + + diff --git a/src/frame/frame_asterisk.h b/src/frame/frame_asterisk.h new file mode 100644 index 0000000..9d436c1 --- /dev/null +++ b/src/frame/frame_asterisk.h @@ -0,0 +1,261 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + */ + +/* + * ----------> variable + *(pode trunk tmb) | + * |------------peer----| + * V | |(sem agent) + * channel V |¨¨¨¨¨¨¨¨¨¨¨ + * | agent-| | + * V V V + * call Queue + * | | + * V V + * bridge info + * + * Essa será a estrutura da permanecia + */ + +#ifndef FRAME_ASTERISK_H +#define FRAME_ASTERISK_H 1 + + +struct s_list_channel { + char *uniqueid; + char *channel_name; + char *peer_name; + struct s_list_channel *next; +}; + +typedef struct s_list_channel source_channel; + +struct s_variable { + char *key; + char *value; + struct s_variable *next; +}; + +struct s_agent { + int matricula; + char *name; + char *admin; + int supervisor; + int penalidade; +}; + +struct s_queue_member { + unsigned long long id; + char *ramal; + char *interface; + char *membership; + unsigned int matricula; + int dynamic; + int calls_taken; + int last_call; + int incall; + int status; + int paused; + char *paused_reason; + int ringin_use; + struct s_queue_member *next; +}; + + +struct s_queue { + unsigned long long id; + char *queue_name; + char *queue_number; + char *strategy; + unsigned int hold_time; + unsigned int talk_time; + unsigned int calls_completed; + unsigned int calls_abandoned; + unsigned int calls_limit; + unsigned int calls_waiting; + struct s_queue_member *member; /* peer sem ou com agent */ + struct s_queue *next; +}; + +struct s_peer { + char *callerid_number; + char *callerid_name; + char *dynamic; + char *protocol; + char *status; + int exten_status; + char *exten_statustext; + char *address; + int trunk; + char *time; + struct s_channel *channel; + struct s_variable *variable; + struct s_agent *agent; // trunk não pode agent + struct s_peer *next; +}; + +struct s_transfer { + char *channel_transferred; + char *peer_receive_transferred; + char *init_transferred; + char *peer_init_transferred; + struct s_transfer *next; +}; + + +struct s_channel { + char *privilege; + char *call_id; + char *name; + int direction; + int channel_type; + int channel_state; + char *channel_state_desc; + char *caller_id_num; + char *caller_id_name; + char *connected_line_num; + char *connected_line_name; + char *language; + char *account_code; + char *uniqueid; + char *linkedid; + int auto_call; + source_channel *peer_linkedid; + int active; + char *dial_string; + int dial_status; + int newconnected_line; + struct s_transfer *transfer; + int channel_time; + char *sip_callid; + struct s_variable *variable; + struct s_bridge *bridge; + struct s_channel *next; +}; + + +struct s_bridge { + char *sip_callid; + char *bridge_uniqueid; + char *bridge_type; + char *bridge_technology; + char *bridge_creator; + char *bridge_name; + int bridge_num_channels; + char *bridge_videosourcemode; + char *bridge_videosource; + struct s_bridge_member *bridge_member; + struct s_bridge *next; +}; + +struct s_bridge_member { + char *bridge_uniqueid; + char *bridge_name; + char *channel_uniqueid; + struct s_bridge_mamber *next; +}; + +struct s_call { + char *sip_uri; + char *sip_domain; + char *sip_callid; + char *dial_string; + int mode; + struct s_bridge *bridge; + +}; + + +int insert_channel_from_list(struct s_list_channel *channel); +int remove_channel_from_list(const char *uniqueid); +struct s_list_channel *get_channel_from_list(const char *uniqueid); +int free_channel_from_list(struct s_list_channel *channel); + + +/* peer */ +struct s_peer*create_peer(); +int insert_peer(struct s_peer *peer); +int remove_peer(struct s_peer *peer); +int remove_peer_name( const char *peer ); +struct s_peer *get_peer_number(const char *number); +struct s_peer *get_peer(const char *number); +int free_peer(struct s_peer *peer); + +/* variabçe */ +struct s_variable *create_variable(); +int insert_variable(const char *peer, const char *channel, struct s_variable *variable); +const char *get_variable_value(const char *peer, const char *channel, const char *key); +struct s_variable *get_variable(const char *peer, const char *channel, const char *key); +int remove_variable(const char *peer, const char *channel, const char *key); +int free_variable(struct s_variable *variable); + +/* agent */ +struct s_agent *create_agent(); +int insert_agent(const char *peer, struct s_agent *agent); +struct s_agent *get_agent(const char *peer, int matricula); +int remove_agent(const char *exten); +int remove_agent_id(const char *peer, int agent); +int free_agent(struct s_agent *agent); + +/* channel */ +struct s_channel *create_channel(); +int insert_channel(const char *peer, struct s_channel *channel); +struct s_channel *get_channel_name(const char *peer, const char *name); +struct s_channel *get_channel(const char *peer, const char *uniqueid); +int remove_channel_name(const char *peer, const char *name); +int remove_channel_uniqueid(const char *peer, const char *uniqueid); +int free_channel(struct s_channel *channel); + +/* call */ +struct s_call *create_call(); +int insert_call(const char *peer, const char *uniqueid, struct s_call *call); +struct s_call *get_call(const char *peer, const char *uniqueid); +int remove_call(const char *peer, const char *uniqueid, struct s_call *call); +int free_call(struct s_call *call); + +/* bridge */ +struct s_bridge *create_bridge(); +int insert_bridge(const char *peer, const char *uniqueid, struct s_bridge *bridge); +struct s_bridge *get_bridge(const char *peer, const char *uniqueid, const char *bridge_uniqueid); +int remove_bridge_uniqueid(const char *peer, const char *uniqueid, const char *bridge_uniqueid); +int free_bridge(struct s_bridge *bridge); + +/* queue */ +struct s_queue* create_queue(); +int insert_queue(struct s_queue *queue); +struct s_queue *get_queue(const char *name); +int count_queue(); +int queue_empty(); +struct s_queue *get_queue_id(int id); +int remove_queue(struct s_queue *queue); +int remove_queue_all(); +int free_queue(struct s_queue *queue); + +/* queue_member */ +struct s_queue_member *create_queue_member(); +int insert_queue_member(const char *queue_name, struct s_queue_member *member); +int count_queue_member(const char *queue_name); +struct s_queue_member *get_queue_member_id(const char *queue_name, size_t id); +struct s_queue_member *get_queue_member(const char *queue_name, const char *member_name); +int remove_queue_member_all(const char *queue_name); +int remove_queue_member(const char *queue_name, const char *member_name); +int free_queue_member(struct s_queue_member *member); + + +#endif diff --git a/src/log/log.c b/src/log/log.c new file mode 100644 index 0000000..8996157 --- /dev/null +++ b/src/log/log.c @@ -0,0 +1,309 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* Essa função deve ser sempre chamada por macros + * line e file é linha e arquivo do fonte */ +int system_log(int flag, const char * str, ...){ + + if(get_privileges() == -1){ + return -1; + } + + va_list arglist; + char str_log[2000]; + va_start(arglist, str); + vsnprintf(str_log, 2000, str, arglist); + + char flag_log[70] = {0}; + switch(flag){ + + case FLAG_EMERG: + strcpy(flag_log, "EMERG\0"); + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, str_log); + closelog(); + break; + + case FLAG_WARNING: + strcpy(flag_log, "WARNING\0"); + break; + + case FLAG_ALERT: + strcpy(flag_log, "ALERT\0"); + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, str_log); + closelog(); + break; + + case FLAG_CRIT: + strcpy(flag_log, "CRITICAL\0"); + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_CRIT, str_log); + closelog(); + break; + + case FLAG_ERR: + strcpy(flag_log, "ERROR\0"); + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_ERR, str_log); + closelog(); + break; + break; + + case FLAG_NOTICE: + strcpy(flag_log, "NOTICE\0"); + break; + + case FLAG_INFO: + strcpy(flag_log, "INFO\0"); + break; + +#ifdef DEBUG + case FLAG_DEBUG: + strcpy(flag_log, "DEBUG\0"); + break; +#endif + + default: + sprintf(flag_log, "LOG"); + break; + } + + va_end(arglist); + + char str_time[100]; // str do tempo + time_t s_time = time(NULL); + struct tm *stime; + + stime = localtime(&s_time); + if (stime == NULL) + return -1; + + /* str de data formatada */ + strftime(str_time, 100, "%a %d/%m/%Y %X", stime); + + + /* verifica os diretórios e os arquivos log antes de granvar */ + if(verify_dir_log() == -1){ + remove_privileges(); + return -1; + } + + int descriptor = open(PATH_LOG FILE_LOG, O_WRONLY|O_APPEND, 0740); + if(descriptor == -1 ){ + _NOTICE("Warning: invalid file descriptor -1 in syscall close()1"); + remove_privileges(); + return -1; + } + + /* formatando a string que será grava no log */ + size_t len_str = strlen_n(flag_log) + strlen_n(str_time) + strlen_n(PATH_LOG FILE_LOG) + 10; + char formated_log[len_str]; + sprintf(formated_log, "[%s] %s \"%s\"", str_time, flag_log, str_log); + + if(write(descriptor, formated_log, strlen_n(formated_log)) == -1){ + switch(errno){ + case EBADF: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Não foi1 possível escrever o log %s%s", PATH_LOG, FILE_LOG); + closelog(); + close(descriptor); + remove_privileges(); + return -1; + break; + + case EFBIG: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Não foi2 possível escrever o log %s%s", PATH_LOG, FILE_LOG); + closelog(); + close(descriptor); + remove_privileges(); + return -1; + break; + + case EINTR: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Não foi3 possível escrever o log %s%s", PATH_LOG, FILE_LOG); + closelog(); + close(descriptor); + remove_privileges(); + return -1; + break; + + default: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Não foi4 possível escrever o log %s%s", PATH_LOG, FILE_LOG); + closelog(); + close(descriptor); + remove_privileges(); + return -1; + break; + } + } + + write(descriptor, "\n", 1); + close(descriptor); + remove_privileges(); + + return 0; +} + + +int verify_dir_log(){ + + int check_syscall = open(PATH_LOG, O_DIRECTORY, MODE_PATH_LOG); + if(check_syscall == -1){ + switch(errno){ + case EACCES: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Sem permissão5 ao Log: %s", PATH_LOG); + closelog(); + return -1; + break; + + case ENOENT: + if(mkdir(PATH_LOG, MODE_PATH_LOG) == -1){ + switch(errno){ + case EACCES: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Sem permissão6 para criar log: %s", PATH_LOG); + closelog(); + return -1; + break; + case ENOMEM: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Out 7of memory"); + closelog(); + return -1; + break; + default: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Não foi8 possível criar LOG"); + closelog(); + return -1; + break; + } + } + break; + + case ENOMEM: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Out of9 memory"); + closelog(); + return -1; + break; + + case ENOTDIR: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Não é um diretór0io %s", PATH_LOG); + closelog(); + return -1; + break; + + default: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Não fo11i possível acessar o log"); + closelog(); + return -1; + break; + } + } + close(check_syscall); + + if(faccessat(0, PATH_LOG FILE_LOG, F_OK, AT_EACCESS ) == -1){ + switch(errno){ + case ENOTDIR: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Proble22ma com o caminho: %s%s", PATH_LOG, FILE_LOG); + closelog(); + return -1; + break; + case EROFS: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Sem prermis33são de escrita: %s%s", PATH_LOG, FILE_LOG); + closelog(); + return -1; + break; + case ENOMEM: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Não há 44 disponível no sistema operacional para escrever %s%s", PATH_LOG, FILE_LOG); + closelog(); + return -1; + break; + case ENOENT: + int descriptor = creat(PATH_LOG FILE_LOG, MODE_FILE_LOG ); + + if( descriptor == -1){ + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Não foi possível criar log %s%s", PATH_LOG, FILE_LOG); + closelog(); + return -1; + } + else{ + close(descriptor); + } + break; + default: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Não foi po66ssível escrever log em: %s%s", PATH_LOG, FILE_LOG); + closelog(); + return -1; + break; + } + } + + if(faccessat(0, PATH_LOG FILE_LOG, W_OK, AT_EACCESS ) == -1){ + switch(errno){ + case EACCES: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Sem permis77são de escrita: %s%s", PATH_LOG, FILE_LOG); + closelog(); + return -1; + break; + case EROFS: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "Sem permis88são de escrita: %s%s", PATH_LOG, FILE_LOG); + closelog(); + return -1; + break; + default: + openlog(NAME_PROGRAM, LOG_PID, LOG_USER); + syslog(LOG_EMERG, "sem permi99ssão de escrita: %s%s", PATH_LOG, FILE_LOG); + closelog(); + return -1; + break; + } + } + + return 0; +} + + +char *strftimecurr(char *buf, size_t len, char *format){ + + struct tm *tm; + size_t bytes; + + time_t time_current = time(NULL); + tm = localtime(&time_current); + + if( tm == NULL ){ + return NULL; + } + + bytes = strftime(buf, len, (!format ? "%d/%m/%Y %X" : format), tm); + + return (bytes == 0 ? NULL : buf); +} + diff --git a/src/log/log.h b/src/log/log.h new file mode 100644 index 0000000..596c2a0 --- /dev/null +++ b/src/log/log.h @@ -0,0 +1,109 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + */ + +#ifndef LOG_H +#define LOG_H 1 + +//#define EEVNEX 1 /* evento não existe */ +//#define EEVERR 2 /* evento não foi formatado errado */ +//#define EDBCONN 3 /* Sem connexão com o banco de dados */ +//#define EAMICONN 4 /* Sem conexxão com o AMI do asterisk */ +//#define ENAMIBR 5 /* Conexão caiu com o AMI */ +//#define EBDBR 6 /* Conexão caiu com o banco de dados */ +//#define EACWR 7 /* action sem resposta */ +//#define EACRERR 8 /* response da action é um erro */ +//#define EAMIREST 9 /* asterisk pode ter sido reiniciado */ +//#define ENWNET 10 /* Sem conexão com a internet */ +//#define EAMILERR 11 /* Erro login AMI */ +//#define EBDLERR 12 /* Erro login no banco de dados */ +//#define EWMEM 13 /* Sem memória disponível */ + +#define EEVNEX "Evento não identificado" /* evento não existe */ +#define EEVERR "Evento está fora de padrão" /* evento não foi formatado errado */ +#define EDBCONN "Sem conexão com o banco de dados" /* Sem connexão com o banco de dados */ +#define EAMICONN "Sem conexão com o AMI" /* Sem conexxão com o AMI do asterisk */ +#define ENAMIBR "Perdeu a conexão com AMI" /* Conexão caiu com o AMI */ +#define EBDBR "Perdeu a conexão com Banco de dados" /* Conexão caiu com o banco de dados */ +#define EACWR "Não foi recebido response para action" /* action sem resposta */ +#define EACRERR "Response da action é um erro" /* response da action é um erro */ +#define EAMIREST "AMI foi reiniciado" /* asterisk pode ter sido reiniciado */ +#define ENWNET "Sem conexão" /* Sem conexão com a internet */ +#define EAMILERR "Erro no login AMI" /* Erro login AMI */ +#define EBDLERR "Erro no login Banco de dados" /* Erro login no banco de dados */ +#define EWMEM "Sem memória disponível" /* Sem memória disponível */ + + +#include +#include + +/* + * MACRO USADO PARA OS LOGS + * total + */ + +#define FLAG_EMERG 128 /* */ + +#define FLAG_ALERT 64 /* */ + +#define FLAG_CRIT 32 /* Deve ser visto urgente */ + +#define FLAG_ERR 16 /* Retornou erro (examplo: conexão falha manager ou BD + * O programa não poderá funcionar normalmente*/ + +#define FLAG_WARNING 8 /* Aconteceu algo não previsto */ + +#define FLAG_NOTICE 4 /* Informação frequente, porém é necessário + * informar com destaque */ + +#define FLAG_INFO 2 /* Informação frequente da aplicação */ + +#ifdef DEBUG + #define FLAG_DEBUG 1 /* Informação de debug, isso será disponível + * se ativar flag na compilação */ +#endif + + +#define PATH_LOG "/var/log/ami_simples/" +#define MODE_PATH_LOG S_IRWXU| S_IRGRP + +#define FILE_LOG "ami_simplesip.log" +#define MODE_FILE_LOG S_IRUSR|S_IWUSR|S_IRGRP + +#define FLAG_LOG_FILE O_APPEND + +#ifdef DEBUG + #define _DEBUG(str) system_log(FLAG_DEBUG, str, __LINE__, __FILE__) +#endif + +#define _ALERT(a, ...) system_log( FLAG_ALERT, a, ##__VA_ARGS__) +#define _INFO(a, ...) system_log( FLAG_INFO, a, ##__VA_ARGS__) +#define _NOTICE(a, ...) system_log( FLAG_NOTICE, a, ##__VA_ARGS__) +#define _WARNING(a, ...) system_log( FLAG_WARNING, a, ##__VA_ARGS__) +#define _ERROR(a, ...) system_log( FLAG_ERR, a, ##__VA_ARGS__) +#define _CRIT(a, ...) system_log( FLAG_CRIT, a, ##__VA_ARGS__) +#define _EMERG(a, ...) system_log( FLAG_EMERG, a, ##__VA_ARGS__) +#define _LOG(a, ...) system_log( 0, a, ##__VA_ARGS__) + +int system_log(int flag, const char * str, ...); + +int verify_dir_log(); + +char *strftimecurr(char *buf, size_t len, char *format); + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..fa9f7e9 --- /dev/null +++ b/src/main.c @@ -0,0 +1,441 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define CLIENT_MANAGER_CONF "/etc/client_ami.conf" // arquivo de configuração desse software +#define NEW_LINE '\n' + + + +/* Ação são enviadoas de início */ +int init_info_manager(){ + + return 0; +} + + +#define FIELD_STR 1 // parse_arguments retirar string +#define FIELD_INTEGER 2 // parse_arguments retirar inteiros + +#define MAX_STR_FILE 1001 + +// +// Comprimento máximo nos valores do /etc/client_ami.conf +// +#define LEN_FIELD_IP 4 +#define LEN_FIELD_PORT 5 +#define LEN_FIELD_USERNAME 10 +#define LEN_FIELD_SECRET 8 +#define LEN_DATABASE_IP 13 +#define LEN_DATABASE_PORT 14 +#define LEN_DATABASE_NAME 15 +#define LEN_DATABASRE_USERNAME 19 +#define LEN_DATABASE_PASSWORD 19 + + +/*! + * Obter valores do /etc/client_ami.conf + * \param p_str + * \param field + * \return p_str + */ +void *arguments_config_get( char *p_str, int field ){ + + char *value; + char *end_value; + + switch( field ){ + case FIELD_STR: + end_value = strchr( p_str, '\"' ); + if(!end_value){ + _ERROR("sem aspa de fechamento \nline:%d\n", __LINE__); + return NULL; + } + strcpy_s(p_str, &value, end_value); + return value; + break; + + case FIELD_INTEGER: + end_value = strchr( p_str, '\n' ); + if(!end_value){ + _ERROR("linha port não pula linha depois da porta \nline:%d\n", __LINE__); + return NULL; + } + strcpy_s(p_str, &value, end_value); + return value; + break; + } + return p_str; +} + + + +/*! + * Procura tags arquivo config \n + * Caso ache a tag o parse\n + * apenas para rodar ainda não feito\n + * \param smanager principal estrutura + */ +void read_arguments( s_manager *smanager){ + int file; + int index_null; + + char str[MAX_STR_FILE]; + char *p_str = NULL, *p_value = NULL; + + char *str_ip = "ip=\""; + char *str_port = "port="; + char *str_username = "username=\""; + char *str_secret = "secret=\""; + char *str_database_ip = "database_ip=\""; + char *str_database_port = "database_port="; + char *str_database_name = "database_name=\""; + char *str_database_username = "database_username=\""; + char *str_database_password = "database_password=\""; + + + file = open( CLIENT_MANAGER_CONF, O_RDONLY ); + if( file == -1 ){ + _EMERG("Arquivo /etc/client_manager/client_manager.conf não encontrado"); + exit(-1); + } + + index_null = read( file, str, MAX_STR_FILE - 1); + str[index_null] = '\0'; + p_str = str; + + + /* ------------------- * + * -------- ip ------- * + *------------- ------ */ + p_value = strstr(p_str, str_ip); + if(!p_value){ + _ERROR("erro de encontrar ip\nnão use espaços\n"); + } + else{ + if( p_value != p_str && *(p_value - 1) != NEW_LINE ){ + _ERROR("Não use espaço no campo ip\n"); + exit(-1); + } + + } + smanager->config_file.ami_ip = arguments_config_get((p_value + LEN_FIELD_IP), FIELD_STR); + + + /* ------------------- * + * ------- port ------ * + *------------- ------ */ + p_value = strstr(p_str, str_port); + if(!p_value){ + _ERROR("erro de encontrar porta\nnão use espaços\n"); + exit(-1); + } + else{ + if( p_value != p_str && *(p_value - 1) != NEW_LINE ){ + _ERROR("Não use espaço no campo porta\n"); + exit(-1); + } + + } + smanager->config_file.ami_port = atoi(arguments_config_get((p_value + LEN_FIELD_PORT), FIELD_INTEGER)); + + + /* ------------------- * + * ----- username ---- * + *------------- ------ */ + p_value = strstr(p_str, str_username); + if(!p_value){ + _ERROR("erro de encontrar username\nnão use espaços\n"); + exit(-1); + } + else{ + if( p_value != p_str && *(p_value - 1) != NEW_LINE ){ + _ERROR("Não use espaço no campo username\n"); + exit(-1); + } + + } + smanager->config_file.ami_username = arguments_config_get((p_value + LEN_FIELD_USERNAME), FIELD_STR); + + + /* ------------------- * + * ----- password ---- * + *-------------------- */ + p_value = strstr(p_str, str_secret); + if(!p_value){ + _ERROR("erro de encontrar secret\nnão use espaços\n"); + exit(-1); + } + else{ + if( p_value != p_str && *(p_value - 1) != NEW_LINE ){ + _ERROR("Não use espaço no campo secret\n"); + exit(-1); + } + + } + smanager->config_file.ami_password = arguments_config_get((p_value + LEN_FIELD_SECRET), FIELD_STR); + + + /* ------------------- * + * --- database ip --- * + *------------- ------ */ + p_value = strstr(p_str, str_database_ip); + if(!p_value){ + _ERROR("erro de encontrar dbip\nnão use espaços\n"); + exit(-1); + } + else{ + if( p_value != p_str && *(p_value - 1) != NEW_LINE ){ + _ERROR("Não use espaço no campo secret\n"); + exit(-1); + } + + } + smanager->config_file.database_ip = arguments_config_get((p_value + LEN_DATABASE_IP), FIELD_STR); + + + /* ---------------* + * database porta * + -------------- */ + p_value = strstr(p_str, str_database_port); + if(!p_value){ + _ERROR("erro de encontrar database_port\nnão use espaços\n"); + exit(-1); + } + else{ + if( p_value != p_str && *(p_value - 1) != NEW_LINE ){ + _ERROR("Não use espaço no campo secret\n"); + exit(-1); + } + + } + smanager->config_file.database_port = atoi(arguments_config_get((p_value + LEN_DATABASE_PORT), FIELD_INTEGER)); + + + /* ------------------- * + * -- database name -- * + *------------- ------ */ + p_value = strstr(p_str, str_database_name); + if(!p_value){ + _ERROR("erro de encontrar dbport\nnão use espaços\n"); + exit(-1); + } + else{ + if( p_value != p_str && *(p_value - 1) != NEW_LINE ){ + _ERROR("Não use espaço no campo secret\n"); + exit(-1); + } + + } + smanager->config_file.database_name = arguments_config_get((p_value + LEN_DATABASE_NAME), FIELD_STR); + + + /*---------------------* + *- database username -* + *------------- -------*/ + p_value = strstr(p_str, str_database_username); + if(!p_value){ + _ERROR("erro de encontrar dsfs\nnão use espaços\n"); + exit(-1); + } + else{ + if( p_value != p_str && *(p_value - 1) != NEW_LINE ){ + _ERROR("Não use espaço no campo secret\n"); + exit(-1); + } + + } + smanager->config_file.database_username = arguments_config_get((p_value + LEN_DATABASRE_USERNAME), FIELD_STR); + + + /* ------------------- * + * database password * + *------------- ------ */ + p_value = strstr(p_str, str_database_password); + if(!p_value){ + _ERROR("erro de encontrar secret\nnão use espaços\n"); + exit(-1); + } + else{ + if( p_value != p_str && *(p_value - 1) != NEW_LINE ){ + _ERROR("Não use espaço no campo secret\n"); + exit(-1); + } + + } + smanager->config_file.database_password = arguments_config_get((p_value + LEN_DATABASE_PASSWORD), FIELD_STR); + + + close(file); +} + + +/*! + * O programa rodar em daemon + */ +int init_daemon(int flags){ + + int maxfd, fd; + + switch (fork()) { + case -1: + return -1; + case 0: break; + default: _exit(EXIT_SUCCESS); + }/* processo roda em background */ + + if (setsid() == -1) + return -1; + + + + if (!(flags & BD_NO_UMASK0)) + umask(0); + + if (!(flags & BD_NO_CHDIR)) + chdir("/");/* muda para o diretório root */ + + if (!(flags & BD_NO_CLOSE_FILES)) { /* veja todos os arquivos abertos */ + maxfd = sysconf(_SC_OPEN_MAX); + + if (maxfd == -1) + /* limite é interterminado... */ + maxfd = BD_MAX_CLOSE; + + for (fd = 0; fd < maxfd; fd++) + close(fd); + } + + if (!(flags & BD_NO_REOPEN_STD_FDS)) { + close(STDIN_FILENO); + + /* Reabre para o /dev/null */ + fd = open("/dev/null", O_RDWR); + if (fd != STDIN_FILENO) + /* 'fd' should be 0 */ + return -1; + if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO) + return -1; + if (dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO) + return -1; + } + + return 0; +} + + + +int main (int argc, char **argv){ + char text_date[32]; + strftimecurr(text_date, 32, NULL); + _NOTICE("start software timestamp %s", text_date); + + s_manager *smanager; + pthread_t thread; +// init_daemon(0); + + smanager = alloc( 1, sizeof(s_manager) ); + + /* + * Será para ler o as config e parametros de entrada /etc/client_ami.conf + */ + read_arguments( smanager ); + + smanager->ami = ami_init(); // inicializar biblioteca ami + if(!smanager->ami){ + _CRIT("Não pode calloc - library_ami"); + return ENOMEM; + } + + ami_set_net( smanager->ami, smanager->config_file.ami_ip, smanager->config_file.ami_port ); + ami_set_credentials( smanager->ami, smanager->config_file.ami_username, smanager->config_file.ami_password ); + ami_c( smanager->ami ); + + int attempt = 0; + while( !ami_is_connected(smanager->ami) || !ami_is_logged(smanager->ami) ){ + sleep(1); + + if(attempt == 2){ + _CRIT("Sem Conexão coma AMI"); + } + else if(attempt > 10){ + attempt = 0; + } + attempt++; + } + _NOTICE("conexão com AMI"); + + #include + printf("Conectado\n"); + + /* Essa thread é do banco de dados */ + pthread_create(&thread, NULL, init_thread, (void *)smanager); + + while(1){ + sleep(10); + } + return 0; +} + + +/*! + * Obter privilégios para poder ser root + *\return 0 sucesso, -1 falha + */ +int get_privileges(){ + + if(seteuid(0) == -1){ + return -1; + } + + return 0; +} + + +/*! + * Remover privilégio como root do programa + * \return 0 sucesso, -1 falha + */ +int remove_privileges(){ + + int uid = getuid(); + + if(seteuid(uid) == -1){ + return -1; + } + + return 0; + +} + diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..f649f00 --- /dev/null +++ b/src/main.h @@ -0,0 +1,165 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + */ + +#ifndef MAIN +#define MAIN 1 + +#define NAME_PROGRAM "ami_simplesip" +#include + +#include +#include +#include + + +#define SUCCESS_RETURN 0 +#define FAIL_RETURN -1 + + +#define LOOP_ON 1 +#define LOOP_OFF 0 + + +/* Bit-mask values for 'flags' argument of becomeDaemon() */ +#define BD_NO_CHDIR 01 /* Don't chdir("/") */ +#define BD_NO_CLOSE_FILES 02 /* Don't close all open files */ +#define BD_NO_REOPEN_STD_FDS 04 /* Don't reopen stdin, stdout, and + stderr to /dev/null */ + +#define BD_NO_UMASK0 010 /* Don't do a umask(0) */ +#define BD_MAX_CLOSE 8192 /* Maximum file descriptors to close if + sysconf(_SC_OPEN_MAX) is indeterminate */ + +/* tipo de tecnologia */ +enum { TECH_SIP = 0, TECH_PJSIP, TECH_IAX2 }; + +/* tipo do canal */ +enum { CHANNEL_LOCAL = 0, CHANNEL_NLOCAL }; + + +/* status da ligação - dial status + DIALSTATUS - This is the status of the call + CHANUNAVAIL -- Ou o peer discado existe, mas não está acessível no momento, por exemplo, endpoint não está registrado ou + foi feita uma tentativa de chamar um local inexistente, por exemplo nome de host DNS inexistente. + CONGESTION --- Canal ou congestionamento de comutação ocorreu ao rotear a chamada. Isso pode ocorrer se houver uma resposta + lenta ou nenhuma resposta da extremidade remota. + NOANSWER ----- A parte chamada não atendeu. + BUSY --------- A parte chamada estava ocupada ou indicou um status de ocupado. Observe que alguns dispositivos SIP + responderão com 486 Ocupado se seus modos Não perturbe estiverem ativos. Nesse caso, você pode usar + DEVICE_STATUS para verificar se o endpoint está realmente em uso, se necessário. + ANSWER ------- A chamada foi atendida. Qualquer outro resultado indica implicitamente que a chamada não foi atendida. + CANCEL ------- A discagem foi cancelada antes que a chamada fosse atendida ou alcançasse algum outro evento de encerramento. + DONTCALL ----- Para os Modos de Privacidade e Triagem. Será definido se a parte chamada optar por enviar a parte chamadora + para o script 'Go Away'. + TORTURE ------ Para os Modos de Privacidade e Triagem. Será definido se o chamador optar por enviar o chamador para o + script 'tortura'. + INVALIDARGS -- Falha na discagem devido a sintaxe inválida. */ +enum { CHANUNAVAIL = 0, CONGESTION, NOANSWER, BUSY, ANSWER, CANCEL, DONTCALL, TORTURE, INVALIDARGS }; + + +enum { RECEIVE_CALL = 0, SEND_CALL }; + + + +/* + * Essa estrutura será usado para pegar os + * argumentos do arquivo /etc/simplesip_manager/client_manager.conf + */ +struct s_config_file { + char *ami_ip; + int ami_port; + char *ami_username; + char *ami_password; + char *database_ip; + int database_port; + char *database_name; + char *database_username; + char *database_password; +}; + + + +typedef struct s_thread { + pthread_t database; + pthread_t main; +} THREAD; + + + +typedef struct s_lactionid { + char actionid_value[128]; + char name_action[64]; + char extra[32]; + time_t use_time; + int restart; + struct s_lactionid *next; +} s_actionid; + + + + +typedef struct { + struct s_config_file config_file; + THREAD thread; + s_actionid *list_actionid; + AMI *ami; +} s_manager; + + + +struct s_device_queue { + char *name; + int ispause; + int local; + char *context; +}; + + + +struct s_devicestate { + char *technology; + char *ramal; + char *name; + int isqueue; + struct s_device_queue *queue; +}; + + + +enum { + SIP = 1, + IAX2 +}; + + + +int init_info_manager(); +int verify_null(char *_str); +int verify_time(int seconds); +void *arguments_config_get(char *p_str, int field); + +int system_log(int flag, const char * str, ...); + +int init_daemon(int flag); +int verify_dir_log(); + +int get_privileges(); +int remove_privileges(); + +#endif diff --git a/src/methods_actions.c b/src/methods_actions.c new file mode 100644 index 0000000..918dcb9 --- /dev/null +++ b/src/methods_actions.c @@ -0,0 +1,157 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + */ + + +#include +#include +#include +#include +#include +#include +#include + + +#define LEN_TIMESTAMP 20 +#define LEN_IDRESPONSE 20 + + +int create_ExtensionStateList(s_manager *smanager, void *args){ + struct ss_action *ExtensionStateList = NULL; + + ExtensionStateList = ami_create_new_action(0); + if(!ExtensionStateList){ + return -1; + } + + s_actionid *action_id = actionid_create(); + if(!action_id){ + ami_destroy_action(ExtensionStateList); + return -1; + } + + set_value_actionid( action_id, "ExtensionStateList", ExtensionStateList->args[0].value, NULL, 1 ); + strcpy(ExtensionStateList->name_action, "ExtensionStateList"); + + if(ami_send_action(smanager->ami, ExtensionStateList) == -1){ + actionid_free( action_id ); + return -1; + } + + actionid_add( &smanager->list_actionid, action_id ); + + ami_destroy_action(ExtensionStateList); + + return 0; +} + + +int create_SIPpeers( s_manager *smanager, void *args ){ + + struct ss_action *SIPpeers = NULL; + + SIPpeers = ami_create_new_action(0); + if(!SIPpeers){ + return -1; + } + + s_actionid *action_id = actionid_create(); + if(!action_id){ + ami_destroy_action(SIPpeers); + return -1; + } + + set_value_actionid( action_id, "SIPpeers", SIPpeers->args[0].value, NULL, 1 ); + strcpy(SIPpeers->name_action, "SIPpeers"); + + if(ami_send_action(smanager->ami, SIPpeers) == -1){ + actionid_free( action_id ); + return -1; + } + + actionid_add( &smanager->list_actionid, action_id ); + + ami_destroy_action(SIPpeers); + + return 0; +} + + +int create_QueueStatus(s_manager *smanager, void *args){ + + struct ss_action *SIPpeers = NULL; + + SIPpeers = ami_create_new_action(0); + if(!SIPpeers){ + return -1; + } + + s_actionid *action_id = actionid_create(); + if(!action_id){ + ami_destroy_action(SIPpeers); + return -1; + } + + set_value_actionid( action_id, "QueueStatus", SIPpeers->args[0].value, NULL, 1 ); + strcpy(SIPpeers->name_action, "QueueStatus"); + + if(ami_send_action(smanager->ami, SIPpeers) == -1){ + actionid_free( action_id ); + return -1; + } + + actionid_add( &smanager->list_actionid, action_id ); + + ami_destroy_action(SIPpeers); + + return 0; +} + + + +int create_Agents( s_manager *smanager, void *args ){ + + struct ss_action *SIPpeers = NULL; + + SIPpeers = ami_create_new_action(0); + if(!SIPpeers){ + return -1; + } + + s_actionid *action_id = actionid_create(); + if(!action_id){ + ami_destroy_action(SIPpeers); + return -1; + } + + set_value_actionid( action_id, "Agents", SIPpeers->args[0].value, NULL, 1 ); + strcpy( SIPpeers->name_action, "Agents" ); + + if(ami_send_action(smanager->ami, SIPpeers) == -1){ + actionid_free( action_id ); + return -1; + } + + actionid_add( &smanager->list_actionid, action_id ); + + ami_destroy_action(SIPpeers); + + return 0; +} + + diff --git a/src/methods_actions.h b/src/methods_actions.h new file mode 100644 index 0000000..965d9d7 --- /dev/null +++ b/src/methods_actions.h @@ -0,0 +1,38 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + * + * + * + * documentation: https://wiki.asterisk.org/wiki/download/attachments/19005471/asterisk-17-reference.pdf?version=1&modificationdate=1582306810980&api=v2 + * + */ + +#ifndef METHODS_ACTIONS +#define METHODS_ACTIONS 1 + +#include + +int create_ExtensionStateList(s_manager *smanager, void *args); +int create_SIPpeers( s_manager *smanager, void *args ); +int create_QueueStatus(s_manager *smanager, void *args); +int create_Agents( s_manager *smanager, void *args ); + + + + +#endif diff --git a/src/parse_actions.c b/src/parse_actions.c new file mode 100644 index 0000000..cb249d9 --- /dev/null +++ b/src/parse_actions.c @@ -0,0 +1,267 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define LEN_TIMESTAMP 20 +#define LEN_STR_ACTION 9 +#define LEN_STR_RESPONSE 9 + + +/* + * NOTA + * + * Manager manda message, porém essas mensagens deve ser obtidas no + * código fonte. A documentação pode ter falhas + */ + + +/* + * Estructura dos métodos actions + */ +static s_methods_actions _methods_actions[] = +{ + /* id, nome, corpo da action, tem argumentos, função para enviar, função para analisa resposta para bd */ + + { Action_SIPpeers, "SIPpeers", create_SIPpeers, NULL}, + { Action_ExtensionStateList, "ExtensionStateList", create_ExtensionStateList, NULL }, + { Action_QueueStatus, "QueueStatus", create_QueueStatus, NULL }, + { Action_Agents, "Agents", create_Agents, NULL }, + + {0} +}; + + +struct s_timeout_actiond _timeout_actiond[] = +{ + { "ExtensionStateList", 60, 0 }, + { "DeviceStateList", 60, 0 }, + { "IAXpeerlist", 60, 0 }, + { "SIPpeers", 60, 1 }, + { "QueueStatus", 600, 1 }, + { "Agents", 600, 1}, + + {0} +} ; + + + +int verify_action( s_manager *smanager ){ + + s_actionid **actionid = &smanager->list_actionid; + + while( *actionid ){ + RESPONSE *response = NULL; + EVENT *event = NULL; + + response = ami_get_actions_response( smanager->ami, (*actionid)->actionid_value ); + if( response ) { + redirect_response( response, smanager ); + ami_destroy_response( smanager->ami, response ); + } + + event = ami_get_actions_event( smanager->ami, (*actionid)->actionid_value ); + if( event ) { + redirect_event( event, smanager ); + ami_destroy_events( smanager->ami, event ); + } + + actionid = &(*actionid)->next; + } + + timeout_actionid( smanager ); + + return 0; +} + + +int timeout_actionid( s_manager *smanager ){ + + if(!smanager->list_actionid){ + return 0; + } + + size_t len = ( sizeof(_timeout_actiond) / sizeof(struct s_timeout_actiond )) - 1; + + int i; + for(i = 0; i < len; i++){ + s_actionid *actionid = smanager->list_actionid; + + while(actionid){ + + if( !strcasecmp( _timeout_actiond[i].method_action, actionid->name_action ) ){ + time_t Elapsed_time = time(NULL) - actionid->use_time; + if( Elapsed_time > _timeout_actiond[i].timeout ){ // tempo esgotado + ami_destroy_action_return(smanager->ami, actionid->actionid_value); + actionid_remove( &smanager->list_actionid, actionid ); + if(_timeout_actiond[i].restart == 1){ + create_action(_timeout_actiond[i].method_action, smanager, NULL); + } + break; + } + } + + actionid = actionid->next; + } + } + + return 0; +} + + +int create_action(const char *action_name, s_manager *smanager, void *arg){ + + size_t len = (sizeof(_methods_actions) / sizeof(struct s_methods_actions)) - 1; + + int i; + for (i = 0; i < len; i++){ + if( !strcasecmp( action_name, _methods_actions[i].method_name ) ){ + if(_methods_actions[i].ptr_func == NULL){continue;} + return _methods_actions[i].ptr_func(smanager, arg); + } + } + + return -1; +} + + +int redirect_response( RESPONSE *response, s_manager *smanager){ + + s_actionid *actionid = smanager->list_actionid; + size_t len = (sizeof(_methods_actions) / sizeof(struct s_methods_actions)) - 1; + + int i; + for (i = 0; i < len; i++){ + if( !strcasecmp( actionid->name_action, _methods_actions[i].method_name ) ){ + if(_methods_actions[i].ptr_func_to_response == NULL){continue;} + + return _methods_actions[i].ptr_func_to_response(response, smanager); + } + } + + return 0; +} + + + +int actionid_remove( s_actionid **list_actionid, s_actionid *actionid ){ + + while(*list_actionid){ + if(*list_actionid == actionid){ + s_actionid *actionid = *list_actionid; + *list_actionid = actionid->next; + actionid_free(actionid); + + return 0; + } + + list_actionid = &(*list_actionid)->next; + } + + return 0; +} + + +int exist_actionid(s_actionid **list_actionid, const char *actionid){ + + while(*list_actionid){ + if(!strcmp((*list_actionid)->actionid_value, actionid)){ + return 0; + } + + list_actionid = &(*list_actionid)->next; + } + + return -1; +} + + +void actionid_free( s_actionid *actionid ){ + + if( !actionid ){ + return; + } + + free(actionid); + + return; +} + + + +int actionid_add( s_actionid **list_actionid, s_actionid *actionid ){ + + while( *list_actionid ){ + list_actionid = &(*list_actionid)->next; + } + + *list_actionid = actionid; + + return 0; +} + + + +s_actionid *actionid_create(){ + + s_actionid *actionid = calloc(1, sizeof(s_actionid)); + if(!actionid){ + return NULL; + } + + return actionid; + +} + + +int set_value_actionid( s_actionid *actionid, const char *method_name, const char *actionid_value, const char *extra_value, int restart ){ + + strcpy( actionid->name_action, method_name ); + + strcpy( actionid->actionid_value, actionid_value ); + + if( extra_value ){ + strcpy( actionid->extra, extra_value ); + } + + actionid->restart = restart; + + actionid->use_time = time(NULL); + + return 0; +} + diff --git a/src/parse_actions.h b/src/parse_actions.h new file mode 100644 index 0000000..36667a6 --- /dev/null +++ b/src/parse_actions.h @@ -0,0 +1,92 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + * + */ + +#ifndef METHOD_ACTION +#define METHOD_ACTION 1 + +#include +#include "main.h" + + +/* + * Defini inteiros para cada method actions + * + */ +enum { + + Action_ExtensionStateList, + Action_SIPpeers, + Action_QueueStatus, + Action_Agents + +}; + +/* GRUPO dos comandos*/ +struct s_group_command { + int action; +}; + +/* parametro para search_actionid */ +enum { + Default = 0, /* todos */ + G_COMMAND +}; + +typedef int CALL_ACTION( s_manager * , void * ); +typedef int CALL_TO_RESPONSE( RESPONSE *, s_manager * ); + +/* + * Estrutura dos métodos Action + * é inicializado no method_action.c + * + */ +struct s_methods_actions { + int id; + const char *method_name; + CALL_ACTION *ptr_func; // send action + CALL_TO_RESPONSE *ptr_func_to_response; //analisar response +}; + + +struct s_timeout_actiond { + char *method_action; + int timeout; + int restart; +}; + + +typedef struct s_methods_actions s_methods_actions; +typedef struct s_generic_method s_actions; +typedef struct s_generic_method_arg s_args; +typedef struct s_generic_method s_responses; + +int redirect_response( RESPONSE *response, s_manager *smanager ); +int verify_action( s_manager *smanager ); +int actionid_remove( s_actionid **list_actionid, s_actionid *actionid ); +void actionid_free( s_actionid *actionid ); +int actionid_add( s_actionid **list_actionid, s_actionid *actionid ); +s_actionid *actionid_create(); +int timeout_actionid( s_manager *smanager ); +int exist_actionid(s_actionid **list_actionid, const char *actionid); +int set_value_actionid( s_actionid *actionid, const char *method_name, const char *actionid_value, const char *extra_value, int restart ); +int create_action(const char *action_name, s_manager *smanager, void *arg); + + +#endif diff --git a/src/parse_events.c b/src/parse_events.c new file mode 100644 index 0000000..63e2b6b --- /dev/null +++ b/src/parse_events.c @@ -0,0 +1,92 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + */ + + +#define STR_EVENT "event:" +#define LEN_STR_EVENT 6 + +#define FINAL_METHOD "\r\n\r\n" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include + + +/* estrutura que conecta nome do evento com a função */ +static s_methods_events _methods_events[] = +{ + { Event_ExtensionStatus, "ExtensionStatus", parse_event_extensionstatus }, + { Event_PeerEntry, "PeerEntry", parse_event_peerentry }, + { Event_PeerlistComplete, "PeerlistComplete", parse_event_peerlistcomplete }, + { Event_PeerStatus, "PeerStatus", parse_event_peerstatus }, + { Event_QueueParams, "QueueParams", parse_event_queueparams }, +// { Event_QueueMember, "QueueMember", parse_event_queuemember }, +// { Event_Agents, "Agents", parse_event_agents }, + + {0} +}; + + + +int verify_event( s_manager *smanager ){ + + EVENT *event; + + event = ami_get_events(smanager->ami); + if( event ){ + redirect_event( event, smanager ); + } + + return 0; +} + + + +int redirect_event( EVENT *event, s_manager *smanager ){ + + while( event ){ + int len = ( sizeof( _methods_events ) / sizeof( s_methods_events ) - 1 ); + int x; + for( x = 0; x < len; x++ ){ + if( !strcasecmp( _methods_events[x].method_name, event->event ) ){ + _methods_events[x].ptr_func(event, smanager); + } + } + + event = event->next; + } + + return 0; +} + + diff --git a/src/parse_events.h b/src/parse_events.h new file mode 100644 index 0000000..1d2d4cc --- /dev/null +++ b/src/parse_events.h @@ -0,0 +1,69 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + */ + + +#ifndef METHOD_EVENTS +#define METHOD_EVENTS 1 + +#include +#include +#include + +/* + * Defini inteiros para cada method actions + * + */ +enum { + Event_ExtensionStatus, + Event_PeerEntry, + Event_PeerlistComplete, + Event_PeerStatus, + Event_QueueParams, + Event_QueueMember, + Event_Agents +}; + + +typedef int CALL_EVENT( EVENT *, s_manager * ); + + +/* + * Estrutura dos métodos Action + * é inicializado no method_action.c + * + */ +typedef struct { + int id; + char *method_name; + CALL_EVENT *ptr_func; +}s_methods_events; + +typedef struct s_generic_method s_events; +typedef struct s_generic_method_arg s_event_args; + + +int redirect_event( EVENT *event, s_manager *smanager ); +int verify_event( s_manager *smanager ); +size_t strlen_event(const char *str, char *p_end, int use_char); +void strcpy_s(const char *str, char **event_name, char *p_end); + + + + +#endif diff --git a/src/peer/peer.c b/src/peer/peer.c new file mode 100644 index 0000000..347a97d --- /dev/null +++ b/src/peer/peer.c @@ -0,0 +1,436 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* + * field peer_callerid_number + * peer_callerid_name (mesmo que peer_callerid_number) + * type + * peer_trunk + */ +int parse_event_extensionstatus(EVENT *event, s_manager *smanager){ + + /* + * Event: ExtensionStatus + * Exten: + * Context: + * Hint: + * Status: + * StatusText: + */ + + /* + * Status - Numerical value of the extension status. Extension status is determined by the combined device state of all items contained in the hint. + *** -2 - The extension was removed from the dialplan. + *** -1 - The extension's hint was removed from the dialplan. + *** 0 - Idle - Related device(s) are in an idle state. + *** 1 - InUse - Related device(s) are in active calls but may take more calls. + *** 2 - Busy - Related device(s) are in active calls and may not take any more calls. + *** 4 - Unavailable - Related device(s) are not reachable. + *** 8 - Ringing - Related device(s) are currently ringing. + *** 9 - InUse&Ringing - Related device(s) are currently ringing and in active calls. + *** 16 - Hold - Related device(s) are currently on hold. + *** 17 - InUse&Hold - Related device(s) are currently on hold and in active calls. + * + * StatusText - Text representation of Status. + *** Idle + *** InUse + *** Busy + *** Unavailable + *** Ringing + *** InUse&Ringing + *** Hold + *** InUse&Hold + *** Unknown - Status does not match any of the above values. + */ + + + const char *exten, *status, *status_text; + int i_status, peer_trunk; + + exten = ami_get_value( smanager->ami, event->args, "Exten" ); + if( !exten ) goto fail; + + { // analisar se é um tronco + if( ami_has_key( smanager->ami, event->args, "ActionID" ) == 0){ + peer_trunk = 0; + } + else{ + struct s_peer *peer = get_peer(exten); + if(peer){ + peer_trunk = peer->trunk; + } + else{ + /* aqui deve ter uma action para atualizar quem sçao troncos */ + create_action("SIPpeers", smanager, NULL); + peer_trunk = 1; + } + } + } + + status = ami_get_value( smanager->ami, event->args, "Status"); + if(!status) { i_status = 0; } else{ i_status = atoi(status); } + + status_text = ami_get_value( smanager->ami, event->args, "StatusText"); + if(!status_text) { goto fail; } + + + {/* banco de dados */ + struct s_table_peer *table_peer; + + table_peer = get_extensionstatus_db( Select_extensionstatus, exten ); + if( table_peer ){ + + if( i_status == -2 || i_status == -1 ){ + /* delete row no banco + * The extension was removed from the dialplan. */ + delete_extensionstatus_db(delete_extensionstatus, table_peer->id); + + struct s_peer *peer = get_peer(exten); + if(peer){ + remove_peer(peer); + } + + free_table_peer(&table_peer); + goto fail; + } + + else if( (table_peer->trunk != peer_trunk) || + table_peer->exten_status != i_status || + strcmp_n(table_peer->exten_statustext, status_text)) { + + if(update_extensionstatus_db(Update_extensionstatus, i_status, status_text, peer_trunk, table_peer->id) == -1){ + _WARNING("Falha %s %d\n", exten, __LINE__); + } + } + + free_table_peer(&table_peer); + } + else if (i_status != -2) { /* inserir novo extension no banco */ + insert_extensionstatus_db(Insert_extensionstatus, exten, i_status, status_text, peer_trunk); + } + + } + + {/* camada de persistência */ + /* struct s_peer { + char *callerid_number; + char *callerid_name; + char *dynamic; + char *protocol; + char *status; + char *exten_status; + char *exten_statustext; + char *address; + int trunk; + char *time; + struct s_channel *channel; + struct s_variable *variable; + struct s_agent *agent; // trunk não pode agent + struct s_peer *next; + }; + */ + + struct s_peer *peer; + peer = get_peer(exten); + + if( !peer ){ // exten não existe na camada + peer = create_peer();// cria estrutura de dados + newstrncpy(&peer->callerid_number, exten); + peer->exten_status = i_status; + newstrncpy(&peer->exten_statustext, status_text); + peer->trunk = peer_trunk; + insert_peer(peer); //inseri o novo peer para persistência + } + else { + if(i_status != -2 && i_status != -1){ + /* + * update se necessário + */ + if(strcmp_n(peer->exten_statustext, status_text) != 0){ + free(peer->exten_statustext); + newstrncpy(&peer->exten_statustext, status_text); + } + + if(peer->exten_status != i_status){ + peer->exten_status = i_status; + } + + if(peer->trunk != peer_trunk){ + peer->trunk = peer_trunk; + } + } + else { + remove_peer(peer); + } + + } + } + + return 1; + +fail: + return -1; +} + + + +int parse_event_peerentry( EVENT *event, s_manager *smanager ){ + /* + Event: PeerEntry + Channeltype: SIP //tipo + ObjectName: 0800 //nome + ChanObjectType: peer // peer pode ser agent + IPaddress: -none- //ip + IPport: 0 //port + Dynamic: yes // não está padronizado com o sistema web + AutoForcerport: no //NAT + Forcerport: yes //NAT + AutoComedia: no + Comedia: yes + VideoSupport: no //suportar vídeo + TextSupport: no //suportar texto + ACL: yes // permit|deny|acl + Status: UNKNOWN //tempo de ping + RealtimeDevice: no //banco de dados asterisk + Description: + Accountcode: + */ + + const char *channeltype, *objectname, *ipaddress; + struct s_table_peer *table_peer = NULL; // table peer value + int peer_trunk; + + channeltype = ami_get_value(smanager->ami, event->args, "Channeltype"); + if(!channeltype) goto fail; + + objectname = ami_get_value(smanager->ami, event->args, "ObjectName"); + if(!objectname) goto fail; + + ipaddress = ami_get_value(smanager->ami, event->args, "IPaddress"); + if(!atoi(ipaddress)) { + ipaddress = NULL; + } + + struct s_peer *peer = get_peer(objectname); // get camada de persistência|verificar se é um tronco + if( peer ){ + peer_trunk = peer->trunk; + } + else{ + peer_trunk = 1; + } + + { /* Database + * collumns id + * callerid_number - ObjectName + * protocol - channeltype + * address - ip + * trunk - boolean + */ + table_peer = get_peerentry_db( Select_peerentry, objectname ); + + if(table_peer){ + if( (table_peer->trunk != peer_trunk) || + (strcmp_n(table_peer->protocol, channeltype) != 0) || + (strcmp_n(table_peer->address, ipaddress) != 0) ) { + + update_peerentry_db(Update_peerentry, channeltype, ipaddress, peer_trunk, table_peer->id); + } + + free_table_peer(&table_peer); + } + else{ + insert_peerentry_db(Insert_peerentry, objectname, channeltype, ipaddress, peer_trunk); + } + } + + {// Camada de persistência + + if(peer ) { + + if(strcmp_n(peer->callerid_number, objectname) != 0){ + free(peer->callerid_number); + newstrncpy(&peer->callerid_number, objectname); + } + + if(strcmp_n(peer->protocol, channeltype) != 0){ + free(peer->protocol); + newstrncpy(&peer->protocol, channeltype); + } + + if(strcmp_n(peer->address, ipaddress) != 0){ + free(peer->address); + newstrncpy(&peer->address, ipaddress); + } + + peer->trunk = peer_trunk; + } + else{ + peer = create_peer(); + + newstrncpy(&peer->callerid_number, objectname); + + newstrncpy(&peer->protocol, channeltype); + + newstrncpy(&peer->address, ipaddress); + + peer->trunk = peer_trunk; + + insert_peer(peer); + } + } + + return 1; + +fail: + return -1; +} + + + +int parse_event_peerstatus( EVENT *event, s_manager *smanager ){ + + /* + * ChannelType - The channel technology of the peer. + * Peer - The name of the peer (including channel technology). + * PeerStatus - New status of the peer. + * Unknown + * Registered + * Unregistered + * Rejected + * Lagged + * Cause - The reason the status has changed. + * Address - New address of the peer. + * Port - New port for the peer. + * Time - Time it takes to reach the peer and receive a response. + */ + + const char *protocol, *peer, *status, *address, *exten; + + protocol = ami_get_value(smanager->ami, event->args, "Channeltype"); + if(!protocol) goto fail; + + peer = ami_get_value(smanager->ami, event->args, "Peer"); + if(!peer) goto fail; + exten = get_parse_exten(peer); + if(!exten) goto fail; + + status = ami_get_value(smanager->ami, event->args, "PeerStatus"); + + address = ami_get_value(smanager->ami, event->args, "Address"); + + { /* Database + * + * + */ + struct s_table_peer *table_peer = NULL; + table_peer = get_peerstatus_db( Select_peerstatus, exten ); + if(table_peer){ + if( strcmp_n( table_peer->protocol, protocol ) || + strcmp_n( table_peer->address, address ) || + strcmp_n( table_peer->status, status )){ + + update_peerstatus_db( Update_peerstatus, protocol, status, address, table_peer->id ); + } + + free_table_peer(&table_peer); + } + else { + insert_peerstatus_db( Insert_peerstatus, exten, protocol, status, address ); + } + } + + { // frame + struct s_peer *peer; + peer = get_peer(exten); + + if(peer){ + if( strcasecmp_n( "Unregistered", status) || + strcasecmp_n( "Rejected", status)) { + + remove_peer(peer); + } + if( strcmp_n( peer->protocol, protocol )){ + free( peer->protocol ); + newstrncpy( &peer->protocol, protocol ); + } + + if( strcmp_n( peer->address, address ) ){ + free(peer->address); + newstrncpy(&peer->address, address ); + } + + if( strcmp_n( peer->status, status )){ + free( peer->status ); + newstrncpy( &peer->status, status ); + } + } + else{ + peer = create_peer(); + + newstrncpy( &peer->callerid_number, exten); + + newstrncpy( &peer->status, status ); + + newstrncpy( &peer->protocol, protocol); + + newstrncpy( &peer->address, address); + + insert_peer( peer); + } + + + } + + return 1; + +fail: + return -1; + +} + + +int parse_event_peerlistcomplete(EVENT *event, s_manager *smanager){ + + + create_action("ExtensionStateList", smanager, NULL); + + return 1; +} + + +int parse_event_extensionstatelistcomplete( EVENT *event, s_manager *smanager ){ + + return 1; +} + + + + diff --git a/src/peer/peer.h b/src/peer/peer.h new file mode 100644 index 0000000..e8efaec --- /dev/null +++ b/src/peer/peer.h @@ -0,0 +1,83 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + */ + +#ifndef PEER +#define PEER 1 + +#include +#include +#include + + + +struct s_table_peer { + unsigned long long id; + char *callerid_number; + char *callerid_name; + char *dynamic; + char *protocol; + char *status; + int exten_status; + char *exten_statustext; + char *address; + int trunk; + MYSQL_TIME *time; + struct s_table_peer *next; +}; + + +int parse_event_peerentry( EVENT *event, s_manager *smanager ); +int parse_event_peerlistcomplete(EVENT *event, s_manager *smanager); +int parse_event_peerstatus( EVENT *event, s_manager *smanager ); +int ExtensionStateListComplete( EVENT *event, s_manager *smanager ); + + +int add_table_peer(struct s_table_peer **D, struct s_table_peer *next); +int free_table_peer(struct s_table_peer **D); + +/* ExtensionStatus */ +int parse_event_extensionstatus(EVENT *event, s_manager *smanager); +int insert_extensionstatus_db(stmt_t type_stmt, + const char *peer_callerid_number, + int status, + const char *status_text, + int peer_trunk ); +int update_extensionstatus_db( stmt_t type_stmt, + int status, + const char *status_text, + int peer_trunk, + unsigned long long int id ); +struct s_table_peer *get_extensionstatus_db(stmt_t type_stmt, const char *peer_callerid_number); +int delete_extensionstatus_db(stmt_t type_stmt, unsigned long long peer_id); + +int update_peer_dynamic(stmt_t type_stmt, int dynamic, unsigned long long peer_id); + +int update_exten_trunk_db(stmt_t type_stmt, char *exten, unsigned long long peer_trunk); + +/* PeerEntry */ +struct s_table_peer *get_peerentry_db(stmt_t type_stmt, const char *objectname); +int update_peerentry_db(stmt_t type_stmt, const char *protocol, const char *peer_address, int peer_trunk, unsigned long long int peer_id); +int insert_peerentry_db(stmt_t type_stmt, const char *peer_callerid_number, const char *protocol, const char *peer_address, int peer_trunk); + +/* PeerStatus */ +struct s_table_peer *get_peerstatus_db(stmt_t type_stmt, const char *peer_callerid_number); +int update_peerstatus_db( stmt_t type_stmt, const char *protocol, const char *status, const char *address, unsigned long long int peer_id ); +int insert_peerstatus_db( stmt_t type_stmt, const char *peer_callerid_number, const char *protocol, const char *status, const char *address ); + +#endif diff --git a/src/peer/peer_db.c b/src/peer/peer_db.c new file mode 100644 index 0000000..d26f0c9 --- /dev/null +++ b/src/peer/peer_db.c @@ -0,0 +1,508 @@ +#include +#include +#include +#include +#include + + + +/* valores tabela peer */ +int add_table_peer(struct s_table_peer **D, struct s_table_peer *next){ + + while(*D){ + D = &(*D)->next; + } + + (*D) = next; + + return 0; +} + + +/* liberar ponteiro da tabela */ +int free_table_peer(struct s_table_peer **D){ + + if((*D) == NULL) + return 0; + + free_table_peer( &(*D)->next ); + + free( (*D)->callerid_number ); + free( (*D)->callerid_name ); + free( (*D)->protocol ); + free( (*D)->status ); + free( (*D)->exten_statustext ); + free( (*D)->address ); + free( (*D)->time ); + free( *D ); + + return 0; +} + + +/* ExtensionStatus */ +struct s_table_peer *get_extensionstatus_db(stmt_t type_stmt, const char *peer_callerid_number){ + // SELECT id, callerid_number, trunk, exten_status, exten_statustext FROM peer where callerid_number = ?; + + MYSQL_STMT *stmt; + stmt = get_stmt(type_stmt); + if(!stmt){ + return NULL; + } + + pthread_mutex_lock(&m_db); + + MARIADB_BIND *bind = create_bind_manager( 1 ); + set_in_mariadb_column(&bind[0], ARG_STR_DB((char *)peer_callerid_number), (char *)peer_callerid_number, 0, isNULL(peer_callerid_number)); + + if(mariadb_stmt_execute(stmt, bind) == -1){ + goto fail; + } + + MARIADB_BIND *bind_result; + bind_result = get_bind_result(stmt); + + struct s_table_peer *table_peer = NULL, *table_peer_next = NULL; + int returnf = 0; + while(1){ + returnf = mysql_stmt_fetch(stmt); + + if(returnf == 1 || returnf == MYSQL_NO_DATA){ + break; + } + + table_peer_next = (struct s_table_peer *)calloc(1, sizeof(struct s_table_peer)); + if(!table_peer_next){ break; } + table_peer_next->next = NULL; + + /* column peer_id */ + table_peer_next->id = *(unsigned long long *)bind_result[0].buffer; + + /* columns peer_callerid_number */ + newstrncpy(&table_peer_next->callerid_number, bind_result[1].buffer); + + /* columns peer_trunk */ + table_peer_next->trunk = *(int *)bind_result[2].buffer; + + /* columns exten_status */ + table_peer_next->exten_status = *(int *)bind_result[3].buffer; + + /* columns exten_statustext */ + newstrncpy(&table_peer_next->exten_statustext, bind_result[4].buffer); + + add_table_peer(&table_peer, table_peer_next); + } + + destroy_bind_manager(bind_result); + + pthread_mutex_unlock(&m_db); + return table_peer; +fail: + pthread_mutex_unlock(&m_db); + return NULL; +} + + + +/* ExtensionStatus */ +int insert_extensionstatus_db(stmt_t type_stmt, + const char *peer_callerid_number, + int status, + const char *status_text, + int peer_trunk ){ + //"INSERT INTO peer ( callerid_number, exten_status, exten_statustext, trunk ) VALUES(?, ?, ?, ?);" + + MYSQL_STMT *stmt; + stmt = get_stmt(type_stmt); + if(!stmt){ + return -1; + } + + pthread_mutex_lock(&m_db); + + MARIADB_BIND *bind = create_bind_manager( 4 ); + set_in_mariadb_column(&bind[0], ARG_STR_DB((char *)peer_callerid_number), (char *)peer_callerid_number, 0, isNULL(peer_callerid_number)); + set_in_mariadb_column(&bind[1], MYSQL_TYPE_LONG, &status, 0, 0); + set_in_mariadb_column(&bind[2], ARG_STR_DB(status_text), (char *)status_text, 0, isNULL(status_text)); + set_in_mariadb_column(&bind[3], MYSQL_TYPE_LONG, &peer_trunk, 0, 0); + + if(mariadb_stmt_execute(stmt, bind) == -1){ + goto fail; + } + + int result = (int)mysql_stmt_affected_rows(stmt); + + pthread_mutex_unlock(&m_db); + return result; + +fail: + pthread_mutex_unlock(&m_db); + return -1; +} + + + +/* ExtensionStatus */ +int update_extensionstatus_db( stmt_t type_stmt, int status, const char *status_text, int peer_trunk, unsigned long long int id ){ + //"UPDATE peer SET exten_status=?, peer.exten_statustext=?, trunk=? WHERE id=?;" + + MYSQL_STMT *stmt; + stmt = get_stmt(type_stmt); + if(!stmt){ + return -1; + } + + pthread_mutex_lock(&m_db); + + MARIADB_BIND *bind = create_bind_manager( 4 ); + + set_in_mariadb_column(&bind[0], MYSQL_TYPE_LONG, &status, 0, 0); + set_in_mariadb_column(&bind[1], ARG_STR_DB(status_text), (char *)status_text, 0, isNULL(status_text)); + set_in_mariadb_column(&bind[2], MYSQL_TYPE_LONG, &peer_trunk, 0, 0); + set_in_mariadb_column(&bind[3], MYSQL_TYPE_LONGLONG, &id, 1, 0); + + + if(mariadb_stmt_execute(stmt, bind) == -1){ + goto fail; + } + + int result = (int)mysql_stmt_affected_rows(stmt); + + pthread_mutex_unlock(&m_db); + return result; + +fail: + pthread_mutex_unlock(&m_db); + return -1; +} + + +int update_peer_dynamic(stmt_t type_stmt, int dynamic, unsigned long long peer_id){ + + MYSQL_STMT *stmt; + stmt = get_stmt(type_stmt); + if(!stmt){ + return -1; + } + + pthread_mutex_lock(&m_db); + MARIADB_BIND *bind = create_bind_manager(2); + set_in_mariadb_column(&bind[0], MYSQL_TYPE_LONGLONG, &dynamic, 0, 0); + set_in_mariadb_column(&bind[1], MYSQL_TYPE_LONGLONG, &peer_id, 0, 0); + + if(mariadb_stmt_execute(stmt, bind) == -1){ + goto fail; + } + + int result = (int)mysql_stmt_affected_rows(stmt); + pthread_mutex_unlock(&m_db); + return result; + +fail: + pthread_mutex_unlock(&m_db); + return -1; + +} + + + +/* ExtensionStatus */ +int delete_extensionstatus_db(stmt_t type_stmt, unsigned long long peer_id){ + + MYSQL_STMT *stmt; + stmt = get_stmt(type_stmt); + if(!stmt){ + return -1; + } + + pthread_mutex_lock(&m_db); + + MARIADB_BIND *bind = create_bind_manager(1); + set_in_mariadb_column(&bind[0], MYSQL_TYPE_LONGLONG, &peer_id, 1, 0); + + if(mariadb_stmt_execute(stmt, bind) == -1){ + goto fail; + } + + int result = (int)mysql_stmt_affected_rows(stmt); + pthread_mutex_unlock(&m_db); + + return result; + +fail: + pthread_mutex_unlock(&m_db); + return -1; +} + + +/* PeerEntry */ +struct s_table_peer *get_peerentry_db( stmt_t type_stmt, const char *objectname ){ + + if(!objectname){ + return NULL; + } + + MYSQL_STMT *stmt; + stmt = get_stmt(type_stmt); + if(!stmt){ + return NULL; + } + + int returnf; + + pthread_mutex_lock(&m_db); + + MARIADB_BIND *bind = create_bind_manager( 1 ); + set_in_mariadb_column(&bind[0], MYSQL_TYPE_STRING, (char *)objectname, 0, isNULL((char *)objectname)); + + if(mariadb_stmt_execute(stmt, bind) == -1) + goto fail; + + MARIADB_BIND *bind_result; + bind_result = get_bind_result(stmt); + + struct s_table_peer *table_peer = NULL, *table_peer_next = NULL; + while(1){ + returnf = mysql_stmt_fetch(stmt); + + if(returnf == 1 || returnf == MYSQL_NO_DATA){ + break; + } + + table_peer_next = (struct s_table_peer *)calloc(1, sizeof(struct s_table_peer)); + if(!table_peer_next){ break; } + table_peer_next->next = NULL; + + /* column peer_id */ + table_peer_next->id = *(unsigned long long *)bind_result[0].buffer; + + /* columns peer_callerid_number */ + newstrncpy(&table_peer_next->callerid_number, bind_result[1].buffer); + + /* columns peer_type */ + newstrncpy(&table_peer_next->protocol, bind_result[2].buffer); + + /* columns peer_address */ + newstrncpy(&table_peer_next->address, bind_result[3].buffer); + + /* columns peer_trunk */ + table_peer_next->trunk = *(int *)bind_result[4].buffer; + + add_table_peer(&table_peer, table_peer_next); + } + + destroy_bind_manager(bind_result); + + pthread_mutex_unlock(&m_db); + + return table_peer; + +fail: + pthread_mutex_unlock(&m_db); + return NULL; + +} + + +/* PeerEntry */ +int update_peerentry_db(stmt_t type_stmt, const char *protocol, const char *peer_address, int peer_trunk, unsigned long long int peer_id){ + + MYSQL_STMT *stmt; + stmt = get_stmt(type_stmt); + if(!stmt){ + return -1; + } + + pthread_mutex_lock(&m_db); + + MARIADB_BIND *bind = create_bind_manager( 4 ); + set_in_mariadb_column(&bind[0], ARG_STR_DB((char *)protocol), (char *)protocol, 0, isNULL((char *)protocol)); + set_in_mariadb_column(&bind[1], ARG_STR_DB((char *)peer_address), (char *)peer_address, 0, isNULL((char *)peer_address)); + set_in_mariadb_column(&bind[2], MYSQL_TYPE_TINY, &peer_trunk, 0, 0); + set_in_mariadb_column(&bind[3], MYSQL_TYPE_LONGLONG, &peer_id, 1, 0); + + if(mariadb_stmt_execute(stmt, bind) == -1){ + goto fail; + } + + int result = (int)mysql_stmt_affected_rows(stmt); + + pthread_mutex_unlock(&m_db); + + return result; + +fail: + pthread_mutex_unlock(&m_db); + return -1; +} + + +/* PeerEntry */ +int insert_peerentry_db(stmt_t type_stmt, const char *peer_callerid_number, const char *protocol, const char *peer_address, int peer_trunk){ + + MYSQL_STMT *stmt; + stmt = get_stmt(type_stmt); + if(!stmt){ + return -1; + } + + pthread_mutex_lock(&m_db); + + MARIADB_BIND *bind = create_bind_manager( 4 ); + set_in_mariadb_column(&bind[0], ARG_STR_DB((char *)peer_callerid_number), (char *)peer_callerid_number, 0, isNULL((char *)peer_callerid_number)); + set_in_mariadb_column(&bind[1], ARG_STR_DB((char *)protocol), (char *)protocol, 0, isNULL((char *)protocol)); + set_in_mariadb_column(&bind[2], ARG_STR_DB((char *)peer_address), (char *)peer_address, 0, isNULL((char *)peer_address)); + set_in_mariadb_column(&bind[3], MYSQL_TYPE_TINY, &peer_trunk, 0, 0); + + + if(mariadb_stmt_execute(stmt, bind) == -1){ + goto fail; + } + + int result = (int)mysql_stmt_affected_rows(stmt); + + pthread_mutex_unlock(&m_db); + return result; + +fail: + pthread_mutex_unlock(&m_db); + return -1; +} + + +struct s_table_peer *get_peerstatus_db(stmt_t type_stmt, const char *peer_callerid_number){ + // SELECT id, callerid_number, protocol, status, address FROM peer where callerid_number = ?; + + if(!peer_callerid_number){ + return NULL; + } + + MYSQL_STMT *stmt; + stmt = get_stmt(type_stmt); + if(!stmt){ + return NULL; + } + + int returnf; + + pthread_mutex_lock(&m_db); + + MARIADB_BIND *bind = create_bind_manager( 1 ); + set_in_mariadb_column(&bind[0], ARG_STR_DB((char *)peer_callerid_number), (char *)peer_callerid_number, 0, isNULL((char *)peer_callerid_number)); + + if(mariadb_stmt_execute(stmt, bind) == -1){ + goto fail; + } + + MARIADB_BIND *bind_result; + bind_result = get_bind_result(stmt); + + struct s_table_peer *table_peer = NULL, *table_peer_next = NULL; + while(1){ + returnf = mysql_stmt_fetch(stmt); + + if(returnf == 1 || returnf == MYSQL_NO_DATA){ + break; + } + + table_peer_next = (struct s_table_peer *)calloc(1, sizeof(struct s_table_peer)); + if(!table_peer_next){ break; } + table_peer_next->next = NULL; + + /* column peer_id */ + table_peer_next->id = *(unsigned long long *)bind_result[0].buffer; + + /* columns callerid_number */ + newstrncpy(&table_peer_next->callerid_number, bind_result[1].buffer); + + /* columns protocol */ + newstrncpy(&table_peer_next->protocol, bind_result[2].buffer); + + /* columns status */ + newstrncpy(&table_peer_next->status, bind_result[3].buffer); + + /* columns address */ + newstrncpy(&table_peer_next->address, bind_result[4].buffer); + + add_table_peer(&table_peer, table_peer_next); + } + + destroy_bind_manager(bind_result); + + pthread_mutex_unlock(&m_db); + return table_peer; + +fail: + pthread_mutex_unlock(&m_db); + return NULL; + +} + + + +int update_peerstatus_db( stmt_t type_stmt, const char *protocol, const char *status, const char *address, unsigned long long int peer_id ){ + // UPDATE peer SET protocol=?, status=?, address=? WHERE id=?; + + MYSQL_STMT *stmt; + stmt = get_stmt(type_stmt); + if(!stmt){ + return -1; + } + + pthread_mutex_lock(&m_db); + + MARIADB_BIND *bind = create_bind_manager( 4 ); + set_in_mariadb_column(&bind[0], ARG_STR_DB((char *)protocol), (char *)protocol, 0, isNULL((char *)protocol)); + set_in_mariadb_column(&bind[1], ARG_STR_DB((char *)status), (char *)status, 0, isNULL((char *)status)); + set_in_mariadb_column(&bind[2], ARG_STR_DB((char *)address), (char *)address, 0, isNULL((char *)address)); + set_in_mariadb_column(&bind[3], MYSQL_TYPE_LONGLONG, &peer_id, 1, 0); + + if(mariadb_stmt_execute(stmt, bind) == -1){ + goto fail; + } + + int result = (int)mysql_stmt_affected_rows(stmt); + + pthread_mutex_unlock(&m_db); + return result; + +fail: + pthread_mutex_unlock(&m_db); + return -1; +} + + + +int insert_peerstatus_db( stmt_t type_stmt, const char *peer_callerid_number, const char *protocol, const char *status, const char *address ){ + // INSERT INTO peer (callerid_number, protocol, status, address) VALUES(?, ?, ?, ?); + + MYSQL_STMT *stmt; + stmt = get_stmt(type_stmt); + if(!stmt){ + return -1; + } + + pthread_mutex_lock(&m_db); + + MARIADB_BIND *bind = create_bind_manager(4); + set_in_mariadb_column(&bind[0], ARG_STR_DB((char *)peer_callerid_number), (char *)peer_callerid_number, 0, isNULL((char *)peer_callerid_number)); + set_in_mariadb_column(&bind[1], ARG_STR_DB((char *)protocol), (char *)protocol, 0, isNULL((char *)protocol)); + set_in_mariadb_column(&bind[2], ARG_STR_DB((char *)status), (char *)status, 0, isNULL((char *)status)); + set_in_mariadb_column(&bind[2], ARG_STR_DB((char *)address), (char *)address, 0, isNULL((char *)address)); + + if(mariadb_stmt_execute(stmt, bind) == -1){ + goto fail; + } + + int result = (int)mysql_stmt_affected_rows(stmt); + + pthread_mutex_unlock(&m_db); + + return result; + +fail: + pthread_mutex_unlock(&m_db); + return -1; +} + + diff --git a/src/queue/queue.c b/src/queue/queue.c new file mode 100644 index 0000000..872a74e --- /dev/null +++ b/src/queue/queue.c @@ -0,0 +1,278 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + * + * + * + * Documentation: https://wiki.asterisk.org/wiki/download/attachments/19005471/Asterisk-17-Reference.pdf?version=1&modificationDate=1582306810980&api=v2 + * + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +int parse_event_queueparams( EVENT *event, s_manager *smanager ){ + + /* Event: QueueParams + * Queue: FINANCEIRO_SIMPLES + * Max: 0 + * Strategy: ringall + * Calls: 0 + * Holdtime: 4 + * TalkTime: 67 + * Completed: 5 + * Abandoned: 3 + * ServiceLevel: 60 + * ServicelevelPerf: 100.0 + * Weight: 0 + * + *---------------------------- + * + * Queue ------- Nome da fila + * Max -------- Quantidades de chamadas permitidas. 0 ilimitado + * Strategy --- Estratégia da fila + * Calls ------ Número de chamadas atualmente esperando em fila + * Holdtime --- uma média de tempo de espera para chamadas que foram atendidas segundos + * TalkTime --- tempo que estão conectado (falando) + * Completed -- chamadas completadas na fila + * Abandoned -- chamadas abandonadas na fila + * ServiceLevel -- serviço primário métrica de performance + * ServicelevelPerf ---serviço segundario métric de perfomance + * Weight + * + * + * strategy - ringall ----- ring all available channels until one answers (default) + * leastrecent - ring interface which was least recently hung up by this queue + * fewestcalls - ring the one with fewest completed calls from this queue + * random ------ ring random interface + * rrmemory ---- round robin with memory, remember where we left off last ring pass + * rrordered --- same as rrmemory, except the queue member order from config fileis preserved + * linear ------ rings interfaces in the order specified in this configuration file. + * If you use dynamic members, the members will be rung in the order in + * which they were added + * wrandom ----- rings random interface, but uses the member's penalty as a weight + * when calculating their metric. So a member with penalty 0 will have + * a metric somewhere between 0 and 1000, and a member with penalty 1 will + * have a metric between 0 and 2000, and a member with penalty 2 will have + * a metric between 0 and 3000. Please note, if using this strategy, the member + * penalty is not the same as when using other queue strategies. It is ONLY used + * as a weight for calculating metric. + * + */ + + const char *queue_name, *max, *strategy, + *calls_waiting, *holdtime, *talktime, + *completed, *abandoned; + unsigned int i_max = 0, i_calls_waiting = 0, i_holdtime = 0, i_talktime = 0, i_completed = 0, i_abandoned = 0; + + queue_name = ami_get_value( smanager->ami, event->args, "Queue" ); + if(!queue_name){ goto fail;} + + max = ami_get_value( smanager->ami, event->args, "Max" ); + if(!max){ goto fail;} + i_max = atoi(max); + + strategy = ami_get_value( smanager->ami, event->args, "Strategy" ); + if(!strategy){ goto fail;} + + calls_waiting = ami_get_value( smanager->ami, event->args, "Calls" ); + if(!calls_waiting) { goto fail;} + i_calls_waiting = atoi(calls_waiting); + + holdtime = ami_get_value( smanager->ami, event->args, "Holdtime" ); + if(!holdtime){ goto fail;} + i_holdtime = atoi(holdtime); + + talktime = ami_get_value( smanager->ami, event->args, "Talktime" ); + if(!talktime){ goto fail;} + i_talktime = atoi(talktime); + + completed = ami_get_value( smanager->ami, event->args, "Completed" ); + if(!completed){ goto fail;} + i_completed = atoi(completed); + + abandoned = ami_get_value( smanager->ami, event->args, "Abandoned" ); + if(!abandoned){ goto fail;} + i_abandoned = atoi(abandoned); + + { // banco de dados + struct s_table_queue *table_queue = get_queueparams_db( Select_queueparams, queue_name ); + + if( table_queue ){ + if( strcmp(table_queue->strategy, strategy) || + table_queue->hold_time != i_holdtime || + table_queue->talk_time != i_talktime || + table_queue->calls_completed != i_completed || + table_queue->calls_abandoned != i_abandoned || + table_queue->calls_waiting != i_calls_waiting || + table_queue->calls_limit != i_max) { + + if( update_queueparams_db( Update_queueparams, strategy, i_holdtime, i_talktime, i_completed, i_abandoned, i_max, i_calls_waiting, table_queue->queue_id ) == -1){ + goto fail; + } + } + } + else { + if( insert_queueparams_db( Insert_queueparams, queue_name, strategy, i_holdtime, i_talktime, i_completed, i_abandoned, i_max, i_calls_waiting ) == -1){ + goto fail; + } + } + } + + { // camada de persistência + + /* struct s_queue { + * unsigned long long id; + * char *queue_name; + * char *queue_number; + * char *strategy; + * unsigned int hold_time; + * unsigned int talk_time; + * unsigned int calls_completed; + * unsigned int calls_abandoned; + * unsigned int calls_limit; + * unsigned int calls_waiting; + * struct s_queue_member *member; + * struct s_queue *next; + * }; + */ + + struct s_queue *queue; + queue = get_queue(queue_name); + if(queue){ + if( strcmp_n(queue->strategy, strategy) ){ + free(queue->strategy); + newstrncpy(&queue->strategy, strategy); + } + + if( queue->hold_time != i_holdtime ){ + queue->hold_time = i_holdtime; + } + + if( queue->talk_time != i_talktime ){ + queue->talk_time = i_talktime; + } + + if( queue->calls_completed != i_completed ){ + queue->calls_completed = i_completed; + } + + if( queue->calls_abandoned != i_abandoned ){ + queue->calls_abandoned = i_abandoned; + } + + if( queue->calls_limit != i_max ){ + queue->calls_limit = i_max; + } + + if( queue->calls_waiting != i_calls_waiting ){ + queue->calls_waiting = i_calls_waiting; + } + } + + else { + queue = create_queue(); + newstrncpy(&queue->queue_name, queue_name); + newstrncpy(&queue->strategy, strategy); + + queue->hold_time = i_holdtime; + queue->talk_time = i_talktime; + queue->calls_completed = i_completed; + queue->calls_abandoned = i_abandoned; + queue->calls_limit = i_max; + queue->calls_waiting = i_calls_waiting; + + insert_queue(queue); + } + + } + + return 1; + +fail: + return -1; +} + + + +int parse_event__queuemember( EVENT *event, s_manager *smanager ){ + + /* + * Queue - O nome da fila + * Name - ramal membro da fila + * Location - O canal membro da fila ou localização + * StateInterface - tecnologia do canal ou localização que ler estado do dispositivo. + * Membership + * { + * dynamic + * realtime + * static + * } + * Penalty - O penalidade associado com membro da fila + * CallsTaken - O número de chamada que esse membro da fila tem participado + * LastCall - A hora em que esse membro atendeu pela última vez, expressa em segundos desde 00:00 de 1º de janeiro de 1970 UTC. + * InCall - Defini 1 se o membro está em uma chamada. Defini para 0 depois se não está, LastCall é atualizado. + * { + * 0 + * 1 + * } + * Status - O estado do dispositivo do membro da fila + * { + * 0 - AST_DEVICE_UNKNOWN + * 1 - AST_DEVICE_NOT_INUSE + * 2 - AST_DEVICE_INUSE + * 3 - AST_DEVICE_BUSY + * 4 - AST_DEVICE_INVALID + * 5 - AST_DEVICE_UNAVAILABLE + * 6 - AST_DEVICE_RINGING + * 7 - AST_DEVICE_RINGINUSE + * 8 - AST_DEVICE_ONHOLD + * } + * Paused + * { + * 0 + * 1 + * } + * + */ + + /* + * Penalidade - Se a estratégia é definida como 'ringall', apenas esses dispositivos membros com o baixo prioridade irá + * tocar. Examplo, se 200 não é ocupado, então apenas 200 vai tocar. Se 200 está ocupado, então apenas 201 e 203 vai tocar. + * Se 200, 201 e 203 estão ocupado, então apenas 202 vai tocar. + * + */ + + + + + + + return 1; +} + + + diff --git a/src/queue/queue.h b/src/queue/queue.h new file mode 100644 index 0000000..b574296 --- /dev/null +++ b/src/queue/queue.h @@ -0,0 +1,66 @@ +/*** + * _____ _ _ _____ + * / ____(_) | | |_ _| + * | (___ _ _ __ ___ _ __ | | ___ ___ | | _ __ + * \___ \| | '_ ` _ \| '_ \| |/ _ \/ __| | | | '_ \ + * ____) | | | | | | | |_) | | __/\__ \ _| |_| |_) | + * |_____/|_|_| |_| |_| .__/|_|\___||___/ |_____| .__/ + * | \/ | | | | | + * | \ / | __ _ _ __ |_|_ _ __ _ ___ _ __ |_| + * | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| + * | | | | (_| | | | | (_| | (_| | __/ | + * |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| + * __/ | + * |___/ + * + * copyright 2022 + * + * + */ + +#ifndef QUEUE +#define QUEUE 1 + +#include +#include +#include + + +struct s_table_queue { + + unsigned long long int queue_id; + char *queue_name; + char *queue_number; + char *strategy; + unsigned long long int hold_time; + unsigned long long int talk_time; + unsigned long long int calls_completed; + unsigned long long int calls_abandoned; + unsigned int calls_limit; + unsigned int calls_waiting; + + struct s_table_queue *next; +}; + + +int parse_event_queueparams( EVENT *event, s_manager *smanager ); +int add_table_queue( struct s_table_queue **D, struct s_table_queue *next ); + +int free_table_queue(struct s_table_queue **D); + +struct s_table_queue *get_queueparams_db( stmt_t type_stmt, const char *queue_name ); + +int update_queueparams_db(stmt_t type_stmt, const char *strategy, unsigned long long int i_holdtime, + unsigned long long int i_talktime, unsigned long long int i_completed, + unsigned long long int i_abandoned, unsigned long long int i_max, + unsigned long long int i_calls_waiting, unsigned long long int queu_id); +int insert_queueparams_db(stmt_t type_stmt, const char *queue_name, const char *strategy, + unsigned long long int i_holdtime, unsigned long long int i_talktime, + unsigned long long int i_completed, unsigned long long int i_abandoned, + unsigned long long int i_max, unsigned long long int i_calls_waiting ); + + + + + +#endif diff --git a/src/queue/queue_db.c b/src/queue/queue_db.c new file mode 100644 index 0000000..ec106ae --- /dev/null +++ b/src/queue/queue_db.c @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include + + +/* valores tabela queue */ +int add_table_queue( struct s_table_queue **D, struct s_table_queue *next ){ + + while(*D){ + D = &(*D)->next; + } + + (*D) = next; + + return 0; +} + + + +/* liberar ponteiro da tabela */ +int free_table_queue(struct s_table_queue **D){ + + if((*D) == NULL) + return 0; + + free_table_queue(&(*D)->next); + + free((*D)->queue_name); + free((*D)->queue_number); + free((*D)->strategy); + free(*D); + + return 0; +} + + +struct s_table_queue *get_queueparams_db( stmt_t type_stmt, const char *queue_name ){ +// "select queue_id, queue_name, strategy, hold_time, talk_time, calls_completed, calls_abandoned, calls_limit, calls_waiting FROM queue where queue_name = ?;", NULL}, + + MYSQL_STMT *stmt; + stmt = get_stmt(type_stmt); + if(!stmt){ + return NULL; + } + + pthread_mutex_lock(&m_db); + + MARIADB_BIND *bind = create_bind_manager( 1 ); + set_in_mariadb_column(&bind[0], ARG_STR_DB((char *)queue_name), (char *)queue_name, 0, isNULL(queue_name)); + + if(mariadb_stmt_execute(stmt, bind) == -1){ + goto fail; + } + + MARIADB_BIND *bind_result; + bind_result = get_bind_result(stmt); + + struct s_table_queue *table_queue = NULL, *table_queue_next = NULL; + int returnf = 0; + while(1){ + returnf = mysql_stmt_fetch(stmt); + + if(returnf == 1 || returnf == MYSQL_NO_DATA){ + break; + } + + + table_queue_next = (struct s_table_queue *) calloc(1, sizeof(struct s_table_queue)); + if(!table_queue_next){ break; } + table_queue_next->next = NULL; + + /* column queue_id */ + table_queue_next->queue_id = *(unsigned long long int *)bind_result[0].buffer; + + /* columns queue_name */ + newstrncpy(&table_queue_next->queue_name, bind_result[1].buffer); + + /* columns queue_name */ + newstrncpy(&table_queue_next->strategy, bind_result[2].buffer); + + /* columns hold_time */ + table_queue_next->hold_time = *(int *)bind_result[3].buffer; + + /* columns talk_time */ + table_queue_next->talk_time = *(int *)bind_result[4].buffer; + + /* columns calls_completed */ + table_queue_next->calls_completed = *(int *)bind_result[5].buffer; + + /* columns calls_abandoned */ + table_queue_next->calls_abandoned = *(int *)bind_result[6].buffer; + + /* columns calls_limit */ + table_queue_next->calls_limit = *(int *)bind_result[7].buffer; + + /* columns calls_waiting */ + table_queue_next->calls_waiting = *(int *)bind_result[8].buffer; + + add_table_queue(&table_queue, table_queue_next); + } + + destroy_bind_manager(bind_result); + + pthread_mutex_unlock(&m_db); + return table_queue; + +fail: + return NULL; + +} + + +int update_queueparams_db(stmt_t type_stmt, const char *strategy, unsigned long long int i_holdtime, + unsigned long long int i_talktime, unsigned long long int i_completed, + unsigned long long int i_abandoned, unsigned long long int i_max, + unsigned long long int i_calls_waiting, unsigned long long int queue_id) { + MYSQL_STMT *stmt; + stmt = get_stmt(type_stmt); + if(!stmt){ + return -1; + } + + pthread_mutex_lock(&m_db); + + MARIADB_BIND *bind = create_bind_manager( 8 ); + + set_in_mariadb_column(&bind[0], ARG_STR_DB(strategy), (char *)strategy, 0, isNULL(strategy)); + set_in_mariadb_column(&bind[1], MYSQL_TYPE_LONGLONG, &i_holdtime, 1, 0); + set_in_mariadb_column(&bind[2], MYSQL_TYPE_LONGLONG, &i_talktime, 1, 0); + set_in_mariadb_column(&bind[3], MYSQL_TYPE_LONGLONG, &i_completed, 1, 0); + set_in_mariadb_column(&bind[4], MYSQL_TYPE_LONGLONG, &i_abandoned, 1, 0); + set_in_mariadb_column(&bind[5], MYSQL_TYPE_LONG, &i_max, 0, 0); + set_in_mariadb_column(&bind[6], MYSQL_TYPE_LONGLONG, &i_calls_waiting, 0, 0); + set_in_mariadb_column(&bind[7], MYSQL_TYPE_LONGLONG, &queue_id, 1, 0); + + + if(mariadb_stmt_execute(stmt, bind) == -1){ + goto fail; + } + + int result = (int)mysql_stmt_affected_rows(stmt); + + pthread_mutex_unlock(&m_db); + return result; + +fail: + pthread_mutex_unlock(&m_db); + return -1; +} + + +int insert_queueparams_db(stmt_t type_stmt, const char *queue_name, const char *strategy, + unsigned long long int i_holdtime, unsigned long long int i_talktime, + unsigned long long int i_completed, unsigned long long int i_abandoned, + unsigned long long int i_max, unsigned long long int i_calls_waiting ){ + + MYSQL_STMT *stmt; + stmt = get_stmt(type_stmt); + if(!stmt){ + return -1; + } + + pthread_mutex_lock(&m_db); + + MARIADB_BIND *bind = create_bind_manager( 8 ); + + set_in_mariadb_column(&bind[0], ARG_STR_DB(queue_name), (char *)queue_name, 0, isNULL(queue_name)); + set_in_mariadb_column(&bind[1], ARG_STR_DB(strategy), (char *)strategy, 0, isNULL(strategy)); + set_in_mariadb_column(&bind[2], MYSQL_TYPE_LONGLONG, &i_holdtime, 1, 0); + set_in_mariadb_column(&bind[3], MYSQL_TYPE_LONGLONG, &i_talktime, 1, 0); + set_in_mariadb_column(&bind[4], MYSQL_TYPE_LONGLONG, &i_completed, 1, 0); + set_in_mariadb_column(&bind[5], MYSQL_TYPE_LONGLONG, &i_abandoned, 1, 0); + set_in_mariadb_column(&bind[6], MYSQL_TYPE_LONG, &i_max, 0, 0); + set_in_mariadb_column(&bind[7], MYSQL_TYPE_LONGLONG, &i_calls_waiting, 0, 0); + + if(mariadb_stmt_execute(stmt, bind) == -1){ + goto fail; + } + + int result = (int)mysql_stmt_affected_rows(stmt); + + pthread_mutex_unlock(&m_db); + return result; + +fail: + pthread_mutex_unlock(&m_db); + return -1; +} + + diff --git a/src/string_functions.c b/src/string_functions.c new file mode 100644 index 0000000..2d49067 --- /dev/null +++ b/src/string_functions.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include +#include +#include +#include + + +/*! + * strcpy_s é similar ao strcpy, porém verifica se tem + * + */ +void strcpy_s(const char *str, char **event_name, char *p_end){ + + size_t bytes = strlen_event(str, p_end, 0); + if(bytes == 0){ + event_name[0] = NULL; + return; + } + + event_name[0] = alloc(1, bytes + 1); + + strncpy(event_name[0], str, bytes); + + event_name[0][bytes] = '\0'; + +} + + +/*! + * O mesmo que strcpy, porém usa alloc (função usa calloc e também + * verifica se houve erro para mandar ao log) + * \param new_str + * \param str + * \return new_str o + */ +char *newstrncpy( char **new_str, const char *str){ + + if(!str){ + new_str[0] = NULL; + return new_str[0]; + } + + + size_t bytes = strlen_n(str); + if(bytes == 0 || bytes == 1){ + new_str[0] = NULL; + return new_str[0]; + } + + new_str[0] = alloc(1, bytes + 1); + + strncpy( new_str[0], str, bytes); + + new_str[0][bytes] = '\0'; + + return new_str[0]; +} + +int strcasecmp_n(const char *str1, const char *str2){ + + if(str1 == NULL && str2 == NULL ){ + return 0; + } + else if(str1 == NULL){ + return -1; + } + else if(str2 == NULL){ + return 1; + } + else { + return strncasecmp(str1, str2, (strlen_n(str1) > strlen_n(str2) ? strlen_n(str1) : strlen_n(str2))); + } + +} + + +int strcmp_n(const char *str1, const char *str2){ + + if(str1 == NULL && str2 == NULL ){ + return 0; + } + else if(str1 == NULL){ + return -1; + } + else if(str2 == NULL){ + return 1; + } + else { + return strncmp(str1, str2, (strlen_n(str1) > strlen_n(str2) ? strlen_n(str1) : strlen_n(str2))); + } +} + +size_t strlen_n(const char *str1){ + if(!str1){ + return 0; + } + + return strlen(str1); +} + +/* + * copia string + * str = [str | dest] + */ +char *str_cpy_in(char *dest, const char *str){ + + size_t len_str, len_dest; + + len_str = strlen_n(str); + len_dest = strlen_n(dest); + + char tmp[len_dest + 1]; + strncpy( tmp, dest, len_dest); + + dest = realloc(dest, len_dest + len_str + 1); + if(!dest){ + printf("não foi possível allocar - line %s - %d\n", __FILE__, __LINE__); + return NULL; + } + + memset(dest, 0, len_dest + len_str); + + strncpy( dest, str, len_str + 1 ); + + strncat( dest, tmp, len_dest ); + + dest[len_dest + len_str] = '\0'; + + return dest; +} + + + + +char *strnstr(const char *s, const char *find, size_t slen){ + char c, sc; + size_t len; + + if ((c = *find++) != '\0') { + len = strlen(find); + do { + do { + if (slen-- < 1 || (sc = *s++) == '\0') + return (NULL); + } while (sc != c); + if (len > slen) + return (NULL); + } while (strncmp(s, find, len) != 0); + s--; + } + + return ((char *)s); +} + + + + + + +size_t strlen_event(const char *str, char *p_end, int use_char){ + + size_t len_str = 0; + + if(use_char == 0){ + while(*str && str != p_end){ + ++len_str; + ++str; + } + } + else if(use_char == 1){ + while(*str && *str != *p_end){ + ++len_str; + ++str; + } + } + + return len_str; +} + + +void *alloc(size_t nmemb, size_t size){ + char *p = NULL; + p = calloc(nmemb, size); + + if (p == NULL){ + _EMERG("Não há memória disponível: %s", strerror(errno)); + return NULL; + } + + return (void *)p; +} + + + +char *verify_device(const char *name, int isqueue){ + + char *only_name = NULL; + + only_name = strstr(name, "Queue:"); + if(only_name) return only_name + 6; + + only_name = strstr(name, "SIP/"); + if(only_name) return only_name + 4; + + only_name = strstr(name, "IAX2/"); + if(only_name) return only_name + 5; + + _NOTICE("AMI retornou devicestate-device: %s", name); + return NULL; +} + + +const char *get_parse_exten( const char *peer ){ + + char *exten = NULL; + + exten = strstr(peer, "/"); + if(exten) + return exten + 1; + + return exten; +} + diff --git a/src/string_functions.h b/src/string_functions.h new file mode 100644 index 0000000..760de7f --- /dev/null +++ b/src/string_functions.h @@ -0,0 +1,23 @@ + +#ifndef STRING_FUNCTIONS_H +#define STRING_FUNCTIONS_H 1 + +#include + + +void strcpy_s(const char *str, char **event_name, char *p_end); +char *newstrncpy( char **new_str, const char *str); +int strcasecmp_n(const char *str1, const char *str2); +int strcmp_n(const char *str1, const char *str2); +size_t strlen_n(const char *str1); +char *str_cpy_in(char *dest, const char *str); +char *strnstr(const char *s, const char *find, size_t slen); +void build_string(char *buf, ...); +int replace_char_null(char *str, char chr, size_t len); +size_t strlen_event(const char *str, char *p_end, int use_char); +void *alloc(size_t nmemb, size_t size); +char *verify_device(const char *name, int isqueue); +const char *get_parse_exten( const char *peer ); + + +#endif \ No newline at end of file diff --git a/systemd/ami_simplesip.service b/systemd/ami_simplesip.service new file mode 100644 index 0000000..662d3ec --- /dev/null +++ b/systemd/ami_simplesip.service @@ -0,0 +1,12 @@ +[Unit] +Description=Client asterisk AMI +After=network.target + +[Service] +Type=forking +Restart=always +RestartSec=2 +ExecStart=/usr/sbin/ami_simplesip + +[Install] +WantedBy=multi-user.target