diff --git a/autogen.sh b/autogen.sh index 06b6a97c95e0c7f4db9c8bd21a358a14784e9187..ac85bd137c9dcf2f99eef6992751d1a92876cdcd 100755 --- a/autogen.sh +++ b/autogen.sh @@ -4,6 +4,6 @@ aclocal -I m4 libtoolize --force autoheader -autoconf -f +autoconf -v -f automake -a ./configure $@ diff --git a/build-package.sh b/build-package.sh index a2c775ac077e8d1f9aedf5cc837277e5f99aca2b..78aa9774b07f8bf2ccc14c2cdb902a8f35adf96c 100755 --- a/build-package.sh +++ b/build-package.sh @@ -2,16 +2,48 @@ # # @author: Yun Liu <yun.liu@savoirfairelinux.com> # -# Build sflphone debian packages for Ubuntu 8.04 +# Build sflphone rpm packages for Fedora 10 and openSUSE 11 # 1 - The SFLphone package must be build with a specific GnuPG key. Please contact us to have more information about that (<sflphoneteam@savoirfairelinux.com>) # 2. The source code can be teched through anonymous http access. So no need of special access. -# 3. After having all the prerequisites, you can run "build-package.sh" to build debian packages for sflphone. -# All the source packages and binary packages will be generated in the current directory. +# 3. After having all the prerequisites, you can run "build-rpm-package.sh" to build rpm packages for sflphone. +# # Refer to http://www.sflphone.org for futher information +# Analyze parameters +if [ "$1" == "--help" ] || [ "$1" == "" ];then + echo -e '\E[34mThis script is used to build sflphone rpm packages on ubuntu series(8.04,8,10,9), Fedora 10 and SUSE 11 platform.' + echo -e '\E[34mYou can add --fedora, --suse or --ubuntu to start packaging.' + echo + echo "The SFLphone package must be build with a specific GnuPG key. Please contact us to have more information about that (<sflphoneteam@savoirfairelinux.com>)" + echo + echo "For fedora and SUSE, you also need to add the following lines to $HOME/.rpmmacros:" + echo -e '\E[32m%_gpg_path /home/yun/.gnupg' + echo -e '\E[32m%_gpg_name Savoir-Faire Linux Inc. (Génération des paquets pour SFLphone) <sflphoneteam@savoirfairelinux.com>' + echo -e '\E[32m%_gpgbin /usr/bin/gpg' + echo + echo -e '\E[34mAfter all these preparations done, you can run ./build-package.sh --platform-name' + echo + echo -e '\E[36mHave fun!' + + tput sgr0 # Reset colors to "normal." + echo + exit 1 +elif [ $1 == "--fedora" ];then + BUILDDIR=$HOME/rpmbuild + platform="fedora" +elif [ $1 == "--suse" ];then + BUILDDIR=/usr/src/packages + platform="suse" +elif [ $1 == "--ubuntu" ];then + platform="ubuntu" +else + echo "This script can only be used for Ubuntu series, Fedora 10 and SUSE 11 platform. Use --help to get more information." + exit 1 +fi + if [ -d "sflphone" ]; then echo "Directory sflphone already exists. Please remove it first." - exit 1 + exit 1 fi # Anonymous git http access @@ -22,33 +54,54 @@ git checkout origin/release -b release # Get system parameters arch_flag=`getconf -a|grep LONG_BIT | sed -e 's/LONG_BIT\s*//'` os_version=`lsb_release -d -s -c | sed -e '1d'` +ver=0.9.2 + +if [ $platform == "ubuntu" ];then + # Generate the changelog, according to the distribution and the git commit messages + cp debian/changelog.$os_version debian/changelog + git-dch --debian-branch=release --release +fi -# Generate the changelog, according to the distribution and the git commit messages -cp debian/changelog.$os_version debian/changelog -git-dch --debian-branch=release --release cd .. # Remove useless git directory rm sflphone/.git/ -rf -# Copy the appropriate control file based on different archtecture -cp sflphone/debian/control.$os_version sflphone/debian/control +# Get the public gpg key to sign the packages +wget -q http://www.sflphone.org/downloads/gpg/sflphone.gpg.asc -O- | gpg --import - -echo "Building sflphone package on Ubuntu $os_version $arch_flag bit architecture...." +if [ $platform == "ubuntu" ];then + # Copy the appropriate control file based on different archtecture + cp sflphone/debian/control.$os_version sflphone/debian/control -# Provide prerequisite directories used by debuild -cp sflphone sflphone-0.9.2 -r -cp sflphone sflphone-0.9.2.orig -r + echo "Building sflphone package on Ubuntu $os_version $arch_flag bit architecture...." + # Provide prerequisite directories used by debuild + cp sflphone sflphone-$ver -r + cp sflphone sflphone-$ver.orig -r + + # Build packages + cd sflphone-$ver/debian; debuild -k'Savoir-Faire Linux Inc.' -# Get the public gpg key to sign the packages -wget -q http://www.sflphone.org/downloads/gpg/sflphone.gpg.asc -O- | gpg --import - + # Post clean + cd .. + rm sflphone-$ver sflphone -rf + echo "Done! All the source packages and binary packages are generated in the current directory" -# Build packages -cd sflphone-0.9.2/debian; debuild -k'Savoir-Faire Linux Inc.' +else + # Prepare for packaging + mv sflphone sflphone-$ver -# Clean -cd ../.. -rm sflphone-0.9.2/ -rf -rm sflphone/ -rf + cp sflphone-$ver/platform/$platform.spec $BUILDDIR/SPECS/ + cp sflphone-$ver/libs/pjproject-1.0/libpj-sfl.pc $BUILDDIR/SOURCES + tar zcvf sflphone-$ver.tar.gz sflphone-$ver -echo "Building package finished successullly!" + rm sflphone-$ver -rf + mv sflphone-$ver.tar.gz $BUILDDIR/SOURCES + echo "Building sflphone package on $platform $arch_flag bit architecture...." + + # Build packages + cd $BUILDDIR/SPECS/ + rpmbuild -ba --sign sflphone.spec + + echo "Done! All source rpms and binary rpms are stored in $BUILDDIR/SRPMS and $BUILDDIR/RPMS" +fi diff --git a/configure.ac b/configure.ac index ec5e368fc955c57166e7d80f9ffdb91eb55fdd76..100f5d33d603f38acaefc85422c0899c844034cb 100644 --- a/configure.ac +++ b/configure.ac @@ -37,7 +37,8 @@ AC_CONFIG_FILES([src/Makefile \ src/audio/codecs/ilbc/Makefile \ src/config/Makefile \ src/dbus/Makefile \ - src/zeroconf/Makefile]) + src/zeroconf/Makefile \ + src/plug-in/Makefile]) dnl Unitary test section AC_CONFIG_FILES([test/Makefile]) diff --git a/globals.mak b/globals.mak index c03985035c4058b00b378f1b0809017241339313..7a0b085b9efbe0f7deefb226deed286cbba7982a 100644 --- a/globals.mak +++ b/globals.mak @@ -1,6 +1,7 @@ # Global variables src=$(top_srcdir) sflcodecdir=$(libdir)/sflphone/codecs +sflplugindir=$(libdir)/sflphone/plugins PJSIP_LIBS = -lpjnath-sfl -lpjsua-sfl -lpjsip-sfl -lpjmedia-sfl -lpjsip-simple-sfl -lpjsip-ua-sfl -lpjmedia-codec-sfl -lpjlib-util-sfl -lpj-sfl @@ -16,5 +17,6 @@ AM_CPPFLAGS = \ @SIP_CFLAGS@ \ @DBUSCPP_CFLAGS@ \ -DCODECS_DIR=\""$(sflcodecdir)"\" \ + -DPLUGINS_DIR=\""$(sflplugindir)"\" \ -DENABLE_TRACE diff --git a/src/Makefile.am b/src/Makefile.am index 2d5e1af3133debef313b4a3046209c7bce46da23..75fc40c9e8d4e1a01bb35f361c1cbd6aa4d3f02d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,7 +23,7 @@ IAXSOURCES = IAXHEADERS = endif -SUBDIRS = audio config dbus $(ZEROCONFDIR) +SUBDIRS = audio config dbus $(ZEROCONFDIR) plug-in # Add here the cpp files to be build with sflphone sflphoned_SOURCES = \ @@ -89,6 +89,7 @@ libsflphone_la_LIBADD = \ ./audio/libaudio.la \ ./dbus/libdbus.la \ ./config/libconfig.la \ + ./plug-in/libplugin.la \ $(IAX_LIBS) libsflphone_la_SOURCES = diff --git a/src/plug-in/Makefile.am b/src/plug-in/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..53bf6b050cba69da07a0f8fd60b2f4b52c8f41d6 --- /dev/null +++ b/src/plug-in/Makefile.am @@ -0,0 +1,8 @@ +include ../../globals.mak + +noinst_LTLIBRARIES = libplugin.la + +libplugin_la_SOURCES = \ + pluginmanager.cpp \ + plugin.h + diff --git a/src/plug-in/plugin.cpp b/src/plug-in/plugin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..495437076549dee7c9bafbdc5ca6782076400d97 --- /dev/null +++ b/src/plug-in/plugin.cpp @@ -0,0 +1,27 @@ +#include "plugin.h" + +::sflphone::Plugin::Plugin( const std::string &filename UNUSED ) +{ + //TODO IMPLEMENT +} + +::sflphone::Plugin::Plugin( const Plugin &plugin UNUSED ) +{ + //TODO IMPLEMENT +} + +::sflphone::Plugin::~Plugin() +{ + //TODO IMPLEMENT +} + +int ::sflphone::Plugin::getCoreVersion( void ) const +{ + //TODO IMPLEMENT + return 1; +} + +void ::sflphone::Plugin::registerPlugin( PluginManager & ) +{ + //TODO IMPLEMENT +} diff --git a/src/plug-in/plugin.h b/src/plug-in/plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..100d6ea5b40bc618e3e6cae9c4435b9b192af676 --- /dev/null +++ b/src/plug-in/plugin.h @@ -0,0 +1,43 @@ +#ifndef PLUGIN_H +#define PLUGIN_H + +#include <string> + +#include "global.h" + +/* + * @file plugin.h + * @brief Define a plugin object + */ + +namespace sflphone { + +class PluginManager; + + class Plugin { + + public: + Plugin( const std::string &name ); + //Plugin( const Plugin &plugin ); + virtual ~Plugin() {} + + public: + /** + * Return the minimal core version required so that the plugin could work + * @return int The version required + */ + virtual int getCoreVersion() const = 0; + + /** + * Register the plugin to the plugin manager + */ + virtual void registerPlugin( PluginManager & ) = 0; + + private: + Plugin &operator =(const Plugin &plugin); + + }; +} + +#endif //PLUGIN_H + diff --git a/src/plug-in/pluginmanager.cpp b/src/plug-in/pluginmanager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6012a59bde292396b2f70fdce8d0a1d458ef8aa7 --- /dev/null +++ b/src/plug-in/pluginmanager.cpp @@ -0,0 +1,98 @@ +#include <dirent.h> +#include <dlfcn.h> + +#include "pluginmanager.h" + +::sflphone::PluginManager::PluginManager():_loadedPlugins() +{ + _instance = this; +} + +::sflphone::PluginManager::~PluginManager() +{ + _instance = 0; +} + +int ::sflphone::PluginManager::loadPlugins( const std::string &path ) +{ + std::string pluginDir, current; + ::sflphone::Plugin *plugin; + DIR *dir; + dirent *dirStruct; + int result=0; + + const std::string pDir = ".."; + const std::string cDir = "."; + + /* The directory in which plugins are dropped. Default: /usr/lib/sflphone/plugins/ */ + ( path == "" )? pluginDir = std::string(PLUGINS_DIR).append("/"):pluginDir = path; + _debug("Loading plugins from %s...\n", pluginDir.c_str()); + + dir = opendir( pluginDir.c_str() ); + /* Test if the directory exists or is readable */ + if( dir ){ + /* Read the directory */ + while( (dirStruct=readdir(dir)) ){ + /* Get the name of the current item in the directory */ + current = dirStruct->d_name; + /* Test if the current item is not the parent or the current directory */ + if( current != pDir && current != cDir ){ + loadDynamicLibrary( current ); + result++; + } + } + } + /* Close the directory */ + closedir( dir ); + + return result; +} + +::sflphone::Plugin* ::sflphone::PluginManager::isPluginLoaded( const std::string &name ) +{ + if(_loadedPlugins.empty()) return NULL; + + /* Use an iterator on the loaded plugins map */ + pluginMap::iterator iter; + + iter = _loadedPlugins.begin(); + while( iter != _loadedPlugins.end() ) { + if ( iter->first == name ) { + /* Return the associated plugin */ + return iter->second; + } + iter++; + } + + /* If we are here, it means that the plugin we were looking for has not been loaded */ + return NULL; +} + +void* ::sflphone::PluginManager::loadDynamicLibrary( const std::string& filename ) { + + void *pluginHandlePtr = NULL; + const char *error; + + _debug("Loading dynamic library %s\n", filename.c_str()); + +#if defined(Q_OS_UNIX) + /* Load the library */ + pluginHandlePtr = dlopen( filename.c_str(), RTLD_LAZY ); + if( !pluginHandlePtr ) { + error = dlerror(); + _debug("Error while opening plug-in: %s\n", error); + } + dlerror(); +#endif + + return pluginHandlePtr; +} + +void ::sflphone::PluginManager::unloadDynamicLibrary( void * pluginHandlePtr ) { + + dlclose( pluginHandlePtr ); + dlerror(); +} + +::sflphone::PluginManager* ::sflphone::PluginManager::_instance = 0; + diff --git a/src/plug-in/pluginmanager.h b/src/plug-in/pluginmanager.h new file mode 100644 index 0000000000000000000000000000000000000000..651a0902f8e0db872052a2b0c234c66866ee92f1 --- /dev/null +++ b/src/plug-in/pluginmanager.h @@ -0,0 +1,72 @@ +#ifndef PLUGIN_MANAGER_H +#define PLUGIN_MANAGER_H + +/* + * @file pluginmanager.h + * @brief Base class of the plugin manager + */ + +#include "plugin.h" +#include "global.h" + +#include <map> +#include <string> + +namespace sflphone { + + class PluginManager { + + public: + /** + * Default constructor + */ + PluginManager(); + + /** + * Destructor + */ + ~PluginManager(); + + /** + * Returns the unique instance of the plugin manager + */ + static PluginManager* instance(); + + /** + * Load all the plugins found in a specific directory + * @param path The absolute path to the directory + * @return int The number of items loaded + */ + int loadPlugins( const std::string &path = "" ); + + /** + * Check if a plugin has been already loaded + * @param name The name of the plugin looked for + * @return Plugin* The pointer on the plugin or NULL if not found + */ + Plugin* isPluginLoaded( const std::string &name ); + + private: + /** + * Load a unix dynamic/shared library + * @param filename The path to the dynamic/shared library + * @return void* A pointer on it + */ + void * loadDynamicLibrary( const std::string &filename ); + + /** + * Unload a unix dynamic/shared library + * @param pluginHandleptr The pointer on the loaded plugin + */ + void unloadDynamicLibrary( void * pluginHandlePtr ); + + /* Map of plugins associated by their string name */ + typedef std::map<std::string, ::sflphone::Plugin*> pluginMap; + pluginMap _loadedPlugins; + + /* The unique static instance */ + static PluginManager* _instance; + }; +} + +#endif //PLUGIN_MANAGER_H diff --git a/test/Makefile.am b/test/Makefile.am index 14411aa140bbc3264308b27f0cd418287544821c..86e70dbc38e76a6fda92b43be969afd51e260781 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,6 +1,6 @@ include ../globals.mak -bin_PROGRAMS = configurationTester +bin_PROGRAMS = configurationTester pluginmanagerTester OBJECT_FILES= \ ../src/sflphoned-managerimpl.o \ @@ -16,6 +16,7 @@ OBJECT_FILES= \ ../src/sflphoned-iaxaccount.o \ ../src/sflphoned-eventthread.o \ ../src/sflphoned-useragent.o \ + ../src/plug-in/pluginmanager.o \ ../src/sflphoned-samplerateconverter.o configurationTester_SOURCES = \ @@ -23,6 +24,11 @@ configurationTester_SOURCES = \ configurationTest.h \ TestMain.cpp +pluginmanagerTester_SOURCES = \ + pluginmanagerTest.h \ + pluginmanagerTest.cpp \ + TestMain.cpp + configurationTester_LDADD = \ ../src/libsflphone.la \ $(SFLPHONE_LIBS) $(ZEROCONFLIB) $(LIB_DNSSD) $(IAX_LIBS) \ @@ -36,4 +42,18 @@ configurationTester_LDADD = \ @SAMPLERATE_LIBS@ \ $(PJSIP_LIBS) \ $(OBJECT_FILES) - + +pluginmanagerTester_LDADD = \ + ../src/libsflphone.la \ + $(SFLPHONE_LIBS) $(ZEROCONFLIB) $(LIB_DNSSD) $(IAX_LIBS) \ + @ALSA_LIBS@ \ + @PULSEAUDIO_LIBS@ \ + @CPPUNIT_LIBS@ \ + @CCEXT2_LIBS@ \ + @CCGNU2_LIBS@ \ + @CCRTP_LIBS@ \ + @DBUSCPP_LIBS@ \ + @SAMPLERATE_LIBS@ \ + $(PJSIP_LIBS) \ + $(OBJECT_FILES) + diff --git a/test/pluginmanagerTest.cpp b/test/pluginmanagerTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b9a3d413a400d422a7510963d36124ba1d026e14 --- /dev/null +++ b/test/pluginmanagerTest.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdio.h> +#include <sstream> + +#include "pluginmanagerTest.h" + +using std::cout; +using std::endl; + +void PluginManagerTest::setUp(){ + // Instanciate the plugin manager object + _pm = new ::sflphone::PluginManager(); +} + +void PluginManagerTest::testLoadPluginDirectory(){ + _pm->loadPlugins(); +} + +void PluginManagerTest::testNonloadedPlugin(){ + CPPUNIT_ASSERT( _pm->isPluginLoaded("test") == NULL ); +} + +void PluginManagerTest::tearDown(){ + // Delete the plugin manager object + delete _pm; _pm=NULL; +} diff --git a/test/pluginmanagerTest.h b/test/pluginmanagerTest.h new file mode 100644 index 0000000000000000000000000000000000000000..e43ba8f7ab09262243fddd3edc21130ddca5dd80 --- /dev/null +++ b/test/pluginmanagerTest.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +// Cppunit import +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCaller.h> +#include <cppunit/TestCase.h> +#include <cppunit/TestSuite.h> + +#include <assert.h> + +// Application import +#include "plug-in/pluginmanager.h" + +/* + * @file pluginManagerTest.cpp + * @brief Regroups unitary tests related to the plugin manager. + */ + +#ifndef _PLUGINMANAGER_TEST_ +#define _PLUGINMANAGER_TEST_ + +class PluginManagerTest : public CppUnit::TestCase { + + /* + * Use cppunit library macros to add unit test the factory + */ + CPPUNIT_TEST_SUITE( PluginManagerTest ); + CPPUNIT_TEST( testLoadPluginDirectory ); + CPPUNIT_TEST( testNonloadedPlugin ); + CPPUNIT_TEST_SUITE_END(); + + public: + PluginManagerTest() : CppUnit::TestCase("Plugin Manager Tests") {} + + /* + * Code factoring - Common resources can be initialized here. + * This method is called by unitcpp before each test + */ + void setUp(); + + /* + * Code factoring - Common resources can be released here. + * This method is called by unitcpp after each test + */ + inline void tearDown(); + + void testLoadPluginDirectory(); + + void testNonloadedPlugin(); + + private: + ::sflphone::PluginManager *_pm; +}; + +/* Register our test module */ +CPPUNIT_TEST_SUITE_REGISTRATION( PluginManagerTest ); + +#endif