From c5edfd19b1aac670a820114621243aacc1abf633 Mon Sep 17 00:00:00 2001 From: HanzZ Date: Sun, 13 Feb 2011 18:51:43 +0100 Subject: [PATCH] User, UserRegistration, UserManager, SQLite3Backend, DiscoInfoResponder and so on... :) --- CMakeLists.txt | 21 +- cmake_modules/sqlite3Config.cmake | 49 ++++ examples/CMakeLists.txt | 1 + examples/server_connect/main.cpp | 25 +- examples/server_connect/sample.cfg | 5 +- examples/usermanager/CMakeLists.txt | 6 + examples/usermanager/main.cpp | 37 +++ examples/usermanager/sample.cfg | 9 + include/transport/logger.h | 61 +++++ include/transport/sqlite3backend.h | 56 ++++ include/transport/storagebackend.h | 57 +++++ include/transport/transport.h | 37 ++- include/transport/user.h | 49 ++++ include/transport/usermanager.h | 55 ++++ include/transport/userregistration.h | 60 +++++ src/CMakeLists.txt | 2 +- src/config.cpp | 2 + src/discoinforesponder.cpp | 92 +++++++ src/discoinforesponder.h | 48 ++++ src/logger.cpp | 95 +++++++ src/sqlite3backend.cpp | 138 ++++++++++ src/transport.cpp | 188 ++++++++++++-- src/user.cpp | 44 ++++ src/usermanager.cpp | 162 ++++++++++++ src/userregistration.cpp | 369 +++++++++++++++++++++++++++ 25 files changed, 1613 insertions(+), 55 deletions(-) create mode 100644 cmake_modules/sqlite3Config.cmake create mode 100644 examples/usermanager/CMakeLists.txt create mode 100644 examples/usermanager/main.cpp create mode 100644 examples/usermanager/sample.cfg create mode 100644 include/transport/logger.h create mode 100644 include/transport/sqlite3backend.h create mode 100644 include/transport/storagebackend.h create mode 100644 include/transport/user.h create mode 100644 include/transport/usermanager.h create mode 100644 include/transport/userregistration.h create mode 100644 src/discoinforesponder.cpp create mode 100644 src/discoinforesponder.h create mode 100644 src/logger.cpp create mode 100644 src/sqlite3backend.cpp create mode 100644 src/user.cpp create mode 100644 src/usermanager.cpp create mode 100644 src/userregistration.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a8cb73df..6f44cb13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,21 @@ set(CMAKE_MODULE_PATH "cmake_modules") set(cppunit_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") find_package(cppunit) +set(sqlite3_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") +find_package(sqlite3) + +message("Supported features") +message("------------------") + +if (SQLITE3_FOUND) + ADD_DEFINITIONS(-DWITH_SQLITE) + include_directories(SQLITE3_INCLUDE_DIR) + message("SQLite3 : yes") +else (SQLITE3_FOUND) + set(SQLITE3_LIBRARIES "") + message("SQLite3 : no") +endif (SQLITE3_FOUND) + if(CMAKE_BUILD_TYPE MATCHES Debug) ADD_DEFINITIONS(-ggdb) ADD_DEFINITIONS(-DDEBUG) @@ -20,7 +35,9 @@ if(CMAKE_BUILD_TYPE MATCHES Debug) ADD_DEFINITIONS(-Woverloaded-virtual) ADD_DEFINITIONS(-Wsign-promo) ADD_DEFINITIONS(-Wundef -Wunused) - message(STATUS "Build type is set to Debug") + message("Debug : yes") +else(CMAKE_BUILD_TYPE MATCHES Debug) + message("Debug : no") endif(CMAKE_BUILD_TYPE MATCHES Debug) SET(TRANSPORT_VERSION 2.0) @@ -29,3 +46,5 @@ include_directories(include) ADD_SUBDIRECTORY(src) ADD_SUBDIRECTORY(include) ADD_SUBDIRECTORY(examples) + +message("------------------") diff --git a/cmake_modules/sqlite3Config.cmake b/cmake_modules/sqlite3Config.cmake new file mode 100644 index 00000000..dddf04e9 --- /dev/null +++ b/cmake_modules/sqlite3Config.cmake @@ -0,0 +1,49 @@ +# - find Sqlite 3 +# SQLITE3_INCLUDE_DIR - Where to find Sqlite 3 header files (directory) +# SQLITE3_LIBRARIES - Sqlite 3 libraries +# SQLITE3_LIBRARY_RELEASE - Where the release library is +# SQLITE3_LIBRARY_DEBUG - Where the debug library is +# SQLITE3_FOUND - Set to TRUE if we found everything (library, includes and executable) + +IF( SQLITE3_INCLUDE_DIR AND SQLITE3_LIBRARY_RELEASE AND SQLITE3_LIBRARY_DEBUG ) + SET(SQLITE3_FIND_QUIETLY TRUE) +ENDIF( SQLITE3_INCLUDE_DIR AND SQLITE3_LIBRARY_RELEASE AND SQLITE3_LIBRARY_DEBUG ) + +FIND_PATH( SQLITE3_INCLUDE_DIR sqlite3.h ) + +FIND_LIBRARY(SQLITE3_LIBRARY_RELEASE NAMES sqlite3 ) + +FIND_LIBRARY(SQLITE3_LIBRARY_DEBUG NAMES sqlite3 sqlite3d HINTS /usr/lib/debug/usr/lib/ ) + +IF( SQLITE3_LIBRARY_RELEASE OR SQLITE3_LIBRARY_DEBUG AND SQLITE3_INCLUDE_DIR ) + SET( SQLITE3_FOUND TRUE ) +ENDIF( SQLITE3_LIBRARY_RELEASE OR SQLITE3_LIBRARY_DEBUG AND SQLITE3_INCLUDE_DIR ) + +IF( SQLITE3_LIBRARY_DEBUG AND SQLITE3_LIBRARY_RELEASE ) + # if the generator supports configuration types then set + # optimized and debug libraries, or if the CMAKE_BUILD_TYPE has a value + IF( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE ) + SET( SQLITE3_LIBRARIES optimized ${SQLITE3_LIBRARY_RELEASE} debug ${SQLITE3_LIBRARY_DEBUG} ) + ELSE( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE ) + # if there are no configuration types and CMAKE_BUILD_TYPE has no value + # then just use the release libraries + SET( SQLITE3_LIBRARIES ${SQLITE3_LIBRARY_RELEASE} ) + ENDIF( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE ) +ELSEIF( SQLITE3_LIBRARY_RELEASE ) + SET( SQLITE3_LIBRARIES ${SQLITE3_LIBRARY_RELEASE} ) +ELSE( SQLITE3_LIBRARY_DEBUG AND SQLITE3_LIBRARY_RELEASE ) + SET( SQLITE3_LIBRARIES ${SQLITE3_LIBRARY_DEBUG} ) +ENDIF( SQLITE3_LIBRARY_DEBUG AND SQLITE3_LIBRARY_RELEASE ) + +IF( SQLITE3_FOUND ) + IF( NOT SQLITE3_FIND_QUIETLY ) + MESSAGE( STATUS "Found Sqlite3 header file in ${SQLITE3_INCLUDE_DIR}") + MESSAGE( STATUS "Found Sqlite3 libraries: ${SQLITE3_LIBRARIES}") + ENDIF( NOT SQLITE3_FIND_QUIETLY ) +ELSE(SQLITE3_FOUND) + IF( SQLITE3_FIND_REQUIRED) + MESSAGE( FATAL_ERROR "Could not find Sqlite3" ) + ELSE( SQLITE3_FIND_REQUIRED) + MESSAGE( STATUS "Optional package Sqlite3 was not found" ) + ENDIF( SQLITE3_FIND_REQUIRED) +ENDIF(SQLITE3_FOUND) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index a97a3918..d2430311 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1 +1,2 @@ ADD_SUBDIRECTORY(server_connect) +ADD_SUBDIRECTORY(usermanager) diff --git a/examples/server_connect/main.cpp b/examples/server_connect/main.cpp index f4110a59..3f6febc5 100644 --- a/examples/server_connect/main.cpp +++ b/examples/server_connect/main.cpp @@ -1,25 +1,10 @@ #include "transport/config.h" #include "transport/transport.h" +#include "transport/logger.h" #include "Swiften/EventLoop/SimpleEventLoop.h" using namespace Transport; -static void onConnected() { - std::cout << "Connected to Jabber Server!\n"; -} - -static void onConnectionError(const Swift::ComponentError&) { - std::cout << "Connection Error!\n"; -} - -static void onXMLIn(const std::string &data) { - std::cout << "[XML IN]" << data << "\n"; -} - -static void onXMLOut(const std::string &data) { - std::cout << "[XML OUT]" << data << "\n"; -} - int main(void) { Config::Variables config; @@ -27,14 +12,12 @@ int main(void) std::cout << "Can't open sample.cfg configuration file.\n"; return 1; } + Swift::logging = true; Swift::SimpleEventLoop eventLoop; - Transport::Transport transport(&eventLoop, config); + Component transport(&eventLoop, config); - transport.onConnected.connect(&onConnected); - transport.onConnectionError.connect(bind(&onConnectionError, _1)); - transport.onXMLIn.connect(bind(&onXMLIn, _1)); - transport.onXMLOut.connect(bind(&onXMLOut, _1)); + Logger logger(&transport); transport.connect(); eventLoop.run(); diff --git a/examples/server_connect/sample.cfg b/examples/server_connect/sample.cfg index 0af1c23f..2c3fda02 100644 --- a/examples/server_connect/sample.cfg +++ b/examples/server_connect/sample.cfg @@ -1,6 +1,5 @@ [service] jid = icq.localhost password = secret -server = localhost -port = 5347 - +server = 127.0.0.1 +port = 8888 diff --git a/examples/usermanager/CMakeLists.txt b/examples/usermanager/CMakeLists.txt new file mode 100644 index 00000000..b78652e1 --- /dev/null +++ b/examples/usermanager/CMakeLists.txt @@ -0,0 +1,6 @@ +FILE(GLOB SRC *.cpp) + +ADD_EXECUTABLE(transport_usermanager ${SRC}) + +TARGET_LINK_LIBRARIES(transport_usermanager transport) + diff --git a/examples/usermanager/main.cpp b/examples/usermanager/main.cpp new file mode 100644 index 00000000..abd00d23 --- /dev/null +++ b/examples/usermanager/main.cpp @@ -0,0 +1,37 @@ +#include "transport/config.h" +#include "transport/transport.h" +#include "transport/usermanager.h" +#include "transport/logger.h" +#include "transport/sqlite3backend.h" +#include "transport/userregistration.h" +#include "Swiften/EventLoop/SimpleEventLoop.h" + +using namespace Transport; + +int main(void) +{ + Config::Variables config; + if (!Config::load("sample.cfg", config)) { + std::cout << "Can't open sample.cfg configuration file.\n"; + return 1; + } + + Swift::SimpleEventLoop eventLoop; + Component transport(&eventLoop, config); + Logger logger(&transport); + + SQLite3Backend sql(config); + logger.setStorageBackend(&sql); + if (!sql.connect()) { + std::cout << "Can't connect to database.\n"; + } + + transport.setStorageBackend(&sql); + + UserManager userManager(&transport); + + UserRegistration userRegistration(&transport, &userManager, &sql); + + transport.connect(); + eventLoop.run(); +} diff --git a/examples/usermanager/sample.cfg b/examples/usermanager/sample.cfg new file mode 100644 index 00000000..fc90fdf8 --- /dev/null +++ b/examples/usermanager/sample.cfg @@ -0,0 +1,9 @@ +[service] +jid = icq.localhost +password = secret +server = 127.0.0.1 +port = 8888 + +[database] +database = test.sql +prefix=icq diff --git a/include/transport/logger.h b/include/transport/logger.h new file mode 100644 index 00000000..229e0f0e --- /dev/null +++ b/include/transport/logger.h @@ -0,0 +1,61 @@ +/** + * libtransport -- C++ library for easy XMPP Transports development + * + * Copyright (C) 2011, Jan Kaluza + * + * 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 2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#pragma once + +#include +#include +#include "Swiften/Swiften.h" + +namespace Transport { + +struct UserInfo; +class User; +class UserManager; +class Component; +class StorageBackend; +class UserRegistration; + +class Logger +{ + public: + Logger(Component *component); + ~Logger(); + + void setStorageBackend(StorageBackend *storage); + void setUserRegistration(UserRegistration *userRegistration); + + private: + // Component + void handleConnected(); + void handleConnectionError(const Swift::ComponentError &error); + void handleXMLIn(const std::string &data); + void handleXMLOut(const std::string &data); + + // StorageBackend + void handleStorageError(const std::string &statement, const std::string &error); + + // UserRegistration + void handleUserRegistered(const UserInfo &user); + void handleUserUnregistered(const UserInfo &user); + void handleUserUpdated(const UserInfo &user); +}; + +} diff --git a/include/transport/sqlite3backend.h b/include/transport/sqlite3backend.h new file mode 100644 index 00000000..dbb53db2 --- /dev/null +++ b/include/transport/sqlite3backend.h @@ -0,0 +1,56 @@ +/** + * libtransport -- C++ library for easy XMPP Transports development + * + * Copyright (C) 2011, Jan Kaluza + * + * 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 2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#pragma once + +#include +#include +#include "Swiften/Swiften.h" +#include "transport/storagebackend.h" +#include "transport/config.h" +#include + +namespace Transport { + +class SQLite3Backend : public StorageBackend +{ + public: + SQLite3Backend(Config::Variables &config); + ~SQLite3Backend(); + + bool connect(); + bool createDatabase(); + + void setUser(const UserInfo &user); + bool getUser(const std::string &barejid, UserInfo &user); + void setUserOnline(long id, bool online); + void removeUser(long id); + + bool getBuddies(long id, std::list &roster); + + private: + bool exec(const std::string &query); + + sqlite3 *m_db; + Config::Variables m_config; + std::string m_prefix; +}; + +} diff --git a/include/transport/storagebackend.h b/include/transport/storagebackend.h new file mode 100644 index 00000000..a040c9d5 --- /dev/null +++ b/include/transport/storagebackend.h @@ -0,0 +1,57 @@ +/** + * libtransport -- C++ library for easy XMPP Transports development + * + * Copyright (C) 2011, Jan Kaluza + * + * 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 2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#pragma once + +#include +#include + +namespace Transport { + +struct UserInfo { + long id; + std::string jid; + std::string uin; + std::string password; + std::string language; + std::string encoding; + bool vip; +}; + +class StorageBackend +{ + public: + virtual ~StorageBackend() {} + + virtual bool connect() = 0; + virtual bool createDatabase() = 0; + + virtual void setUser(const UserInfo &user) = 0; + virtual bool getUser(const std::string &barejid, UserInfo &user) = 0; + virtual void setUserOnline(long id, bool online) = 0; + virtual void removeUser(long id) = 0; + + virtual bool getBuddies(long id, std::list &roster) = 0; + + boost::signal onStorageError; + +}; + +} diff --git a/include/transport/transport.h b/include/transport/transport.h index 133ae54b..c885e461 100644 --- a/include/transport/transport.h +++ b/include/transport/transport.h @@ -29,8 +29,12 @@ #include "Swiften/Presence/PresenceOracle.h" #include "Swiften/Network/BoostTimerFactory.h" #include "Swiften/Network/BoostIOServiceThread.h" +#include #include "transport/config.h" +#define tr(lang,STRING) (STRING) +#define _(STRING) (STRING) + namespace Transport { // typedef enum { CLIENT_FEATURE_ROSTERX = 2, // CLIENT_FEATURE_XHTML_IM = 4, @@ -40,32 +44,42 @@ namespace Transport { // // class SpectrumDiscoInfoResponder; // class SpectrumRegisterHandler; + class StorageBackend; + class DiscoInfoResponder; - class Transport { + class Component { public: - Transport(Swift::EventLoop *loop, Config::Variables &config); - ~Transport(); + Component(Swift::EventLoop *loop, Config::Variables &config); + ~Component(); // Connect to server void connect(); + void setStorageBackend(StorageBackend *backend); + + void setTransportFeatures(std::list &features); + void setBuddyFeatures(std::list &features); + + Swift::JID &getJID() { return m_jid; } + boost::signal onConnectionError; boost::signal onConnected; boost::signal onXMLOut; boost::signal onXMLIn; + boost::signal onUserPresenceReceived; private: void handleConnected(); void handleConnectionError(const Swift::ComponentError &error); -// void handlePresenceReceived(Swift::Presence::ref presence); + void handlePresenceReceived(Swift::Presence::ref presence); // void handleMessageReceived(Swift::Message::ref message); -// void handlePresence(Swift::Presence::ref presence); -// void handleSubscription(Swift::Presence::ref presence); -// void handleProbePresence(Swift::Presence::ref presence); + void handlePresence(Swift::Presence::ref presence); + void handleSubscription(Swift::Presence::ref presence); + void handleProbePresence(Swift::Presence::ref presence); void handleDataRead(const Swift::String &data); void handleDataWritten(const Swift::String &data); -// void handleDiscoInfoResponse(boost::shared_ptr info, const boost::optional& error, const Swift::JID& jid); + void handleDiscoInfoResponse(boost::shared_ptr info, Swift::ErrorPayload::ref error, const Swift::JID& jid); // void handleCapsChanged(const Swift::JID& jid); Swift::BoostNetworkFactories *m_factories; @@ -76,10 +90,15 @@ namespace Transport { Swift::CapsManager *m_capsManager; Swift::CapsMemoryStorage *m_capsMemoryStorage; Swift::PresenceOracle *m_presenceOracle; -// SpectrumDiscoInfoResponder *m_discoInfoResponder; + StorageBackend *m_storageBackend; + DiscoInfoResponder *m_discoInfoResponder; // SpectrumRegisterHandler *m_registerHandler; int m_reconnectCount; Config::Variables m_config; + std::string m_protocol; Swift::JID m_jid; + + friend class User; + friend class UserRegistration; }; } diff --git a/include/transport/user.h b/include/transport/user.h new file mode 100644 index 00000000..2c4f6f65 --- /dev/null +++ b/include/transport/user.h @@ -0,0 +1,49 @@ +/** + * libtransport -- C++ library for easy XMPP Transports development + * + * Copyright (C) 2011, Jan Kaluza + * + * 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 2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#pragma once + +#include +#include "Swiften/Swiften.h" +#include "Swiften/Presence/PresenceOracle.h" +#include "Swiften/Disco/EntityCapsManager.h" + +namespace Transport { + +class Component; + +// Representation of XMPP User +class User { + public: + User(const Swift::JID &jid, const std::string &username, const std::string &password, Component * component); + virtual ~User(); + + const Swift::JID &getJID(); + + const char *getLang() { return "en"; } + + private: + Swift::JID m_jid; + Swift::Component *m_component; + Swift::EntityCapsManager *m_entityCapsManager; + Swift::PresenceOracle *m_presenceOracle; +}; + +} diff --git a/include/transport/usermanager.h b/include/transport/usermanager.h new file mode 100644 index 00000000..7c072e76 --- /dev/null +++ b/include/transport/usermanager.h @@ -0,0 +1,55 @@ +/** + * libtransport -- C++ library for easy XMPP Transports development + * + * Copyright (C) 2011, Jan Kaluza + * + * 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 2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#pragma once + +#include +#include +#include "Swiften/Swiften.h" + +namespace Transport { + +class User; +class Component; + +// Class for managing online XMPP users. +class UserManager +{ + public: + UserManager(Component *component); + ~UserManager(); + + // User * + User *getUserByJID(const std::string &barejid); + + // Returns count of online users; + int userCount(); + + void removeUser(User *user) {} + + private: + void handlePresence(Swift::Presence::ref presence); + + long m_onlineBuddies; + User *m_cachedUser; + std::map m_users; +}; + +} diff --git a/include/transport/userregistration.h b/include/transport/userregistration.h new file mode 100644 index 00000000..368cecc7 --- /dev/null +++ b/include/transport/userregistration.h @@ -0,0 +1,60 @@ +/** + * libtransport -- C++ library for easy XMPP Transports development + * + * Copyright (C) 2011, Jan Kaluza + * + * 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 2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#pragma once + +#include "Swiften/Swiften.h" +#include "Swiften/Queries/GetResponder.h" +#include "Swiften/Queries/SetResponder.h" +#include "Swiften/Elements/InBandRegistrationPayload.h" + +namespace Transport { + +struct UserInfo; +class Component; +class StorageBackend; +class UserManager; + +class UserRegistration : Swift::GetResponder, Swift::SetResponder { + public: + UserRegistration(Component *component, UserManager *userManager, StorageBackend *storageBackend); + ~UserRegistration(); + + // Registers new user, returns false if user was already registered. + bool registerUser(const UserInfo &user); + + // Unregisters user, returns true if user was successfully unregistered. + bool unregisterUser(const std::string &barejid); + + boost::signal onUserRegistered; + boost::signal onUserUnregistered; + boost::signal onUserUpdated; + + private: + bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const Swift::String& id, boost::shared_ptr payload); + bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const Swift::String& id, boost::shared_ptr payload); + + Component *m_component; + StorageBackend *m_storageBackend; + UserManager *m_userManager; + +}; + +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aff7bf95..c5bfe390 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,7 +7,7 @@ FILE(GLOB HEADERS ../../include/transport/*.h) ADD_LIBRARY(transport SHARED ${HEADERS} ${SRC}) ADD_DEFINITIONS(-fPIC) -TARGET_LINK_LIBRARIES(transport -lSwiften -lresolv -lidn -lz -lpthread -lexpat -lidn -lboost_date_time -lboost_system -lboost_filesystem -lboost_program_options -lboost_regex -lboost_thread-mt -lboost_signals -lz -lssl -lcrypto -lexpat -lresolv -lc -lxml2 -export-dynamic) +TARGET_LINK_LIBRARIES(transport -lSwiften -lresolv -lidn -lz -lpthread -lexpat -lidn -lboost_date_time -lboost_system -lboost_filesystem -lboost_program_options -lboost_regex -lboost_thread-mt -lboost_signals -lz -lssl -lcrypto -lexpat -lresolv -lc -lxml2 ${SQLITE3_LIBRARIES}) SET_TARGET_PROPERTIES(transport PROPERTIES VERSION ${TRANSPORT_VERSION} SOVERSION ${TRANSPORT_VERSION} diff --git a/src/config.cpp b/src/config.cpp index 03c2f341..aba38ebc 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -36,6 +36,8 @@ bool load(const std::string &configfile, Variables &variables, boost::program_op ("service.server", value(), "set compression level") ("service.password", value(), "set compression level") ("service.port", value(), "set compression level") + ("database.database", value(), "set compression level") + ("database.prefix", value(), "set compression level") ; diff --git a/src/discoinforesponder.cpp b/src/discoinforesponder.cpp new file mode 100644 index 00000000..b6661bd5 --- /dev/null +++ b/src/discoinforesponder.cpp @@ -0,0 +1,92 @@ +/** + * XMPP - libpurple transport + * + * Copyright (C) 2009, Jan Kaluza + * + * 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 2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "discoinforesponder.h" + +#include +#include +#include "Swiften/Disco/DiscoInfoResponder.h" +#include "Swiften/Queries/IQRouter.h" +#include "Swiften/Elements/DiscoInfo.h" +#include "Swiften/Swiften.h" + +using namespace Swift; +using namespace boost; + +namespace Transport { + +DiscoInfoResponder::DiscoInfoResponder(Swift::IQRouter *router) : Swift::GetResponder(router) { + m_transportInfo.addIdentity(DiscoInfo::Identity("libtransport", "gateway", "identity")); + + m_buddyInfo.addIdentity(DiscoInfo::Identity("libtransport", "client", "pc")); + std::list features; + features.push_back("jabber:iq:register"); + features.push_back("jabber:iq:gateway"); + features.push_back("http://jabber.org/protocol/disco#info"); + features.push_back("http://jabber.org/protocol/commands"); + setTransportFeatures(features); + + features.clear(); + features.push_back("http://jabber.org/protocol/disco#items"); + features.push_back("http://jabber.org/protocol/disco#info"); + setBuddyFeatures(features); +} + +DiscoInfoResponder::~DiscoInfoResponder() { + +} + +void DiscoInfoResponder::setTransportFeatures(std::list &features) { + for (std::list::iterator it = features.begin(); it != features.end(); it++) { + if (!m_transportInfo.hasFeature(*it)) { + m_transportInfo.addFeature(*it); + } + } +} + +void DiscoInfoResponder::setBuddyFeatures(std::list &f) { + for (std::list::iterator it = f.begin(); it != f.end(); it++) { + if (!m_buddyInfo.hasFeature(*it)) { + m_buddyInfo.addFeature(*it); + } + } + + CapsInfoGenerator caps(""); + onBuddyCapsInfoChanged(caps.generateCapsInfo(m_buddyInfo)); +} + +bool DiscoInfoResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const Swift::String& id, boost::shared_ptr info) { + if (!info->getNode().isEmpty()) { + sendError(from, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel); + return true; + } + + // presence for transport + if (to.getNode().isEmpty()) { + sendResponse(from, id, boost::shared_ptr(new DiscoInfo(m_transportInfo))); + } + // presence for buddy + else { + sendResponse(from, id, boost::shared_ptr(new DiscoInfo(m_buddyInfo))); + } + return true; +} + +} diff --git a/src/discoinforesponder.h b/src/discoinforesponder.h new file mode 100644 index 00000000..47ec0715 --- /dev/null +++ b/src/discoinforesponder.h @@ -0,0 +1,48 @@ +/** + * libtransport -- C++ library for easy XMPP Transports development + * + * Copyright (C) 2011, Jan Kaluza + * + * 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 2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#pragma once + +#include +#include "Swiften/Swiften.h" +#include "Swiften/Queries/GetResponder.h" +#include "Swiften/Elements/DiscoInfo.h" +#include "Swiften/Elements/CapsInfo.h" + +namespace Transport { + +class DiscoInfoResponder : public Swift::GetResponder { + public: + DiscoInfoResponder(Swift::IQRouter *router); + ~DiscoInfoResponder(); + + void setTransportFeatures(std::list &features); + void setBuddyFeatures(std::list &features); + + boost::signal onBuddyCapsInfoChanged; + + private: + virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const Swift::String& id, boost::shared_ptr payload); + + Swift::DiscoInfo m_transportInfo; + Swift::DiscoInfo m_buddyInfo; +}; + +} \ No newline at end of file diff --git a/src/logger.cpp b/src/logger.cpp new file mode 100644 index 00000000..dedb9278 --- /dev/null +++ b/src/logger.cpp @@ -0,0 +1,95 @@ +/** + * libtransport -- C++ library for easy XMPP Transports development + * + * Copyright (C) 2011, Jan Kaluza + * + * 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 2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "transport/logger.h" +#include "transport/usermanager.h" +#include "transport/user.h" +#include "transport/transport.h" +#include "transport/storagebackend.h" +#include "transport/userregistration.h" +#include + +using namespace boost; + +namespace Transport { + +Logger::Logger(Component *component) { + component->onConnected.connect(bind(&Logger::handleConnected, this)); + component->onConnectionError.connect(bind(&Logger::handleConnectionError, this, _1)); + component->onXMLIn.connect(bind(&Logger::handleXMLIn, this, _1)); + component->onXMLOut.connect(bind(&Logger::handleXMLOut, this, _1)); +} + +Logger::~Logger(){ +} + +void Logger::setStorageBackend(StorageBackend *storage) { + storage->onStorageError.connect(bind(&Logger::handleStorageError, this, _1, _2)); +} + +void Logger::setUserRegistration(UserRegistration *userRegistration) { + userRegistration->onUserRegistered.connect(bind(&Logger::handleUserRegistered, this, _1)); + userRegistration->onUserUnregistered.connect(bind(&Logger::handleUserUnregistered, this, _1)); + userRegistration->onUserUpdated.connect(bind(&Logger::handleUserUpdated, this, _1)); +} + + +void Logger::handleConnected() { + std::cout << "[COMPONENT] Connected to Jabber Server!\n"; +} + +void Logger::handleConnectionError(const Swift::ComponentError &error) { + std::cout << "[COMPONENT] Connection Error!\n"; + switch (error.getType()) { + case Swift::ComponentError::UnknownError: std::cout << "[COMPONENT] Disconnect reason: UnknownError\n"; break; + case Swift::ComponentError::ConnectionError: std::cout << "[COMPONENT] Disconnect reason: ConnectionError\n"; break; + case Swift::ComponentError::ConnectionReadError: std::cout << "[COMPONENT] Disconnect reason: ConnectionReadError\n"; break; + case Swift::ComponentError::ConnectionWriteError: std::cout << "[COMPONENT] Disconnect reason: ConnectionWriteError\n"; break; + case Swift::ComponentError::XMLError: std::cout << "[COMPONENT] Disconnect reason: XMLError\n"; break; + case Swift::ComponentError::AuthenticationFailedError: std::cout << "[COMPONENT] Disconnect reason: AuthenticationFailedError\n"; break; + case Swift::ComponentError::UnexpectedElementError: std::cout << "[COMPONENT] Disconnect reason: UnexpectedElementError\n"; break; + }; +} + +void Logger::handleXMLIn(const std::string &data) { + std::cout << "[XML IN] " << data << "\n"; +} + +void Logger::handleXMLOut(const std::string &data) { + std::cout << "[XML OUT] " << data << "\n"; +} + +void Logger::handleStorageError(const std::string &statement, const std::string &error) { + std::cout << "[SQL ERROR] \"" << error << "\" during statement \"" << statement << "\"\n"; +} + +void Logger::handleUserRegistered(const UserInfo &user) { + std::cout << "[REGISTRATION] User \"" << user.jid << "\" registered as \"" << user.uin << "\"\n"; +} + +void Logger::handleUserUnregistered(const UserInfo &user) { + std::cout << "[REGISTRATION] User \"" << user.jid << "\" unregistered \"" << user.uin << "\"\n"; +} + +void Logger::handleUserUpdated(const UserInfo &user) { + std::cout << "[REGISTRATION] User \"" << user.jid << "\" updated \"" << user.uin << "\"\n"; +} + +} diff --git a/src/sqlite3backend.cpp b/src/sqlite3backend.cpp new file mode 100644 index 00000000..73da62ee --- /dev/null +++ b/src/sqlite3backend.cpp @@ -0,0 +1,138 @@ +/** + * libtransport -- C++ library for easy XMPP Transports development + * + * Copyright (C) 2011, Jan Kaluza + * + * 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 2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "transport/sqlite3backend.h" +#include + +#define SQLITE_DB_VERSION 3 + +using namespace boost; + +namespace Transport { + +SQLite3Backend::SQLite3Backend(Config::Variables &config) { + m_config = config; + m_db = NULL; + m_prefix = m_config["database.prefix"].as(); +} + +SQLite3Backend::~SQLite3Backend(){ + if (m_db) { + sqlite3_close(m_db); + } +} + +bool SQLite3Backend::connect() { + if (sqlite3_open(m_config["database.database"].as().c_str(), &m_db)) { + sqlite3_close(m_db); + return false; + } + return createDatabase(); +} + +bool SQLite3Backend::createDatabase() { + int not_exist = exec("CREATE TABLE " + m_prefix + "buddies (" + " id INTEGER PRIMARY KEY NOT NULL," + " user_id int(10) NOT NULL," + " uin varchar(255) NOT NULL," + " subscription varchar(20) NOT NULL," + " nickname varchar(255) NOT NULL," + " groups varchar(255) NOT NULL," + " flags int(4) NOT NULL DEFAULT '0'" + ");"); + + if (not_exist) { + exec("CREATE UNIQUE INDEX IF NOT EXISTS user_id ON " + m_prefix + "buddies (user_id, uin);"); + + exec("CREATE TABLE IF NOT EXISTS " + m_prefix + "buddies_settings (" + " user_id int(10) NOT NULL," + " buddy_id int(10) NOT NULL," + " var varchar(50) NOT NULL," + " type int(4) NOT NULL," + " value varchar(255) NOT NULL," + " PRIMARY KEY (buddy_id, var)" + ");"); + + exec("CREATE INDEX IF NOT EXISTS user_id02 ON " + m_prefix + "buddies_settings (user_id);"); + + exec("CREATE TABLE IF NOT EXISTS " + m_prefix + "users (" + " id INTEGER PRIMARY KEY NOT NULL," + " jid varchar(255) NOT NULL," + " uin varchar(4095) NOT NULL," + " password varchar(255) NOT NULL," + " language varchar(25) NOT NULL," + " encoding varchar(50) NOT NULL DEFAULT 'utf8'," + " last_login datetime," + " vip int(1) NOT NULL DEFAULT '0'," + " online int(1) NOT NULL DEFAULT '0'" + ");"); + + exec("CREATE UNIQUE INDEX IF NOT EXISTS jid ON " + m_prefix + "users (jid);"); + + exec("CREATE TABLE " + m_prefix + "users_settings (" + " user_id int(10) NOT NULL," + " var varchar(50) NOT NULL," + " type int(4) NOT NULL," + " value varchar(255) NOT NULL," + " PRIMARY KEY (user_id, var)" + ");"); + + exec("CREATE INDEX IF NOT EXISTS user_id03 ON " + m_prefix + "users_settings (user_id);"); + + exec("CREATE TABLE IF NOT EXISTS " + m_prefix + "db_version (" + " ver INTEGER NOT NULL DEFAULT '3'" + ");"); + exec("REPLACE INTO " + m_prefix + "db_version (ver) values(3)"); + } + return true; +} + +bool SQLite3Backend::exec(const std::string &query) { + char *errMsg = 0; + int rc = sqlite3_exec(m_db, query.c_str(), NULL, 0, &errMsg); + if (rc != SQLITE_OK) { + onStorageError(query, errMsg); + sqlite3_free(errMsg); + return false; + } + return true; +} + +void SQLite3Backend::setUser(const UserInfo &user) { + +} + +bool SQLite3Backend::getUser(const std::string &barejid, UserInfo &user) { + return true; +} + +void SQLite3Backend::setUserOnline(long id, bool online) { + +} + +bool SQLite3Backend::getBuddies(long id, std::list &roster) { + return true; +} + +void SQLite3Backend::removeUser(long id) { + +} + +} diff --git a/src/transport.cpp b/src/transport.cpp index dc7ea025..3c15d24e 100644 --- a/src/transport.cpp +++ b/src/transport.cpp @@ -20,15 +20,18 @@ #include "transport/transport.h" #include +#include "transport/storagebackend.h" +#include "discoinforesponder.h" using namespace Swift; using namespace boost; namespace Transport { -Transport::Transport(Swift::EventLoop *loop, Config::Variables &config) { +Component::Component(Swift::EventLoop *loop, Config::Variables &config) { m_reconnectCount = 0; m_config = config; + m_storageBackend = NULL; for (Config::Variables::iterator i = config.begin() ; i != config.end() ; ++i ) { @@ -40,33 +43,33 @@ Transport::Transport(Swift::EventLoop *loop, Config::Variables &config) { m_factories = new BoostNetworkFactories(loop); m_reconnectTimer = m_factories->getTimerFactory()->createTimer(1000); - m_reconnectTimer->onTick.connect(bind(&Transport::connect, this)); + m_reconnectTimer->onTick.connect(bind(&Component::connect, this)); - m_component = new Component(loop, m_factories, m_jid, m_config["service.password"].as()); + m_component = new Swift::Component(loop, m_factories, m_jid, m_config["service.password"].as()); m_component->setSoftwareVersion("", ""); - m_component->onConnected.connect(bind(&Transport::handleConnected, this)); - m_component->onError.connect(bind(&Transport::handleConnectionError, this, _1)); - m_component->onDataRead.connect(bind(&Transport::handleDataRead, this, _1)); - m_component->onDataWritten.connect(bind(&Transport::handleDataWritten, this, _1)); -// m_component->onPresenceReceived.connect(bind(&Transport::handlePresenceReceived, this, _1)); -// m_component->onMessageReceived.connect(bind(&Transport::handleMessageReceived, this, _1)); + m_component->onConnected.connect(bind(&Component::handleConnected, this)); + m_component->onError.connect(bind(&Component::handleConnectionError, this, _1)); + m_component->onDataRead.connect(bind(&Component::handleDataRead, this, _1)); + m_component->onDataWritten.connect(bind(&Component::handleDataWritten, this, _1)); + m_component->onPresenceReceived.connect(bind(&Component::handlePresenceReceived, this, _1)); +// m_component->onMessageReceived.connect(bind(&Component::handleMessageReceived, this, _1)); m_capsMemoryStorage = new CapsMemoryStorage(); m_capsManager = new CapsManager(m_capsMemoryStorage, m_component->getStanzaChannel(), m_component->getIQRouter()); m_entityCapsManager = new EntityCapsManager(m_capsManager, m_component->getStanzaChannel()); -// m_entityCapsManager->onCapsChanged.connect(boost::bind(&Transport::handleCapsChanged, this, _1)); +// m_entityCapsManager->onCapsChanged.connect(boost::bind(&Component::handleCapsChanged, this, _1)); m_presenceOracle = new PresenceOracle(m_component->getStanzaChannel()); -// m_presenceOracle->onPresenceChange.connect(bind(&Transport::handlePresence, this, _1)); + m_presenceOracle->onPresenceChange.connect(bind(&Component::handlePresence, this, _1)); -// m_discoInfoResponder = new SpectrumDiscoInfoResponder(m_component->getIQRouter()); -// m_discoInfoResponder->start(); + m_discoInfoResponder = new DiscoInfoResponder(m_component->getIQRouter()); + m_discoInfoResponder->start(); // // m_registerHandler = new SpectrumRegisterHandler(m_component); // m_registerHandler->start(); } -Transport::~Transport() { +Component::~Component() { delete m_presenceOracle; delete m_entityCapsManager; delete m_capsManager; @@ -77,31 +80,176 @@ Transport::~Transport() { delete m_factories; } -void Transport::connect() { +void Component::setStorageBackend(StorageBackend *backend) { + m_storageBackend = backend; +} + +void Component::setTransportFeatures(std::list &features) { + m_discoInfoResponder->setTransportFeatures(features); +} + +void Component::setBuddyFeatures(std::list &features) { + // TODO: handle caps change + m_discoInfoResponder->setBuddyFeatures(features); +} + +void Component::connect() { m_reconnectCount++; m_component->connect(m_config["service.server"].as(), m_config["service.port"].as()); m_reconnectTimer->stop(); } -void Transport::handleConnected() { +void Component::handleConnected() { onConnected(); m_reconnectCount = 0; } -void Transport::handleConnectionError(const ComponentError &error) { +void Component::handleConnectionError(const ComponentError &error) { onConnectionError(error); // if (m_reconnectCount == 2) -// Transport::instance()->userManager()->removeAllUsers(); +// Component::instance()->userManager()->removeAllUsers(); m_reconnectTimer->start(); } -void Transport::handleDataRead(const String &data) { +void Component::handleDataRead(const String &data) { onXMLIn(data.getUTF8String()); } -void Transport::handleDataWritten(const String &data) { +void Component::handleDataWritten(const String &data) { onXMLOut(data.getUTF8String()); } +void Component::handlePresenceReceived(Swift::Presence::ref presence) { + switch(presence->getType()) { + case Swift::Presence::Subscribe: + case Swift::Presence::Subscribed: + case Swift::Presence::Unsubscribe: + case Swift::Presence::Unsubscribed: + handleSubscription(presence); + break; + case Swift::Presence::Available: + case Swift::Presence::Unavailable: + break; + case Swift::Presence::Probe: + handleProbePresence(presence); + break; + default: + break; + }; +} + +void Component::handlePresence(Swift::Presence::ref presence) { + bool isMUC = presence->getPayload() != NULL; + + // filter out login/logout presence spam + if (!presence->getTo().getNode().isEmpty() && isMUC == false) + return; + + // filter out bad presences + if (!presence->getFrom().isValid()) { + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo(presence->getFrom()); + response->setFrom(presence->getTo()); + response->setType(Swift::Presence::Error); + + response->addPayload(boost::shared_ptr(new ErrorPayload(ErrorPayload::JIDMalformed, ErrorPayload::Modify))); + + m_component->sendPresence(response); + return; + } + + // check if we have this client's capabilities and ask for them + bool haveFeatures = false; + if (presence->getType() != Swift::Presence::Unavailable) { + boost::shared_ptr capsInfo = presence->getPayload(); + if (capsInfo && capsInfo->getHash() == "sha-1") { + haveFeatures = m_entityCapsManager->getCaps(presence->getFrom()) != DiscoInfo::ref(); + std::cout << "has capsInfo " << haveFeatures << "\n"; + } + else { + GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(presence->getFrom(), m_component->getIQRouter()); + discoInfoRequest->onResponse.connect(boost::bind(&Component::handleDiscoInfoResponse, this, _1, _2, presence->getFrom())); + discoInfoRequest->send(); + } + } + + onUserPresenceReceived(presence); +} + +void Component::handleProbePresence(Swift::Presence::ref presence) { + +} + +void Component::handleSubscription(Swift::Presence::ref presence) { + // answer to subscibe + if (presence->getType() == Swift::Presence::Subscribe && presence->getTo().getNode().isEmpty()) { +// Log(presence->getFrom().toString().getUTF8String(), "Subscribe presence received => sending subscribed"); + Swift::Presence::ref response = Swift::Presence::create(); + response->setFrom(presence->getTo()); + response->setTo(presence->getFrom()); + response->setType(Swift::Presence::Subscribed); + m_component->sendPresence(response); + return; + } + + if (m_protocol == "irc") { + return; + } + +// User *user; +// std::string barejid = presence->getTo().toBare().toString().getUTF8String(); +// std::string userkey = presence->getFrom().toBare().toString().getUTF8String(); +// if (Transport::instance()->protocol()->tempAccountsAllowed()) { +// std::string server = barejid.substr(barejid.find("%") + 1, barejid.length() - barejid.find("%")); +// userkey += server; +// } + +// user = (User *) Transport::instance()->userManager()->getUserByJID(userkey); +// if (user) { +// user->handleSubscription(presence); +// } +// else if (presence->getType() == Swift::Presence::Unsubscribe) { +// Swift::Presence::ref response = Swift::Presence::create(); +// response->setFrom(presence->getTo()); +// response->setTo(presence->getFrom()); +// response->setType(Swift::Presence::Unsubscribed); +// m_component->sendPresence(response); +// } +// else { +// // Log(presence->getFrom().toString().getUTF8String(), "Subscribe presence received, but this user is not logged in"); +// } +} + +void Component::handleDiscoInfoResponse(boost::shared_ptr discoInfo, Swift::ErrorPayload::ref error, const Swift::JID& jid) { +// AbstractUser *user = Transport::instance()->userManager()->getUserByJID(jid.toBare().toString().getUTF8String()); +// +// std::string resource = jid.getResource().getUTF8String(); +// if (user && user->hasResource(resource)) { +// if (user->getResource(resource).caps == 0) { +// int capabilities = 0; +// +// for (std::vector< String >::const_iterator it = discoInfo->getFeatures().begin(); it != discoInfo->getFeatures().end(); ++it) { +// if (*it == "http://jabber.org/protocol/rosterx") { +// capabilities |= CLIENT_FEATURE_ROSTERX; +// } +// else if (*it == "http://jabber.org/protocol/xhtml-im") { +// capabilities |= CLIENT_FEATURE_XHTML_IM; +// } +// else if (*it == "http://jabber.org/protocol/si/profile/file-transfer") { +// capabilities |= CLIENT_FEATURE_FILETRANSFER; +// } +// else if (*it == "http://jabber.org/protocol/chatstates") { +// capabilities |= CLIENT_FEATURE_CHATSTATES; +// } +// } +// +// user->setResource(resource, -256, capabilities); +// if (user->readyForConnect()) { +// user->connect(); +// } +// } +// } +} + } diff --git a/src/user.cpp b/src/user.cpp new file mode 100644 index 00000000..c7bd18ce --- /dev/null +++ b/src/user.cpp @@ -0,0 +1,44 @@ +/** + * libtransport -- C++ library for easy XMPP Transports development + * + * Copyright (C) 2011, Jan Kaluza + * + * 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 2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "transport/user.h" +#include "transport/transport.h" +#include "Swiften/Swiften.h" + +namespace Transport { + +User::User(const Swift::JID &jid, const std::string &username, const std::string &password, Component *component) { + m_jid = jid; + + m_component = component->m_component; + m_presenceOracle = component->m_presenceOracle; + m_entityCapsManager = component->m_entityCapsManager; +// m_activeResource = m_jid.getResource(); + +} +User::~User(){ + +} + +const Swift::JID &User::getJID() { + return m_jid; +} + +} diff --git a/src/usermanager.cpp b/src/usermanager.cpp new file mode 100644 index 00000000..812dc3da --- /dev/null +++ b/src/usermanager.cpp @@ -0,0 +1,162 @@ +/** + * libtransport -- C++ library for easy XMPP Transports development + * + * Copyright (C) 2011, Jan Kaluza + * + * 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 2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "transport/usermanager.h" +#include "transport/user.h" +#include "transport/transport.h" + +namespace Transport { + +UserManager::UserManager(Component *component) { + m_cachedUser = NULL; + m_onlineBuddies = 0; + + component->onUserPresenceReceived.connect(bind(&UserManager::handlePresence, this, _1)); +} + +UserManager::~UserManager(){ +} + +User *UserManager::getUserByJID(const std::string &barejid){ + if (m_cachedUser && barejid == m_cachedUser->getJID().toBare().toString().getUTF8String()) { + return m_cachedUser; + } + + if (m_users.find(barejid) != m_users.end()) { + User *user = m_users[barejid]; + m_cachedUser = user; + return user; + } + return NULL; +} + +int UserManager::userCount() { + return m_users.size(); +} + +void UserManager::handlePresence(Swift::Presence::ref presence) { +// std::string barejid = presence->getTo().toBare().toString().getUTF8String(); +// std::string userkey = presence->getFrom().toBare().toString().getUTF8String(); +// // if (Transport::instance()->protocol()->tempAccountsAllowed()) { +// // std::string server = barejid.substr(barejid.find("%") + 1, barejid.length() - barejid.find("%")); +// // userkey += server; +// // } +// +// User *user = getUserByJID(userkey); +// if (user ) { +// user->handlePresence(presence); +// } +// else { +// // // No user, unavailable presence... nothing to do +// // if (presence->getType() == Swift::Presence::Unavailable) { +// // Swift::Presence::ref response = Swift::Presence::create(); +// // response->setTo(presence->getFrom()); +// // response->setFrom(Swift::JID(Transport::instance()->jid())); +// // response->setType(Swift::Presence::Unavailable); +// // m_component->sendPresence(response); +// // +// // // UserRow res = Transport::instance()->sql()->getUserByJid(userkey); +// // // if (res.id != -1) { +// // // Transport::instance()->sql()->setUserOnline(res.id, false); +// // // } +// // return; +// // } +// // UserRow res = Transport::instance()->sql()->getUserByJid(userkey); +// // if (res.id == -1 && !Transport::instance()->protocol()->tempAccountsAllowed()) { +// // // presence from unregistered user +// // Log(presence->getFrom().toString().getUTF8String(), "This user is not registered"); +// // return; +// // } +// // else { +// // if (res.id == -1 && Transport::instance()->protocol()->tempAccountsAllowed()) { +// // res.jid = userkey; +// // res.uin = presence->getFrom().toBare().toString().getUTF8String(); +// // res.password = ""; +// // res.language = "en"; +// // res.encoding = CONFIG().encoding; +// // res.vip = 0; +// // Transport::instance()->sql()->addUser(res); +// // res = Transport::instance()->sql()->getUserByJid(userkey); +// // } +// // +// // bool isVip = res.vip; +// // std::list const &x = CONFIG().allowedServers; +// // if (CONFIG().onlyForVIP && !isVip && std::find(x.begin(), x.end(), presence->getFrom().getDomain().getUTF8String()) == x.end()) { +// // Log(presence->getFrom().toString().getUTF8String(), "This user is not VIP, can't login..."); +// // return; +// // } +// // +// // Log(presence->getFrom().toString().getUTF8String(), "Creating new User instance"); +// // +// // if (Transport::instance()->protocol()->tempAccountsAllowed()) { +// // std::string server = barejid.substr(barejid.find("%") + 1, barejid.length() - barejid.find("%")); +// // res.uin = presence->getTo().getResource().getUTF8String() + "@" + server; +// // } +// // else { +// // if (purple_accounts_find(res.uin.c_str(), Transport::instance()->protocol()->protocol().c_str()) != NULL) { +// // PurpleAccount *act = purple_accounts_find(res.uin.c_str(), Transport::instance()->protocol()->protocol().c_str()); +// // user = Transport::instance()->userManager()->getUserByAccount(act); +// // if (user) { +// // Log(presence->getFrom().toString().getUTF8String(), "This account is already connected by another jid " << user->jid()); +// // return; +// // } +// // } +// // } +// // user = (AbstractUser *) new User(res, userkey, m_component, m_presenceOracle, m_entityCapsManager); +// // user->setFeatures(isVip ? CONFIG().VIPFeatures : CONFIG().transportFeatures); +// // // if (c != NULL) +// // // if (Transport::instance()->hasClientCapabilities(c->findAttribute("ver"))) +// // // user->setResource(stanza.from().resource(), stanza.priority(), Transport::instance()->getCapabilities(c->findAttribute("ver"))); +// // // +// // Transport::instance()->userManager()->addUser(user); +// // user->receivedPresence(presence); +// // // if (protocol()->tempAccountsAllowed()) { +// // // std::string server = stanza.to().username().substr(stanza.to().username().find("%") + 1, stanza.to().username().length() - stanza.to().username().find("%")); +// // // server = stanza.from().bare() + server; +// // // purple_timeout_add_seconds(15, &connectUser, g_strdup(server.c_str())); +// // // } +// // // else +// // // purple_timeout_add_seconds(15, &connectUser, g_strdup(stanza.from().bare().c_str())); +// // // } +// // } +// // // if (stanza.presence() == Presence::Unavailable && stanza.to().username() == ""){ +// // // Log(stanza.from().full(), "User is already logged out => sending unavailable presence"); +// // // Tag *tag = new Tag("presence"); +// // // tag->addAttribute( "to", stanza.from().bare() ); +// // // tag->addAttribute( "type", "unavailable" ); +// // // tag->addAttribute( "from", jid() ); +// // // j->send( tag ); +// // // } +// } +// +// if (presence->getType() == Swift::Presence::Unavailable) { +// if (user) { +// Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(presence->getFrom().toBare()); +// if (presence->getType() == Swift::Presence::Unavailable && (!highest || (highest && highest->getType() == Swift::Presence::Unavailable))) { +// Transport::instance()->userManager()->removeUser(user); +// } +// } +// else if (user && Transport::instance()->protocol()->tempAccountsAllowed() && !((User *) user)->hasOpenedMUC()) { +// Transport::instance()->userManager()->removeUser(user); +// } +// } +} + +} diff --git a/src/userregistration.cpp b/src/userregistration.cpp new file mode 100644 index 00000000..cd84c35c --- /dev/null +++ b/src/userregistration.cpp @@ -0,0 +1,369 @@ +/** + * libtransport -- C++ library for easy XMPP Transports development + * + * Copyright (C) 2011, Jan Kaluza + * + * 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 2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "transport/userregistration.h" +#include "transport/usermanager.h" +#include "transport/storagebackend.h" +#include "transport/transport.h" +#include "transport/user.h" +#include "Swiften/Elements/ErrorPayload.h" +#include + +using namespace Swift; + +namespace Transport { + +UserRegistration::UserRegistration(Component *component, UserManager *userManager, StorageBackend *storageBackend) : Swift::GetResponder(component->m_component->getIQRouter()), Swift::SetResponder(component->m_component->getIQRouter()) { + m_component = component; + m_storageBackend = storageBackend; + m_userManager = userManager; + Swift::GetResponder::start(); + Swift::SetResponder::start(); +} + +UserRegistration::~UserRegistration(){ +} + +bool UserRegistration::registerUser(const UserInfo &row) { + // TODO: move this check to sql()->addUser(...) and let it return bool + UserInfo user; + bool registered = m_storageBackend->getUser(row.jid, user); + // This user is already registered + if (registered) + return false; + + m_storageBackend->setUser(row); + + Swift::Presence::ref response = Swift::Presence::create(); + response->setFrom(m_component->getJID()); + response->setTo(Swift::JID(row.jid)); + response->setType(Swift::Presence::Subscribe); + + m_component->m_component->sendPresence(response); + + onUserRegistered(row); + return true; +} + +bool UserRegistration::unregisterUser(const std::string &barejid) { + UserInfo userInfo; + bool registered = m_storageBackend->getUser(barejid, userInfo); + // This user is not registered + if (!registered) + return false; + + onUserUnregistered(userInfo); + + Swift::Presence::ref response; + + User *user = m_userManager->getUserByJID(barejid); + + // roster contains already escaped jids + std::list roster; + m_storageBackend->getBuddies(userInfo.id, roster); + + for(std::list::iterator u = roster.begin(); u != roster.end() ; u++){ + std::string name = *u; + + response = Swift::Presence::create(); + response->setTo(Swift::JID(barejid)); + response->setFrom(Swift::JID(name + "@" + m_component->getJID().toString())); + response->setType(Swift::Presence::Unsubscribe); + m_component->m_component->sendPresence(response); + + response = Swift::Presence::create(); + response->setTo(Swift::JID(barejid)); + response->setFrom(Swift::JID(name + "@" + m_component->getJID().toString())); + response->setType(Swift::Presence::Unsubscribed); + m_component->m_component->sendPresence(response); + } + + // Remove user from database + m_storageBackend->removeUser(userInfo.id); + + // Disconnect the user + if (user) { + m_userManager->removeUser(user); + } + + response = Swift::Presence::create(); + response->setTo(Swift::JID(barejid)); + response->setFrom(m_component->getJID()); + response->setType(Swift::Presence::Unsubscribe); + m_component->m_component->sendPresence(response); + + response = Swift::Presence::create(); + response->setTo(Swift::JID(barejid)); + response->setFrom(m_component->getJID()); + response->setType(Swift::Presence::Unsubscribed); + m_component->m_component->sendPresence(response); + + return true; +} + +bool UserRegistration::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const Swift::String& id, boost::shared_ptr payload) { + if (m_component->m_config["service.protocol"].as() == "irc") { + Swift::GetResponder::sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify); + return true; + } + + std::string barejid = from.toBare().toString().getUTF8String(); + +// User *user = m_userManager->getUserByJID(barejid); + if (!m_component->m_config["registration.enable_public_registration"].as()) { + std::list const &x = m_component->m_config["registration.enable_public_registration"].as >(); + if (std::find(x.begin(), x.end(), from.getDomain().getUTF8String()) == x.end()) { +// Log("UserRegistration", "This user has no permissions to register an account"); + Swift::GetResponder::sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify); + return true; + } + } + +// const char *_language = user ? user->getLang() : m_component->m_config["registration.language"].as().c_str(); + + boost::shared_ptr reg(new InBandRegistrationPayload()); + + UserInfo res; + bool registered = m_storageBackend->getUser(barejid, res); + + std::string instructions = m_component->m_config["registration.reg_instructions"].as(); + + reg->setInstructions(instructions); + reg->setRegistered(res.id != -1); + reg->setUsername(res.uin); + if (m_component->m_config["service.protocol"].as() != "twitter" && m_component->m_config["service.protocol"].as() != "bonjour") + reg->setPassword(res.password); + + std::string usernameField = m_component->m_config["registration.reg_username_field"].as(); + + Form::ref form(new Form(Form::FormType)); + form->setTitle(tr(_language, _("Registration"))); + form->setInstructions(tr(_language, instructions)); + + HiddenFormField::ref type = HiddenFormField::create(); + type->setName("FORM_TYPE"); + type->setValue("jabber:iq:register"); + form->addField(type); + + TextSingleFormField::ref username = TextSingleFormField::create(); + username->setName("username"); + username->setLabel(tr(_language, usernameField)); + username->setValue(res.uin); + username->setRequired(true); + form->addField(username); + + if (m_component->m_config["service.protocol"].as() != "twitter" && m_component->m_config["service.protocol"].as() != "bonjour") { + TextPrivateFormField::ref password = TextPrivateFormField::create(); + password->setName("password"); + password->setLabel(tr(_language, _("Password"))); + password->setRequired(true); + form->addField(password); + } + + ListSingleFormField::ref language = ListSingleFormField::create(); + language->setName("language"); + language->setLabel(tr(_language, _("Language"))); + if (registered) + language->setValue(res.language); + else + language->setValue(m_component->m_config["registration.language"].as()); +// std::map languages = localization.getLanguages(); +// for (std::map ::iterator it = languages.begin(); it != languages.end(); it++) { +// language->addOption(FormField::Option((*it).second, (*it).first)); +// } + form->addField(language); + + TextSingleFormField::ref encoding = TextSingleFormField::create(); + encoding->setName("encoding"); + encoding->setLabel(tr(_language, _("Encoding"))); + if (registered) + encoding->setValue(res.encoding); + else + encoding->setValue(m_component->m_config["registration.encoding"].as()); + form->addField(encoding); + + if (registered) { + BooleanFormField::ref boolean = BooleanFormField::create(); + boolean->setName("unregister"); + boolean->setLabel(tr(_language, _("Remove your registration"))); + boolean->setValue(0); + form->addField(boolean); + } + + reg->setForm(form); + + Swift::GetResponder::sendResponse(from, id, reg); + + return true; +} + +bool UserRegistration::handleSetRequest(const Swift::JID& from, const Swift::JID& to, const Swift::String& id, boost::shared_ptr payload) { + if (m_component->m_config["service.protocol"].as() == "irc") { + Swift::GetResponder::sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify); + return true; + } + + std::string barejid = from.toBare().toString().getUTF8String(); + +// AbstractUser *user = m_component->userManager()->getUserByJID(barejid); + if (!m_component->m_config["registration.enable_public_registration"].as()) { + std::list const &x = m_component->m_config["registration.enable_public_registration"].as >(); + if (std::find(x.begin(), x.end(), from.getDomain().getUTF8String()) == x.end()) { +// Log("UserRegistration", "This user has no permissions to register an account"); + Swift::SetResponder::sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify); + return true; + } + } + + UserInfo res; + bool registered = m_storageBackend->getUser(barejid, res); + + std::string encoding; + std::string language; + + Form::ref form = payload->getForm(); + if (form) { + const std::vector fields = form->getFields(); + for (std::vector::const_iterator it = fields.begin(); it != fields.end(); it++) { + TextSingleFormField::ref textSingle = boost::dynamic_pointer_cast(*it); + if (textSingle) { + if (textSingle->getName() == "username") { + payload->setUsername(textSingle->getValue()); + } + else if (textSingle->getName() == "encoding") { + encoding = textSingle->getValue().getUTF8String(); + } + continue; + } + + TextPrivateFormField::ref textPrivate = boost::dynamic_pointer_cast(*it); + if (textPrivate) { + if (textPrivate->getName() == "password") { + payload->setPassword(textPrivate->getValue()); + } + continue; + } + + ListSingleFormField::ref listSingle = boost::dynamic_pointer_cast(*it); + if (listSingle) { + if (listSingle->getName() == "language") { + language = listSingle->getValue().getUTF8String(); + } + continue; + } + + BooleanFormField::ref boolean = boost::dynamic_pointer_cast(*it); + if (boolean) { + if (boolean->getName() == "unregister") { + if (boolean->getValue()) { + payload->setRemove(true); + } + } + continue; + } + } + } + + if (payload->isRemove()) { + unregisterUser(barejid); + Swift::SetResponder::sendResponse(from, id, InBandRegistrationPayload::ref()); + return true; + } + + if (!payload->getUsername() || !payload->getPassword()) { + Swift::SetResponder::sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); + return true; + } + + // Register or change password + if (payload->getUsername()->isEmpty() || + (payload->getPassword()->isEmpty() && m_component->m_config["service.protocol"].as() != "twitter" && m_component->m_config["service.protocol"].as() != "bonjour") +// || localization.getLanguages().find(language) == localization.getLanguages().end() + ) + { + Swift::SetResponder::sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); + return true; + } + + if (m_component->m_config["service.protocol"].as() == "xmpp") { + // User tries to register himself. + if ((Swift::JID(*payload->getUsername()).toBare() == from.toBare())) { + Swift::SetResponder::sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); + return true; + } + + // User tries to register someone who's already registered. + UserInfo user_row; + bool registered = m_storageBackend->getUser(Swift::JID(*payload->getUsername()).toBare().toString().getUTF8String(), user_row); + if (registered) { + Swift::SetResponder::sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); + return true; + } + } + + std::string username = payload->getUsername()->getUTF8String(); +// m_component->protocol()->prepareUsername(username); + + std::string newUsername(username); + if (!m_component->m_config["registration.username_mask"].as().empty()) { + newUsername = m_component->m_config["registration.username_mask"].as(); +// replace(newUsername, "$username", username.c_str()); + } + +// if (!m_component->protocol()->isValidUsername(newUsername)) { +// Log("UserRegistration", "This is not valid username: "<< newUsername); +// Swift::SetResponder::sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); +// return true; +// } + +// #if GLIB_CHECK_VERSION(2,14,0) +// if (!m_component->m_config["registration.reg_allowed_usernames"].as().empty() && +// !g_regex_match_simple(m_component->m_config["registration.reg_allowed_usernames"].as(), newUsername.c_str(),(GRegexCompileFlags) (G_REGEX_CASELESS | G_REGEX_EXTENDED), (GRegexMatchFlags) 0)) { +// Log("UserRegistration", "This is not valid username: "<< newUsername); +// Swift::SetResponder::sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); +// return true; +// } +// #endif + if (!registered) { + res.jid = barejid; + res.uin = username; + res.password = payload->getPassword()->getUTF8String(); + res.language = language; + res.encoding = encoding; + res.vip = 0; + + registerUser(res); + } + else { + // change passwordhttp://soumar.jabbim.cz/phpmyadmin/index.php +// Log("UserRegistration", "changing user password: "<< barejid << ", " << username); + res.jid = barejid; + res.password = payload->getPassword()->getUTF8String(); + res.language = language; + res.encoding = encoding; + m_storageBackend->setUser(res); + onUserUpdated(res); + } + + Swift::SetResponder::sendResponse(from, id, InBandRegistrationPayload::ref()); + return true; +} + +}