diff --git a/restcomm/restcomm.application/src/main/webapp/WEB-INF/conf/extensions.xml b/restcomm/restcomm.application/src/main/webapp/WEB-INF/conf/extensions.xml index 358d6c5b80..3fb0ce7ea0 100644 --- a/restcomm/restcomm.application/src/main/webapp/WEB-INF/conf/extensions.xml +++ b/restcomm/restcomm.application/src/main/webapp/WEB-INF/conf/extensions.xml @@ -10,6 +10,7 @@ the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> + - \ No newline at end of file + + \ No newline at end of file diff --git a/restcomm/restcomm.application/src/main/webapp/WEB-INF/data/hsql/restcomm.script b/restcomm/restcomm.application/src/main/webapp/WEB-INF/data/hsql/restcomm.script index fa4718a037..44bbe2d31f 100644 --- a/restcomm/restcomm.application/src/main/webapp/WEB-INF/data/hsql/restcomm.script +++ b/restcomm/restcomm.application/src/main/webapp/WEB-INF/data/hsql/restcomm.script @@ -21,6 +21,7 @@ CREATE MEMORY TABLE "restcomm_gateways"("sid" VARCHAR(34) NOT NULL PRIMARY KEY," CREATE MEMORY TABLE "restcomm_media_servers" ( "ms_id" INT GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1) NOT NULL, "local_ip" VARCHAR(34) NOT NULL, "local_port" INT NOT NULL, "remote_ip" VARCHAR(34) NOT NULL UNIQUE, "remote_port" INT NOT NULL, "compatibility" VARCHAR(34) DEFAULT 'rms', "response_timeout" VARCHAR(34), "external_address" VARCHAR(34)) CREATE MEMORY TABLE "restcomm_media_resource_broker_entity" ("conference_sid" VARCHAR(34) NOT NULL, "slave_ms_id" VARCHAR(34) NOT NULL, "slave_ms_bridge_ep_id" VARCHAR(34),"slave_ms_cnf_ep_id" VARCHAR(34),"is_bridged_together" BOOLEAN DEFAULT FALSE,PRIMARY KEY ("conference_sid" , "slave_ms_id")) CREATE MEMORY TABLE PUBLIC."restcomm_extensions_configuration"("sid" VARCHAR(34) NOT NULL PRIMARY KEY,"extension" VARCHAR(255) NOT NULL,"configuration_data" VARCHAR(16777216),"configuration_type" VARCHAR(255) NOT NULL,"date_created" TIMESTAMP NOT NULL,"date_updated" TIMESTAMP, "enabled" BOOLEAN DEFAULT TRUE NOT NULL) +CREATE MEMORY TABLE PUBLIC."restcomm_accounts_extensions" ("account_sid" VARCHAR(34) NOT NULL, "extension_sid" VARCHAR(34) NOT NULL, PRIMARY KEY("account_sid", "extension_sid"), "configuration_data" VARCHAR(16777216)) CREATE MEMORY TABLE "restcomm_geolocation"("sid" VARCHAR(34) NOT NULL PRIMARY KEY, "date_created" DATETIME NOT NULL, "date_updated" DATETIME NOT NULL, "date_executed" DATETIME NOT NULL, "account_sid" VARCHAR(34) NOT NULL, "source" VARCHAR(30), "device_identifier" VARCHAR(30) NOT NULL, "geolocation_type" VARCHAR(15) NOT NULL, "response_status" VARCHAR(30), "cell_id" VARCHAR(10), "location_area_code" VARCHAR(10), "mobile_country_code" INTEGER, "mobile_network_code" VARCHAR(3), "network_entity_address" BIGINT, "age_of_location_info" INTEGER, "device_latitude" VARCHAR(15), "device_longitude" VARCHAR(15), "accuracy" BIGINT, "physical_address" VARCHAR(50), "internet_address" VARCHAR(50), "formatted_address" VARCHAR(200), "location_timestamp" DATETIME, "event_geofence_latitude" VARCHAR(15), "event_geofence_longitude" VARCHAR(15), "radius" BIGINT, "geolocation_positioning_type" VARCHAR(15), "last_geolocation_response" VARCHAR(10), "cause" VARCHAR(150), "api_version" VARCHAR(10) NOT NULL, "uri" LONGVARCHAR NOT NULL) CREATE USER SA PASSWORD "" GRANT DBA TO SA diff --git a/restcomm/restcomm.application/src/main/webapp/WEB-INF/scripts/mariadb/init.sql b/restcomm/restcomm.application/src/main/webapp/WEB-INF/scripts/mariadb/init.sql index c3cd1c4ce2..1edcae09a4 100644 --- a/restcomm/restcomm.application/src/main/webapp/WEB-INF/scripts/mariadb/init.sql +++ b/restcomm/restcomm.application/src/main/webapp/WEB-INF/scripts/mariadb/init.sql @@ -381,6 +381,13 @@ date_updated DATETIME, enabled BOOLEAN NOT NULL DEFAULT TRUE ); +CREATE TABLE restcomm_accounts_extensions ( +account_sid VARCHAR(34) NOT NULL, +extension_sid VARCHAR(34) NOT NULL, +configuration_data LONGTEXT NOT NULL, +PRIMARY KEY (account_sid, extension_sid) +); + INSERT INTO restcomm_accounts VALUES ( "ACae6e420f425248d6a26948c17a9e2acf", Date("2012-04-24"), diff --git a/restcomm/restcomm.application/src/main/webapp/WEB-INF/scripts/mariadb/sql/extensions-configuration.xml b/restcomm/restcomm.application/src/main/webapp/WEB-INF/scripts/mariadb/sql/extensions-configuration.xml index cf1ed3a48f..261c3e385b 100644 --- a/restcomm/restcomm.application/src/main/webapp/WEB-INF/scripts/mariadb/sql/extensions-configuration.xml +++ b/restcomm/restcomm.application/src/main/webapp/WEB-INF/scripts/mariadb/sql/extensions-configuration.xml @@ -61,4 +61,24 @@ + + + + + INSERT INTO restcomm_accounts_extensions (account_sid, extension_sid, configuration_data) + VALUES (#{account_sid}, #{extension_sid}, #{configuration_data}); + + + + UPDATE restcomm_accounts_extensions SET configuration_data=#{configuration_data} + WHERE account_sid=#{account_sid} AND extension_sid=#{extension_sid}; + + + + DELETE FROM restcomm_accounts_extensions + WHERE account_sid=#{account_sid} AND extension_sid=#{extension_sid}; + diff --git a/restcomm/restcomm.application/src/main/webapp/WEB-INF/sql/extensions-configuration.xml b/restcomm/restcomm.application/src/main/webapp/WEB-INF/sql/extensions-configuration.xml index 552915e6db..b20ac5233b 100644 --- a/restcomm/restcomm.application/src/main/webapp/WEB-INF/sql/extensions-configuration.xml +++ b/restcomm/restcomm.application/src/main/webapp/WEB-INF/sql/extensions-configuration.xml @@ -67,4 +67,24 @@ - \ No newline at end of file + + + + + INSERT INTO "restcomm_accounts_extensions" ("account_sid", "extension_sid", "configuration_data") + VALUES (#{account_sid}, #{extension_sid}, #{configuration_data}); + + + + UPDATE "restcomm_accounts_extensions" SET "configuration_data"=#{configuration_data} + WHERE "account_sid"=#{account_sid} AND "extension_sid"=#{extension_sid}; + + + + DELETE FROM "restcomm_accounts_extensions" + WHERE "account_sid"=#{account_sid} AND "extension_sid"=#{extension_sid}; + + diff --git a/restcomm/restcomm.dao/src/main/java/org/restcomm/connect/dao/ExtensionsConfigurationDao.java b/restcomm/restcomm.dao/src/main/java/org/restcomm/connect/dao/ExtensionsConfigurationDao.java index 4c5ea677bb..a66a4484dc 100644 --- a/restcomm/restcomm.dao/src/main/java/org/restcomm/connect/dao/ExtensionsConfigurationDao.java +++ b/restcomm/restcomm.dao/src/main/java/org/restcomm/connect/dao/ExtensionsConfigurationDao.java @@ -97,4 +97,33 @@ public interface ExtensionsConfigurationDao { * @return */ boolean validate(ExtensionConfiguration extensionConfiguration); + + /** + * Get account specific ExtensionConfiguration + * @param accountSid + * @param extensionSid + * @return ExtensionConfiguration + */ + ExtensionConfiguration getAccountExtensionConfiguration(String accountSid, String extensionSid); + + /** + * Add a new account specific ExtensionConfiguration + * @param extensionConfiguration + * @param accountSid + */ + void addAccountExtensionConfiguration(ExtensionConfiguration extensionConfiguration, Sid accountSid) throws ConfigurationException; + + /** + * Update an existing account specific ExtensionConfiguration + * @param extensionConfiguration + * @param accountSid + */ + void updateAccountExtensionConfiguration(ExtensionConfiguration extensionConfiguration, Sid accountSid) throws ConfigurationException; + + /** + * Delete account specific ExtensionConfiguration + * @param accountSid + * @param extensionSid + */ + void deleteAccountExtensionConfiguration(String accountSid, String extensionSid); } diff --git a/restcomm/restcomm.dao/src/main/java/org/restcomm/connect/dao/mybatis/MybatisExtensionsConfigurationDao.java b/restcomm/restcomm.dao/src/main/java/org/restcomm/connect/dao/mybatis/MybatisExtensionsConfigurationDao.java index 4e3a58918d..e5dfb2f6ee 100644 --- a/restcomm/restcomm.dao/src/main/java/org/restcomm/connect/dao/mybatis/MybatisExtensionsConfigurationDao.java +++ b/restcomm/restcomm.dao/src/main/java/org/restcomm/connect/dao/mybatis/MybatisExtensionsConfigurationDao.java @@ -259,6 +259,13 @@ private ExtensionConfiguration toExtensionConfiguration(final Map map) { + final Sid sid = new Sid((String)map.get("extension")); + final String extension = (String) map.get("extension"); + final Object confData = map.get("configuration_data"); + return new ExtensionConfiguration(sid, extension, true, confData, null, null, null); + } + private Map toMap(final ExtensionConfiguration extensionConfiguration) { final Map map = new HashMap(); map.put("sid", DaoUtils.writeSid(extensionConfiguration.getSid())); @@ -276,4 +283,86 @@ private Map toMap(final ExtensionConfiguration extensionConfigur map.put("enabled", extensionConfiguration.isEnabled()); return map; } + + @Override + public ExtensionConfiguration getAccountExtensionConfiguration(String accountSid, String extensionSid) { + final SqlSession session = sessions.openSession(); + ExtensionConfiguration extensionConfiguration = null; + try { + Map params = new HashMap(); + params.put("account_sid", accountSid.toString()); + params.put("extension_sid", extensionSid.toString()); + final Map result = session.selectOne(namespace + "getAccountExtensionConfiguration", params); + if (result != null) { + extensionConfiguration = toAccountsExtensionConfiguration(result); + } + return extensionConfiguration; + } finally { + session.close(); + } + } + + @Override + public void addAccountExtensionConfiguration(ExtensionConfiguration extensionConfiguration, Sid accountSid) throws ConfigurationException { + final SqlSession session = sessions.openSession(); + try { + if (extensionConfiguration != null && extensionConfiguration.getConfigurationData() != null) { + if (validate(extensionConfiguration)) { + final Map map = new HashMap(); + map.put("account_sid", DaoUtils.writeSid(accountSid)); + map.put("extension_sid", DaoUtils.writeSid(extensionConfiguration.getSid())); + + if (extensionConfiguration.getConfigurationData() != null) + map.put("configuration_data", extensionConfiguration.getConfigurationData()); + + session.insert(namespace + "addAccountExtensionConfiguration", map); + session.commit(); + } else { + throw new ConfigurationException("Exception trying to add new configuration, validation failed. configuration type: " + + extensionConfiguration.getConfigurationType()); + } + } + } finally { + session.close(); + } + } + + @Override + public void updateAccountExtensionConfiguration(ExtensionConfiguration extensionConfiguration, Sid accountSid) + throws ConfigurationException { + final SqlSession session = sessions.openSession(); + try { + if (extensionConfiguration != null && extensionConfiguration.getConfigurationData() != null) { + if (validate(extensionConfiguration)) { + final Map map = new HashMap(); + map.put("account_sid", DaoUtils.writeSid(accountSid)); + map.put("extension_sid", DaoUtils.writeSid(extensionConfiguration.getSid())); + + if (extensionConfiguration.getConfigurationData() != null) + map.put("configuration_data", extensionConfiguration.getConfigurationData()); + session.update(namespace + "updateAccountExtensionConfiguration", map); + } else { + throw new ConfigurationException("Exception trying to update configuration, validation failed. configuration type: " + + extensionConfiguration.getConfigurationType()); + } + } + session.commit(); + } finally { + session.close(); + } + } + + @Override + public void deleteAccountExtensionConfiguration(String accountSid, String extensionSid) { + final SqlSession session = sessions.openSession(); + try { + Map params = new HashMap(); + params.put("account_sid", accountSid.toString()); + params.put("extension_sid", extensionSid.toString()); + session.delete(namespace + "deleteAccountExtensionConfiguration", params); + session.commit(); + } finally { + session.close(); + } + } } diff --git a/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/ExtensionRequest.java b/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/ExtensionRequest.java new file mode 100755 index 0000000000..76231eba0d --- /dev/null +++ b/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/ExtensionRequest.java @@ -0,0 +1,46 @@ +/* + * TeleStax, Open Source Cloud Communications + * Copyright 2011-2013, Telestax Inc and individual contributors + * by the @authors tag. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.restcomm.connect.extension.api; +import org.apache.commons.configuration.Configuration; + +public class ExtensionRequest { + private Object payload; + private Configuration configuration; + + public ExtensionRequest() {} + + public Object getObject() { + return payload; + } + + public void setObject(Object object) { + this.payload = object; + } + + public void setConfiguration(Configuration configuration) { + this.configuration = configuration; + } + + public Configuration getConfiguration() { + return this.configuration; + } + +} diff --git a/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/ExtensionResponse.java b/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/ExtensionResponse.java index 065e2aff1f..2bbf8c71b6 100644 --- a/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/ExtensionResponse.java +++ b/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/ExtensionResponse.java @@ -26,7 +26,7 @@ */ public class ExtensionResponse { private Object object; - private boolean allowed; + private boolean allowed = true; public ExtensionResponse() {} diff --git a/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/MessageExtensionResponse.java b/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/MessageExtensionResponse.java new file mode 100644 index 0000000000..de1b637ff5 --- /dev/null +++ b/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/MessageExtensionResponse.java @@ -0,0 +1,27 @@ +/* + * TeleStax, Open Source Cloud Communications + * Copyright 2011-2013, Telestax Inc and individual contributors + * by the @authors tag. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +/** + * When an Extension returns a MessageReponse, RC will reconfigure the message it will send + */ +package org.restcomm.connect.extension.api; +public class MessageExtensionResponse extends ExtensionResponse { + //TODO: needs discussion, definition +} diff --git a/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/NodeExtensionResponse.java b/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/NodeExtensionResponse.java new file mode 100644 index 0000000000..ed3b2f4965 --- /dev/null +++ b/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/NodeExtensionResponse.java @@ -0,0 +1,27 @@ +/* + * TeleStax, Open Source Cloud Communications + * Copyright 2011-2013, Telestax Inc and individual contributors + * by the @authors tag. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +/** + * When an Extension returns a NodeReponse, RC will reconfigure the specific RC node it is in + */ +package org.restcomm.connect.extension.api; +public class NodeExtensionResponse extends ExtensionResponse { + //TODO: needs discussion, definition +} diff --git a/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/RestcommExtensionGeneric.java b/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/RestcommExtensionGeneric.java index 1fd04ff3a2..96116be9c6 100644 --- a/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/RestcommExtensionGeneric.java +++ b/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/RestcommExtensionGeneric.java @@ -57,7 +57,7 @@ public interface RestcommExtensionGeneric { * and either block/allow it or modify the session before Restcomm process it * @return ExtensionResponse see ExtensionResponse */ - ExtensionResponse preOutboundAction(Object message); + ExtensionResponse preOutboundAction(ExtensionRequest extensionRequest); /** * Method that will be executed AFTER the process of an Outbound session * Implement this method so you will be able to check the Outgoing session @@ -78,4 +78,15 @@ public interface RestcommExtensionGeneric { */ ExtensionResponse postApiAction(ApiRequest apiRequest); + /** + * Extension name getter + * @return String name of Extension + */ + String getName(); + + /** + * Extension version getter + * @return String version of Extension + */ + String getVersion(); } diff --git a/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/SessionExtensionResponse.java b/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/SessionExtensionResponse.java new file mode 100644 index 0000000000..9d8013a87f --- /dev/null +++ b/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/SessionExtensionResponse.java @@ -0,0 +1,44 @@ +/* + * TeleStax, Open Source Cloud Communications + * Copyright 2011-2013, Telestax Inc and individual contributors + * by the @authors tag. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +/** + * When an Extension returns a SessionReponse, RC will reconfigure the session it is in + */ +package org.restcomm.connect.extension.api; +import org.apache.commons.configuration.Configuration; +public class SessionExtensionResponse extends ExtensionResponse { + //TODO: needs discussion, definition + /** + * The Extension is expected to populate the session specific + * Configuration + */ + public void setConfiguration(Configuration configuration){ + super.setObject(configuration); + } + + /** + * RC expects an Extension to populate Configuration when it returns + * this SessionResponse + * @return Configuration object + */ + public Configuration getConfiguration(){ + return (Configuration) super.getObject(); + } +} diff --git a/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/SystemExtensionResponse.java b/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/SystemExtensionResponse.java new file mode 100644 index 0000000000..4df9d6a039 --- /dev/null +++ b/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/SystemExtensionResponse.java @@ -0,0 +1,27 @@ +/* + * TeleStax, Open Source Cloud Communications + * Copyright 2011-2013, Telestax Inc and individual contributors + * by the @authors tag. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +/** + * When an Extension returns a SystemReponse, RC will reconfigure itself systemwide + */ +package org.restcomm.connect.extension.api; +public class SystemExtensionResponse extends ExtensionResponse { + //TODO: needs discussion, definition +} diff --git a/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/TransactionExtensionResponse.java b/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/TransactionExtensionResponse.java new file mode 100644 index 0000000000..c3ac493ab2 --- /dev/null +++ b/restcomm/restcomm.extension.api/src/main/java/org/restcomm/connect/extension/api/TransactionExtensionResponse.java @@ -0,0 +1,28 @@ +/* + * TeleStax, Open Source Cloud Communications + * Copyright 2011-2013, Telestax Inc and individual contributors + * by the @authors tag. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +/** + * When an Extension returns a TransactionReponse, RC will reconfigure the transaction it + * is in + */ +package org.restcomm.connect.extension.api; +public class TransactionExtensionResponse extends ExtensionResponse { + //TODO: needs discussion, definition +} \ No newline at end of file diff --git a/restcomm/restcomm.extension.controller/pom.xml b/restcomm/restcomm.extension.controller/pom.xml index 3b982e8935..94e5d2fe42 100644 --- a/restcomm/restcomm.extension.controller/pom.xml +++ b/restcomm/restcomm.extension.controller/pom.xml @@ -46,6 +46,22 @@ ${sipservletapi.version} provided + + org.restcomm + restcomm-connect.dao + ${project.version} + + + org.restcomm + restcomm-connect.commons + ${project.version} + + + org.apache.maven + maven-artifact + 3.1.0 + + \ No newline at end of file diff --git a/restcomm/restcomm.extension.controller/src/main/java/org/restcomm/connect/extension/configuration/DefaultExtensionConfiguration.java b/restcomm/restcomm.extension.controller/src/main/java/org/restcomm/connect/extension/configuration/DefaultExtensionConfiguration.java new file mode 100755 index 0000000000..fa5690730a --- /dev/null +++ b/restcomm/restcomm.extension.controller/src/main/java/org/restcomm/connect/extension/configuration/DefaultExtensionConfiguration.java @@ -0,0 +1,262 @@ +/* + * TeleStax, Open Source Cloud Communications + * Copyright 2011-2016, Telestax Inc and individual contributors + * by the @authors tag. + * + * This program is free software: you can redistribute it and/or modify + * under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation; either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see + */ +package org.restcomm.connect.extension.configuration; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.InputStream; +import java.util.HashMap; + +import java.util.Map; + +import org.apache.ibatis.exceptions.PersistenceException; +import org.apache.log4j.Logger; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.joda.time.DateTime; +import org.restcomm.connect.commons.dao.Sid; +import org.restcomm.connect.dao.DaoManager; +import org.restcomm.connect.dao.ExtensionsConfigurationDao; +import org.restcomm.connect.extension.api.ConfigurationException; +import org.restcomm.connect.extension.api.ExtensionConfiguration; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.stream.JsonReader; + +public class DefaultExtensionConfiguration { + public enum PropertyType { + VERSION("version"); + private String value; + + private PropertyType(String value) { + this.value = value; + } + }; + + private static final Logger logger = Logger.getLogger(DefaultExtensionConfiguration.class); + private boolean workingWithLocalConf; + private ExtensionsConfigurationDao extensionConfigurationDao; + private ExtensionConfiguration extensionConfiguration; + private JsonObject configurationJsonObj; + private JsonParser jsonParser; + private DaoManager daoManager; + private Gson gson; + private JsonObject defaultConfigurationJsonObj; + private String extensionName; + private DefaultArtifactVersion defVersion; + private HashMap specificConfigurationMap; + private Sid sid; + private String localConfigPath; + + public DefaultExtensionConfiguration() { + this.sid = new Sid("EX00000000000000000000000000000001"); + this.localConfigPath = ""; + } + + public DefaultExtensionConfiguration(final DaoManager daoManager, String extensionName, String localConfigPath) { + try { + init(daoManager, extensionName, localConfigPath); + } catch (Exception e) { + logger.error("Exception initializing"); + } + } + + public void init(final DaoManager daoManager, String extensionName, String localConfigPath) throws ConfigurationException { + try { + this.setDaoManager(daoManager); + this.extensionConfigurationDao = daoManager.getExtensionsConfigurationDao(); + + if (extensionName.isEmpty() && localConfigPath.isEmpty()) { + throw new ConfigurationException("extensionName or local config cant be empty"); + } + if (!extensionName.isEmpty()) { + this.extensionName = extensionName; + } + if (!localConfigPath.isEmpty()) { + // Load the default extensionConfiguration from file + this.defaultConfigurationJsonObj = loadDefaultConfiguration(localConfigPath); + + configurationJsonObj = this.defaultConfigurationJsonObj; + + // Get the extension name from default extensionConfiguration + String temp = defaultConfigurationJsonObj.get("extension_name").getAsString(); + if (!temp.isEmpty()) { + extensionName = temp; + } + defVersion = new DefaultArtifactVersion(defaultConfigurationJsonObj.get("version").getAsString()); + } + // Load extensionConfiguration from DB + extensionConfiguration = extensionConfigurationDao.getConfigurationByName(extensionName); + + // try fetch sid from name + if (extensionConfiguration == null) { + // If extensionConfiguration from DB is null then add the default values to DB + this.sid = Sid.generate(Sid.Type.EXTENSION_CONFIGURATION); + extensionConfiguration = new ExtensionConfiguration(sid, this.extensionName, true, + defaultConfigurationJsonObj.toString(), ExtensionConfiguration.configurationType.JSON, DateTime.now()); + extensionConfigurationDao.addConfiguration(extensionConfiguration); + + } else { + // Get configuration object + this.sid = extensionConfiguration.getSid(); + // try get default config data + JsonObject dbConfiguration = null; + + DefaultArtifactVersion currentVersion = null; + try { + dbConfiguration = (JsonObject) jsonParser.parse((String) extensionConfiguration.getConfigurationData()); + if (dbConfiguration.get("version") != null) { + currentVersion = new DefaultArtifactVersion(dbConfiguration.get("version").getAsString()); + } + + if (dbConfiguration != null && (currentVersion == null || currentVersion.compareTo(defVersion) < 0)) { + if (logger.isInfoEnabled()) { + logger.info("Configuration found in the DB is older version than the default one: " + + defVersion.toString()); + } + + for (Map.Entry jsonElementEntry : defaultConfigurationJsonObj.entrySet()) { + if (!jsonElementEntry.getKey().equalsIgnoreCase("specifics_configuration") + && dbConfiguration.get(jsonElementEntry.getKey()) == null) { + dbConfiguration.add(jsonElementEntry.getKey(), jsonElementEntry.getValue()); + } + } + if (dbConfiguration.get("version") != null) { + dbConfiguration.remove("version"); + } + dbConfiguration.addProperty("version", defaultConfigurationJsonObj.get("version").getAsString()); + + extensionConfiguration = new ExtensionConfiguration(extensionConfiguration.getSid(), extensionName, + extensionConfiguration.isEnabled(), dbConfiguration.toString(), + ExtensionConfiguration.configurationType.JSON, DateTime.now()); + extensionConfigurationDao.updateConfiguration(extensionConfiguration); + } + configurationJsonObj = dbConfiguration; + // Load Specific Configuration Map + // loadSpecificConfigurationMap(configurationJsonObj); + } catch (Exception e) { + } + + } + if (logger.isInfoEnabled()) { + logger.info("Finished loading configuration for extension: " + extensionName); + } + } catch (ConfigurationException configurationException) { + String errorMessage = "Exception during " + this.getClass() + " Configuration constructor "; + if (logger.isDebugEnabled()) { + logger.debug(errorMessage + configurationException); + } + throw new ConfigurationException(errorMessage); + } catch (PersistenceException persistenceException) { + if (logger.isDebugEnabled()) { + logger.debug("PersistenceException during " + this.getClass() + " init, will fallback to default configuration"); + } + workingWithLocalConf = true; + } catch (IOException e) { + logger.debug("IOException during " + this.getClass()); + } + } + + public JsonObject loadDefaultConfiguration(String localConfigFilePath) throws IOException { + JsonObject jsonObj = null; + jsonParser = new JsonParser(); + InputStream in = (InputStream) getClass().getResourceAsStream(localConfigFilePath); + BufferedReader inReader = new BufferedReader(new InputStreamReader(in)); + JsonReader reader = new JsonReader(inReader); + JsonElement jsonElement = jsonParser.parse(reader); + jsonObj = (JsonObject) jsonElement; + in.close(); + inReader.close(); + reader.close(); + return jsonObj; + } + + public void reloadConfiguration() { + if (!workingWithLocalConf) { + if (extensionConfigurationDao.isLatestVersionByName(extensionName, extensionConfiguration.getDateUpdated())) { + extensionConfiguration = extensionConfigurationDao.getConfigurationByName(extensionName); + String updatedConf = (String) extensionConfiguration.getConfigurationData(); + configurationJsonObj = (JsonObject) jsonParser.parse(updatedConf); + // loadSpecificConfigurationMap(configurationJsonObj); + if (logger.isInfoEnabled()) { + logger.info(this.extensionName + " extension configuration reloaded"); + } + } + } + } + + public boolean isEnabled() { + reloadConfiguration(); + if (extensionConfiguration != null) { + return extensionConfiguration.isEnabled(); + } else { + return true; + } + } + + public String getVersion() { + reloadConfiguration(); + String ver = configurationJsonObj.get(PropertyType.VERSION.value).getAsString(); + return ver; + } + + public Sid getSid() { + return this.sid; + } + + public void loadSpecificConfigurationMap(final JsonObject json) { + JsonArray specificConfJsonArray = json.getAsJsonArray(); + // JsonArray specificConfJsonArray = json.getAsJsonArray("specifics_configuration"); + // if (specificConfJsonArray != null) { + // specificConfigurationMap = new HashMap(); + // Iterator iter = specificConfJsonArray.iterator(); + // while (iter.hasNext()) { + // JsonElement elem = iter.next(); + // if (elem.getAsJsonObject().get("sid") != null) { + // specificConfigurationMap.put(sid, value); + // } + // } + // } + // return map + } + + // getConfigAsJson + // getConfigAsConfiguration + // getConfigAsHashMap + /*public void getSpecificConfigurationMapAsXml() { + }*/ + + /** + * @return the daoManager + */ + public DaoManager getDaoManager() { + return daoManager; + } + + /** + * @param daoManager the daoManager to set + */ + public void setDaoManager(DaoManager daoManager) { + this.daoManager = daoManager; + } +} diff --git a/restcomm/restcomm.extension.controller/src/main/java/org/restcomm/connect/extension/controller/ExtensionController.java b/restcomm/restcomm.extension.controller/src/main/java/org/restcomm/connect/extension/controller/ExtensionController.java index d3912c76ac..0668660738 100644 --- a/restcomm/restcomm.extension.controller/src/main/java/org/restcomm/connect/extension/controller/ExtensionController.java +++ b/restcomm/restcomm.extension.controller/src/main/java/org/restcomm/connect/extension/controller/ExtensionController.java @@ -1,8 +1,16 @@ package org.restcomm.connect.extension.controller; +import org.restcomm.connect.extension.api.ExtensionRequest; +import org.restcomm.connect.extension.api.ExtensionResponse; import org.restcomm.connect.extension.api.ExtensionType; +import org.restcomm.connect.extension.api.MessageExtensionResponse; +import org.restcomm.connect.extension.api.NodeExtensionResponse; import org.restcomm.connect.extension.api.RestcommExtension; import org.restcomm.connect.extension.api.RestcommExtensionGeneric; +import org.restcomm.connect.extension.api.SessionExtensionResponse; +import org.restcomm.connect.extension.api.SystemExtensionResponse; +import org.restcomm.connect.extension.api.TransactionExtensionResponse; +import org.apache.commons.configuration.Configuration; import org.apache.log4j.Logger; import java.util.List; @@ -80,4 +88,62 @@ public void registerExtension(final RestcommExtensionGeneric extension) { } } } + + public ExtensionResponse executePreOutboundAction(final ExtensionRequest er, List extensions) { + //FIXME: if we have more than one extension in chain + // and all of them are successful, we only receive the last + // extensionResponse + ExtensionResponse response = new ExtensionResponse(); + if (extensions != null && extensions.size() > 0) { + + for (RestcommExtensionGeneric extension : extensions) { + if(logger.isInfoEnabled()) { + logger.info( extension.getName()+" is enabled="+extension.isEnabled()); + } + if (extension.isEnabled()) { + response = extension.preOutboundAction(er); + //fail fast + if (!response.isAllowed()){ + break; + } + } + } + } + return response; + } + + public ExtensionResponse executePostOutboundAction(final Object er, List extensions) { + ExtensionResponse response = new ExtensionResponse(); + //TODO: implement actual calls + return response; + } + + //FIXME: there must be a fixed contract between the returned extensions object + // and how the system will reconfigure itself with the type of ExtensionResponse + // for now we will just map SessionExtensionResponse to Configuration object + //FIXME: method signature is too restrictive + public Object handleExtensionResponse(ExtensionResponse response, Configuration configuration){ + //check type of extension + //FIXME: hack to default + Object object = configuration; + if(response instanceof SystemExtensionResponse){ + //TODO:return systemwide level customization behaviour + } + if(response instanceof NodeExtensionResponse){ + //TODO:return node level customization behaviour + } + if(response instanceof SessionExtensionResponse){ + SessionExtensionResponse ser = (SessionExtensionResponse) response; + Configuration config = ser.getConfiguration(); + + object = config; + } + if(response instanceof TransactionExtensionResponse){ + //TODO:return transaction level customization behaviour + } + if(response instanceof MessageExtensionResponse){ + //TODO:return message level customization behaviour + } + return object; + } } diff --git a/restcomm/restcomm.http/src/main/java/org/restcomm/connect/http/ExtensionsConfigurationEndpoint.java b/restcomm/restcomm.http/src/main/java/org/restcomm/connect/http/ExtensionsConfigurationEndpoint.java index ba54e6713b..856877f42e 100644 --- a/restcomm/restcomm.http/src/main/java/org/restcomm/connect/http/ExtensionsConfigurationEndpoint.java +++ b/restcomm/restcomm.http/src/main/java/org/restcomm/connect/http/ExtensionsConfigurationEndpoint.java @@ -22,6 +22,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.thoughtworks.xstream.XStream; + import org.apache.commons.configuration.Configuration; import org.joda.time.DateTime; import org.restcomm.connect.commons.dao.Sid; @@ -87,23 +88,44 @@ void init() { * @param responseType * @return */ - protected Response getConfiguration(final String extensionId, final MediaType responseType) { + protected Response getConfiguration(final String extensionId, final Sid accountSid, final MediaType responseType) { //Parameter "extensionId" could be the extension Sid or extension name. if (!isSuperAdmin()) { throw new InsufficientPermission(); } ExtensionConfiguration extensionConfiguration = null; + ExtensionConfiguration extensionAccountConfiguration = null; + Sid extensionSid = null; + String extensionName = null; + + if(Sid.pattern.matcher(extensionId).matches()){ + extensionSid = new Sid(extensionId); + } else { + extensionName = extensionId; + } if (Sid.pattern.matcher(extensionId).matches()) { try { - extensionConfiguration = extensionsConfigurationDao.getConfigurationBySid(new Sid(extensionId)); + extensionConfiguration = extensionsConfigurationDao.getConfigurationBySid(extensionSid); } catch (Exception e) { return status(NOT_FOUND).build(); } } else { try { - extensionConfiguration = extensionsConfigurationDao.getConfigurationByName(extensionId); + extensionConfiguration = extensionsConfigurationDao.getConfigurationByName(extensionName); + } catch (Exception e) { + return status(NOT_FOUND).build(); + } + } + + if (accountSid!=null) { + if(extensionSid == null ){ + extensionSid = extensionConfiguration.getSid(); + } + try { + extensionAccountConfiguration = extensionsConfigurationDao.getAccountExtensionConfiguration(accountSid.toString(), extensionSid.toString()); + extensionConfiguration.setConfigurationData(extensionAccountConfiguration.getConfigurationData(), extensionAccountConfiguration.getConfigurationType()); } catch (Exception e) { return status(NOT_FOUND).build(); } @@ -116,7 +138,7 @@ protected Response getConfiguration(final String extensionId, final MediaType re final RestCommResponse response = new RestCommResponse(extensionConfiguration); return ok(xstream.toXML(response), APPLICATION_XML).build(); } else if (APPLICATION_JSON_TYPE == responseType) { - return ok(gson.toJson(extensionConfiguration), APPLICATION_JSON).build(); + return ok(gson.toJson(extensionConfiguration.getConfigurationData()), APPLICATION_JSON).build(); } else { return null; } @@ -146,6 +168,7 @@ private ExtensionConfiguration createFrom(final MultivaluedMap d DateTime dateCreated = DateTime.now(); DateTime dateUpdated = DateTime.now(); ExtensionConfiguration extensionConfiguration = new ExtensionConfiguration(sid, extension, enabled, configurationData, configurationType, dateCreated, dateUpdated); + return extensionConfiguration; } @@ -154,17 +177,37 @@ protected Response postConfiguration(final MultivaluedMap data, throw new InsufficientPermission(); } - ExtensionConfiguration extensionConfiguration = null; - try { - extensionConfiguration = createFrom(data, responseType); - } catch (final NullPointerException exception) { - return status(BAD_REQUEST).entity(exception.getMessage()).build(); + Sid accountSid = null; + + String accountSidQuery = data.getFirst("AccountSid"); + if(accountSidQuery != null && !accountSidQuery.isEmpty()){ + accountSid = new Sid(accountSidQuery); } + //if extension doesnt exist, add new extension + String extensionName = data.getFirst("ExtensionName"); + ExtensionConfiguration extensionConfiguration = extensionsConfigurationDao.getConfigurationByName(extensionName); - try { - extensionsConfigurationDao.addConfiguration(extensionConfiguration); - } catch (ConfigurationException exception) { - return status(NOT_ACCEPTABLE).entity(exception.getMessage()).build(); + if(extensionConfiguration==null){ + try { + extensionConfiguration = createFrom(data, responseType); + } catch (final NullPointerException exception) { + return status(BAD_REQUEST).entity(exception.getMessage()).build(); + } + try { + extensionsConfigurationDao.addConfiguration(extensionConfiguration); + } catch (ConfigurationException exception) { + return status(NOT_ACCEPTABLE).entity(exception.getMessage()).build(); + } + } + if (accountSid!=null) { + try { + Object configurationData = data.getFirst("ConfigurationData"); + // if accountSid exists, then this configuration is account specific, if it doesnt then its global config + extensionConfiguration.setConfigurationData(configurationData, extensionConfiguration.getConfigurationType()); + extensionsConfigurationDao.addAccountExtensionConfiguration(extensionConfiguration, accountSid); + } catch (ConfigurationException exception) { + return status(NOT_ACCEPTABLE).entity(exception.getMessage()).build(); + } } if (APPLICATION_JSON_TYPE == responseType) { @@ -192,6 +235,12 @@ protected Response updateConfiguration(String extensionSid, MultivaluedMaprestcomm-connect.sms.api - + + org.restcomm.smpp + smpp-extensions + 1.0.13 + org.restcomm restcomm-connect.commons diff --git a/restcomm/restcomm.sms.api/src/main/java/org/restcomm/connect/sms/api/SmsSessionRequest.java b/restcomm/restcomm.sms.api/src/main/java/org/restcomm/connect/sms/api/SmsSessionRequest.java index e6d722a914..0665fdb146 100644 --- a/restcomm/restcomm.sms.api/src/main/java/org/restcomm/connect/sms/api/SmsSessionRequest.java +++ b/restcomm/restcomm.sms.api/src/main/java/org/restcomm/connect/sms/api/SmsSessionRequest.java @@ -22,7 +22,7 @@ import java.util.concurrent.ConcurrentHashMap; import javax.servlet.sip.SipServletRequest; - +import org.restcomm.smpp.parameter.TlvSet; import org.restcomm.connect.commons.annotations.concurrency.Immutable; /** @@ -36,6 +36,7 @@ public final class SmsSessionRequest { private final Encoding encoding; private final SipServletRequest origRequest; private final ConcurrentHashMap customHeaders; + private final TlvSet tlvSet; public enum Encoding { UCS_2("UCS-2"), @@ -52,9 +53,17 @@ public String toString() { return name; } } - - //TODO need to check which is using the SmsSessionRequest and modify accordingly to include or not the custom headers - public SmsSessionRequest(final String from, final String to, final String body, final Encoding encoding, final SipServletRequest origRequest, final ConcurrentHashMap customHeaders) { + //FIXME: all signatures should be changed, encoding, sipReq, tlvSet are optional and should come after headers + //(from, to, body, headers, encoding, sipReq, tlvSet) + //(from, to, body, headers, encoding, sipReq) + //(from, to, body, headers, encoding, tlvSet) + //(from, to, body, headers, sipReq, tlvSet) + //(from, to, body, headers, encoding) + //(from, to, body, headers, sipReq) + //(from, to, body, headers, tlvSet) + //(from, to, body) + + public SmsSessionRequest(final String from, final String to, final String body, final Encoding encoding, final SipServletRequest origRequest, TlvSet tlvSet, final ConcurrentHashMap customHeaders) { super(); this.from = from; this.to = to; @@ -62,18 +71,41 @@ public SmsSessionRequest(final String from, final String to, final String body, this.body = body; this.customHeaders = customHeaders; this.encoding = encoding; + this.tlvSet = tlvSet; + + } + + //TODO need to check which is using the SmsSessionRequest and modify accordingly to include or not the custom headers + public SmsSessionRequest(final String from, final String to, final String body, final Encoding encoding, final SipServletRequest origRequest, final ConcurrentHashMap customHeaders) { + this(from, to, body, encoding, origRequest, null, customHeaders); + } + + public SmsSessionRequest(final String from, final String to, final String body, final Encoding encoding, final TlvSet tlvSet, final ConcurrentHashMap customHeaders) { + this(from, to, body, encoding, null, tlvSet, customHeaders); + } + + public SmsSessionRequest(final String from, final String to, final String body, final SipServletRequest origRequest, final TlvSet tlvSet, final ConcurrentHashMap customHeaders) { + this(from, to, body, Encoding.GSM, null, tlvSet, customHeaders); } public SmsSessionRequest(final String from, final String to, final String body, final SipServletRequest origRequest, final ConcurrentHashMap customHeaders) { - this(from, to, body, Encoding.GSM, origRequest, customHeaders); + this(from, to, body, Encoding.GSM, origRequest, null, customHeaders); } public SmsSessionRequest(final String from, final String to, final String body, final Encoding encoding, final ConcurrentHashMap customHeaders) { - this(from, to, body, encoding, null, customHeaders); + this(from, to, body, encoding, null, null, customHeaders); + } + + public SmsSessionRequest(final String from, final String to, final String body, final TlvSet tlvSet, final ConcurrentHashMap customHeaders) { + this(from, to, body, Encoding.GSM, null, null, customHeaders); } public SmsSessionRequest(final String from, final String to, final String body, final ConcurrentHashMap customHeaders) { - this(from, to, body, Encoding.GSM, null, customHeaders); + this(from, to, body, Encoding.GSM, null, null, customHeaders); + } + + public SmsSessionRequest(final String from, final String to, final String body) { + this(from, to, body, Encoding.GSM, null, null, null); } public String from() { @@ -92,6 +124,10 @@ public Encoding encoding() { return encoding; } + public TlvSet getTlvSet() { + return tlvSet; + } + public SipServletRequest getOrigRequest() { return origRequest; } diff --git a/restcomm/restcomm.sms/pom.xml b/restcomm/restcomm.sms/pom.xml index b4778f7ce3..709d8ced9d 100644 --- a/restcomm/restcomm.sms/pom.xml +++ b/restcomm/restcomm.sms/pom.xml @@ -17,6 +17,11 @@ + + org.restcomm.smpp + smpp-extensions + 1.0.13 + log4j log4j diff --git a/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/SmsService.java b/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/SmsService.java index dc6dc33088..86ff8949a9 100644 --- a/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/SmsService.java +++ b/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/SmsService.java @@ -48,6 +48,7 @@ import org.restcomm.connect.dao.entities.SmsMessage.Direction; import org.restcomm.connect.dao.entities.SmsMessage.Status; import org.restcomm.connect.extension.api.ExtensionResponse; +import org.restcomm.connect.extension.api.ExtensionRequest; import org.restcomm.connect.extension.api.ExtensionType; import org.restcomm.connect.extension.api.RestcommExtensionException; import org.restcomm.connect.extension.api.RestcommExtensionGeneric; @@ -60,10 +61,13 @@ import org.restcomm.connect.sms.api.SmsServiceResponse; import org.restcomm.connect.sms.api.SmsSessionAttribute; import org.restcomm.connect.sms.api.SmsSessionRequest; + import org.restcomm.connect.telephony.api.TextMessage; import org.restcomm.connect.telephony.api.util.B2BUAHelper; import org.restcomm.connect.telephony.api.util.CallControlHelper; +import org.restcomm.smpp.parameter.TlvSet; + import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.sip.SipApplicationSession; @@ -72,6 +76,7 @@ import javax.servlet.sip.SipServletRequest; import javax.servlet.sip.SipServletResponse; import javax.servlet.sip.SipURI; + import java.io.IOException; import java.math.BigDecimal; import java.net.URI; @@ -208,8 +213,8 @@ private void message(final Object message) throws IOException { final SipServletResponse trying = request.createResponse(SipServletResponse.SC_TRYING); trying.send(); - - ActorRef session = session(); + //TODO:do extensions check here too? + ActorRef session = session(this.configuration); // Create an SMS detail record. final Sid sid = Sid.generate(Sid.Type.SMS_MESSAGE); final SmsMessage.Builder builder = SmsMessage.builder(); @@ -236,7 +241,8 @@ private void message(final Object message) throws IOException { // Store the sms record in the sms session. session.tell(new SmsSessionAttribute("record", record), self()); // Send the SMS. - final SmsSessionRequest sms = new SmsSessionRequest(client.getLogin(), toUser, new String(request.getRawContent()),request, null); + TlvSet tlvSet = new TlvSet(); + final SmsSessionRequest sms = new SmsSessionRequest(client.getLogin(), toUser, new String(request.getRawContent()), request, tlvSet, null); monitoringService.tell(new TextMessage(((SipURI)request.getFrom().getURI()).getUser(), ((SipURI)request.getTo().getURI()).getUser(), TextMessage.SmsState.INBOUND_TO_PROXY_OUT), self); session.tell(sms, self()); } @@ -312,7 +318,8 @@ private boolean redirectToHostedSmsApp(final ActorRef self, final SipServletRequ } interpreter = builder.build(); } - final ActorRef session = session(); + //TODO:do extensions check here too? + final ActorRef session = session(this.configuration); session.tell(request, self); final StartInterpreter start = new StartInterpreter(session); interpreter.tell(start, self); @@ -325,39 +332,34 @@ private boolean redirectToHostedSmsApp(final ActorRef self, final SipServletRequ return isFoundHostedApp; } - private boolean executePreOutboundAction(final Object message) { - if (extensions != null && extensions.size() > 0) { - for (RestcommExtensionGeneric extension : extensions) { - if (extension.isEnabled()) { - ExtensionResponse response = extension.preOutboundAction(message); - if (!response.isAllowed()) - return false; - } - } - } - return true; - } - - private boolean executePostOutboundAction(final Object message) { - return true; - } - @Override public void onReceive(final Object message) throws Exception { final UntypedActorContext context = getContext(); final Class klass = message.getClass(); final ActorRef self = self(); final ActorRef sender = sender(); + ExtensionController ec = ExtensionController.getInstance(); if (CreateSmsSession.class.equals(klass)) { - if (executePreOutboundAction(message)) { - final ActorRef session = session(); + //retrieve extension object + //FIXME:we need a real interface here rather than amending a preexisting request interface + ExtensionRequest er = new ExtensionRequest(); + er.setObject(message); + er.setConfiguration(this.configuration); + + ExtensionResponse extensionResponse = ec.executePreOutboundAction(er, this.extensions); + if (extensionResponse.isAllowed()) { + //pass in response object to sms session + Object obj = ec.handleExtensionResponse(extensionResponse, this.configuration); + //FIXME:not all instances of extensions should modify + //a session configuration, we should do checks here + final ActorRef session = session((Configuration)obj); final SmsServiceResponse response = new SmsServiceResponse(session); sender.tell(response, self); } else { final SmsServiceResponse response = new SmsServiceResponse(new RestcommExtensionException("Now allowed to create SmsSession")); sender.tell(response, self()); } - executePostOutboundAction(message); + ec.executePostOutboundAction(message, this.extensions); } else if (DestroySmsSession.class.equals(klass)) { final DestroySmsSession request = (DestroySmsSession) message; final ActorRef session = request.session(); @@ -418,13 +420,13 @@ private SipURI outboundInterface() { return result; } - private ActorRef session() { + private ActorRef session(final Configuration p_configuration) { final Props props = new Props(new UntypedActorFactory() { private static final long serialVersionUID = 1L; @Override public UntypedActor create() throws Exception { - return new SmsSession(configuration, sipFactory, outboundInterface(), storage, monitoringService, servletContext); + return new SmsSession(p_configuration, sipFactory, outboundInterface(), storage, monitoringService, servletContext); } }); return system.actorOf(props); diff --git a/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/SmsSession.java b/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/SmsSession.java index dc1a9847b2..4b4cc4f3b2 100644 --- a/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/SmsSession.java +++ b/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/SmsSession.java @@ -25,6 +25,9 @@ import akka.event.LoggingAdapter; import com.cloudhopper.commons.charset.Charset; import com.cloudhopper.commons.charset.CharsetUtil; +import com.cloudhopper.commons.util.ByteArrayUtil; +import com.cloudhopper.smpp.tlv.Tlv; +import com.cloudhopper.smpp.SmppConstants; import com.google.common.collect.ImmutableMap; import org.apache.commons.configuration.Configuration; import org.restcomm.connect.sms.api.GetLastSmsRequest; @@ -45,6 +48,7 @@ import org.restcomm.connect.sms.smpp.SmppMessageHandler; import org.restcomm.connect.sms.smpp.SmppOutboundMessageEntity; import org.restcomm.connect.telephony.api.TextMessage; +import org.restcomm.smpp.parameter.TlvSet; import javax.servlet.ServletContext; import javax.servlet.sip.SipApplicationSession; @@ -61,6 +65,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; + /** * @author quintana.thomas@gmail.com (Thomas Quintana) */ @@ -78,6 +83,7 @@ public final class SmsSession extends UntypedActor { private ConcurrentHashMap customRequestHeaderMap = new ConcurrentHashMap(); // Map for custom headers from HTTP App Server (when creating outbound SIP MESSAGE) private ConcurrentHashMap customHttpHeaderMap; + private TlvSet tlvSet; private final DaoManager storage; @@ -113,6 +119,17 @@ public SmsSession(final Configuration configuration, final SipFactory factory, f this.externalIP = this.configuration.subset("runtime-settings").getString("external-ip"); if (externalIP == null || externalIP.isEmpty() || externalIP.equals("")) externalIP = defaultHost; + + this.tlvSet = new TlvSet(); + if(!this.configuration.subset("outbound-sms").isEmpty()) { + //TODO: handle arbitrary keys instead of just TAG_DEST_NETWORK_ID + try { + String valStr = this.configuration.subset("outbound-sms").getString("destination_network_id"); + this.tlvSet.addOptionalParameter(new Tlv(SmppConstants.TAG_DEST_NETWORK_ID,ByteArrayUtil.toByteArray(Integer.parseInt(valStr)))); + } catch (Exception e) { + logger.error("Error while parsing tlv configuration " + e); + } + } } private void inbound(final Object message) throws IOException { @@ -135,7 +152,8 @@ private void inbound(final Object message) throws IOException { } } // Store the last sms event. - last = new SmsSessionRequest(from, to, body, customRequestHeaderMap); + + last = new SmsSessionRequest(from, to, body, this.tlvSet, customRequestHeaderMap); if (initial == null) { initial = last; } @@ -154,7 +172,8 @@ private void inbound(final Object message) throws IOException { encoding = SmsSessionRequest.Encoding.GSM; } // Store the last sms event. - last = new SmsSessionRequest (request.getSmppFrom(), request.getSmppTo(), request.getSmppContent(), encoding, null); + + last = new SmsSessionRequest (request.getSmppFrom(), request.getSmppTo(), request.getSmppContent(), encoding, request.getTlvSet(), null); if (initial == null) { initial = last; } @@ -275,7 +294,7 @@ private void outbound(final Object message) { if(logger.isInfoEnabled()) { logger.info("Destination is not a local registered client, therefore, sending through SMPP to: " + last.to() ); } - if (sendUsingSmpp(last.from(), last.to(), last.body(), charset)) + if (sendUsingSmpp(last.from(), last.to(), last.body(), tlvSet, charset)) return; } @@ -332,16 +351,19 @@ private void outbound(final Object message) { } // Log the exception. logger.error(exception.getMessage(), exception); - }} - + } + } private boolean sendUsingSmpp(String from, String to, String body, Charset encoding) { + return sendUsingSmpp(from, to, body, null, encoding); + } + private boolean sendUsingSmpp(String from, String to, String body, TlvSet tlvSet, Charset encoding) { if ((SmppClientOpsThread.getSmppSession() != null && SmppClientOpsThread.getSmppSession().isBound()) && smppMessageHandler != null) { if(logger.isInfoEnabled()) { logger.info("SMPP session is available and connected, outbound message will be forwarded to : " + to ); logger.info("Encoding: " + encoding ); } try { - final SmppOutboundMessageEntity sms = new SmppOutboundMessageEntity(to, from, body, encoding); + final SmppOutboundMessageEntity sms = new SmppOutboundMessageEntity(to, from, body, encoding, tlvSet); smppMessageHandler.tell(sms, null); }catch (final Exception exception) { // Log the exception. diff --git a/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/smpp/SmppInboundMessageEntity.java b/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/smpp/SmppInboundMessageEntity.java index af640df549..00efb68030 100644 --- a/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/smpp/SmppInboundMessageEntity.java +++ b/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/smpp/SmppInboundMessageEntity.java @@ -1,5 +1,7 @@ package org.restcomm.connect.sms.smpp; +import org.restcomm.smpp.parameter.TlvSet; + import com.cloudhopper.commons.charset.Charset; public class SmppInboundMessageEntity { @@ -9,19 +11,24 @@ public class SmppInboundMessageEntity { private final String smppFrom; private final String smppContent; private final Charset smppEncoding; - - + private final TlvSet tlvSet; public SmppInboundMessageEntity(String smppTo, String smppFrom, String smppContent, Charset smppEncoding){ + this(smppTo, smppFrom, smppContent, smppEncoding, null); + } + public SmppInboundMessageEntity(String smppTo, String smppFrom, String smppContent, Charset smppEncoding, TlvSet tlvSet){ this.smppTo = smppTo; this.smppFrom = smppFrom; this.smppContent = smppContent; this.smppEncoding = smppEncoding; + this.tlvSet = tlvSet; } - + public final TlvSet getTlvSet(){ + return tlvSet; + } public final String getSmppTo(){ return smppTo; diff --git a/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/smpp/SmppMessageHandler.java b/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/smpp/SmppMessageHandler.java index 09a775ce98..a434af6ad7 100644 --- a/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/smpp/SmppMessageHandler.java +++ b/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/smpp/SmppMessageHandler.java @@ -16,6 +16,7 @@ import com.cloudhopper.smpp.type.SmppInvalidArgumentException; import com.cloudhopper.smpp.type.SmppTimeoutException; import com.cloudhopper.smpp.type.UnrecoverablePduException; +import com.cloudhopper.smpp.tlv.Tlv; import com.google.i18n.phonenumbers.PhoneNumberUtil; import org.apache.commons.configuration.Configuration; import org.restcomm.connect.commons.dao.Sid; @@ -26,12 +27,19 @@ import org.restcomm.connect.dao.IncomingPhoneNumbersDao; import org.restcomm.connect.dao.entities.Application; import org.restcomm.connect.dao.entities.IncomingPhoneNumber; +import org.restcomm.connect.extension.api.ExtensionRequest; +import org.restcomm.connect.extension.api.ExtensionResponse; +import org.restcomm.connect.extension.api.ExtensionType; +import org.restcomm.connect.extension.api.RestcommExtensionException; +import org.restcomm.connect.extension.api.RestcommExtensionGeneric; +import org.restcomm.connect.extension.controller.ExtensionController; import org.restcomm.connect.interpreter.StartInterpreter; import org.restcomm.connect.monitoringservice.MonitoringService; import org.restcomm.connect.sms.SmsSession; import org.restcomm.connect.sms.api.CreateSmsSession; import org.restcomm.connect.sms.api.DestroySmsSession; import org.restcomm.connect.sms.api.SmsServiceResponse; +import org.restcomm.smpp.parameter.TlvSet; import javax.servlet.ServletContext; import javax.servlet.sip.SipFactory; @@ -40,6 +48,7 @@ import java.io.IOException; import java.net.URI; import java.util.List; +import java.util.Collection; public class SmppMessageHandler extends UntypedActor { @@ -50,6 +59,8 @@ public class SmppMessageHandler extends UntypedActor { private final Configuration configuration; private final SipFactory sipFactory; private final ActorRef monitoringService; + //List of extensions for SmsService + List extensions; public SmppMessageHandler(final ServletContext servletContext) { this.servletContext = servletContext; @@ -57,6 +68,11 @@ public SmppMessageHandler(final ServletContext servletContext) { this.configuration = (Configuration) servletContext.getAttribute(Configuration.class.getName()); this.sipFactory = (SipFactory) servletContext.getAttribute(SipFactory.class.getName()); this.monitoringService = (ActorRef) servletContext.getAttribute(MonitoringService.class.getName()); + //FIXME:Should new ExtensionType.SmppMessageHandler be defined? + extensions = ExtensionController.getInstance().getExtensions(ExtensionType.SmsService); + if (logger.isInfoEnabled()) { + logger.info("SmsService extensions: "+(extensions != null ? extensions.size() : "0")); + } } @Override @@ -64,6 +80,7 @@ public void onReceive(Object message) throws Exception { final UntypedActorContext context = getContext(); final ActorRef sender = sender(); final ActorRef self = self(); + ExtensionController ec = ExtensionController.getInstance(); if (message instanceof SmppInboundMessageEntity){ if(logger.isInfoEnabled()) { logger.info("SmppMessageHandler processing Inbound Message " + message.toString()); @@ -75,9 +92,20 @@ public void onReceive(Object message) throws Exception { } outbound((SmppOutboundMessageEntity) message); } else if (message instanceof CreateSmsSession) { - final ActorRef session = session(); - final SmsServiceResponse response = new SmsServiceResponse(session); - sender.tell(response, self); + ExtensionRequest er = new ExtensionRequest(); + er.setObject(message); + er.setConfiguration(this.configuration); + ExtensionResponse extensionResponse = ec.executePreOutboundAction(er, this.extensions); + if (extensionResponse.isAllowed()) { + Object obj = ec.handleExtensionResponse(extensionResponse, this.configuration); + final ActorRef session = session((Configuration)obj); + final SmsServiceResponse response = new SmsServiceResponse(session); + sender.tell(response, self); + } else { + final SmsServiceResponse response = new SmsServiceResponse(new RestcommExtensionException("Now allowed to create SmsSession")); + sender.tell(response, self()); + } + ec.executePostOutboundAction(message, this.extensions); }else if (message instanceof DestroySmsSession) { final DestroySmsSession destroySmsSession = (DestroySmsSession) message; final ActorRef session = destroySmsSession.session(); @@ -153,8 +181,9 @@ private boolean redirectToHostedSmsApp(final ActorRef self, final SmppInboundMes builder.setFallbackMethod(number.getSmsFallbackMethod()); } interpreter = builder.build(); - - final ActorRef session = session(); + Configuration cfg = this.configuration; + //Extension + final ActorRef session = session(cfg); session.tell(request, self); final StartInterpreter start = new StartInterpreter(session); interpreter.tell(start, self); @@ -181,13 +210,13 @@ private SipURI outboundInterface() { return result; } - private ActorRef session() { + private ActorRef session(final Configuration p_configuration) { final Props props = new Props(new UntypedActorFactory() { private static final long serialVersionUID = 1L; @Override public UntypedActor create() throws Exception { - return new SmsSession(configuration, sipFactory, outboundInterface(), storage, monitoringService, servletContext); + return new SmsSession(p_configuration, sipFactory, outboundInterface(), storage, monitoringService, servletContext); } }); return system.actorOf(props); @@ -214,6 +243,18 @@ public void outbound(SmppOutboundMessageEntity request) throws SmppInvalidArgume } submit0.setShortMessage(textBytes); + + TlvSet tlvSet = request.getTlvSet(); + + if(tlvSet!=null) { + for (Tlv tlv : (Collection)tlvSet.getOptionalParameters()) { + submit0.setOptionalParameter(tlv); + } + }else{ + if(logger.isInfoEnabled()) { + logger.info("TlvSet is null"); + } + } try { if(logger.isInfoEnabled()) { logger.info("Sending SubmitSM for " + request); diff --git a/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/smpp/SmppOutboundMessageEntity.java b/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/smpp/SmppOutboundMessageEntity.java index 3c2f4942b0..e70d1fdff6 100644 --- a/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/smpp/SmppOutboundMessageEntity.java +++ b/restcomm/restcomm.sms/src/main/java/org/restcomm/connect/sms/smpp/SmppOutboundMessageEntity.java @@ -1,5 +1,7 @@ package org.restcomm.connect.sms.smpp; +import org.restcomm.smpp.parameter.TlvSet; + import com.cloudhopper.commons.charset.Charset; public class SmppOutboundMessageEntity { @@ -9,15 +11,23 @@ public class SmppOutboundMessageEntity { private final String smppFrom; private final String smppContent; private final Charset smppEncoding; + private final TlvSet tlvSet; public SmppOutboundMessageEntity(String smppTo, String smppFrom, String smppContent, Charset smppEncoding){ + this(smppTo, smppFrom, smppContent, smppEncoding, null); + } + public SmppOutboundMessageEntity(String smppTo, String smppFrom, String smppContent, Charset smppEncoding, TlvSet tlvSet){ this.smppTo = smppTo; this.smppFrom = smppFrom; this.smppContent = smppContent; this.smppEncoding = smppEncoding; + this.tlvSet = tlvSet; + } + public final TlvSet getTlvSet(){ + return tlvSet; } @@ -48,6 +58,10 @@ public String toString() { .append(smppContent) .append(",Encoding=") .append(smppEncoding); + if(tlvSet!=null){ + builder.append(",TlvSet=") + .append(tlvSet.toString()); + } return super.toString(); } diff --git a/restcomm/restcomm.telephony/src/main/java/org/restcomm/connect/telephony/CallManager.java b/restcomm/restcomm.telephony/src/main/java/org/restcomm/connect/telephony/CallManager.java index da76d16b7b..bc9bc7eda5 100644 --- a/restcomm/restcomm.telephony/src/main/java/org/restcomm/connect/telephony/CallManager.java +++ b/restcomm/restcomm.telephony/src/main/java/org/restcomm/connect/telephony/CallManager.java @@ -30,10 +30,13 @@ import akka.event.Logging; import akka.event.LoggingAdapter; import akka.util.Timeout; + import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat; + import gov.nist.javax.sip.header.UserAgent; + import org.apache.commons.configuration.Configuration; import org.joda.time.DateTime; import org.restcomm.connect.commons.configuration.RestcommConfiguration; @@ -57,6 +60,7 @@ import org.restcomm.connect.dao.entities.Notification; import org.restcomm.connect.dao.entities.Registration; import org.restcomm.connect.extension.api.CallRequest; +import org.restcomm.connect.extension.api.ExtensionRequest; import org.restcomm.connect.extension.api.ExtensionResponse; import org.restcomm.connect.extension.api.ExtensionType; import org.restcomm.connect.extension.api.RestcommExtensionException; @@ -86,6 +90,7 @@ import org.restcomm.connect.telephony.api.UpdateCallScript; import org.restcomm.connect.telephony.api.util.B2BUAHelper; import org.restcomm.connect.telephony.api.util.CallControlHelper; + import scala.concurrent.Await; import scala.concurrent.Future; import scala.concurrent.duration.Duration; @@ -105,6 +110,7 @@ import javax.servlet.sip.TelURL; import javax.sip.header.RouteHeader; import javax.sip.message.Response; + import java.io.IOException; import java.net.InetAddress; import java.net.URI; @@ -476,9 +482,14 @@ private void invite(final Object message) throws IOException, NumberParseExcepti if(logger.isInfoEnabled()) { logger.info("Client is not null: " + client.getLogin() + " will try to proxy to client: "+ toClient); } + CallRequest callRequest = new CallRequest(fromUser, toUser, CallRequest.Type.CLIENT, client.getAccountSid(), false, false); - if (executePreOutboundAction(callRequest)) { + ExtensionController ec = ExtensionController.getInstance(); + ExtensionRequest er = new ExtensionRequest(); + er.setObject(callRequest); + ExtensionResponse extensionResponse = ec.executePreOutboundAction(er, this.extensions); + if (extensionResponse.isAllowed()) { if (B2BUAHelper.redirectToB2BUA(request, client, toClient, storage, sipFactory, patchForNatB2BUASessions)) { if(logger.isInfoEnabled()) { logger.info("Call to CLIENT. myHostIp: " + myHostIp + " mediaExternalIp: " + mediaExternalIp + " toHost: " @@ -505,7 +516,7 @@ private void invite(final Object message) throws IOException, NumberParseExcepti final SipServletResponse resp = request.createResponse(SC_FORBIDDEN, "Call not allowed"); resp.send(); } - executePostOutboundAction(callRequest); + ec.executePostOutboundAction(callRequest, this.extensions); return; } else { // toClient is null or we couldn't make the b2bua call to another client. check if this call is for a registered @@ -536,7 +547,11 @@ private void invite(final Object message) throws IOException, NumberParseExcepti if (proxyURI != null && !proxyURI.isEmpty()) { // String destination = ((SipURI)request.getTo().getURI()).getUser(); CallRequest callRequest = new CallRequest(fromUser,toUser, CallRequest.Type.PSTN, client.getAccountSid(), false, false); - if (executePreOutboundAction(callRequest)) { + ExtensionController ec = ExtensionController.getInstance(); + ExtensionRequest er = new ExtensionRequest(); + er.setObject(callRequest); + ExtensionResponse extensionResponse = ec.executePreOutboundAction(er, this.extensions); + if (extensionResponse.isAllowed()) { proxyOut(request, client, toUser, toHost, toHostIpAddress, toPort, outboundIntf, proxyURI, proxyUsername, proxyPassword, from, to, callToSipUri); } else { final SipServletResponse response = request.createResponse(SC_FORBIDDEN, "Call request not allowed"); @@ -545,7 +560,7 @@ private void invite(final Object message) throws IOException, NumberParseExcepti logger.debug("Call request now allowed: "+callRequest.toString()); } } - executePostOutboundAction(callRequest); + ec.executePostOutboundAction(callRequest, this.extensions); return; } else { String msg = "Restcomm tried to proxy this call to an outbound party but it seems the outbound proxy is not configured."; @@ -1484,9 +1499,13 @@ private void update(final Object message) throws Exception { private void outbound(final Object message, final ActorRef sender) throws ServletParseException { final CreateCall request = (CreateCall) message; CallRequest callRequest = new CallRequest(request.from(), request.to(), CallRequest.Type.valueOf(request.type().name()), request.accountId(), request.isFromApi(), request.parentCallSid() != null); + ExtensionController ec = ExtensionController.getInstance(); + ExtensionRequest er = new ExtensionRequest(); + er.setObject(callRequest); + ExtensionResponse extensionResponse = ec.executePreOutboundAction(er, this.extensions); switch (request.type()) { case CLIENT: { - if (executePreOutboundAction(callRequest)) { + if (extensionResponse.isAllowed()) { outboundToClient(request, sender); } else { //Extensions didn't allowed this call @@ -1494,11 +1513,11 @@ private void outbound(final Object message, final ActorRef sender) throws Servle logger.warning(errMsg); sender.tell(new CallManagerResponse(new RestcommExtensionException(errMsg), this.createCallRequest), self()); } - executePostOutboundAction(callRequest); + ec.executePostOutboundAction(callRequest, this.extensions); break; } case PSTN: { - if (executePreOutboundAction(callRequest)) { + if (extensionResponse.isAllowed()) { outboundToPstn(request, sender); } else { //Extensions didn't allowed this call @@ -1506,14 +1525,14 @@ private void outbound(final Object message, final ActorRef sender) throws Servle logger.warning(errMsg); sender.tell(new CallManagerResponse(new RestcommExtensionException(errMsg), this.createCallRequest), self()); } - executePostOutboundAction(callRequest); + ec.executePostOutboundAction(callRequest, this.extensions); break; } case SIP: { if (actAsImsUa) { outboundToIms(request, sender); } - else if (executePreOutboundAction(callRequest)) { + else if (extensionResponse.isAllowed()) { outboundToSip(request, sender); } else { //Extensions didn't allowed this call @@ -1521,29 +1540,12 @@ else if (executePreOutboundAction(callRequest)) { logger.warning(errMsg); sender.tell(new CallManagerResponse(new RestcommExtensionException(errMsg), this.createCallRequest), self()); } - executePostOutboundAction(callRequest); + ec.executePostOutboundAction(callRequest, this.extensions); break; } } } - private boolean executePreOutboundAction(final Object message) { - if (extensions != null && extensions.size() > 0) { - for (RestcommExtensionGeneric extension : extensions) { - if (extension.isEnabled()) { - ExtensionResponse response = extension.preOutboundAction(message); - if (!response.isAllowed()) - return false; - } - } - } - return true; - } - - private boolean executePostOutboundAction(final CallRequest callRequest) { - return false; - } - private void outboundToClient(final CreateCall request, final ActorRef sender) throws ServletParseException { SipURI outboundIntf = null; SipURI from = null;