From 0d99e8c85d38ca3a5c11230714c5d3ea210d2cbc Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Tue, 31 Mar 2015 14:14:15 +0300 Subject: [PATCH] Initial swiften 3 support --- CMakeLists.txt | 1044 ++--- backends/swiften/main.cpp | 902 ++-- backends/twitter/CMakeLists.txt | 13 +- backends/twitter/TwitterPlugin.cpp | 1780 ++++---- backends/twitter/TwitterPlugin.h | 370 +- backends/twitter/libtwitcurl/twitcurl.cpp | 1 + cmake_modules/SwiftenConfig.cmake | 76 +- include/Swiften/Elements/PubSubItem.cpp | 14 - include/Swiften/Elements/PubSubItem.h | 64 - include/Swiften/Elements/PubSubPayload.cpp | 14 - include/Swiften/Elements/PubSubPayload.h | 57 - .../Swiften/Elements/PubSubPublishPayload.cpp | 16 - .../Swiften/Elements/PubSubPublishPayload.h | 43 - .../Elements/PubSubSubscribePayload.cpp | 16 - .../Swiften/Elements/PubSubSubscribePayload.h | 40 - .../Elements/PubSubSubscriptionPayload.cpp | 16 - .../Elements/PubSubSubscriptionPayload.h | 60 - .../CombinedOutgoingFileTransferManager.cpp | 218 +- .../CombinedOutgoingFileTransferManager.h | 113 +- .../FileTransfer/MyOutgoingSIFileTransfer.cpp | 229 +- .../FileTransfer/MyOutgoingSIFileTransfer.h | 114 +- .../Swiften/Network/DummyConnectionServer.cpp | 40 - .../Swiften/Network/DummyConnectionServer.h | 49 - .../Network/DummyConnectionServerFactory.cpp | 23 - .../Network/DummyConnectionServerFactory.h | 28 - .../Swiften/Network/DummyNetworkFactories.h | 162 +- .../PayloadParsers/PubSubItemParser.cpp | 30 - .../Parser/PayloadParsers/PubSubItemParser.h | 24 - .../PayloadParsers/PubSubPayloadParser.cpp | 25 - .../PayloadParsers/PubSubPayloadParser.h | 24 - .../PubSubPublishPayloadParser.cpp | 30 - .../PubSubPublishPayloadParser.h | 24 - .../PubSubSubscribePayloadParser.cpp | 32 - .../PubSubSubscribePayloadParser.h | 24 - .../PubSubSubscriptionPayloadParser.cpp | 51 - .../PubSubSubscriptionPayloadParser.h | 24 - .../PubSubItemSerializer.cpp | 38 - .../PayloadSerializers/PubSubItemSerializer.h | 22 - .../PubSubPayloadSerializer.cpp | 36 - .../PubSubPayloadSerializer.h | 23 - .../PubSubPublishPayloadSerializer.cpp | 40 - .../PubSubPublishPayloadSerializer.h | 23 - .../PubSubSubscribePayloadSerializer.cpp | 34 - .../PubSubSubscribePayloadSerializer.h | 19 - .../PubSubSubscriptionPayloadSerializer.cpp | 49 - .../PubSubSubscriptionPayloadSerializer.h | 19 - .../Server/ServerFromClientSession.cpp | 366 +- .../Swiften/Server/ServerFromClientSession.h | 176 +- .../Swiften/Server/ServerStanzaChannel.cpp | 355 +- include/Swiften/Server/ServerStanzaChannel.h | 120 +- include/transport/filetransfermanager.h | 151 +- include/transport/networkpluginserver.h | 394 +- include/transport/settingsadhoccommand.h | 152 +- include/transport/userregistration.h | 172 +- msvc-deps/CMakeLists.txt | 3 +- msvc-deps/curl | 2 +- src/discoinforesponder.cpp | 328 +- src/discoinforesponder.h | 140 +- src/filetransfermanager.cpp | 191 +- src/networkpluginserver.cpp | 4047 +++++++++-------- src/settingsadhoccommand.cpp | 327 +- src/transport.cpp | 718 +-- src/user.cpp | 1008 ++-- src/usermanager.cpp | 1208 ++--- src/userregistration.cpp | 1161 ++--- 65 files changed, 8152 insertions(+), 8960 deletions(-) delete mode 100644 include/Swiften/Elements/PubSubItem.cpp delete mode 100644 include/Swiften/Elements/PubSubItem.h delete mode 100644 include/Swiften/Elements/PubSubPayload.cpp delete mode 100644 include/Swiften/Elements/PubSubPayload.h delete mode 100644 include/Swiften/Elements/PubSubPublishPayload.cpp delete mode 100644 include/Swiften/Elements/PubSubPublishPayload.h delete mode 100644 include/Swiften/Elements/PubSubSubscribePayload.cpp delete mode 100644 include/Swiften/Elements/PubSubSubscribePayload.h delete mode 100644 include/Swiften/Elements/PubSubSubscriptionPayload.cpp delete mode 100644 include/Swiften/Elements/PubSubSubscriptionPayload.h delete mode 100644 include/Swiften/Network/DummyConnectionServer.cpp delete mode 100644 include/Swiften/Network/DummyConnectionServer.h delete mode 100644 include/Swiften/Network/DummyConnectionServerFactory.cpp delete mode 100644 include/Swiften/Network/DummyConnectionServerFactory.h delete mode 100644 include/Swiften/Parser/PayloadParsers/PubSubItemParser.cpp delete mode 100644 include/Swiften/Parser/PayloadParsers/PubSubItemParser.h delete mode 100644 include/Swiften/Parser/PayloadParsers/PubSubPayloadParser.cpp delete mode 100644 include/Swiften/Parser/PayloadParsers/PubSubPayloadParser.h delete mode 100644 include/Swiften/Parser/PayloadParsers/PubSubPublishPayloadParser.cpp delete mode 100644 include/Swiften/Parser/PayloadParsers/PubSubPublishPayloadParser.h delete mode 100644 include/Swiften/Parser/PayloadParsers/PubSubSubscribePayloadParser.cpp delete mode 100644 include/Swiften/Parser/PayloadParsers/PubSubSubscribePayloadParser.h delete mode 100644 include/Swiften/Parser/PayloadParsers/PubSubSubscriptionPayloadParser.cpp delete mode 100644 include/Swiften/Parser/PayloadParsers/PubSubSubscriptionPayloadParser.h delete mode 100644 include/Swiften/Serializer/PayloadSerializers/PubSubItemSerializer.cpp delete mode 100644 include/Swiften/Serializer/PayloadSerializers/PubSubItemSerializer.h delete mode 100644 include/Swiften/Serializer/PayloadSerializers/PubSubPayloadSerializer.cpp delete mode 100644 include/Swiften/Serializer/PayloadSerializers/PubSubPayloadSerializer.h delete mode 100644 include/Swiften/Serializer/PayloadSerializers/PubSubPublishPayloadSerializer.cpp delete mode 100644 include/Swiften/Serializer/PayloadSerializers/PubSubPublishPayloadSerializer.h delete mode 100644 include/Swiften/Serializer/PayloadSerializers/PubSubSubscribePayloadSerializer.cpp delete mode 100644 include/Swiften/Serializer/PayloadSerializers/PubSubSubscribePayloadSerializer.h delete mode 100644 include/Swiften/Serializer/PayloadSerializers/PubSubSubscriptionPayloadSerializer.cpp delete mode 100644 include/Swiften/Serializer/PayloadSerializers/PubSubSubscriptionPayloadSerializer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b7ae408..1eaf2d0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,522 +1,522 @@ -cmake_minimum_required(VERSION 2.6) -project(libtransport) - -message(STATUS "Variables to override default places where to find libraries:") -message(STATUS "|- cppunit : -DCPPUNIT_INCLUDE_DIR, -DCPPUNIT_LIBRARY") -message(STATUS "|- swiften : -DSWIFTEN_INCLUDE_DIR, -DSWIFTEN_LIBRARY") -message(STATUS " |- zlib : -DZLIB_LIBRARY") -message(STATUS " |- expat : -DEXPAT_LIBRARY") -message(STATUS " |-libidn : -DLIBIDN_LIBRARY") -message(STATUS " |-libxml : -DLIBXML_LIBRARY") -message(STATUS "|- boost : -DBOOST_INCLUDEDIR, -DBOOST_LIBRARYDIR") -message(STATUS "|- protobuf: -DPROTOBUF_INCLUDE_DIR, -DPROTOBUF_LIBRARY") -message(STATUS " : -DPROTOBUF_PROTOC_EXECUTABLE") -message(STATUS "|- log4cxx : -DLOG4CXX_INCLUDE_DIR, -DLOG4CXX_LIBRARY") -message(STATUS "|- purple : -DPURPLE_INCLUDE_DIR, -DPURPLE_LIBRARY") -message(STATUS " : -DPURPLE_NOT_RUNTIME - enables compilation with libpurple.lib") - -option(ENABLE_SQLITE3 "Build with SQLite3 support" ON) -option(ENABLE_MYSQL "Build with MySQL support" ON) -option(ENABLE_PQXX "Build with Postgres supoort" ON) - -option(ENABLE_FROTZ "Build Frotz plugin" ON) -option(ENABLE_IRC "Build IRC plugin" ON) -option(ENABLE_PURPLE "Build Libpurple plugin" ON) -option(ENABLE_SMSTOOLS3 "Build SMSTools3 plugin" ON) -option(ENABLE_SKYPE "Build Skype plugin" ON) -option(ENABLE_SWIFTEN "Build Swiften plugin" ON) -option(ENABLE_TWITTER "Build Twitter plugin" ON) -option(ENABLE_YAHOO2 "Build Libyahoo2 plugin" ON) - -option(ENABLE_DOCS "Build Docs" ON) -# option(ENABLE_LOG "Build with logging using Log4cxx" ON) -option(ENABLE_TESTS "Build Tests using CppUnit" OFF) - -MACRO(LIST_CONTAINS var value) - SET(${var}) - FOREACH (value2 ${ARGN}) - IF (${value} STREQUAL ${value2}) - SET(${var} TRUE) - ENDIF (${value} STREQUAL ${value2}) - ENDFOREACH (value2) -ENDMACRO(LIST_CONTAINS) - -if(NOT LIB_INSTALL_DIR) - set(LIB_INSTALL_DIR "lib") -endif() - -set(CMAKE_MODULE_PATH "cmake_modules") - -###### Prerequisites ###### - - -# FIND SWIFTEN - -set(Swiften_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -find_package(Swiften) - -if(NOT SWIFTEN_FOUND) - if (ZLIB_LIBRARY) - set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${ZLIB_LIBRARY}) - endif() - if (EXPAT_LIBRARY) - set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${EXPAT_LIBRARY}) - endif() - if (LIBIDN_LIBRARY) - set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${LIBIDN_LIBRARY}) - endif() - if (LIBXML_LIBRARY) - set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${LIBXML_LIBRARY}) - endif() - set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Dnsapi") - set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Crypt32") - set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Secur32") - set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Iphlpapi") - set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Winscard") - message(STATUS "Using swiften: ${SWIFTEN_INCLUDE_DIR} ${SWIFTEN_LIBRARY}") -endif() - -# FIND BOOST -set(Boost_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -if (WIN32) - set(Boost_USE_STATIC_LIBS ON) - set(Boost_USE_MULTITHREADED ON) - set(Boost_USE_STATIC_RUNTIME OFF) - find_package(Boost COMPONENTS program_options date_time system filesystem regex thread signals REQUIRED) -else(WIN32) - LIST_CONTAINS(contains -lboost_program_options ${SWIFTEN_LIBRARY}) - if(contains) - message(STATUS "Using non-multithreaded boost") - set(Boost_USE_MULTITHREADED 0) - endif(contains) - set(Boost_FIND_QUIETLY ON) - find_package(Boost COMPONENTS program_options date_time system filesystem regex thread-mt signals) - if (NOT Boost_FOUND) - set(Boost_FIND_QUIETLY OFF) - find_package(Boost COMPONENTS program_options date_time system filesystem regex thread signals REQUIRED) - endif() -endif(WIN32) - -message( STATUS "Found Boost: ${Boost_VERSION}, ${Boost_LIBRARIES}, ${Boost_INCLUDE_DIR}") - -if (${Boost_VERSION} GREATER 104999) - message( STATUS "Using BOOST_FILESYSTEM_VERSION=3") - add_definitions(-DBOOST_FILESYSTEM_VERSION=3) -endif() - -# FIND POPT -if (NOT WIN32) - set(popt_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") - find_package(popt REQUIRED) -endif() - -###### Database ###### - -# FIND SQLITE3 -if (ENABLE_SQLITE3) - if (MSVC) - ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/msvc-deps) - else() - if (WIN32) - ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/msvc-deps/sqlite3) - else() - set(sqlite3_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") - find_package(sqlite3) - endif() - endif() -endif() - -# FIND MYSQL -if(ENABLE_MYSQL) - set(mysql_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") - find_package(mysql) -endif() - -# FIND PQXX -if(ENABLE_PQXX) - set(pqxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") - find_package(pqxx) -endif() - -###### Plugins ###### - -# FIND LIBPURPLE -if(ENABLE_PURPLE) - set(purple_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") - find_package(purple) - - if (WIN32) - if (PURPLE_NOT_RUNTIME) - add_definitions(-DPURPLE_RUNTIME=0) - else(PURPLE_NOT_RUNTIME) - add_definitions(-DPURPLE_RUNTIME=1) - endif(PURPLE_NOT_RUNTIME) - else() - add_definitions(-DPURPLE_RUNTIME=0) - endif() - - # FIND LIBEVENT - set(event_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") - find_package(event) -endif() - -# FIND GLIB -if(ENABLE_SKYPE OR ENABLE_PURPLE) -# if (GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES) -# set(GLIB2_FOUND TRUE) -# else() - set(glib_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") - find_package(glib) -# endif() -endif() - -# FIND LIBXML2 -# set(libxml2_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -# find_package(libxml2) - -# FIND PROTOBUF -set(Protobuf_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -find_package(Protobuf REQUIRED) - -if (NOT PROTOBUF_FOUND AND PROTOBUF_INCLUDE_DIR AND PROTOBUF_LIBRARY) - set(PROTOBUF_FOUND 1) - set(PROTOBUF_INCLUDE_DIRS ${PROTOBUF_INCLUDE_DIR}) - if (PROTOBUF_PROTOC_EXECUTABLE) - else() - set(PROTOBUF_PROTOC_EXECUTABLE protoc) - endif() - message(STATUS "Using protobuf: ${PROTOBUF_INCLUDE_DIRS} ${PROTOBUF_LIBRARY}") -endif() - -if (WIN32) - add_definitions(-DSWIFTEN_STATIC=1) - ADD_DEFINITIONS(-D_UNICODE) - ADD_DEFINITIONS(-DUNICODE) -endif() - - -if (CMAKE_COMPILER_IS_GNUCXX) -set(openssl_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -find_package(openssl) -endif() - -if(ENABLE_IRC) - set(Communi_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") - find_package(Communi) - - INCLUDE(FindQt4) - FIND_PACKAGE(Qt4 COMPONENTS QtCore QtNetwork) - # ADD_DEFINITIONS(${SWIFTEN_CFLAGS}) - ADD_DEFINITIONS(-DSUPPORT_LEGACY_CAPS) - # ADD_DEFINITIONS(-DBOOST_FILESYSTEM_VERSION=2) -endif() - -set(event_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -find_package(event) - -if (NOT WIN32 AND ENABLE_SKYPE) -set(dbus_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -find_package(dbus) -endif() - -# if(ENABLE_YAHOO2) -# set(yahoo2_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -# find_package(yahoo2) -# endif() - -####### Miscallanous ###### - -if(ENABLE_DOCS) - find_package(Doxygen) -endif() - -# if(ENABLE_LOG) - if(LOG4CXX_INCLUDE_DIR AND LOG4CXX_LIBRARY) - set(LOG4CXX_LIBRARIES ${LOG4CXX_LIBRARY}) - set(LOG4CXX_FOUND 1) - message(STATUS "Using log4cxx: ${CPPUNIT_INCLUDE_DIR} ${LOG4CXX_INCLUDE_DIR}") - else() - set(log4cxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") - find_package(log4cxx) - endif() -# endif() - -# FIND CPPUNIT -if(ENABLE_TESTS) - set(cppunit_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") - find_package(cppunit) - - if(NOT CPPUNIT_FOUND AND CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARY) - set(CCPUNIT_LIBRARIES ${CPPUNIT_LIBRARY}) - set(CPPUNIT_FOUND 1) - message(STATUS "Using cppunit: ${CPPUNIT_INCLUDE_DIR} ${CPPUNIT_LIBRARIES}") - endif() -endif() - -if (APPLE) - FIND_LIBRARY(IOKIT_FRAMEWORK IOKit) - FIND_LIBRARY(SECURITY_FRAMEWORK Security) - FIND_LIBRARY(APPKIT_FRAMEWORK AppKit) - FIND_LIBRARY(SYSTEMCONFIGURATION_FRAMEWORK SystemConfiguration) - FIND_LIBRARY(SECURITYINTERFACE_FRAMEWORK SecurityInterface) - MARK_AS_ADVANCED(IOKIT_FRAMEWORK APPKIT_FRAMEWORK SYSTEMCONFIGURATION_FRAMEWORK SECURITY_FRAMEWORK SECURITYINTERFACE_FRAMEWORK) - SET (APPLE_FRAMEWORKS ${IOKIT_FRAMEWORK} ${APPKIT_FRAMEWORK} ${SYSTEMCONFIGURATION_FRAMEWORK} ${SECURITY_FRAMEWORK} ${SECURITYINTERFACE_FRAMEWORK}) -endif() - -message(" Supported features") -message("-----------------------") - -if (SPECTRUM_VERSION) - ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}") -else (SPECTRUM_VERSION) - if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) - if (NOT GIT_EXECUTABLE) - set (GIT_EXECUTABLE git) - endif() - execute_process(COMMAND ${GIT_EXECUTABLE} "--git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git" rev-parse --short HEAD - OUTPUT_VARIABLE GIT_REVISION - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - set(SPECTRUM_VERSION 2.0.0-beta-git-${GIT_REVISION}) - ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}") - else (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) - set(SPECTRUM_VERSION 2.0.0-alpha) - ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}") - endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) -endif (SPECTRUM_VERSION) - -message("Version : " ${SPECTRUM_VERSION}) - -if (SQLITE3_FOUND) - ADD_DEFINITIONS(-DWITH_SQLITE) - include_directories(${SQLITE3_INCLUDE_DIR}) - message("SQLite3 : yes") -else (SQLITE3_FOUND) - if (WIN32) - ADD_DEFINITIONS(-DWITH_SQLITE) - include_directories("${CMAKE_SOURCE_DIR}/msvc-deps/sqlite3") - message("SQLite3 : bundled") - else() - set(SQLITE3_LIBRARIES "") - if(ENABLE_SQLITE3) - message("SQLite3 : no (install sqlite3)") - else(ENABLE_SQLITE3) - message("SQLite3 : no (user disabled)") - endif() - endif() -endif (SQLITE3_FOUND) - -if (MYSQL_FOUND) - ADD_DEFINITIONS(-DWITH_MYSQL) - include_directories(${MYSQL_INCLUDE_DIR}) - message("MySQL : yes") -else (MYSQL_FOUND) - set(MYSQL_LIBRARIES "") - if(ENABLE_MYSQL) - message("MySQL : no (install mysql-devel)") - else(ENABLE_MYSQL) - message("MySQL : no (user disabled)") - endif() -endif (MYSQL_FOUND) - -if (PQXX_FOUND) - ADD_DEFINITIONS(-DWITH_PQXX) - include_directories(${PQXX_INCLUDE_DIR}) - message("PostgreSQL : yes") -else (PQXX_FOUND) - set(PQXX_LIBRARY "") - set(PQ_LIBRARY "") - if(ENABLE_PQXX) - message("PostgreSQL : no (install libpqxx-devel)") - else(ENABLE_PQXX) - message("PostgreSQL : no (user disabled)") - endif() -endif (PQXX_FOUND) - -if (PROTOBUF_FOUND) - ADD_DEFINITIONS(-DWITH_PROTOBUF) - include_directories(${PROTOBUF_INCLUDE_DIRS}) - message("Network plugins : yes") - - if(PURPLE_FOUND) - message("Libpurple plugin : yes") - include_directories(${PURPLE_INCLUDE_DIR}) - include_directories(${GLIB2_INCLUDE_DIR}) - else() - if(ENABLE_PURPLE) - message("Libpurple plugin : no (install libpurple)") - else(ENABLE_PURPLE) - message("Libpurple plugin : no (user disabled)") - endif() - endif() - - if (HAVE_EVENT) - ADD_DEFINITIONS(-DWITH_LIBEVENT) - include_directories(${EVENT_INCLUDE_DIRS}) - message(" libev eventloop : yes") - else() - if(ENABLE_PURPLE) - message(" libev eventloop : no (install libev-devel)") - endif() - endif() - - if(IRC_FOUND) - ADD_DEFINITIONS(-DCOMMUNI_SHARED) - message("IRC plugin : yes") - include_directories(${QT_QTNETWORK_INCLUDE_DIR}) - include_directories(${IRC_INCLUDE_DIR}) - include(${QT_USE_FILE}) - else() - if(ENABLE_IRC) - message("IRC plugin : no (install libCommuni and libprotobuf-dev)") - else(ENABLE_IRC) - message("IRC plugin : no (user disabled)") - endif() - endif() - if(ENABLE_TWITTER) - message("Twitter plugin : yes") - else(ENABLE_TWITTER) - message("Twitter plugin : no (user disabled)") - endif() - if (NOT WIN32) - if(ENABLE_FROTZ) - message("Frotz plugin : yes") - else() - message("Frotz plugin : no (user disabled)") - endif() - if(ENABLE_SMSTOOLS3) - message("SMSTools3 plugin : yes") - else() - message("SMSTools3 plugin : no (user disabled)") - endif() - if(${LIBDBUSGLIB_FOUND}) - message("Skype plugin : yes") - include_directories(${LIBDBUSGLIB_INCLUDE_DIRS}) - else() - if(ENABLE_SKYPE) - message("Skype plugin : no (install dbus-glib-devel)") - else(ENABLE_SKYPE) - message("Skype plugin : no (user disabled)") - endif() - endif() - else() - message("Frotz plugin : no (does not run on Win32)") - message("SMSTools3 plugin : no (does not run on Win32)") - message("Skype plugin : no (does not run on Win32)") - endif() - -# if(YAHOO2_FOUND) -# message("Libyahoo2 plugin : yes") -# include_directories(${YAHOO2_INCLUDE_DIR}) -# else() - if(ENABLE_YAHOO2) - set(YAHOO2_FOUND 1) - message("Libyahoo2 plugin : yes") - else(ENABLE_YAHOO2) - message("Libyahoo2 plugin : no (user disabled)") - endif() -# endif() - - if(ENABLE_SWIFTEN) - message("Swiften plugin : yes") - else() - message("Swiften plugin : no (user disabled)") - endif() -else() - message("Network plugins : no (install libprotobuf-dev)") - message("Libpurple plugin : no (install libpurple and libprotobuf-dev)") - message("IRC plugin : no (install libircclient-qt and libprotobuf-dev)") - message("Frotz plugin : no (install libprotobuf-dev)") - message("SMSTools3 plugin : no (install libprotobuf-dev)") - message("Swiften plugin : no (install libprotobuf-dev)") - message("Twitter plugin : no (install libprotobuf-dev)") -endif() - -if (LOG4CXX_FOUND) - message("Log4cxx : yes") - include_directories(${LOG4CXX_INCLUDE_DIR}) - ADD_DEFINITIONS(-DWITH_LOG4CXX) -else() - set(LOG4CXX_LIBRARIES "") - if (WIN32) - message("Log4cxx : no (install log4cxx-devel)") - else() - message(FATAL_ERROR "Log4cxx : no (install log4cxx-devel)") - endif() -endif() - -if (WIN32) - ADD_DEFINITIONS(-DLOG4CXX_STATIC) - ADD_DEFINITIONS(-D_WIN32_WINNT=0x501) - ADD_DEFINITIONS(-DWIN32_LEAN_AND_MEAN) - ADD_DEFINITIONS(-DBOOST_USE_WINDOWS_H) - ADD_DEFINITIONS(-DBOOST_THREAD_USE_LIB) -endif() - -if(CMAKE_BUILD_TYPE MATCHES Debug) - if (CMAKE_COMPILER_IS_GNUCXX) - ADD_DEFINITIONS(-O0) - ADD_DEFINITIONS(-ggdb) - endif() - ADD_DEFINITIONS(-DDEBUG) - message("Debug : yes") -else(CMAKE_BUILD_TYPE MATCHES Debug) - message("Debug : no (run \"cmake . -DCMAKE_BUILD_TYPE=Debug\")") -endif(CMAKE_BUILD_TYPE MATCHES Debug) - - -SET(TRANSPORT_VERSION 2.0) -SET(PROJECT_VERSION 2.0) -include_directories(include) - - -include_directories(${EVENT_INCLUDE_DIRS}) -include_directories(${SWIFTEN_INCLUDE_DIR}) -include_directories(${Boost_INCLUDE_DIRS}) - -if (CMAKE_COMPILER_IS_GNUCXX) -include_directories(${OPENSSL_INCLUDE_DIR}) -endif() - -ADD_SUBDIRECTORY(src) -ADD_SUBDIRECTORY(plugin) -ADD_SUBDIRECTORY(include) -ADD_SUBDIRECTORY(spectrum) -ADD_SUBDIRECTORY(backends) -if (NOT WIN32) - ADD_SUBDIRECTORY(spectrum_manager) -# ADD_SUBDIRECTORY(spectrum2_send_message) -endif() - -if (CPPUNIT_FOUND) - message("tests : yes") - include_directories(${CPPUNIT_INCLUDE_DIR}) -else() - if(ENABLE_TESTS) - message("tests : no (install CPPUnit)") - else(ENABLE_TESTS) - message("tests : no (user disabled)") - endif() -endif() - -if(DOXYGEN_FOUND) - message("Docs : yes") - ADD_SUBDIRECTORY(docs) -else(DOXYGEN_FOUND) - if(ENABLE_DOCS) - message("Docs : no (install doxygen)") - else(ENABLE_DOCS) - message("Docs : no (user disabled)") - endif() -endif(DOXYGEN_FOUND) - -message("----------------------") - -if(NOT SQLITE3_FOUND AND NOT MYSQL_FOUND AND NOT PQXX_FOUND) - if(ENABLE_SQLITE3 OR ENABLE_MYSQL OR ENABLE_PQXX) - message("Could not find any database - Please install at least one of sqlite3-devel, mysql-devel or pqxx-devel if you want to use transport mode.") - else(ENABLE_SQLITE3 OR ENABLE_MYSQL OR ENABLE_PQXX) - message("Please enable at least one of SQLITE3, MYSQL, PQXX databases to use transport mode.") - endif() -endif() - +cmake_minimum_required(VERSION 2.6) +project(libtransport) + +message(STATUS "Variables to override default places where to find libraries:") +message(STATUS "|- cppunit : -DCPPUNIT_INCLUDE_DIR, -DCPPUNIT_LIBRARY") +message(STATUS "|- swiften : -DSWIFTEN_INCLUDE_DIR, -DSWIFTEN_LIBRARY") +message(STATUS " |- zlib : -DZLIB_LIBRARY") +message(STATUS " |- expat : -DEXPAT_LIBRARY") +message(STATUS " |-libidn : -DLIBIDN_LIBRARY") +message(STATUS " |-libxml : -DLIBXML_LIBRARY") +message(STATUS "|- boost : -DBOOST_INCLUDEDIR, -DBOOST_LIBRARYDIR") +message(STATUS "|- protobuf: -DPROTOBUF_INCLUDE_DIR, -DPROTOBUF_LIBRARY") +message(STATUS " : -DPROTOBUF_PROTOC_EXECUTABLE") +message(STATUS "|- log4cxx : -DLOG4CXX_INCLUDE_DIR, -DLOG4CXX_LIBRARY") +message(STATUS "|- purple : -DPURPLE_INCLUDE_DIR, -DPURPLE_LIBRARY") +message(STATUS " : -DPURPLE_NOT_RUNTIME - enables compilation with libpurple.lib") + +option(ENABLE_SQLITE3 "Build with SQLite3 support" ON) +option(ENABLE_MYSQL "Build with MySQL support" ON) +option(ENABLE_PQXX "Build with Postgres supoort" ON) + +option(ENABLE_FROTZ "Build Frotz plugin" ON) +option(ENABLE_IRC "Build IRC plugin" ON) +option(ENABLE_PURPLE "Build Libpurple plugin" ON) +option(ENABLE_SMSTOOLS3 "Build SMSTools3 plugin" ON) +option(ENABLE_SKYPE "Build Skype plugin" ON) +option(ENABLE_SWIFTEN "Build Swiften plugin" ON) +option(ENABLE_TWITTER "Build Twitter plugin" ON) +option(ENABLE_YAHOO2 "Build Libyahoo2 plugin" ON) + +option(ENABLE_DOCS "Build Docs" ON) +# option(ENABLE_LOG "Build with logging using Log4cxx" ON) +option(ENABLE_TESTS "Build Tests using CppUnit" OFF) + +MACRO(LIST_CONTAINS var value) + SET(${var}) + FOREACH (value2 ${ARGN}) + IF (${value} STREQUAL ${value2}) + SET(${var} TRUE) + ENDIF (${value} STREQUAL ${value2}) + ENDFOREACH (value2) +ENDMACRO(LIST_CONTAINS) + +if(NOT LIB_INSTALL_DIR) + set(LIB_INSTALL_DIR "lib") +endif() + +set(CMAKE_MODULE_PATH "cmake_modules") + +###### Prerequisites ###### + + +# FIND SWIFTEN + +set(Swiften_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") +find_package(Swiften) + +if(NOT SWIFTEN_FOUND) + if (ZLIB_LIBRARY) + set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${ZLIB_LIBRARY}) + endif() + if (EXPAT_LIBRARY) + set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${EXPAT_LIBRARY}) + endif() + if (LIBIDN_LIBRARY) + set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${LIBIDN_LIBRARY}) + endif() + if (LIBXML_LIBRARY) + set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${LIBXML_LIBRARY}) + endif() + set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Dnsapi") + set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Crypt32") + set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Secur32") + set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Iphlpapi") + set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Winscard") + message(STATUS "Using swiften: ${SWIFTEN_INCLUDE_DIR} ${SWIFTEN_LIBRARY}") +endif() + +# FIND BOOST +if (WIN32) + set(Boost_USE_STATIC_LIBS ON) + set(Boost_USE_MULTITHREADED ON) + set(Boost_USE_STATIC_RUNTIME OFF) + find_package(Boost COMPONENTS program_options date_time system filesystem regex thread signals REQUIRED) +else(WIN32) + LIST_CONTAINS(contains -lboost_program_options ${SWIFTEN_LIBRARY}) + if(contains) + message(STATUS "Using non-multithreaded boost") + set(Boost_USE_MULTITHREADED 0) + endif(contains) + set(Boost_FIND_QUIETLY ON) + find_package(Boost COMPONENTS program_options date_time system filesystem regex thread-mt signals) + if (NOT Boost_FOUND) + set(Boost_FIND_QUIETLY OFF) + find_package(Boost COMPONENTS program_options date_time system filesystem regex thread signals REQUIRED) + endif() +endif(WIN32) + +message( STATUS "Found Boost: ${Boost_VERSION}, ${Boost_LIBRARIES}, ${Boost_INCLUDE_DIR}") + +if (${Boost_VERSION} GREATER 104999) + message( STATUS "Using BOOST_FILESYSTEM_VERSION=3") + add_definitions(-DBOOST_FILESYSTEM_VERSION=3) +endif() + +# FIND POPT +if (NOT WIN32) + set(popt_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") + find_package(popt REQUIRED) +endif() + +###### Database ###### + +# FIND SQLITE3 +if (ENABLE_SQLITE3) + if (MSVC) + set(SQLITE3_FOUND 1) + ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/msvc-deps) + else() + if (WIN32) + ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/msvc-deps/sqlite3) + else() + set(sqlite3_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") + find_package(sqlite3) + endif() + endif() +endif() + +# FIND MYSQL +if(ENABLE_MYSQL) + set(mysql_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") + find_package(mysql) +endif() + +# FIND PQXX +if(ENABLE_PQXX) + set(pqxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") + find_package(pqxx) +endif() + +###### Plugins ###### + +# FIND LIBPURPLE +if(ENABLE_PURPLE) + set(purple_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") + find_package(purple) + + if (WIN32) + if (PURPLE_NOT_RUNTIME) + add_definitions(-DPURPLE_RUNTIME=0) + else(PURPLE_NOT_RUNTIME) + add_definitions(-DPURPLE_RUNTIME=1) + endif(PURPLE_NOT_RUNTIME) + else() + add_definitions(-DPURPLE_RUNTIME=0) + endif() + + # FIND LIBEVENT + set(event_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") + find_package(event) +endif() + +# FIND GLIB +if(ENABLE_SKYPE OR ENABLE_PURPLE) +# if (GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES) +# set(GLIB2_FOUND TRUE) +# else() + set(glib_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") + find_package(glib) +# endif() +endif() + +# FIND LIBXML2 +# set(libxml2_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") +# find_package(libxml2) + +# FIND PROTOBUF +set(Protobuf_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") +find_package(Protobuf REQUIRED) + +if (NOT PROTOBUF_FOUND AND PROTOBUF_INCLUDE_DIR AND PROTOBUF_LIBRARY) + set(PROTOBUF_FOUND 1) + set(PROTOBUF_INCLUDE_DIRS ${PROTOBUF_INCLUDE_DIR}) + if (PROTOBUF_PROTOC_EXECUTABLE) + else() + set(PROTOBUF_PROTOC_EXECUTABLE protoc) + endif() + message(STATUS "Using protobuf: ${PROTOBUF_INCLUDE_DIRS} ${PROTOBUF_LIBRARY}") +endif() + +if (WIN32) + add_definitions(-DSWIFTEN_STATIC=1) + ADD_DEFINITIONS(-D_UNICODE) + ADD_DEFINITIONS(-DUNICODE) +endif() + + +if (CMAKE_COMPILER_IS_GNUCXX) +set(openssl_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") +find_package(openssl) +endif() + +if(ENABLE_IRC) + set(Communi_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") + find_package(Communi) + + INCLUDE(FindQt4) + FIND_PACKAGE(Qt4 COMPONENTS QtCore QtNetwork) + # ADD_DEFINITIONS(${SWIFTEN_CFLAGS}) + ADD_DEFINITIONS(-DSUPPORT_LEGACY_CAPS) + # ADD_DEFINITIONS(-DBOOST_FILESYSTEM_VERSION=2) +endif() + +set(event_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") +find_package(event) + +if (NOT WIN32 AND ENABLE_SKYPE) +set(dbus_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") +find_package(dbus) +endif() + +# if(ENABLE_YAHOO2) +# set(yahoo2_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") +# find_package(yahoo2) +# endif() + +####### Miscallanous ###### + +if(ENABLE_DOCS) + find_package(Doxygen) +endif() + +# if(ENABLE_LOG) + if(LOG4CXX_INCLUDE_DIR AND LOG4CXX_LIBRARY) + set(LOG4CXX_LIBRARIES ${LOG4CXX_LIBRARY}) + set(LOG4CXX_FOUND 1) + message(STATUS "Using log4cxx: ${CPPUNIT_INCLUDE_DIR} ${LOG4CXX_INCLUDE_DIR}") + else() + set(log4cxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") + find_package(log4cxx) + endif() +# endif() + +# FIND CPPUNIT +if(ENABLE_TESTS) + set(cppunit_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") + find_package(cppunit) + + if(NOT CPPUNIT_FOUND AND CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARY) + set(CCPUNIT_LIBRARIES ${CPPUNIT_LIBRARY}) + set(CPPUNIT_FOUND 1) + message(STATUS "Using cppunit: ${CPPUNIT_INCLUDE_DIR} ${CPPUNIT_LIBRARIES}") + endif() +endif() + +if (APPLE) + FIND_LIBRARY(IOKIT_FRAMEWORK IOKit) + FIND_LIBRARY(SECURITY_FRAMEWORK Security) + FIND_LIBRARY(APPKIT_FRAMEWORK AppKit) + FIND_LIBRARY(SYSTEMCONFIGURATION_FRAMEWORK SystemConfiguration) + FIND_LIBRARY(SECURITYINTERFACE_FRAMEWORK SecurityInterface) + MARK_AS_ADVANCED(IOKIT_FRAMEWORK APPKIT_FRAMEWORK SYSTEMCONFIGURATION_FRAMEWORK SECURITY_FRAMEWORK SECURITYINTERFACE_FRAMEWORK) + SET (APPLE_FRAMEWORKS ${IOKIT_FRAMEWORK} ${APPKIT_FRAMEWORK} ${SYSTEMCONFIGURATION_FRAMEWORK} ${SECURITY_FRAMEWORK} ${SECURITYINTERFACE_FRAMEWORK}) +endif() + +message(" Supported features") +message("-----------------------") + +if (SPECTRUM_VERSION) + ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}") +else (SPECTRUM_VERSION) + if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) + if (NOT GIT_EXECUTABLE) + set (GIT_EXECUTABLE git) + endif() + execute_process(COMMAND ${GIT_EXECUTABLE} "--git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git" rev-parse --short HEAD + OUTPUT_VARIABLE GIT_REVISION + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + set(SPECTRUM_VERSION 2.0.0-beta-git-${GIT_REVISION}) + ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}") + else (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) + set(SPECTRUM_VERSION 2.0.0-alpha) + ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}") + endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) +endif (SPECTRUM_VERSION) + +message("Version : " ${SPECTRUM_VERSION}) + +if (SQLITE3_FOUND) + ADD_DEFINITIONS(-DWITH_SQLITE) + include_directories(${SQLITE3_INCLUDE_DIR}) + message("SQLite3 : yes") +else (SQLITE3_FOUND) + if (WIN32) + ADD_DEFINITIONS(-DWITH_SQLITE) + include_directories("${CMAKE_SOURCE_DIR}/msvc-deps/sqlite3") + message("SQLite3 : bundled") + else() + set(SQLITE3_LIBRARIES "") + if(ENABLE_SQLITE3) + message("SQLite3 : no (install sqlite3)") + else(ENABLE_SQLITE3) + message("SQLite3 : no (user disabled)") + endif() + endif() +endif (SQLITE3_FOUND) + +if (MYSQL_FOUND) + ADD_DEFINITIONS(-DWITH_MYSQL) + include_directories(${MYSQL_INCLUDE_DIR}) + message("MySQL : yes") +else (MYSQL_FOUND) + set(MYSQL_LIBRARIES "") + if(ENABLE_MYSQL) + message("MySQL : no (install mysql-devel)") + else(ENABLE_MYSQL) + message("MySQL : no (user disabled)") + endif() +endif (MYSQL_FOUND) + +if (PQXX_FOUND) + ADD_DEFINITIONS(-DWITH_PQXX) + include_directories(${PQXX_INCLUDE_DIR}) + message("PostgreSQL : yes") +else (PQXX_FOUND) + set(PQXX_LIBRARY "") + set(PQ_LIBRARY "") + if(ENABLE_PQXX) + message("PostgreSQL : no (install libpqxx-devel)") + else(ENABLE_PQXX) + message("PostgreSQL : no (user disabled)") + endif() +endif (PQXX_FOUND) + +if (PROTOBUF_FOUND) + ADD_DEFINITIONS(-DWITH_PROTOBUF) + include_directories(${PROTOBUF_INCLUDE_DIRS}) + message("Network plugins : yes") + + if(PURPLE_FOUND) + message("Libpurple plugin : yes") + include_directories(${PURPLE_INCLUDE_DIR}) + include_directories(${GLIB2_INCLUDE_DIR}) + else() + if(ENABLE_PURPLE) + message("Libpurple plugin : no (install libpurple)") + else(ENABLE_PURPLE) + message("Libpurple plugin : no (user disabled)") + endif() + endif() + + if (HAVE_EVENT) + ADD_DEFINITIONS(-DWITH_LIBEVENT) + include_directories(${EVENT_INCLUDE_DIRS}) + message(" libev eventloop : yes") + else() + if(ENABLE_PURPLE) + message(" libev eventloop : no (install libev-devel)") + endif() + endif() + + if(IRC_FOUND) + ADD_DEFINITIONS(-DCOMMUNI_SHARED) + message("IRC plugin : yes") + include_directories(${QT_QTNETWORK_INCLUDE_DIR}) + include_directories(${IRC_INCLUDE_DIR}) + include(${QT_USE_FILE}) + else() + if(ENABLE_IRC) + message("IRC plugin : no (install libCommuni and libprotobuf-dev)") + else(ENABLE_IRC) + message("IRC plugin : no (user disabled)") + endif() + endif() + if(ENABLE_TWITTER) + message("Twitter plugin : yes") + else(ENABLE_TWITTER) + message("Twitter plugin : no (user disabled)") + endif() + if (NOT WIN32) + if(ENABLE_FROTZ) + message("Frotz plugin : yes") + else() + message("Frotz plugin : no (user disabled)") + endif() + if(ENABLE_SMSTOOLS3) + message("SMSTools3 plugin : yes") + else() + message("SMSTools3 plugin : no (user disabled)") + endif() + if(${LIBDBUSGLIB_FOUND}) + message("Skype plugin : yes") + include_directories(${LIBDBUSGLIB_INCLUDE_DIRS}) + else() + if(ENABLE_SKYPE) + message("Skype plugin : no (install dbus-glib-devel)") + else(ENABLE_SKYPE) + message("Skype plugin : no (user disabled)") + endif() + endif() + else() + message("Frotz plugin : no (does not run on Win32)") + message("SMSTools3 plugin : no (does not run on Win32)") + message("Skype plugin : no (does not run on Win32)") + endif() + +# if(YAHOO2_FOUND) +# message("Libyahoo2 plugin : yes") +# include_directories(${YAHOO2_INCLUDE_DIR}) +# else() + if(ENABLE_YAHOO2) + set(YAHOO2_FOUND 1) + message("Libyahoo2 plugin : yes") + else(ENABLE_YAHOO2) + message("Libyahoo2 plugin : no (user disabled)") + endif() +# endif() + + if(ENABLE_SWIFTEN) + message("Swiften plugin : yes") + else() + message("Swiften plugin : no (user disabled)") + endif() +else() + message("Network plugins : no (install libprotobuf-dev)") + message("Libpurple plugin : no (install libpurple and libprotobuf-dev)") + message("IRC plugin : no (install libircclient-qt and libprotobuf-dev)") + message("Frotz plugin : no (install libprotobuf-dev)") + message("SMSTools3 plugin : no (install libprotobuf-dev)") + message("Swiften plugin : no (install libprotobuf-dev)") + message("Twitter plugin : no (install libprotobuf-dev)") +endif() + +if (LOG4CXX_FOUND) + message("Log4cxx : yes") + include_directories(${LOG4CXX_INCLUDE_DIR}) + ADD_DEFINITIONS(-DWITH_LOG4CXX) +else() + set(LOG4CXX_LIBRARIES "") + if (WIN32) + message("Log4cxx : no (install log4cxx-devel)") + else() + message(FATAL_ERROR "Log4cxx : no (install log4cxx-devel)") + endif() +endif() + +if (WIN32) + ADD_DEFINITIONS(-DLOG4CXX_STATIC) + ADD_DEFINITIONS(-D_WIN32_WINNT=0x501) + ADD_DEFINITIONS(-DWIN32_LEAN_AND_MEAN) + ADD_DEFINITIONS(-DBOOST_USE_WINDOWS_H) + ADD_DEFINITIONS(-DBOOST_THREAD_USE_LIB) +endif() + +if(CMAKE_BUILD_TYPE MATCHES Debug) + if (CMAKE_COMPILER_IS_GNUCXX) + ADD_DEFINITIONS(-O0) + ADD_DEFINITIONS(-ggdb) + endif() + ADD_DEFINITIONS(-DDEBUG) + message("Debug : yes") +else(CMAKE_BUILD_TYPE MATCHES Debug) + message("Debug : no (run \"cmake . -DCMAKE_BUILD_TYPE=Debug\")") +endif(CMAKE_BUILD_TYPE MATCHES Debug) + + +SET(TRANSPORT_VERSION 2.0) +SET(PROJECT_VERSION 2.0) +include_directories(include) + + +include_directories(${EVENT_INCLUDE_DIRS}) +include_directories(${SWIFTEN_INCLUDE_DIR}) +include_directories(${Boost_INCLUDE_DIRS}) + +if (CMAKE_COMPILER_IS_GNUCXX) +include_directories(${OPENSSL_INCLUDE_DIR}) +endif() + +ADD_SUBDIRECTORY(src) +ADD_SUBDIRECTORY(plugin) +ADD_SUBDIRECTORY(include) +ADD_SUBDIRECTORY(spectrum) +ADD_SUBDIRECTORY(backends) +if (NOT WIN32) + ADD_SUBDIRECTORY(spectrum_manager) +# ADD_SUBDIRECTORY(spectrum2_send_message) +endif() + +if (CPPUNIT_FOUND) + message("tests : yes") + include_directories(${CPPUNIT_INCLUDE_DIR}) +else() + if(ENABLE_TESTS) + message("tests : no (install CPPUnit)") + else(ENABLE_TESTS) + message("tests : no (user disabled)") + endif() +endif() + +if(DOXYGEN_FOUND) + message("Docs : yes") + ADD_SUBDIRECTORY(docs) +else(DOXYGEN_FOUND) + if(ENABLE_DOCS) + message("Docs : no (install doxygen)") + else(ENABLE_DOCS) + message("Docs : no (user disabled)") + endif() +endif(DOXYGEN_FOUND) + +message("----------------------") + +if(NOT SQLITE3_FOUND AND NOT MYSQL_FOUND AND NOT PQXX_FOUND) + if(ENABLE_SQLITE3 OR ENABLE_MYSQL OR ENABLE_PQXX) + message("Could not find any database - Please install at least one of sqlite3-devel, mysql-devel or pqxx-devel if you want to use transport mode.") + else(ENABLE_SQLITE3 OR ENABLE_MYSQL OR ENABLE_PQXX) + message("Please enable at least one of SQLITE3, MYSQL, PQXX databases to use transport mode.") + endif() +endif() + diff --git a/backends/swiften/main.cpp b/backends/swiften/main.cpp index bb39465a..ae1094b7 100644 --- a/backends/swiften/main.cpp +++ b/backends/swiften/main.cpp @@ -1,447 +1,455 @@ -// Transport includes -#include "transport/config.h" -#include "transport/networkplugin.h" -#include "transport/logging.h" - -#include "boost/date_time/posix_time/posix_time.hpp" - -// Swiften -#include "Swiften/Swiften.h" - -#ifndef WIN32 -// for signal handler -#include "unistd.h" -#include "signal.h" -#include "sys/wait.h" -#include "sys/signal.h" -#endif - -#ifndef __FreeBSD__ -#ifndef __MACH__ -// malloc_trim -#include "malloc.h" -#endif -#endif - -// Boost -#include -using namespace boost::filesystem; -using namespace boost::program_options; -using namespace Transport; - -DEFINE_LOGGER(logger, "Swiften"); -DEFINE_LOGGER(logger_xml, "backend.xml"); - -// eventloop -Swift::SimpleEventLoop *loop_; - -// Plugins -class SwiftenPlugin; -NetworkPlugin *np = NULL; -Swift::XMPPSerializer *serializer; - -class ForwardIQHandler : public Swift::IQHandler { - public: - std::map m_id2resource; - - ForwardIQHandler(NetworkPlugin *np, const std::string &user) { - m_np = np; - m_user = user; - } - - bool handleIQ(boost::shared_ptr iq) { - if (iq->getPayload() != NULL) { - return false; - } - if (iq->getType() == Swift::IQ::Get) { - m_id2resource[iq->getID()] = iq->getFrom().getResource(); - } - - iq->setTo(m_user); - std::string xml = safeByteArrayToString(serializer->serializeElement(iq)); - m_np->sendRawXML(xml); - return true; - } - - private: - NetworkPlugin *m_np; - std::string m_user; - -}; - -class SwiftenPlugin : public NetworkPlugin, Swift::XMPPParserClient { - public: - Swift::BoostNetworkFactories *m_factories; - Swift::BoostIOServiceThread m_boostIOServiceThread; - boost::shared_ptr m_conn; - bool m_firstPing; - - Swift::FullPayloadSerializerCollection collection; - Swift::XMPPParser *m_xmppParser; - Swift::FullPayloadParserFactoryCollection m_collection2; - - SwiftenPlugin(Config *config, Swift::SimpleEventLoop *loop, const std::string &host, int port) : NetworkPlugin() { - this->config = config; - m_firstPing = true; - m_factories = new Swift::BoostNetworkFactories(loop); - m_conn = m_factories->getConnectionFactory()->createConnection(); - m_conn->onDataRead.connect(boost::bind(&SwiftenPlugin::_handleDataRead, this, _1)); - m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(host), port)); - - serializer = new Swift::XMPPSerializer(&collection, Swift::ClientStreamType); - m_xmppParser = new Swift::XMPPParser(this, &m_collection2, m_factories->getXMLParserFactory()); - m_xmppParser->parse(""); - - LOG4CXX_INFO(logger, "Starting the plugin."); - } - - // NetworkPlugin uses this method to send the data to networkplugin server - void sendData(const std::string &string) { - m_conn->write(Swift::createSafeByteArray(string)); - } - - // This method has to call handleDataRead with all received data from network plugin server - void _handleDataRead(boost::shared_ptr data) { - if (m_firstPing) { - m_firstPing = false; - NetworkPlugin::PluginConfig cfg; - cfg.setRawXML(true); - sendConfig(cfg); - } - std::string d(data->begin(), data->end()); - handleDataRead(d); - } - - void handleStreamStart(const Swift::ProtocolHeader&) {} - - void handleElement(boost::shared_ptr element) { - boost::shared_ptr stanza = boost::dynamic_pointer_cast(element); - if (!stanza) { - return; - } - - std::string user = stanza->getFrom().toBare(); - - boost::shared_ptr client = m_users[user]; - if (!client) - return; - - stanza->setFrom(client->getJID()); - - boost::shared_ptr message = boost::dynamic_pointer_cast(stanza); - if (message) { - client->sendMessage(message); - return; - } - - boost::shared_ptr presence = boost::dynamic_pointer_cast(stanza); - if (presence) { - client->sendPresence(presence); - return; - } - - boost::shared_ptr iq = boost::dynamic_pointer_cast(stanza); - if (iq) { - if (m_handlers[user]->m_id2resource.find(stanza->getID()) != m_handlers[user]->m_id2resource.end()) { - std::string resource = m_handlers[user]->m_id2resource[stanza->getID()]; - if (resource.empty()) { - iq->setTo(Swift::JID(iq->getTo().getNode(), iq->getTo().getDomain())); - } else { - iq->setTo(Swift::JID(iq->getTo().getNode(), iq->getTo().getDomain(), resource)); - } - - m_handlers[user]->m_id2resource.erase(stanza->getID()); - } - client->getIQRouter()->sendIQ(iq); - return; - } - } - - void handleStreamEnd() {} - - void handleRawXML(const std::string &xml) { - m_xmppParser->parse(xml); - } - - void handleSwiftDisconnected(const std::string &user, const boost::optional &error) { - std::string message = ""; - bool reconnect = false; - if (error) { - switch(error->getType()) { - case Swift::ClientError::UnknownError: message = ("Unknown Error"); reconnect = true; break; - case Swift::ClientError::DomainNameResolveError: message = ("Unable to find server"); break; - case Swift::ClientError::ConnectionError: message = ("Error connecting to server"); break; - case Swift::ClientError::ConnectionReadError: message = ("Error while receiving server data"); reconnect = true; break; - case Swift::ClientError::ConnectionWriteError: message = ("Error while sending data to the server"); reconnect = true; break; - case Swift::ClientError::XMLError: message = ("Error parsing server data"); reconnect = true; break; - case Swift::ClientError::AuthenticationFailedError: message = ("Login/password invalid"); break; - case Swift::ClientError::CompressionFailedError: message = ("Error while compressing stream"); break; - case Swift::ClientError::ServerVerificationFailedError: message = ("Server verification failed"); break; - case Swift::ClientError::NoSupportedAuthMechanismsError: message = ("Authentication mechanisms not supported"); break; - case Swift::ClientError::UnexpectedElementError: message = ("Unexpected response"); break; - case Swift::ClientError::ResourceBindError: message = ("Error binding resource"); break; - case Swift::ClientError::SessionStartError: message = ("Error starting session"); break; - case Swift::ClientError::StreamError: message = ("Stream error"); break; - case Swift::ClientError::TLSError: message = ("Encryption error"); break; - case Swift::ClientError::ClientCertificateLoadError: message = ("Error loading certificate (Invalid password?)"); break; - case Swift::ClientError::ClientCertificateError: message = ("Certificate not authorized"); break; - - case Swift::ClientError::UnknownCertificateError: message = ("Unknown certificate"); break; - case Swift::ClientError::CertificateExpiredError: message = ("Certificate has expired"); break; - case Swift::ClientError::CertificateNotYetValidError: message = ("Certificate is not yet valid"); break; - case Swift::ClientError::CertificateSelfSignedError: message = ("Certificate is self-signed"); break; - case Swift::ClientError::CertificateRejectedError: message = ("Certificate has been rejected"); break; - case Swift::ClientError::CertificateUntrustedError: message = ("Certificate is not trusted"); break; - case Swift::ClientError::InvalidCertificatePurposeError: message = ("Certificate cannot be used for encrypting your connection"); break; - case Swift::ClientError::CertificatePathLengthExceededError: message = ("Certificate path length constraint exceeded"); break; - case Swift::ClientError::InvalidCertificateSignatureError: message = ("Invalid certificate signature"); break; - case Swift::ClientError::InvalidCAError: message = ("Invalid Certificate Authority"); break; - case Swift::ClientError::InvalidServerIdentityError: message = ("Certificate does not match the host identity"); break; - } - } - LOG4CXX_INFO(logger, user << ": Disconnected " << message); - handleDisconnected(user, reconnect ? 0 : 3, message); - - boost::shared_ptr client = m_users[user]; - if (client) { - client->onConnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user)); - client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1)); - client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1)); - m_users.erase(user); - m_handlers.erase(user); - } - -#ifndef WIN32 -#ifndef __FreeBSD__ -#ifndef __MACH__ - // force returning of memory chunks allocated by libxml2 to kernel - malloc_trim(0); -#endif -#endif -#endif - } - - void handleSwiftConnected(const std::string &user) { - LOG4CXX_INFO(logger, user << ": Connected to XMPP server."); - handleConnected(user); - m_users[user]->requestRoster(); - Swift::Presence::ref response = Swift::Presence::create(); - response->setFrom(m_users[user]->getJID()); - m_users[user]->sendPresence(response); - } - - void handleSwiftRosterReceived(const std::string &user) { - Swift::PresenceOracle *oracle = m_users[user]->getPresenceOracle(); - BOOST_FOREACH(const Swift::XMPPRosterItem &item, m_users[user]->getRoster()->getItems()) { - Swift::Presence::ref lastPresence = oracle->getLastPresence(item.getJID()); - pbnetwork::StatusType status = lastPresence ? ((pbnetwork::StatusType) lastPresence->getShow()) : pbnetwork::STATUS_NONE; - handleBuddyChanged(user, item.getJID().toBare().toString(), - item.getName(), item.getGroups(), status); - } - } - - void handleSwiftPresenceChanged(const std::string &user, Swift::Presence::ref presence) { -// boost::shared_ptr client = m_users[user]; -// if (client->getMUCRegistry()->isMUC(presence->getFrom().toBare())) { -// return; -// } -// -// if (presence->getPayload() != NULL || presence->getPayload() != NULL) { -// return; -// } -// -// LOG4CXX_INFO(logger, user << ": " << presence->getFrom().toBare().toString() << " presence changed"); -// -// std::string message = presence->getStatus(); -// std::string photo = ""; -// -// boost::shared_ptr update = presence->getPayload(); -// if (update) { -// photo = update->getPhotoHash(); -// } -// -// boost::optional item = m_users[user]->getRoster()->getItem(presence->getFrom()); -// if (item) { -// handleBuddyChanged(user, presence->getFrom().toBare().toString(), item->getName(), item->getGroups(), (pbnetwork::StatusType) presence->getShow(), message, photo); -// } -// else { -// std::vector groups; -// handleBuddyChanged(user, presence->getFrom().toBare().toString(), presence->getFrom().toBare(), groups, (pbnetwork::StatusType) presence->getShow(), message, photo); -// } - presence->setTo(user); - std::string xml = safeByteArrayToString(serializer->serializeElement(presence)); - sendRawXML(xml); - } - - void handleSwiftMessageReceived(const std::string &user, Swift::Message::ref message) { - message->setTo(user); - std::string xml = safeByteArrayToString(serializer->serializeElement(message)); - sendRawXML(xml); - } - - void handleSwiftenDataRead(const Swift::SafeByteArray &data) { - std::string d = safeByteArrayToString(data); - if (!boost::starts_with(d, " client = boost::make_shared(Swift::JID(legacyName + "/Spectrum"), password, m_factories); - m_users[user] = client; - client->setAlwaysTrustCertificates(); - client->onConnected.connect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user)); - client->onDisconnected.connect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1)); - client->onMessageReceived.connect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1)); - client->getRoster()->onInitialRosterPopulated.connect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user)); - client->getPresenceOracle()->onPresenceChange.connect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1)); - client->onDataRead.connect(boost::bind(&SwiftenPlugin::handleSwiftenDataRead, this, _1)); - client->onDataWritten.connect(boost::bind(&SwiftenPlugin::handleSwiftenDataWritten, this, _1)); - client->getSubscriptionManager()->onPresenceSubscriptionRequest.connect(boost::bind(&SwiftenPlugin::handleSubscriptionRequest, this, user, _1, _2, _3)); - client->getSubscriptionManager()->onPresenceSubscriptionRevoked.connect(boost::bind(&SwiftenPlugin::handleSubscriptionRevoked, this, user, _1, _2)); - Swift::ClientOptions opt; - opt.allowPLAINWithoutTLS = true; - client->connect(opt); - - boost::shared_ptr handler = boost::make_shared(this, user); - client->getIQRouter()->addHandler(handler); - m_handlers[user] = handler; - } - - void handleSubscriptionRequest(const std::string &user, const Swift::JID& jid, const std::string& message, Swift::Presence::ref presence) { - handleSwiftPresenceChanged(user, presence); - } - - void handleSubscriptionRevoked(const std::string &user, const Swift::JID& jid, const std::string& message) { - Swift::Presence::ref presence = Swift::Presence::create(); - presence->setTo(user); - presence->setFrom(jid); - presence->setType(Swift::Presence::Unsubscribe); - handleSwiftPresenceChanged(user, presence); - } - - void handleLogoutRequest(const std::string &user, const std::string &legacyName) { - boost::shared_ptr client = m_users[user]; - if (client) { - client->onConnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user)); -// client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1)); - client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1)); - client->getRoster()->onInitialRosterPopulated.disconnect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user)); - client->getPresenceOracle()->onPresenceChange.disconnect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1)); - client->disconnect(); - } - } - - void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &xhtml = "", const std::string &id = "") { - } - - void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) { - } - - void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector &groups) { - boost::shared_ptr client = m_users[user]; - if (client) { - LOG4CXX_INFO(logger, user << ": Added/Updated buddy " << buddyName << "."); - if (!client->getRoster()->containsJID(buddyName) || client->getRoster()->getSubscriptionStateForJID(buddyName) != Swift::RosterItemPayload::Both) { - Swift::RosterItemPayload item; - item.setName(alias); - item.setJID(buddyName); - item.setGroups(groups); - boost::shared_ptr roster(new Swift::RosterPayload()); - roster->addItem(item); - Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter()); -// request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); - request->send(); - client->getSubscriptionManager()->requestSubscription(buddyName); - } - else { - Swift::JID contact(buddyName); - Swift::RosterItemPayload item(contact, alias, client->getRoster()->getSubscriptionStateForJID(contact)); - item.setGroups(groups); - boost::shared_ptr roster(new Swift::RosterPayload()); - roster->addItem(item); - Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter()); -// request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); - request->send(); - } - - } - } - - void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector &groups) { - boost::shared_ptr client = m_users[user]; - if (client) { - Swift::RosterItemPayload item(buddyName, "", Swift::RosterItemPayload::Remove); - boost::shared_ptr roster(new Swift::RosterPayload()); - roster->addItem(item); - Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter()); -// request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); - request->send(); - } - } - - void handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password) { - - } - - void handleLeaveRoomRequest(const std::string &user, const std::string &room) { - - } - - private: - Config *config; - std::map > m_users; - std::map > m_handlers; -}; - -#ifndef WIN32 -static void spectrum_sigchld_handler(int sig) -{ - int status; - pid_t pid; - - do { - pid = waitpid(-1, &status, WNOHANG); - } while (pid != 0 && pid != (pid_t)-1); - - if ((pid == (pid_t) - 1) && (errno != ECHILD)) { - char errmsg[BUFSIZ]; - snprintf(errmsg, BUFSIZ, "Warning: waitpid() returned %d", pid); - perror(errmsg); - } -} -#endif - - -int main (int argc, char* argv[]) { - std::string host; - int port; - -#ifndef WIN32 - if (signal(SIGCHLD, spectrum_sigchld_handler) == SIG_ERR) { - std::cout << "SIGCHLD handler can't be set\n"; - return -1; - } -#endif - - std::string error; - Config *cfg = Config::createFromArgs(argc, argv, error, host, port); - if (cfg == NULL) { - std::cerr << error; - return 1; - } - - Logging::initBackendLogging(cfg); - - Swift::SimpleEventLoop eventLoop; - loop_ = &eventLoop; - np = new SwiftenPlugin(cfg, &eventLoop, host, port); - loop_->run(); - - return 0; -} +// Transport includes +#include "transport/config.h" +#include "transport/networkplugin.h" +#include "transport/logging.h" + +#include "boost/date_time/posix_time/posix_time.hpp" + +// Swiften +#include "Swiften/Swiften.h" +#include +#define HAVE_SWIFTEN_3 SWIFTEN_VERSION >= 0x030000 + +#ifndef WIN32 +// for signal handler +#include "unistd.h" +#include "signal.h" +#include "sys/wait.h" +#include "sys/signal.h" +#endif + +#ifndef __FreeBSD__ +#ifndef __MACH__ +// malloc_trim +#include "malloc.h" +#endif +#endif + +// Boost +#include +using namespace boost::filesystem; +using namespace boost::program_options; +using namespace Transport; + +DEFINE_LOGGER(logger, "Swiften"); +DEFINE_LOGGER(logger_xml, "backend.xml"); + +// eventloop +Swift::SimpleEventLoop *loop_; + +// Plugins +class SwiftenPlugin; +NetworkPlugin *np = NULL; +Swift::XMPPSerializer *serializer; + +class ForwardIQHandler : public Swift::IQHandler { + public: + std::map m_id2resource; + + ForwardIQHandler(NetworkPlugin *np, const std::string &user) { + m_np = np; + m_user = user; + } + + bool handleIQ(boost::shared_ptr iq) { + if (iq->getPayload() != NULL) { + return false; + } + if (iq->getType() == Swift::IQ::Get) { + m_id2resource[iq->getID()] = iq->getFrom().getResource(); + } + + iq->setTo(m_user); + std::string xml = safeByteArrayToString(serializer->serializeElement(iq)); + m_np->sendRawXML(xml); + return true; + } + + private: + NetworkPlugin *m_np; + std::string m_user; + +}; + +class SwiftenPlugin : public NetworkPlugin, Swift::XMPPParserClient { + public: + Swift::BoostNetworkFactories *m_factories; + Swift::BoostIOServiceThread m_boostIOServiceThread; + boost::shared_ptr m_conn; + bool m_firstPing; + + Swift::FullPayloadSerializerCollection collection; + Swift::XMPPParser *m_xmppParser; + Swift::FullPayloadParserFactoryCollection m_collection2; + + SwiftenPlugin(Config *config, Swift::SimpleEventLoop *loop, const std::string &host, int port) : NetworkPlugin() { + this->config = config; + m_firstPing = true; + m_factories = new Swift::BoostNetworkFactories(loop); + m_conn = m_factories->getConnectionFactory()->createConnection(); + m_conn->onDataRead.connect(boost::bind(&SwiftenPlugin::_handleDataRead, this, _1)); + m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(host), port)); +#if HAVE_SWIFTEN_3 + serializer = new Swift::XMPPSerializer(&collection, Swift::ClientStreamType, false); +#else + serializer = new Swift::XMPPSerializer(&collection, Swift::ClientStreamType); +#endif + m_xmppParser = new Swift::XMPPParser(this, &m_collection2, m_factories->getXMLParserFactory()); + m_xmppParser->parse(""); + + LOG4CXX_INFO(logger, "Starting the plugin."); + } + + // NetworkPlugin uses this method to send the data to networkplugin server + void sendData(const std::string &string) { + m_conn->write(Swift::createSafeByteArray(string)); + } + + // This method has to call handleDataRead with all received data from network plugin server + void _handleDataRead(boost::shared_ptr data) { + if (m_firstPing) { + m_firstPing = false; + NetworkPlugin::PluginConfig cfg; + cfg.setRawXML(true); + sendConfig(cfg); + } + std::string d(data->begin(), data->end()); + handleDataRead(d); + } + + void handleStreamStart(const Swift::ProtocolHeader&) {} +#if HAVE_SWIFTEN_3 + void handleElement(boost::shared_ptr element) { +#else + void handleElement(boost::shared_ptr element) { +#endif + boost::shared_ptr stanza = boost::dynamic_pointer_cast(element); + if (!stanza) { + return; + } + + std::string user = stanza->getFrom().toBare(); + + boost::shared_ptr client = m_users[user]; + if (!client) + return; + + stanza->setFrom(client->getJID()); + + boost::shared_ptr message = boost::dynamic_pointer_cast(stanza); + if (message) { + client->sendMessage(message); + return; + } + + boost::shared_ptr presence = boost::dynamic_pointer_cast(stanza); + if (presence) { + client->sendPresence(presence); + return; + } + + boost::shared_ptr iq = boost::dynamic_pointer_cast(stanza); + if (iq) { + if (m_handlers[user]->m_id2resource.find(stanza->getID()) != m_handlers[user]->m_id2resource.end()) { + std::string resource = m_handlers[user]->m_id2resource[stanza->getID()]; + if (resource.empty()) { + iq->setTo(Swift::JID(iq->getTo().getNode(), iq->getTo().getDomain())); + } else { + iq->setTo(Swift::JID(iq->getTo().getNode(), iq->getTo().getDomain(), resource)); + } + + m_handlers[user]->m_id2resource.erase(stanza->getID()); + } + client->getIQRouter()->sendIQ(iq); + return; + } + } + + void handleStreamEnd() {} + + void handleRawXML(const std::string &xml) { + m_xmppParser->parse(xml); + } + + void handleSwiftDisconnected(const std::string &user, const boost::optional &error) { + std::string message = ""; + bool reconnect = false; + if (error) { + switch(error->getType()) { + case Swift::ClientError::UnknownError: message = ("Unknown Error"); reconnect = true; break; + case Swift::ClientError::DomainNameResolveError: message = ("Unable to find server"); break; + case Swift::ClientError::ConnectionError: message = ("Error connecting to server"); break; + case Swift::ClientError::ConnectionReadError: message = ("Error while receiving server data"); reconnect = true; break; + case Swift::ClientError::ConnectionWriteError: message = ("Error while sending data to the server"); reconnect = true; break; + case Swift::ClientError::XMLError: message = ("Error parsing server data"); reconnect = true; break; + case Swift::ClientError::AuthenticationFailedError: message = ("Login/password invalid"); break; + case Swift::ClientError::CompressionFailedError: message = ("Error while compressing stream"); break; + case Swift::ClientError::ServerVerificationFailedError: message = ("Server verification failed"); break; + case Swift::ClientError::NoSupportedAuthMechanismsError: message = ("Authentication mechanisms not supported"); break; + case Swift::ClientError::UnexpectedElementError: message = ("Unexpected response"); break; + case Swift::ClientError::ResourceBindError: message = ("Error binding resource"); break; + case Swift::ClientError::SessionStartError: message = ("Error starting session"); break; + case Swift::ClientError::StreamError: message = ("Stream error"); break; + case Swift::ClientError::TLSError: message = ("Encryption error"); break; + case Swift::ClientError::ClientCertificateLoadError: message = ("Error loading certificate (Invalid password?)"); break; + case Swift::ClientError::ClientCertificateError: message = ("Certificate not authorized"); break; + + case Swift::ClientError::UnknownCertificateError: message = ("Unknown certificate"); break; + case Swift::ClientError::CertificateExpiredError: message = ("Certificate has expired"); break; + case Swift::ClientError::CertificateNotYetValidError: message = ("Certificate is not yet valid"); break; + case Swift::ClientError::CertificateSelfSignedError: message = ("Certificate is self-signed"); break; + case Swift::ClientError::CertificateRejectedError: message = ("Certificate has been rejected"); break; + case Swift::ClientError::CertificateUntrustedError: message = ("Certificate is not trusted"); break; + case Swift::ClientError::InvalidCertificatePurposeError: message = ("Certificate cannot be used for encrypting your connection"); break; + case Swift::ClientError::CertificatePathLengthExceededError: message = ("Certificate path length constraint exceeded"); break; + case Swift::ClientError::InvalidCertificateSignatureError: message = ("Invalid certificate signature"); break; + case Swift::ClientError::InvalidCAError: message = ("Invalid Certificate Authority"); break; + case Swift::ClientError::InvalidServerIdentityError: message = ("Certificate does not match the host identity"); break; + } + } + LOG4CXX_INFO(logger, user << ": Disconnected " << message); + handleDisconnected(user, reconnect ? 0 : 3, message); + + boost::shared_ptr client = m_users[user]; + if (client) { + client->onConnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user)); + client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1)); + client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1)); + m_users.erase(user); + m_handlers.erase(user); + } + +#ifndef WIN32 +#ifndef __FreeBSD__ +#ifndef __MACH__ + // force returning of memory chunks allocated by libxml2 to kernel + malloc_trim(0); +#endif +#endif +#endif + } + + void handleSwiftConnected(const std::string &user) { + LOG4CXX_INFO(logger, user << ": Connected to XMPP server."); + handleConnected(user); + m_users[user]->requestRoster(); + Swift::Presence::ref response = Swift::Presence::create(); + response->setFrom(m_users[user]->getJID()); + m_users[user]->sendPresence(response); + } + + void handleSwiftRosterReceived(const std::string &user) { + Swift::PresenceOracle *oracle = m_users[user]->getPresenceOracle(); + BOOST_FOREACH(const Swift::XMPPRosterItem &item, m_users[user]->getRoster()->getItems()) { + Swift::Presence::ref lastPresence = oracle->getLastPresence(item.getJID()); + pbnetwork::StatusType status = lastPresence ? ((pbnetwork::StatusType) lastPresence->getShow()) : pbnetwork::STATUS_NONE; + handleBuddyChanged(user, item.getJID().toBare().toString(), + item.getName(), item.getGroups(), status); + } + } + + void handleSwiftPresenceChanged(const std::string &user, Swift::Presence::ref presence) { +// boost::shared_ptr client = m_users[user]; +// if (client->getMUCRegistry()->isMUC(presence->getFrom().toBare())) { +// return; +// } +// +// if (presence->getPayload() != NULL || presence->getPayload() != NULL) { +// return; +// } +// +// LOG4CXX_INFO(logger, user << ": " << presence->getFrom().toBare().toString() << " presence changed"); +// +// std::string message = presence->getStatus(); +// std::string photo = ""; +// +// boost::shared_ptr update = presence->getPayload(); +// if (update) { +// photo = update->getPhotoHash(); +// } +// +// boost::optional item = m_users[user]->getRoster()->getItem(presence->getFrom()); +// if (item) { +// handleBuddyChanged(user, presence->getFrom().toBare().toString(), item->getName(), item->getGroups(), (pbnetwork::StatusType) presence->getShow(), message, photo); +// } +// else { +// std::vector groups; +// handleBuddyChanged(user, presence->getFrom().toBare().toString(), presence->getFrom().toBare(), groups, (pbnetwork::StatusType) presence->getShow(), message, photo); +// } + presence->setTo(user); + std::string xml = safeByteArrayToString(serializer->serializeElement(presence)); + sendRawXML(xml); + } + + void handleSwiftMessageReceived(const std::string &user, Swift::Message::ref message) { + message->setTo(user); + std::string xml = safeByteArrayToString(serializer->serializeElement(message)); + sendRawXML(xml); + } + + void handleSwiftenDataRead(const Swift::SafeByteArray &data) { + std::string d = safeByteArrayToString(data); + if (!boost::starts_with(d, " client = boost::make_shared(Swift::JID(legacyName + "/Spectrum"), password, m_factories); + m_users[user] = client; + client->setAlwaysTrustCertificates(); + client->onConnected.connect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user)); + client->onDisconnected.connect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1)); + client->onMessageReceived.connect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1)); + client->getRoster()->onInitialRosterPopulated.connect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user)); + client->getPresenceOracle()->onPresenceChange.connect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1)); + client->onDataRead.connect(boost::bind(&SwiftenPlugin::handleSwiftenDataRead, this, _1)); + client->onDataWritten.connect(boost::bind(&SwiftenPlugin::handleSwiftenDataWritten, this, _1)); + client->getSubscriptionManager()->onPresenceSubscriptionRequest.connect(boost::bind(&SwiftenPlugin::handleSubscriptionRequest, this, user, _1, _2, _3)); + client->getSubscriptionManager()->onPresenceSubscriptionRevoked.connect(boost::bind(&SwiftenPlugin::handleSubscriptionRevoked, this, user, _1, _2)); + Swift::ClientOptions opt; + opt.allowPLAINWithoutTLS = true; + client->connect(opt); + + boost::shared_ptr handler = boost::make_shared(this, user); + client->getIQRouter()->addHandler(handler); + m_handlers[user] = handler; + } + + void handleSubscriptionRequest(const std::string &user, const Swift::JID& jid, const std::string& message, Swift::Presence::ref presence) { + handleSwiftPresenceChanged(user, presence); + } + + void handleSubscriptionRevoked(const std::string &user, const Swift::JID& jid, const std::string& message) { + Swift::Presence::ref presence = Swift::Presence::create(); + presence->setTo(user); + presence->setFrom(jid); + presence->setType(Swift::Presence::Unsubscribe); + handleSwiftPresenceChanged(user, presence); + } + + void handleLogoutRequest(const std::string &user, const std::string &legacyName) { + boost::shared_ptr client = m_users[user]; + if (client) { + client->onConnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user)); +// client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1)); + client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1)); + client->getRoster()->onInitialRosterPopulated.disconnect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user)); + client->getPresenceOracle()->onPresenceChange.disconnect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1)); + client->disconnect(); + } + } + + void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &xhtml = "", const std::string &id = "") { + } + + void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) { + } + + void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector &groups) { + boost::shared_ptr client = m_users[user]; + if (client) { + LOG4CXX_INFO(logger, user << ": Added/Updated buddy " << buddyName << "."); + if (!client->getRoster()->containsJID(buddyName) || client->getRoster()->getSubscriptionStateForJID(buddyName) != Swift::RosterItemPayload::Both) { + Swift::RosterItemPayload item; + item.setName(alias); + item.setJID(buddyName); + item.setGroups(groups); + boost::shared_ptr roster(new Swift::RosterPayload()); + roster->addItem(item); + Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter()); +// request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); + request->send(); + client->getSubscriptionManager()->requestSubscription(buddyName); + } + else { + Swift::JID contact(buddyName); + Swift::RosterItemPayload item(contact, alias, client->getRoster()->getSubscriptionStateForJID(contact)); + item.setGroups(groups); + boost::shared_ptr roster(new Swift::RosterPayload()); + roster->addItem(item); + Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter()); +// request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); + request->send(); + } + + } + } + + void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector &groups) { + boost::shared_ptr client = m_users[user]; + if (client) { + Swift::RosterItemPayload item(buddyName, "", Swift::RosterItemPayload::Remove); + boost::shared_ptr roster(new Swift::RosterPayload()); + roster->addItem(item); + Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter()); +// request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); + request->send(); + } + } + + void handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password) { + + } + + void handleLeaveRoomRequest(const std::string &user, const std::string &room) { + + } + + private: + Config *config; + std::map > m_users; + std::map > m_handlers; +}; + +#ifndef WIN32 +static void spectrum_sigchld_handler(int sig) +{ + int status; + pid_t pid; + + do { + pid = waitpid(-1, &status, WNOHANG); + } while (pid != 0 && pid != (pid_t)-1); + + if ((pid == (pid_t) - 1) && (errno != ECHILD)) { + char errmsg[BUFSIZ]; + snprintf(errmsg, BUFSIZ, "Warning: waitpid() returned %d", pid); + perror(errmsg); + } +} +#endif + + +int main (int argc, char* argv[]) { + std::string host; + int port; + +#ifndef WIN32 + if (signal(SIGCHLD, spectrum_sigchld_handler) == SIG_ERR) { + std::cout << "SIGCHLD handler can't be set\n"; + return -1; + } +#endif + + std::string error; + Config *cfg = Config::createFromArgs(argc, argv, error, host, port); + if (cfg == NULL) { + std::cerr << error; + return 1; + } + + Logging::initBackendLogging(cfg); + + Swift::SimpleEventLoop eventLoop; + loop_ = &eventLoop; + np = new SwiftenPlugin(cfg, &eventLoop, host, port); + loop_->run(); + + return 0; +} diff --git a/backends/twitter/CMakeLists.txt b/backends/twitter/CMakeLists.txt index 7853267e..12ecb84a 100644 --- a/backends/twitter/CMakeLists.txt +++ b/backends/twitter/CMakeLists.txt @@ -2,11 +2,14 @@ include_directories (${libtransport_SOURCE_DIR}/backends/twitter/libtwitcurl) FILE(GLOB SRC *.cpp libtwitcurl/*.cpp Requests/*.cpp) add_executable(spectrum2_twitter_backend ${SRC}) -if (NOT WIN32) -target_link_libraries(spectrum2_twitter_backend curl transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES}) -else () -include_directories("${CMAKE_SOURCE_DIR}/msvc-deps/curl/include") -target_link_libraries(spectrum2_twitter_backend libcurl transport ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES}) +find_package(curl) + +if(CURL_FOUND) +message(STATUS "Using curl ${CURL_VERSION_STRING}: ${CURL_INCLUDE_DIRS} ${CURL_LIBRARIES}") +include_directories (${CURL_INCLUDE_DIRS}) +target_link_libraries(spectrum2_twitter_backend transport ${CURL_LIBRARIES} ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES}) +else() +message(FATAL_ERROR "curl not found") endif() INSTALL(TARGETS spectrum2_twitter_backend RUNTIME DESTINATION bin) diff --git a/backends/twitter/TwitterPlugin.cpp b/backends/twitter/TwitterPlugin.cpp index 99a103d6..543e2dbb 100644 --- a/backends/twitter/TwitterPlugin.cpp +++ b/backends/twitter/TwitterPlugin.cpp @@ -1,886 +1,894 @@ -#include "TwitterPlugin.h" -#include "Requests/StatusUpdateRequest.h" -#include "Requests/DirectMessageRequest.h" -#include "Requests/TimelineRequest.h" -#include "Requests/FetchFriends.h" -#include "Requests/HelpMessageRequest.h" -#include "Requests/PINExchangeProcess.h" -#include "Requests/OAuthFlow.h" -#include "Requests/CreateFriendRequest.h" -#include "Requests/DestroyFriendRequest.h" -#include "Requests/RetweetRequest.h" -#include "Requests/ProfileImageRequest.h" -#include "Swiften/StringCodecs/Hexify.h" - -DEFINE_LOGGER(logger, "Twitter Backend"); - -TwitterPlugin *np = NULL; -Swift::SimpleEventLoop *loop_; // Event Loop - -const std::string OLD_APP_KEY = "PCWAdQpyyR12ezp2fVwEhw"; -const std::string OLD_APP_SECRET = "EveLmCXJIg2R7BTCpm6OWV8YyX49nI0pxnYXh7JMvDg"; - -#define abs(x) ((x)<0?-(x):(x)) -#define SHA(x) (Swift::Hexify::hexify(Swift::SHA1::getHash(Swift::createByteArray((x))))) - -//Compares two +ve intergers 'a' and 'b' represented as strings -static int cmp(std::string a, std::string b) -{ - int diff = abs((int)a.size() - (int)b.size()); - if(a.size() < b.size()) a = std::string(diff,'0') + a; - else b = std::string(diff,'0') + b; - - if(a == b) return 0; - if(a < b) return -1; - return 1; -} - - -TwitterPlugin::TwitterPlugin(Config *config, Swift::SimpleEventLoop *loop, StorageBackend *storagebackend, const std::string &host, int port) : NetworkPlugin() -{ - this->config = config; - this->storagebackend = storagebackend; - this->m_firstPing = true; - - if (CONFIG_HAS_KEY(config, "twitter.consumer_key") == false) { - consumerKey = "5mFePMiJi0KpeURONkelg"; - } - else { - consumerKey = CONFIG_STRING(config, "twitter.consumer_key"); - } - if (CONFIG_HAS_KEY(config, "twitter.consumer_secret") == false) { - consumerSecret = "YFZCDJwRhbkccXEnaYr1waCQejTJcOY8F7l5Wim3FA"; - } - else { - consumerSecret = CONFIG_STRING(config, "twitter.consumer_secret"); - } - - if (consumerSecret.empty() || consumerKey.empty()) { - LOG4CXX_ERROR(logger, "Consumer key and Consumer secret can't be empty."); - exit(1); - } - - adminLegacyName = "twitter.com"; - adminChatRoom = "#twitter"; - adminNickName = "twitter"; - adminAlias = "twitter"; - - OAUTH_KEY = "twitter_oauth_token"; - OAUTH_SECRET = "twitter_oauth_secret"; - MODE = "mode"; - - m_factories = new Swift::BoostNetworkFactories(loop); - m_conn = m_factories->getConnectionFactory()->createConnection(); - m_conn->onDataRead.connect(boost::bind(&TwitterPlugin::_handleDataRead, this, _1)); - m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(host), port)); - - tp = new ThreadPool(loop_, 10); - - tweet_timer = m_factories->getTimerFactory()->createTimer(90000); - message_timer = m_factories->getTimerFactory()->createTimer(90000); - - tweet_timer->onTick.connect(boost::bind(&TwitterPlugin::pollForTweets, this)); - message_timer->onTick.connect(boost::bind(&TwitterPlugin::pollForDirectMessages, this)); - - tweet_timer->start(); - message_timer->start(); - - LOG4CXX_INFO(logger, "Starting the plugin."); -} - -TwitterPlugin::~TwitterPlugin() -{ - delete storagebackend; - std::set::iterator it; - for(it = onlineUsers.begin() ; it != onlineUsers.end() ; it++) delete userdb[*it].sessions; - delete tp; -} - -// Send data to NetworkPlugin server -void TwitterPlugin::sendData(const std::string &string) -{ - m_conn->write(Swift::createSafeByteArray(string)); -} - -// Receive date from the NetworkPlugin server and invoke the appropirate payload handler (implement in the NetworkPlugin class) -void TwitterPlugin::_handleDataRead(boost::shared_ptr data) -{ - if (m_firstPing) { - m_firstPing = false; - // Users can join the network without registering if we allow - // one user to connect multiple IRC networks. - NetworkPlugin::PluginConfig cfg; - cfg.setNeedPassword(false); - sendConfig(cfg); - } - - std::string d(data->begin(), data->end()); - handleDataRead(d); -} - -// User trying to login into his twitter account -void TwitterPlugin::handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) -{ - if(userdb.count(user) && (userdb[user].connectionState == NEW || - userdb[user].connectionState == CONNECTED || - userdb[user].connectionState == WAITING_FOR_PIN)) { - LOG4CXX_INFO(logger, std::string("A session corresponding to ") + user + std::string(" is already active")) - return; - } - - LOG4CXX_INFO(logger, std::string("Received login request for ") + user) - initUserSession(user, legacyName, password); - handleConnected(user); - - LOG4CXX_INFO(logger, "SPECTRUM 1 USER? - " << (userdb[user].spectrum1User? "true" : "false")) - - LOG4CXX_INFO(logger, user << ": Adding Buddy " << adminLegacyName << " " << adminAlias) - handleBuddyChanged(user, adminLegacyName, adminAlias, std::vector(), pbnetwork::STATUS_ONLINE); - userdb[user].nickName = ""; - - LOG4CXX_INFO(logger, "Querying database for usersettings of " << user) - std::string key, secret; - getUserOAuthKeyAndSecret(user, key, secret); - - if(key == "" || secret == "") { - LOG4CXX_INFO(logger, "Intiating OAuth Flow for user " << user) - setTwitterMode(user, 0); - tp->runAsThread(new OAuthFlow(np, userdb[user].sessions, user, userdb[user].sessions->getTwitterUsername())); - } else { - LOG4CXX_INFO(logger, user << " is already registerd. Using the stored oauth key and secret") - LOG4CXX_INFO(logger, key << " " << secret) - pinExchangeComplete(user, key, secret); - } -} - -// User logging out -void TwitterPlugin::handleLogoutRequest(const std::string &user, const std::string &legacyName) -{ - if (userdb.count(user)) { - delete userdb[user].sessions; - userdb[user].sessions = NULL; - userdb[user].connectionState = DISCONNECTED; - } - - if(onlineUsers.count(user)) { - onlineUsers.erase(user); - } -} - -// User joining a Chatroom -void TwitterPlugin::handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password) -{ - if(room == adminChatRoom) { - LOG4CXX_INFO(logger, "Received Join Twitter room request for " << user) - - setTwitterMode(user, 2); - handleParticipantChanged(user, adminNickName, room, 0, pbnetwork::STATUS_ONLINE); - userdb[user].nickName = nickname; - handleMessage(user, adminChatRoom, "Connected to Twitter room! Populating your followers list", adminNickName); - tp->runAsThread(new FetchFriends(userdb[user].sessions, user, - boost::bind(&TwitterPlugin::populateRoster, this, _1, _2, _3, _4))); - } else { - setTwitterMode(user, 0); - LOG4CXX_ERROR(logger, "Couldn't connect to chatroom - " << room <<"! Try twitter-chatroom as the chatroom to access Twitter account") - handleMessage(user, adminLegacyName, "Couldn't connect to chatroom! Try twitter-chatroom as the chatroom to access Twitter account"); - } -} - -// User leaving a Chatroom -void TwitterPlugin::handleLeaveRoomRequest(const std::string &user, const std::string &room) -{ - if(room == adminChatRoom && onlineUsers.count(user)) { - LOG4CXX_INFO(logger, "Leaving chatroom! Switching back to default mode 0") - setTwitterMode(user, 0); - handleBuddyChanged(user, adminLegacyName, adminAlias, std::vector(), pbnetwork::STATUS_ONLINE); - } -} - -// Messages to be sent to Twitter -void TwitterPlugin::handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml, const std::string &/*id*/) -{ - - LOG4CXX_INFO(logger, "Received " << user << " --> " << legacyName << " - " << message) - - if(legacyName == adminLegacyName || legacyName == adminChatRoom) { - std::string cmd = "", data = ""; - - /** Parsing the message - Assuming message format to be [ ]***/ - int i; - for(i=0 ; irunAsThread(new PINExchangeProcess(np, userdb[user].sessions, user, data)); - else if(cmd == "#help") - tp->runAsThread(new HelpMessageRequest(user, CONFIG_STRING(config, "service.jid"), boost::bind(&TwitterPlugin::helpMessageResponse, this, _1, _2))); - else if(cmd[0] == '@') { - std::string username = cmd.substr(1); - tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, username, data, - boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4))); - } - else if(cmd == "#status") - tp->runAsThread(new StatusUpdateRequest(userdb[user].sessions, user, data, - boost::bind(&TwitterPlugin::statusUpdateResponse, this, _1, _2))); - else if(cmd == "#timeline") - tp->runAsThread(new TimelineRequest(userdb[user].sessions, user, data, "", - boost::bind(&TwitterPlugin::displayTweets, this, _1, _2, _3, _4))); - else if(cmd == "#friends") - tp->runAsThread(new FetchFriends(userdb[user].sessions, user, - boost::bind(&TwitterPlugin::displayFriendlist, this, _1, _2, _3, _4))); - else if(cmd == "#follow") - tp->runAsThread(new CreateFriendRequest(userdb[user].sessions, user, data.substr(0,data.find('@')), - boost::bind(&TwitterPlugin::createFriendResponse, this, _1, _2, _3, _4))); - else if(cmd == "#unfollow") - tp->runAsThread(new DestroyFriendRequest(userdb[user].sessions, user, data.substr(0,data.find('@')), - boost::bind(&TwitterPlugin::deleteFriendResponse, this, _1, _2, _3))); - else if(cmd == "#retweet") - tp->runAsThread(new RetweetRequest(userdb[user].sessions, user, data, - boost::bind(&TwitterPlugin::RetweetResponse, this, _1, _2))); - else if(cmd == "#mode") { - int m = 0; - m = atoi(data.c_str()); - mode prevm = userdb[user].twitterMode; - - if((mode)m == userdb[user].twitterMode) return; //If same as current mode return - if(m < 0 || m > 2) { // Invalid modes - handleMessage(user, adminLegacyName, std::string("Error! Unknown mode ") + data + ". Allowed values 0,1,2." ); - return; - } - - setTwitterMode(user, m); - if((userdb[user].twitterMode == SINGLECONTACT || userdb[user].twitterMode == CHATROOM) && prevm == MULTIPLECONTACT) clearRoster(user); - else if(userdb[user].twitterMode == MULTIPLECONTACT) - tp->runAsThread(new FetchFriends(userdb[user].sessions, user, boost::bind(&TwitterPlugin::populateRoster, this, _1, _2, _3, _4))); - - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - std::string("Changed mode to ") + data, userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - - LOG4CXX_INFO(logger, user << ": Changed mode to " << data << " <" << (userdb[user].twitterMode == CHATROOM ? adminNickName : "") << ">" ) - } - - else if(userdb[user].twitterMode == CHATROOM) { - std::string buddy = message.substr(0, message.find(":")); - if(userdb[user].buddies.count(buddy) == 0) { - tp->runAsThread(new StatusUpdateRequest(userdb[user].sessions, user, message, - boost::bind(&TwitterPlugin::statusUpdateResponse, this, _1, _2))); - } else { - data = message.substr(message.find(":")+1); //Can parse better??:P - tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, buddy, data, - boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4))); - } - } - else handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - "Unknown command! Type #help for a list of available commands.", userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - } - - else { - std::string buddy = legacyName; - if(userdb[user].twitterMode == CHATROOM) buddy = legacyName.substr(legacyName.find("/") + 1); - if(legacyName != "twitter") { - tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, buddy, message, - boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4))); - } - } -} - -void TwitterPlugin::handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector &groups) -{ - if(userdb[user].connectionState != CONNECTED) { - LOG4CXX_ERROR(logger, user << " is not connected to twitter!") - return; - } - - LOG4CXX_INFO(logger, user << " - Adding Twitter contact " << buddyName) - tp->runAsThread(new CreateFriendRequest(userdb[user].sessions, user, buddyName, - boost::bind(&TwitterPlugin::createFriendResponse, this, _1, _2, _3, _4))); -} - -void TwitterPlugin::handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector &groups) -{ - if(userdb[user].connectionState != CONNECTED) { - LOG4CXX_ERROR(logger, user << " is not connected to twitter!") - return; - } - - LOG4CXX_INFO(logger, user << " - Removing Twitter contact " << buddyName) - tp->runAsThread(new DestroyFriendRequest(userdb[user].sessions, user, buddyName, - boost::bind(&TwitterPlugin::deleteFriendResponse, this, _1, _2, _3))); -} - -void TwitterPlugin::handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) -{ - if(userdb[user].connectionState != CONNECTED) { - LOG4CXX_ERROR(logger, user << " is not connected to twitter!") - return; - } - - LOG4CXX_INFO(logger, user << " - VCardRequest for " << legacyName << ", " << userdb[user].buddiesInfo[legacyName].getProfileImgURL()) - - if(getTwitterMode(user) != SINGLECONTACT && userdb[user].buddies.count(legacyName) - && userdb[user].buddiesInfo[legacyName].getProfileImgURL().length()) { - if(userdb[user].buddiesImgs.count(legacyName) == 0) { - tp->runAsThread(new ProfileImageRequest(config, user, legacyName, userdb[user].buddiesInfo[legacyName].getProfileImgURL(), id, - boost::bind(&TwitterPlugin::profileImageResponse, this, _1, _2, _3, _4, _5))); - } - handleVCard(user, id, legacyName, legacyName, "", userdb[user].buddiesImgs[legacyName]); - } -} - -void TwitterPlugin::pollForTweets() -{ - boost::mutex::scoped_lock lock(userlock); - std::set::iterator it = onlineUsers.begin(); - while(it != onlineUsers.end()) { - std::string user = *it; - tp->runAsThread(new TimelineRequest(userdb[user].sessions, user, "", getMostRecentTweetIDUnsafe(user), - boost::bind(&TwitterPlugin::displayTweets, this, _1, _2, _3, _4))); - it++; - } - tweet_timer->start(); -} - -void TwitterPlugin::pollForDirectMessages() -{ - boost::mutex::scoped_lock lock(userlock); - std::set::iterator it = onlineUsers.begin(); - while(it != onlineUsers.end()) { - std::string user = *it; - tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, "", getMostRecentDMIDUnsafe(user), - boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4))); - it++; - } - message_timer->start(); -} - - -bool TwitterPlugin::getUserOAuthKeyAndSecret(const std::string user, std::string &key, std::string &secret) -{ - boost::mutex::scoped_lock lock(dblock); - - UserInfo info; - if(storagebackend->getUser(user, info) == false) { - LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") - return false; - } - - key="", secret=""; int type; - storagebackend->getUserSetting((long)info.id, OAUTH_KEY, type, key); - storagebackend->getUserSetting((long)info.id, OAUTH_SECRET, type, secret); - return true; -} - -bool TwitterPlugin::checkSpectrum1User(const std::string user) -{ - boost::mutex::scoped_lock lock(dblock); - - UserInfo info; - if(storagebackend->getUser(user, info) == false) { - LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") - return false; - } - - std::string first_synchronization_done = ""; - int type; - storagebackend->getUserSetting((long)info.id, "first_synchronization_done", type, first_synchronization_done); - - LOG4CXX_INFO(logger, "first_synchronization_done: " << first_synchronization_done) - - if(first_synchronization_done.length()) return true; - return false; -} - -int TwitterPlugin::getTwitterMode(const std::string user) -{ - boost::mutex::scoped_lock lock(dblock); - - UserInfo info; - if(storagebackend->getUser(user, info) == false) { - LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") - return -1; - } - - int type; int m; - std::string s_m; - storagebackend->getUserSetting((long)info.id, MODE, type, s_m); - if(s_m == "") { - s_m = "0"; - storagebackend->updateUserSetting((long)info.id, MODE, s_m); - } - m = atoi(s_m.c_str()); - return m; -} - -bool TwitterPlugin::setTwitterMode(const std::string user, int m) -{ - boost::mutex::scoped_lock lock(dblock); - - UserInfo info; - if(storagebackend->getUser(user, info) == false) { - LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") - return false; - } - - if(m < 0 || m > 2) { - LOG4CXX_ERROR(logger, "Unknown mode " << m <<". Using default mode 0") - m = 0; - } - - userdb[user].twitterMode = (mode)m; - - //int type; - std::string s_m = std::string(1,m+'0'); - LOG4CXX_ERROR(logger, "Storing mode " << m <<" for user " << user) - storagebackend->updateUserSetting((long)info.id, MODE, s_m); - return true; -} - -bool TwitterPlugin::storeUserOAuthKeyAndSecret(const std::string user, const std::string OAuthKey, const std::string OAuthSecret) -{ - - boost::mutex::scoped_lock lock(dblock); - - UserInfo info; - if(storagebackend->getUser(user, info) == false) { - LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") - return false; - } - - storagebackend->updateUserSetting((long)info.id, OAUTH_KEY, OAuthKey); - storagebackend->updateUserSetting((long)info.id, OAUTH_SECRET, OAuthSecret); - return true; -} - -void TwitterPlugin::initUserSession(const std::string user, const std::string legacyName, const std::string password) -{ - boost::mutex::scoped_lock lock(userlock); - - std::string username = legacyName; - std::string passwd = password; - LOG4CXX_INFO(logger, username + " " + passwd) - - userdb[user].sessions = new twitCurl(); - if(CONFIG_HAS_KEY(config,"proxy.server")) { - std::string ip = CONFIG_STRING(config,"proxy.server"); - - std::ostringstream out; - out << CONFIG_INT(config,"proxy.port"); - std::string port = out.str(); - - std::string puser = CONFIG_STRING(config,"proxy.user"); - std::string ppasswd = CONFIG_STRING(config,"proxy.password"); - - LOG4CXX_INFO(logger, ip << " " << port << " " << puser << " " << ppasswd) - - if(ip != "localhost" && port != "0") { - userdb[user].sessions->setProxyServerIp(ip); - userdb[user].sessions->setProxyServerPort(port); - userdb[user].sessions->setProxyUserName(puser); - userdb[user].sessions->setProxyPassword(ppasswd); - } - } - - //Check if the user is spectrum1 user - userdb[user].spectrum1User = checkSpectrum1User(user); - - userdb[user].connectionState = NEW; - userdb[user].legacyName = username; - userdb[user].sessions->setTwitterUsername(username); - userdb[user].sessions->setTwitterPassword(passwd); - - if(!userdb[user].spectrum1User) { - userdb[user].sessions->getOAuth().setConsumerKey(consumerKey); - userdb[user].sessions->getOAuth().setConsumerSecret(consumerSecret); - } else { - userdb[user].sessions->getOAuth().setConsumerKey(OLD_APP_KEY); - userdb[user].sessions->getOAuth().setConsumerSecret(OLD_APP_SECRET); - } -} - -void TwitterPlugin::OAuthFlowComplete(const std::string user, twitCurl *obj) -{ - boost::mutex::scoped_lock lock(userlock); - - delete userdb[user].sessions; - userdb[user].sessions = obj->clone(); - userdb[user].connectionState = WAITING_FOR_PIN; -} - -void TwitterPlugin::pinExchangeComplete(const std::string user, const std::string OAuthAccessTokenKey, const std::string OAuthAccessTokenSecret) -{ - boost::mutex::scoped_lock lock(userlock); - - userdb[user].sessions->getOAuth().setOAuthTokenKey( OAuthAccessTokenKey ); - userdb[user].sessions->getOAuth().setOAuthTokenSecret( OAuthAccessTokenSecret ); - userdb[user].connectionState = CONNECTED; - userdb[user].twitterMode = (mode)getTwitterMode(user); - - if(userdb[user].twitterMode == MULTIPLECONTACT) { - tp->runAsThread(new FetchFriends(userdb[user].sessions, user, boost::bind(&TwitterPlugin::populateRoster, this, _1, _2, _3, _4))); - } - - onlineUsers.insert(user); - userdb[user].mostRecentTweetID = ""; - userdb[user].mostRecentDirectMessageID = ""; -} - -void TwitterPlugin::updateLastTweetID(const std::string user, const std::string ID) -{ - boost::mutex::scoped_lock lock(userlock); - userdb[user].mostRecentTweetID = ID; - - UserInfo info; - if(storagebackend->getUser(user, info) == false) { - LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") - return; - } - - storagebackend->updateUserSetting((long)info.id, "twitter_last_tweet", ID); -} - -std::string TwitterPlugin::getMostRecentTweetIDUnsafe(const std::string user) -{ - std::string ID = ""; - if(onlineUsers.count(user)) { - ID = userdb[user].mostRecentTweetID; - if (ID.empty()) { - int type; - UserInfo info; - if(storagebackend->getUser(user, info) == false) { - LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") - } - else { - storagebackend->getUserSetting(info.id, "twitter_last_tweet", type, ID); - } - } - } - return ID; -} - -std::string TwitterPlugin::getMostRecentTweetID(const std::string user) -{ - boost::mutex::scoped_lock lock(userlock); - return getMostRecentTweetIDUnsafe(user); -} - -void TwitterPlugin::updateLastDMID(const std::string user, const std::string ID) -{ - boost::mutex::scoped_lock lock(userlock); - userdb[user].mostRecentDirectMessageID = ID; - - UserInfo info; - if(storagebackend->getUser(user, info) == false) { - LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") - return; - } - - storagebackend->updateUserSetting((long)info.id, "twitter_last_dm", ID); -} - -std::string TwitterPlugin::getMostRecentDMIDUnsafe(const std::string user) { - std::string ID = ""; - if(onlineUsers.count(user)) { - ID = userdb[user].mostRecentDirectMessageID; - if (ID.empty()) { - int type; - UserInfo info; - if(storagebackend->getUser(user, info) == false) { - LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") - } - else { - storagebackend->getUserSetting(info.id, "twitter_last_dm", type, ID); - } - } - } - return ID; -} - -std::string TwitterPlugin::getMostRecentDMID(const std::string user) -{ - boost::mutex::scoped_lock lock(userlock); - return getMostRecentDMIDUnsafe(user); -} - -/************************************** Twitter response functions **********************************/ -void TwitterPlugin::statusUpdateResponse(std::string &user, Error &errMsg) -{ - if(errMsg.getMessage().length()) { - if (errMsg.isCurlError()) { - handleDisconnected(user, 3, errMsg.getMessage()); - return; - } - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - } else { - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - "Status Update successful", userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - } -} - -void TwitterPlugin::helpMessageResponse(std::string &user, std::string &msg) -{ - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - msg, userdb[user].twitterMode == CHATROOM ? adminNickName : ""); -} - -void TwitterPlugin::clearRoster(const std::string user) -{ - if(userdb[user].buddies.size() == 0) return; - std::set::iterator it = userdb[user].buddies.begin(); - while(it != userdb[user].buddies.end()) { - handleBuddyRemoved(user, *it); - it++; - } - userdb[user].buddies.clear(); -} - -void TwitterPlugin::populateRoster(std::string &user, std::vector &friends, std::vector &friendAvatars, Error &errMsg) -{ - if(errMsg.getMessage().length() == 0) - { - for(int i=0 ; i(), - pbnetwork::STATUS_ONLINE, lastTweet, SHA(friendAvatars[i])); - } - else if(userdb[user].twitterMode == CHATROOM) - handleParticipantChanged(user, friends[i].getScreenName(), adminChatRoom, 0, pbnetwork::STATUS_ONLINE); - - /*handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - friends[i].getScreenName() + " - " + friends[i].getLastStatus().getTweet(), - userdb[user].twitterMode == CHATROOM ? adminNickName : "");*/ - } - } else { - if (errMsg.isCurlError()) { - handleDisconnected(user, 3, errMsg.getMessage()); - return; - } - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - std::string("Error populating roster - ") + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - } - - if(userdb[user].twitterMode == CHATROOM) handleParticipantChanged(user, userdb[user].nickName, adminChatRoom, 0, pbnetwork::STATUS_ONLINE); -} - -void TwitterPlugin::displayFriendlist(std::string &user, std::vector &friends, std::vector &friendAvatars, Error &errMsg) -{ - if(errMsg.getMessage().length() == 0) - { - std::string userlist = "\n***************USER LIST****************\n"; - for(int i=0 ; i < friends.size() ; i++) { - userlist += " - " + friends[i].getUserName() + " (" + friends[i].getScreenName() + ")\n"; - } - userlist += "***************************************\n"; - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - userlist, userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - } else { - if (errMsg.isCurlError()) { - handleDisconnected(user, 3, errMsg.getMessage()); - return; - } - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - } - -} - -void TwitterPlugin::displayTweets(std::string &user, std::string &userRequested, std::vector &tweets , Error &errMsg) -{ - if(errMsg.getMessage().length() == 0) { - std::map lastTweet; - std::map::iterator it; - - for(int i = tweets.size() - 1 ; i >= 0 ; i--) { - if(userdb[user].twitterMode != CHATROOM) { - std::string m = " - " + tweets[i].getUserData().getScreenName() + ": " + tweets[i].getTweet() + " (MsgId: " + (tweets[i].getRetweetID().empty() ? tweets[i].getID() : tweets[i].getRetweetID()) + ")\n"; - handleMessage(user, adminLegacyName, m, "", "", tweets[i].getCreationTime(), true); - - std::string scrname = tweets[i].getUserData().getScreenName(); - if(lastTweet.count(scrname) == 0 || cmp(tweets[lastTweet[scrname]].getID(), tweets[i].getID()) <= 0) lastTweet[scrname] = i; - - } else { - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - tweets[i].getTweet() + " (MsgId: " + (tweets[i].getRetweetID().empty() ? tweets[i].getID() : tweets[i].getRetweetID()) + ")", tweets[i].getUserData().getScreenName(), "", tweets[i].getCreationTime(), true); - } - } - - if(userdb[user].twitterMode == MULTIPLECONTACT) { - //Set as status user's last tweet - for(it=lastTweet.begin() ; it!=lastTweet.end() ; it++) { - int t = it->second; - handleBuddyChanged(user, tweets[t].getUserData().getScreenName(), tweets[t].getUserData().getUserName(), - std::vector(), pbnetwork::STATUS_ONLINE, tweets[t].getTweet()); - } - } - - if((userRequested == "" || userRequested == user) && tweets.size()) { - std::string tweetID = getMostRecentTweetID(user); - if(tweetID != tweets[0].getID()) updateLastTweetID(user, tweets[0].getID()); - } - - } else { - if (errMsg.isCurlError()) { - handleDisconnected(user, 3, errMsg.getMessage()); - return; - } - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - } -} - -void TwitterPlugin::directMessageResponse(std::string &user, std::string &username, std::vector &messages, Error &errMsg) -{ - if(errMsg.getCode() == "93") //Permission Denied - return; - - if(errMsg.getMessage().length()) { - if (errMsg.isCurlError()) { - handleDisconnected(user, 3, errMsg.getMessage()); - return; - } - - if(username != "") - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - std::string("Error while sending direct message! - ") + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - else - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - std::string("Error while fetching direct messages! - ") + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - return; - } - - if(username != "") { - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - "Message delivered!", userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - return; - } - - if(!messages.size()) return; - - if(userdb[user].twitterMode == SINGLECONTACT) { - - std::string msglist = ""; - std::string msgID = getMostRecentDMID(user); - std::string maxID = msgID; - - for(int i=0 ; i < messages.size() ; i++) { - if(cmp(msgID, messages[i].getID()) == -1) { - msglist += " - " + messages[i].getSenderData().getScreenName() + ": " + messages[i].getMessage() + "\n"; - if(cmp(maxID, messages[i].getID()) == -1) maxID = messages[i].getID(); - } - } - - if(msglist.length()) handleMessage(user, adminLegacyName, msglist, ""); - updateLastDMID(user, maxID); - - } else { - - std::string msgID = getMostRecentDMID(user); - std::string maxID = msgID; - - for(int i=0 ; i < messages.size() ; i++) { - if(cmp(msgID, messages[i].getID()) == -1) { - if(userdb[user].twitterMode == MULTIPLECONTACT) - handleMessage(user, messages[i].getSenderData().getScreenName(), messages[i].getMessage(), ""); - else - handleMessage(user, adminChatRoom, messages[i].getMessage() + " - ", messages[i].getSenderData().getScreenName()); - if(cmp(maxID, messages[i].getID()) == -1) maxID = messages[i].getID(); - } - } - - if(maxID == getMostRecentDMID(user)) LOG4CXX_INFO(logger, "No new direct messages for " << user) - updateLastDMID(user, maxID); - } -} - -void TwitterPlugin::createFriendResponse(std::string &user, User &frnd, std::string &img, Error &errMsg) -{ - if(errMsg.getMessage().length()) { - if (errMsg.isCurlError()) { - handleDisconnected(user, 3, errMsg.getMessage()); - return; - } - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - return; - } - - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - std::string("You are now following ") + frnd.getScreenName(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - - userdb[user].buddies.insert(frnd.getScreenName()); - userdb[user].buddiesInfo[frnd.getScreenName()] = frnd; - userdb[user].buddiesImgs[frnd.getScreenName()] = img; - - LOG4CXX_INFO(logger, user << " - " << frnd.getScreenName() << ", " << frnd.getProfileImgURL()) - if(userdb[user].twitterMode == MULTIPLECONTACT) { - handleBuddyChanged(user, frnd.getScreenName(), frnd.getUserName(), std::vector(), pbnetwork::STATUS_ONLINE, "", SHA(img)); - } else if(userdb[user].twitterMode == CHATROOM) { - handleParticipantChanged(user, frnd.getScreenName(), adminChatRoom, 0, pbnetwork::STATUS_ONLINE); - } -} - -void TwitterPlugin::deleteFriendResponse(std::string &user, User &frnd, Error &errMsg) -{ - if(errMsg.getMessage().length()) { - if (errMsg.isCurlError()) { - handleDisconnected(user, 3, errMsg.getMessage()); - return; - } - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - return; - } - - LOG4CXX_INFO(logger, user << " - " << frnd.getScreenName() << ", " << frnd.getProfileImgURL()) - userdb[user].buddies.erase(frnd.getScreenName()); - - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - std::string("You are not following ") + frnd.getScreenName() + " anymore", userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - - if (userdb[user].twitterMode == CHATROOM) { - handleParticipantChanged(user, frnd.getScreenName(), adminLegacyName, 0, pbnetwork::STATUS_NONE); - } - - if(userdb[user].twitterMode == MULTIPLECONTACT) { - handleBuddyRemoved(user, frnd.getScreenName()); - } -} - - -void TwitterPlugin::RetweetResponse(std::string &user, Error &errMsg) -{ - if(errMsg.getMessage().length()) { - if (errMsg.isCurlError()) { - handleDisconnected(user, 3, errMsg.getMessage()); - return; - } - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - } else { - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - "Retweet successful", userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - } -} - -void TwitterPlugin::profileImageResponse(std::string &user, std::string &buddy, std::string &img, unsigned int reqID, Error &errMsg) -{ - if(errMsg.getMessage().length()) { - if (errMsg.isCurlError()) { - handleDisconnected(user, 3, errMsg.getMessage()); - return; - } - handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, - errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); - } else { - LOG4CXX_INFO(logger, user << " - Sending VCard for " << buddy) - handleVCard(user, reqID, buddy, buddy, "", img); - } -} +#include "TwitterPlugin.h" +#include "Requests/StatusUpdateRequest.h" +#include "Requests/DirectMessageRequest.h" +#include "Requests/TimelineRequest.h" +#include "Requests/FetchFriends.h" +#include "Requests/HelpMessageRequest.h" +#include "Requests/PINExchangeProcess.h" +#include "Requests/OAuthFlow.h" +#include "Requests/CreateFriendRequest.h" +#include "Requests/DestroyFriendRequest.h" +#include "Requests/RetweetRequest.h" +#include "Requests/ProfileImageRequest.h" +#include "Swiften/StringCodecs/Hexify.h" + +DEFINE_LOGGER(logger, "Twitter Backend"); + +TwitterPlugin *np = NULL; +Swift::SimpleEventLoop *loop_; // Event Loop + +const std::string OLD_APP_KEY = "PCWAdQpyyR12ezp2fVwEhw"; +const std::string OLD_APP_SECRET = "EveLmCXJIg2R7BTCpm6OWV8YyX49nI0pxnYXh7JMvDg"; + +#define abs(x) ((x)<0?-(x):(x)) +#define SHA(x) (Swift::Hexify::hexify(Swift::SHA1::getHash(Swift::createByteArray((x))))) + +//Compares two +ve intergers 'a' and 'b' represented as strings +static int cmp(std::string a, std::string b) +{ + int diff = abs((int)a.size() - (int)b.size()); + if(a.size() < b.size()) a = std::string(diff,'0') + a; + else b = std::string(diff,'0') + b; + + if(a == b) return 0; + if(a < b) return -1; + return 1; +} + + +TwitterPlugin::TwitterPlugin(Config *config, Swift::SimpleEventLoop *loop, StorageBackend *storagebackend, const std::string &host, int port) : NetworkPlugin() +{ + this->config = config; + this->storagebackend = storagebackend; + this->m_firstPing = true; + + if (CONFIG_HAS_KEY(config, "twitter.consumer_key") == false) { + consumerKey = "5mFePMiJi0KpeURONkelg"; + } + else { + consumerKey = CONFIG_STRING(config, "twitter.consumer_key"); + } + if (CONFIG_HAS_KEY(config, "twitter.consumer_secret") == false) { + consumerSecret = "YFZCDJwRhbkccXEnaYr1waCQejTJcOY8F7l5Wim3FA"; + } + else { + consumerSecret = CONFIG_STRING(config, "twitter.consumer_secret"); + } + + if (consumerSecret.empty() || consumerKey.empty()) { + LOG4CXX_ERROR(logger, "Consumer key and Consumer secret can't be empty."); + exit(1); + } + + adminLegacyName = "twitter.com"; + adminChatRoom = "#twitter"; + adminNickName = "twitter"; + adminAlias = "twitter"; + + OAUTH_KEY = "twitter_oauth_token"; + OAUTH_SECRET = "twitter_oauth_secret"; + MODE = "mode"; + + m_factories = new Swift::BoostNetworkFactories(loop); + m_conn = m_factories->getConnectionFactory()->createConnection(); + m_conn->onDataRead.connect(boost::bind(&TwitterPlugin::_handleDataRead, this, _1)); + m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(host), port)); + + tp = new ThreadPool(loop_, 10); + + tweet_timer = m_factories->getTimerFactory()->createTimer(90000); + message_timer = m_factories->getTimerFactory()->createTimer(90000); + + tweet_timer->onTick.connect(boost::bind(&TwitterPlugin::pollForTweets, this)); + message_timer->onTick.connect(boost::bind(&TwitterPlugin::pollForDirectMessages, this)); + + tweet_timer->start(); + message_timer->start(); + + LOG4CXX_INFO(logger, "Starting the plugin."); +} + +TwitterPlugin::~TwitterPlugin() +{ + delete storagebackend; + std::set::iterator it; + for(it = onlineUsers.begin() ; it != onlineUsers.end() ; it++) delete userdb[*it].sessions; + delete tp; +} + +// Send data to NetworkPlugin server +void TwitterPlugin::sendData(const std::string &string) +{ + m_conn->write(Swift::createSafeByteArray(string)); +} + +// Receive date from the NetworkPlugin server and invoke the appropirate payload handler (implement in the NetworkPlugin class) +void TwitterPlugin::_handleDataRead(boost::shared_ptr data) +{ + if (m_firstPing) { + m_firstPing = false; + // Users can join the network without registering if we allow + // one user to connect multiple IRC networks. + NetworkPlugin::PluginConfig cfg; + cfg.setNeedPassword(false); + sendConfig(cfg); + } + + std::string d(data->begin(), data->end()); + handleDataRead(d); +} + +// User trying to login into his twitter account +void TwitterPlugin::handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) +{ + if(userdb.count(user) && (userdb[user].connectionState == NEW || + userdb[user].connectionState == CONNECTED || + userdb[user].connectionState == WAITING_FOR_PIN)) { + LOG4CXX_INFO(logger, std::string("A session corresponding to ") + user + std::string(" is already active")) + return; + } + + LOG4CXX_INFO(logger, std::string("Received login request for ") + user) + initUserSession(user, legacyName, password); + handleConnected(user); + + LOG4CXX_INFO(logger, "SPECTRUM 1 USER? - " << (userdb[user].spectrum1User? "true" : "false")) + + LOG4CXX_INFO(logger, user << ": Adding Buddy " << adminLegacyName << " " << adminAlias) + handleBuddyChanged(user, adminLegacyName, adminAlias, std::vector(), pbnetwork::STATUS_ONLINE); + userdb[user].nickName = ""; + + LOG4CXX_INFO(logger, "Querying database for usersettings of " << user) + std::string key, secret; + getUserOAuthKeyAndSecret(user, key, secret); + + if(key == "" || secret == "") { + LOG4CXX_INFO(logger, "Intiating OAuth Flow for user " << user) + setTwitterMode(user, 0); + tp->runAsThread(new OAuthFlow(np, userdb[user].sessions, user, userdb[user].sessions->getTwitterUsername())); + } else { + LOG4CXX_INFO(logger, user << " is already registerd. Using the stored oauth key and secret") + LOG4CXX_INFO(logger, key << " " << secret) + pinExchangeComplete(user, key, secret); + } +} + +// User logging out +void TwitterPlugin::handleLogoutRequest(const std::string &user, const std::string &legacyName) +{ + if (userdb.count(user)) { + delete userdb[user].sessions; + userdb[user].sessions = NULL; + userdb[user].connectionState = DISCONNECTED; + } + + if(onlineUsers.count(user)) { + onlineUsers.erase(user); + } +} + +// User joining a Chatroom +void TwitterPlugin::handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password) +{ + if(room == adminChatRoom) { + LOG4CXX_INFO(logger, "Received Join Twitter room request for " << user) + + setTwitterMode(user, 2); + handleParticipantChanged(user, adminNickName, room, 0, pbnetwork::STATUS_ONLINE); + userdb[user].nickName = nickname; + handleMessage(user, adminChatRoom, "Connected to Twitter room! Populating your followers list", adminNickName); + tp->runAsThread(new FetchFriends(userdb[user].sessions, user, + boost::bind(&TwitterPlugin::populateRoster, this, _1, _2, _3, _4))); + } else { + setTwitterMode(user, 0); + LOG4CXX_ERROR(logger, "Couldn't connect to chatroom - " << room <<"! Try twitter-chatroom as the chatroom to access Twitter account") + handleMessage(user, adminLegacyName, "Couldn't connect to chatroom! Try twitter-chatroom as the chatroom to access Twitter account"); + } +} + +// User leaving a Chatroom +void TwitterPlugin::handleLeaveRoomRequest(const std::string &user, const std::string &room) +{ + if(room == adminChatRoom && onlineUsers.count(user)) { + LOG4CXX_INFO(logger, "Leaving chatroom! Switching back to default mode 0") + setTwitterMode(user, 0); + handleBuddyChanged(user, adminLegacyName, adminAlias, std::vector(), pbnetwork::STATUS_ONLINE); + } +} + +// Messages to be sent to Twitter +void TwitterPlugin::handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml, const std::string &/*id*/) +{ + + LOG4CXX_INFO(logger, "Received " << user << " --> " << legacyName << " - " << message) + + if(legacyName == adminLegacyName || legacyName == adminChatRoom) { + std::string cmd = "", data = ""; + + /** Parsing the message - Assuming message format to be [ ]***/ + int i; + for(i=0 ; irunAsThread(new PINExchangeProcess(np, userdb[user].sessions, user, data)); + else if(cmd == "#help") + tp->runAsThread(new HelpMessageRequest(user, CONFIG_STRING(config, "service.jid"), boost::bind(&TwitterPlugin::helpMessageResponse, this, _1, _2))); + else if(cmd[0] == '@') { + std::string username = cmd.substr(1); + tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, username, data, + boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4))); + } + else if(cmd == "#status") + tp->runAsThread(new StatusUpdateRequest(userdb[user].sessions, user, data, + boost::bind(&TwitterPlugin::statusUpdateResponse, this, _1, _2))); + else if(cmd == "#timeline") + tp->runAsThread(new TimelineRequest(userdb[user].sessions, user, data, "", + boost::bind(&TwitterPlugin::displayTweets, this, _1, _2, _3, _4))); + else if(cmd == "#friends") + tp->runAsThread(new FetchFriends(userdb[user].sessions, user, + boost::bind(&TwitterPlugin::displayFriendlist, this, _1, _2, _3, _4))); + else if(cmd == "#follow") + tp->runAsThread(new CreateFriendRequest(userdb[user].sessions, user, data.substr(0,data.find('@')), + boost::bind(&TwitterPlugin::createFriendResponse, this, _1, _2, _3, _4))); + else if(cmd == "#unfollow") + tp->runAsThread(new DestroyFriendRequest(userdb[user].sessions, user, data.substr(0,data.find('@')), + boost::bind(&TwitterPlugin::deleteFriendResponse, this, _1, _2, _3))); + else if(cmd == "#retweet") + tp->runAsThread(new RetweetRequest(userdb[user].sessions, user, data, + boost::bind(&TwitterPlugin::RetweetResponse, this, _1, _2))); + else if(cmd == "#mode") { + int m = 0; + m = atoi(data.c_str()); + mode prevm = userdb[user].twitterMode; + + if((mode)m == userdb[user].twitterMode) return; //If same as current mode return + if(m < 0 || m > 2) { // Invalid modes + handleMessage(user, adminLegacyName, std::string("Error! Unknown mode ") + data + ". Allowed values 0,1,2." ); + return; + } + + setTwitterMode(user, m); + if((userdb[user].twitterMode == SINGLECONTACT || userdb[user].twitterMode == CHATROOM) && prevm == MULTIPLECONTACT) clearRoster(user); + else if(userdb[user].twitterMode == MULTIPLECONTACT) + tp->runAsThread(new FetchFriends(userdb[user].sessions, user, boost::bind(&TwitterPlugin::populateRoster, this, _1, _2, _3, _4))); + + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + std::string("Changed mode to ") + data, userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + + LOG4CXX_INFO(logger, user << ": Changed mode to " << data << " <" << (userdb[user].twitterMode == CHATROOM ? adminNickName : "") << ">" ) + } + + else if(userdb[user].twitterMode == CHATROOM) { + std::string buddy = message.substr(0, message.find(":")); + if(userdb[user].buddies.count(buddy) == 0) { + tp->runAsThread(new StatusUpdateRequest(userdb[user].sessions, user, message, + boost::bind(&TwitterPlugin::statusUpdateResponse, this, _1, _2))); + } else { + data = message.substr(message.find(":")+1); //Can parse better??:P + tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, buddy, data, + boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4))); + } + } + else handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + "Unknown command! Type #help for a list of available commands.", userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + } + + else { + std::string buddy = legacyName; + if(userdb[user].twitterMode == CHATROOM) buddy = legacyName.substr(legacyName.find("/") + 1); + if(legacyName != "twitter") { + tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, buddy, message, + boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4))); + } + } +} + +void TwitterPlugin::handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector &groups) +{ + if(userdb[user].connectionState != CONNECTED) { + LOG4CXX_ERROR(logger, user << " is not connected to twitter!") + return; + } + + LOG4CXX_INFO(logger, user << " - Adding Twitter contact " << buddyName) + tp->runAsThread(new CreateFriendRequest(userdb[user].sessions, user, buddyName, + boost::bind(&TwitterPlugin::createFriendResponse, this, _1, _2, _3, _4))); +} + +void TwitterPlugin::handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector &groups) +{ + if(userdb[user].connectionState != CONNECTED) { + LOG4CXX_ERROR(logger, user << " is not connected to twitter!") + return; + } + + LOG4CXX_INFO(logger, user << " - Removing Twitter contact " << buddyName) + tp->runAsThread(new DestroyFriendRequest(userdb[user].sessions, user, buddyName, + boost::bind(&TwitterPlugin::deleteFriendResponse, this, _1, _2, _3))); +} + +void TwitterPlugin::handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) +{ + if(userdb[user].connectionState != CONNECTED) { + LOG4CXX_ERROR(logger, user << " is not connected to twitter!") + return; + } + + LOG4CXX_INFO(logger, user << " - VCardRequest for " << legacyName << ", " << userdb[user].buddiesInfo[legacyName].getProfileImgURL()) + + if(getTwitterMode(user) != SINGLECONTACT && userdb[user].buddies.count(legacyName) + && userdb[user].buddiesInfo[legacyName].getProfileImgURL().length()) { + if(userdb[user].buddiesImgs.count(legacyName) == 0) { + tp->runAsThread(new ProfileImageRequest(config, user, legacyName, userdb[user].buddiesInfo[legacyName].getProfileImgURL(), id, + boost::bind(&TwitterPlugin::profileImageResponse, this, _1, _2, _3, _4, _5))); + } + handleVCard(user, id, legacyName, legacyName, "", userdb[user].buddiesImgs[legacyName]); + } +} + +void TwitterPlugin::pollForTweets() +{ + boost::mutex::scoped_lock lock(userlock); + std::set::iterator it = onlineUsers.begin(); + while(it != onlineUsers.end()) { + std::string user = *it; + tp->runAsThread(new TimelineRequest(userdb[user].sessions, user, "", getMostRecentTweetIDUnsafe(user), + boost::bind(&TwitterPlugin::displayTweets, this, _1, _2, _3, _4))); + it++; + } + tweet_timer->start(); +} + +void TwitterPlugin::pollForDirectMessages() +{ + boost::mutex::scoped_lock lock(userlock); + std::set::iterator it = onlineUsers.begin(); + while(it != onlineUsers.end()) { + std::string user = *it; + tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, "", getMostRecentDMIDUnsafe(user), + boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4))); + it++; + } + message_timer->start(); +} + + +bool TwitterPlugin::getUserOAuthKeyAndSecret(const std::string user, std::string &key, std::string &secret) +{ + boost::mutex::scoped_lock lock(dblock); + + UserInfo info; + if(storagebackend->getUser(user, info) == false) { + LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") + return false; + } + + key="", secret=""; int type; + storagebackend->getUserSetting((long)info.id, OAUTH_KEY, type, key); + storagebackend->getUserSetting((long)info.id, OAUTH_SECRET, type, secret); + return true; +} + +bool TwitterPlugin::checkSpectrum1User(const std::string user) +{ + boost::mutex::scoped_lock lock(dblock); + + UserInfo info; + if(storagebackend->getUser(user, info) == false) { + LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") + return false; + } + + std::string first_synchronization_done = ""; + int type; + storagebackend->getUserSetting((long)info.id, "first_synchronization_done", type, first_synchronization_done); + + LOG4CXX_INFO(logger, "first_synchronization_done: " << first_synchronization_done) + + if(first_synchronization_done.length()) return true; + return false; +} + +int TwitterPlugin::getTwitterMode(const std::string user) +{ + boost::mutex::scoped_lock lock(dblock); + + UserInfo info; + if(storagebackend->getUser(user, info) == false) { + LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") + return -1; + } + + int type; int m; + std::string s_m; + storagebackend->getUserSetting((long)info.id, MODE, type, s_m); + if(s_m == "") { + s_m = "0"; + storagebackend->updateUserSetting((long)info.id, MODE, s_m); + } + m = atoi(s_m.c_str()); + return m; +} + +bool TwitterPlugin::setTwitterMode(const std::string user, int m) +{ + boost::mutex::scoped_lock lock(dblock); + + UserInfo info; + if(storagebackend->getUser(user, info) == false) { + LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") + return false; + } + + if(m < 0 || m > 2) { + LOG4CXX_ERROR(logger, "Unknown mode " << m <<". Using default mode 0") + m = 0; + } + + userdb[user].twitterMode = (mode)m; + + //int type; + std::string s_m = std::string(1,m+'0'); + LOG4CXX_ERROR(logger, "Storing mode " << m <<" for user " << user) + storagebackend->updateUserSetting((long)info.id, MODE, s_m); + return true; +} + +bool TwitterPlugin::storeUserOAuthKeyAndSecret(const std::string user, const std::string OAuthKey, const std::string OAuthSecret) +{ + + boost::mutex::scoped_lock lock(dblock); + + UserInfo info; + if(storagebackend->getUser(user, info) == false) { + LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") + return false; + } + + storagebackend->updateUserSetting((long)info.id, OAUTH_KEY, OAuthKey); + storagebackend->updateUserSetting((long)info.id, OAUTH_SECRET, OAuthSecret); + return true; +} + +void TwitterPlugin::initUserSession(const std::string user, const std::string legacyName, const std::string password) +{ + boost::mutex::scoped_lock lock(userlock); + + std::string username = legacyName; + std::string passwd = password; + LOG4CXX_INFO(logger, username + " " + passwd) + + userdb[user].sessions = new twitCurl(); + if(CONFIG_HAS_KEY(config,"proxy.server")) { + std::string ip = CONFIG_STRING(config,"proxy.server"); + + std::ostringstream out; + out << CONFIG_INT(config,"proxy.port"); + std::string port = out.str(); + + std::string puser = CONFIG_STRING(config,"proxy.user"); + std::string ppasswd = CONFIG_STRING(config,"proxy.password"); + + LOG4CXX_INFO(logger, ip << " " << port << " " << puser << " " << ppasswd) + + if(ip != "localhost" && port != "0") { + userdb[user].sessions->setProxyServerIp(ip); + userdb[user].sessions->setProxyServerPort(port); + userdb[user].sessions->setProxyUserName(puser); + userdb[user].sessions->setProxyPassword(ppasswd); + } + } + + //Check if the user is spectrum1 user + userdb[user].spectrum1User = checkSpectrum1User(user); + + userdb[user].connectionState = NEW; + userdb[user].legacyName = username; + userdb[user].sessions->setTwitterUsername(username); + userdb[user].sessions->setTwitterPassword(passwd); + + if(!userdb[user].spectrum1User) { + userdb[user].sessions->getOAuth().setConsumerKey(consumerKey); + userdb[user].sessions->getOAuth().setConsumerSecret(consumerSecret); + } else { + userdb[user].sessions->getOAuth().setConsumerKey(OLD_APP_KEY); + userdb[user].sessions->getOAuth().setConsumerSecret(OLD_APP_SECRET); + } +} + +void TwitterPlugin::OAuthFlowComplete(const std::string user, twitCurl *obj) +{ + boost::mutex::scoped_lock lock(userlock); + + delete userdb[user].sessions; + userdb[user].sessions = obj->clone(); + userdb[user].connectionState = WAITING_FOR_PIN; +} + +void TwitterPlugin::pinExchangeComplete(const std::string user, const std::string OAuthAccessTokenKey, const std::string OAuthAccessTokenSecret) +{ + boost::mutex::scoped_lock lock(userlock); + + userdb[user].sessions->getOAuth().setOAuthTokenKey( OAuthAccessTokenKey ); + userdb[user].sessions->getOAuth().setOAuthTokenSecret( OAuthAccessTokenSecret ); + userdb[user].connectionState = CONNECTED; + userdb[user].twitterMode = (mode)getTwitterMode(user); + + if(userdb[user].twitterMode == MULTIPLECONTACT) { + tp->runAsThread(new FetchFriends(userdb[user].sessions, user, boost::bind(&TwitterPlugin::populateRoster, this, _1, _2, _3, _4))); + } + + onlineUsers.insert(user); + userdb[user].mostRecentTweetID = ""; + userdb[user].mostRecentDirectMessageID = ""; +} + +void TwitterPlugin::updateLastTweetID(const std::string user, const std::string ID) +{ + boost::mutex::scoped_lock lock(userlock); + userdb[user].mostRecentTweetID = ID; + + UserInfo info; + if(storagebackend->getUser(user, info) == false) { + LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") + return; + } + + storagebackend->updateUserSetting((long)info.id, "twitter_last_tweet", ID); +} + +std::string TwitterPlugin::getMostRecentTweetIDUnsafe(const std::string user) +{ + std::string ID = ""; + if(onlineUsers.count(user)) { + ID = userdb[user].mostRecentTweetID; + if (ID.empty()) { + int type; + UserInfo info; + if(storagebackend->getUser(user, info) == false) { + LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") + } + else { + storagebackend->getUserSetting(info.id, "twitter_last_tweet", type, ID); + } + } + } + return ID; +} + +std::string TwitterPlugin::getMostRecentTweetID(const std::string user) +{ + boost::mutex::scoped_lock lock(userlock); + return getMostRecentTweetIDUnsafe(user); +} + +void TwitterPlugin::updateLastDMID(const std::string user, const std::string ID) +{ + boost::mutex::scoped_lock lock(userlock); + userdb[user].mostRecentDirectMessageID = ID; + + UserInfo info; + if(storagebackend->getUser(user, info) == false) { + LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") + return; + } + + storagebackend->updateUserSetting((long)info.id, "twitter_last_dm", ID); +} + +std::string TwitterPlugin::getMostRecentDMIDUnsafe(const std::string user) { + std::string ID = ""; + if(onlineUsers.count(user)) { + ID = userdb[user].mostRecentDirectMessageID; + if (ID.empty()) { + int type; + UserInfo info; + if(storagebackend->getUser(user, info) == false) { + LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!") + } + else { + storagebackend->getUserSetting(info.id, "twitter_last_dm", type, ID); + } + } + } + return ID; +} + +std::string TwitterPlugin::getMostRecentDMID(const std::string user) +{ + boost::mutex::scoped_lock lock(userlock); + return getMostRecentDMIDUnsafe(user); +} + +/************************************** Twitter response functions **********************************/ +void TwitterPlugin::statusUpdateResponse(std::string &user, Error &errMsg) +{ + if(errMsg.getMessage().length()) { + if (errMsg.isCurlError()) { + handleDisconnected(user, 3, errMsg.getMessage()); + return; + } + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + } else { + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + "Status Update successful", userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + } +} + +void TwitterPlugin::helpMessageResponse(std::string &user, std::string &msg) +{ + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + msg, userdb[user].twitterMode == CHATROOM ? adminNickName : ""); +} + +void TwitterPlugin::clearRoster(const std::string user) +{ + if(userdb[user].buddies.size() == 0) return; + std::set::iterator it = userdb[user].buddies.begin(); + while(it != userdb[user].buddies.end()) { + handleBuddyRemoved(user, *it); + it++; + } + userdb[user].buddies.clear(); +} + +void TwitterPlugin::populateRoster(std::string &user, std::vector &friends, std::vector &friendAvatars, Error &errMsg) +{ + if(errMsg.getMessage().length() == 0) + { + for(int i=0 ; i(), +#if HAVE_SWIFTEN_3 + pbnetwork::STATUS_ONLINE, lastTweet, Swift::byteArrayToString(cryptoProvider->getSHA1Hash(Swift::createByteArray(friendAvatars[i])))); +#else + pbnetwork::STATUS_ONLINE, lastTweet, SHA(friendAvatars[i])); +#endif + } + else if(userdb[user].twitterMode == CHATROOM) + handleParticipantChanged(user, friends[i].getScreenName(), adminChatRoom, 0, pbnetwork::STATUS_ONLINE); + + /*handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + friends[i].getScreenName() + " - " + friends[i].getLastStatus().getTweet(), + userdb[user].twitterMode == CHATROOM ? adminNickName : "");*/ + } + } else { + if (errMsg.isCurlError()) { + handleDisconnected(user, 3, errMsg.getMessage()); + return; + } + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + std::string("Error populating roster - ") + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + } + + if(userdb[user].twitterMode == CHATROOM) handleParticipantChanged(user, userdb[user].nickName, adminChatRoom, 0, pbnetwork::STATUS_ONLINE); +} + +void TwitterPlugin::displayFriendlist(std::string &user, std::vector &friends, std::vector &friendAvatars, Error &errMsg) +{ + if(errMsg.getMessage().length() == 0) + { + std::string userlist = "\n***************USER LIST****************\n"; + for(int i=0 ; i < friends.size() ; i++) { + userlist += " - " + friends[i].getUserName() + " (" + friends[i].getScreenName() + ")\n"; + } + userlist += "***************************************\n"; + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + userlist, userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + } else { + if (errMsg.isCurlError()) { + handleDisconnected(user, 3, errMsg.getMessage()); + return; + } + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + } + +} + +void TwitterPlugin::displayTweets(std::string &user, std::string &userRequested, std::vector &tweets , Error &errMsg) +{ + if(errMsg.getMessage().length() == 0) { + std::map lastTweet; + std::map::iterator it; + + for(int i = tweets.size() - 1 ; i >= 0 ; i--) { + if(userdb[user].twitterMode != CHATROOM) { + std::string m = " - " + tweets[i].getUserData().getScreenName() + ": " + tweets[i].getTweet() + " (MsgId: " + (tweets[i].getRetweetID().empty() ? tweets[i].getID() : tweets[i].getRetweetID()) + ")\n"; + handleMessage(user, adminLegacyName, m, "", "", tweets[i].getCreationTime(), true); + + std::string scrname = tweets[i].getUserData().getScreenName(); + if(lastTweet.count(scrname) == 0 || cmp(tweets[lastTweet[scrname]].getID(), tweets[i].getID()) <= 0) lastTweet[scrname] = i; + + } else { + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + tweets[i].getTweet() + " (MsgId: " + (tweets[i].getRetweetID().empty() ? tweets[i].getID() : tweets[i].getRetweetID()) + ")", tweets[i].getUserData().getScreenName(), "", tweets[i].getCreationTime(), true); + } + } + + if(userdb[user].twitterMode == MULTIPLECONTACT) { + //Set as status user's last tweet + for(it=lastTweet.begin() ; it!=lastTweet.end() ; it++) { + int t = it->second; + handleBuddyChanged(user, tweets[t].getUserData().getScreenName(), tweets[t].getUserData().getUserName(), + std::vector(), pbnetwork::STATUS_ONLINE, tweets[t].getTweet()); + } + } + + if((userRequested == "" || userRequested == user) && tweets.size()) { + std::string tweetID = getMostRecentTweetID(user); + if(tweetID != tweets[0].getID()) updateLastTweetID(user, tweets[0].getID()); + } + + } else { + if (errMsg.isCurlError()) { + handleDisconnected(user, 3, errMsg.getMessage()); + return; + } + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + } +} + +void TwitterPlugin::directMessageResponse(std::string &user, std::string &username, std::vector &messages, Error &errMsg) +{ + if(errMsg.getCode() == "93") //Permission Denied + return; + + if(errMsg.getMessage().length()) { + if (errMsg.isCurlError()) { + handleDisconnected(user, 3, errMsg.getMessage()); + return; + } + + if(username != "") + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + std::string("Error while sending direct message! - ") + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + else + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + std::string("Error while fetching direct messages! - ") + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + return; + } + + if(username != "") { + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + "Message delivered!", userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + return; + } + + if(!messages.size()) return; + + if(userdb[user].twitterMode == SINGLECONTACT) { + + std::string msglist = ""; + std::string msgID = getMostRecentDMID(user); + std::string maxID = msgID; + + for(int i=0 ; i < messages.size() ; i++) { + if(cmp(msgID, messages[i].getID()) == -1) { + msglist += " - " + messages[i].getSenderData().getScreenName() + ": " + messages[i].getMessage() + "\n"; + if(cmp(maxID, messages[i].getID()) == -1) maxID = messages[i].getID(); + } + } + + if(msglist.length()) handleMessage(user, adminLegacyName, msglist, ""); + updateLastDMID(user, maxID); + + } else { + + std::string msgID = getMostRecentDMID(user); + std::string maxID = msgID; + + for(int i=0 ; i < messages.size() ; i++) { + if(cmp(msgID, messages[i].getID()) == -1) { + if(userdb[user].twitterMode == MULTIPLECONTACT) + handleMessage(user, messages[i].getSenderData().getScreenName(), messages[i].getMessage(), ""); + else + handleMessage(user, adminChatRoom, messages[i].getMessage() + " - ", messages[i].getSenderData().getScreenName()); + if(cmp(maxID, messages[i].getID()) == -1) maxID = messages[i].getID(); + } + } + + if(maxID == getMostRecentDMID(user)) LOG4CXX_INFO(logger, "No new direct messages for " << user) + updateLastDMID(user, maxID); + } +} + +void TwitterPlugin::createFriendResponse(std::string &user, User &frnd, std::string &img, Error &errMsg) +{ + if(errMsg.getMessage().length()) { + if (errMsg.isCurlError()) { + handleDisconnected(user, 3, errMsg.getMessage()); + return; + } + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + return; + } + + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + std::string("You are now following ") + frnd.getScreenName(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + + userdb[user].buddies.insert(frnd.getScreenName()); + userdb[user].buddiesInfo[frnd.getScreenName()] = frnd; + userdb[user].buddiesImgs[frnd.getScreenName()] = img; + + LOG4CXX_INFO(logger, user << " - " << frnd.getScreenName() << ", " << frnd.getProfileImgURL()) + if(userdb[user].twitterMode == MULTIPLECONTACT) { +#if HAVE_SWIFTEN_3 + handleBuddyChanged(user, frnd.getScreenName(), frnd.getUserName(), std::vector(), pbnetwork::STATUS_ONLINE, "", Swift::byteArrayToString(cryptoProvider->getSHA1Hash(Swift::createByteArray(img)))); +#else + handleBuddyChanged(user, frnd.getScreenName(), frnd.getUserName(), std::vector(), pbnetwork::STATUS_ONLINE, "", SHA(img)); +#endif + } else if(userdb[user].twitterMode == CHATROOM) { + handleParticipantChanged(user, frnd.getScreenName(), adminChatRoom, 0, pbnetwork::STATUS_ONLINE); + } +} + +void TwitterPlugin::deleteFriendResponse(std::string &user, User &frnd, Error &errMsg) +{ + if(errMsg.getMessage().length()) { + if (errMsg.isCurlError()) { + handleDisconnected(user, 3, errMsg.getMessage()); + return; + } + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + return; + } + + LOG4CXX_INFO(logger, user << " - " << frnd.getScreenName() << ", " << frnd.getProfileImgURL()) + userdb[user].buddies.erase(frnd.getScreenName()); + + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + std::string("You are not following ") + frnd.getScreenName() + " anymore", userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + + if (userdb[user].twitterMode == CHATROOM) { + handleParticipantChanged(user, frnd.getScreenName(), adminLegacyName, 0, pbnetwork::STATUS_NONE); + } + + if(userdb[user].twitterMode == MULTIPLECONTACT) { + handleBuddyRemoved(user, frnd.getScreenName()); + } +} + + +void TwitterPlugin::RetweetResponse(std::string &user, Error &errMsg) +{ + if(errMsg.getMessage().length()) { + if (errMsg.isCurlError()) { + handleDisconnected(user, 3, errMsg.getMessage()); + return; + } + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + } else { + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + "Retweet successful", userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + } +} + +void TwitterPlugin::profileImageResponse(std::string &user, std::string &buddy, std::string &img, unsigned int reqID, Error &errMsg) +{ + if(errMsg.getMessage().length()) { + if (errMsg.isCurlError()) { + handleDisconnected(user, 3, errMsg.getMessage()); + return; + } + handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : ""); + } else { + LOG4CXX_INFO(logger, user << " - Sending VCard for " << buddy) + handleVCard(user, reqID, buddy, buddy, "", img); + } +} diff --git a/backends/twitter/TwitterPlugin.h b/backends/twitter/TwitterPlugin.h index 9f4bd2c5..0854852e 100644 --- a/backends/twitter/TwitterPlugin.h +++ b/backends/twitter/TwitterPlugin.h @@ -1,181 +1,189 @@ -#ifndef TWITTER_PLUGIN -#define TWITTER_PLUGIN - -#include "transport/config.h" -#include "transport/networkplugin.h" -#include "transport/logging.h" -#include "transport/sqlite3backend.h" -#include "transport/mysqlbackend.h" -#include "transport/pqxxbackend.h" -#include "transport/storagebackend.h" -#include "transport/threadpool.h" - -#include "Swiften/Swiften.h" -#ifndef _WIN32 -#include "unistd.h" -#include "signal.h" -#include "sys/wait.h" -#include "sys/signal.h" -#endif -#include -#include -#include -#include - -#include "twitcurl.h" -#include "TwitterResponseParser.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "Swiften/StringCodecs/SHA1.h" - -using namespace boost::filesystem; -using namespace boost::program_options; -using namespace Transport; - -#define STR(x) (std::string("(") + x.from + ", " + x.to + ", " + x.message + ")") - -class TwitterPlugin; -extern TwitterPlugin *np; -extern Swift::SimpleEventLoop *loop_; // Event Loop - - -class TwitterPlugin : public NetworkPlugin { - public: - Swift::BoostNetworkFactories *m_factories; - Swift::BoostIOServiceThread m_boostIOServiceThread; - boost::shared_ptr m_conn; - Swift::Timer::ref tweet_timer; - Swift::Timer::ref message_timer; - StorageBackend *storagebackend; - - TwitterPlugin(Config *config, Swift::SimpleEventLoop *loop, StorageBackend *storagebackend, const std::string &host, int port); - ~TwitterPlugin(); - - // Send data to NetworkPlugin server - void sendData(const std::string &string); - - // Receive date from the NetworkPlugin server and invoke the appropirate payload handler (implement in the NetworkPlugin class) - void _handleDataRead(boost::shared_ptr data); - - // User trying to login into his twitter account - void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password); - - // User logging out - void handleLogoutRequest(const std::string &user, const std::string &legacyName); - - void handleJoinRoomRequest(const std::string &/*user*/, const std::string &/*room*/, const std::string &/*nickname*/, const std::string &/*pasword*/); - - void handleLeaveRoomRequest(const std::string &/*user*/, const std::string &/*room*/); - - void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml = "", const std::string &id = ""); - - void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector &groups); - - void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector &groups); - - void handleVCardRequest(const std::string &/*user*/, const std::string &/*legacyName*/, unsigned int /*id*/); - - void pollForTweets(); - - void pollForDirectMessages(); - - bool getUserOAuthKeyAndSecret(const std::string user, std::string &key, std::string &secret); - - bool checkSpectrum1User(const std::string user); - - bool storeUserOAuthKeyAndSecret(const std::string user, const std::string OAuthKey, const std::string OAuthSecret); - - void initUserSession(const std::string user, const std::string legacyName, const std::string password); - - void OAuthFlowComplete(const std::string user, twitCurl *obj); - - void pinExchangeComplete(const std::string user, const std::string OAuthAccessTokenKey, const std::string OAuthAccessTokenSecret); - - void updateLastTweetID(const std::string user, const std::string ID); - - std::string getMostRecentTweetID(const std::string user); - - void updateLastDMID(const std::string user, const std::string ID); - - std::string getMostRecentDMID(const std::string user); - - void clearRoster(const std::string user); - - int getTwitterMode(const std::string user); - - bool setTwitterMode(const std::string user, int m); - - /****************** Twitter response handlers **************************************/ - void statusUpdateResponse(std::string &user, Error &errMsg); - - void helpMessageResponse(std::string &user, std::string &msg); - - void populateRoster(std::string &user, std::vector &friends, std::vector &friendAvatars, Error &errMsg); - - void displayFriendlist(std::string &user, std::vector &friends, std::vector &friendAvatars, Error &errMsg); - - void displayTweets(std::string &user, std::string &userRequested, std::vector &tweets , Error &errMsg); - - void directMessageResponse(std::string &user, std::string &username, std::vector &messages, Error &errMsg); - - void createFriendResponse(std::string &user, User &frnd, std::string &img, Error &errMsg); - - void deleteFriendResponse(std::string &user, User &frnd, Error &errMsg); - - void RetweetResponse(std::string &user, Error &errMsg); - - void profileImageResponse(std::string &user, std::string &buddy, std::string &img, unsigned int reqID, Error &errMsg); - /***********************************************************************************/ - - private: - std::string getMostRecentTweetIDUnsafe(const std::string user); - std::string getMostRecentDMIDUnsafe(const std::string user); - - enum status {NEW, WAITING_FOR_PIN, CONNECTED, DISCONNECTED}; - enum mode {SINGLECONTACT, MULTIPLECONTACT, CHATROOM}; - - Config *config; - std::string adminLegacyName; - std::string adminChatRoom; - std::string adminNickName; - std::string adminAlias; - - std::string consumerKey; - std::string consumerSecret; - std::string OAUTH_KEY; - std::string OAUTH_SECRET; - std::string MODE; - - boost::mutex dblock, userlock; - - ThreadPool *tp; - std::set onlineUsers; - struct UserData - { - std::string legacyName; - bool spectrum1User; //Legacy support - User userTwitterObj; - std::string userImg; - twitCurl* sessions; - status connectionState; - std::string mostRecentTweetID; - std::string mostRecentDirectMessageID; - std::string nickName; - std::set buddies; - std::map buddiesInfo; - std::map buddiesImgs; - mode twitterMode; - - UserData() { sessions = NULL; } - }; - std::map userdb; - bool m_firstPing; -}; -#endif +#ifndef TWITTER_PLUGIN +#define TWITTER_PLUGIN + +#include "transport/config.h" +#include "transport/networkplugin.h" +#include "transport/logging.h" +#include "transport/sqlite3backend.h" +#include "transport/mysqlbackend.h" +#include "transport/pqxxbackend.h" +#include "transport/storagebackend.h" +#include "transport/threadpool.h" + +#include "Swiften/Swiften.h" +#ifndef _WIN32 +#include "unistd.h" +#include "signal.h" +#include "sys/wait.h" +#include "sys/signal.h" +#endif +#include +#include +#include +#include + +#include "twitcurl.h" +#include "TwitterResponseParser.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#define HAVE_SWIFTEN_3 SWIFTEN_VERSION >= 0x030000 +#if HAVE_SWIFTEN_3 +#include +#include +#else +#include "Swiften/StringCodecs/SHA1.h" +#endif +using namespace boost::filesystem; +using namespace boost::program_options; +using namespace Transport; + +#define STR(x) (std::string("(") + x.from + ", " + x.to + ", " + x.message + ")") + +class TwitterPlugin; +extern TwitterPlugin *np; +extern Swift::SimpleEventLoop *loop_; // Event Loop + + +class TwitterPlugin : public NetworkPlugin { + public: + Swift::BoostNetworkFactories *m_factories; + Swift::BoostIOServiceThread m_boostIOServiceThread; + boost::shared_ptr m_conn; +#if HAVE_SWIFTEN_3 + boost::shared_ptr cryptoProvider; +#endif + Swift::Timer::ref tweet_timer; + Swift::Timer::ref message_timer; + StorageBackend *storagebackend; + + TwitterPlugin(Config *config, Swift::SimpleEventLoop *loop, StorageBackend *storagebackend, const std::string &host, int port); + ~TwitterPlugin(); + + // Send data to NetworkPlugin server + void sendData(const std::string &string); + + // Receive date from the NetworkPlugin server and invoke the appropirate payload handler (implement in the NetworkPlugin class) + void _handleDataRead(boost::shared_ptr data); + + // User trying to login into his twitter account + void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password); + + // User logging out + void handleLogoutRequest(const std::string &user, const std::string &legacyName); + + void handleJoinRoomRequest(const std::string &/*user*/, const std::string &/*room*/, const std::string &/*nickname*/, const std::string &/*pasword*/); + + void handleLeaveRoomRequest(const std::string &/*user*/, const std::string &/*room*/); + + void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml = "", const std::string &id = ""); + + void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector &groups); + + void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector &groups); + + void handleVCardRequest(const std::string &/*user*/, const std::string &/*legacyName*/, unsigned int /*id*/); + + void pollForTweets(); + + void pollForDirectMessages(); + + bool getUserOAuthKeyAndSecret(const std::string user, std::string &key, std::string &secret); + + bool checkSpectrum1User(const std::string user); + + bool storeUserOAuthKeyAndSecret(const std::string user, const std::string OAuthKey, const std::string OAuthSecret); + + void initUserSession(const std::string user, const std::string legacyName, const std::string password); + + void OAuthFlowComplete(const std::string user, twitCurl *obj); + + void pinExchangeComplete(const std::string user, const std::string OAuthAccessTokenKey, const std::string OAuthAccessTokenSecret); + + void updateLastTweetID(const std::string user, const std::string ID); + + std::string getMostRecentTweetID(const std::string user); + + void updateLastDMID(const std::string user, const std::string ID); + + std::string getMostRecentDMID(const std::string user); + + void clearRoster(const std::string user); + + int getTwitterMode(const std::string user); + + bool setTwitterMode(const std::string user, int m); + + /****************** Twitter response handlers **************************************/ + void statusUpdateResponse(std::string &user, Error &errMsg); + + void helpMessageResponse(std::string &user, std::string &msg); + + void populateRoster(std::string &user, std::vector &friends, std::vector &friendAvatars, Error &errMsg); + + void displayFriendlist(std::string &user, std::vector &friends, std::vector &friendAvatars, Error &errMsg); + + void displayTweets(std::string &user, std::string &userRequested, std::vector &tweets , Error &errMsg); + + void directMessageResponse(std::string &user, std::string &username, std::vector &messages, Error &errMsg); + + void createFriendResponse(std::string &user, User &frnd, std::string &img, Error &errMsg); + + void deleteFriendResponse(std::string &user, User &frnd, Error &errMsg); + + void RetweetResponse(std::string &user, Error &errMsg); + + void profileImageResponse(std::string &user, std::string &buddy, std::string &img, unsigned int reqID, Error &errMsg); + /***********************************************************************************/ + + private: + std::string getMostRecentTweetIDUnsafe(const std::string user); + std::string getMostRecentDMIDUnsafe(const std::string user); + + enum status {NEW, WAITING_FOR_PIN, CONNECTED, DISCONNECTED}; + enum mode {SINGLECONTACT, MULTIPLECONTACT, CHATROOM}; + + Config *config; + std::string adminLegacyName; + std::string adminChatRoom; + std::string adminNickName; + std::string adminAlias; + + std::string consumerKey; + std::string consumerSecret; + std::string OAUTH_KEY; + std::string OAUTH_SECRET; + std::string MODE; + + boost::mutex dblock, userlock; + + ThreadPool *tp; + std::set onlineUsers; + struct UserData + { + std::string legacyName; + bool spectrum1User; //Legacy support + User userTwitterObj; + std::string userImg; + twitCurl* sessions; + status connectionState; + std::string mostRecentTweetID; + std::string mostRecentDirectMessageID; + std::string nickName; + std::set buddies; + std::map buddiesInfo; + std::map buddiesImgs; + mode twitterMode; + + UserData() { sessions = NULL; } + }; + std::map userdb; + bool m_firstPing; +}; +#endif diff --git a/backends/twitter/libtwitcurl/twitcurl.cpp b/backends/twitter/libtwitcurl/twitcurl.cpp index 9ad529a7..bce135c6 100644 --- a/backends/twitter/libtwitcurl/twitcurl.cpp +++ b/backends/twitter/libtwitcurl/twitcurl.cpp @@ -1,4 +1,5 @@ #define NOMINMAX +#include #include #include "twitcurlurls.h" #include "twitcurl.h" diff --git a/cmake_modules/SwiftenConfig.cmake b/cmake_modules/SwiftenConfig.cmake index e79bb785..cd7dbaf9 100644 --- a/cmake_modules/SwiftenConfig.cmake +++ b/cmake_modules/SwiftenConfig.cmake @@ -1,38 +1,38 @@ -FIND_LIBRARY(SWIFTEN_LIBRARY NAMES Swiften HINTS ../lib) -FIND_PATH(SWIFTEN_INCLUDE_DIR NAMES "Swiften/Swiften.h" PATH_SUFFIXES libSwiften Swiften HINTS ../include) - -if( SWIFTEN_LIBRARY AND SWIFTEN_INCLUDE_DIR ) - find_program(SWIFTEN_CONFIG_EXECUTABLE NAMES swiften-config DOC "swiften-config executable" HINTS ../bin) - set( SWIFTEN_CFLAGS "" ) - if (SWIFTEN_CONFIG_EXECUTABLE) - execute_process( - COMMAND ${SWIFTEN_CONFIG_EXECUTABLE} --libs - OUTPUT_VARIABLE SWIFTEN_LIB) - string(REGEX REPLACE "[\r\n]" " " SWIFTEN_LIB ${SWIFTEN_LIB}) - string(REGEX REPLACE " +$" "" SWIFTEN_LIB ${SWIFTEN_LIB}) - string(REGEX REPLACE " " ";" SWIFTEN_LIB ${SWIFTEN_LIB}) - set(SWIFTEN_LIBRARY "") - foreach(f ${SWIFTEN_LIB}) - STRING(SUBSTRING ${f} 0 2 f_out) - STRING(COMPARE EQUAL ${f_out} "/L" IS_PATH) - if(${IS_PATH}) - string(REGEX REPLACE "/LIBPATH:" "" f_replaced "${f}") - message("Added link directory: ${f_replaced}") - link_directories(${f_replaced}) - else() - list(APPEND SWIFTEN_LIBRARY ${f}) - endif() - endforeach(f) - set( SWIFTEN_FOUND 1 ) - else() - message( STATUS "Could NOT find swiften-config" ) - endif() - - if (SWIFTEN_FOUND) - set( SWIFTEN_INCLUDE_DIR ${SWIFTEN_INCLUDE_DIR} ) - message( STATUS "Found libSwiften: ${SWIFTEN_LIBRARY}, ${SWIFTEN_INCLUDE_DIR}") - endif() - -else( SWIFTEN_LIBRARY AND SWIFTEN_INCLUDE_DIR ) - message( STATUS "Could NOT find libSwiften" ) -endif( SWIFTEN_LIBRARY AND SWIFTEN_INCLUDE_DIR ) +FIND_LIBRARY(SWIFTEN_LIBRARY NAMES Swiften Swiften3 HINTS ../lib) +FIND_PATH(SWIFTEN_INCLUDE_DIR NAMES "Swiften/Swiften.h" PATH_SUFFIXES libSwiften Swiften HINTS ../include) + +if( SWIFTEN_LIBRARY AND SWIFTEN_INCLUDE_DIR ) + find_program(SWIFTEN_CONFIG_EXECUTABLE NAMES swiften-config DOC "swiften-config executable" HINTS ../bin) + set( SWIFTEN_CFLAGS "" ) + if (SWIFTEN_CONFIG_EXECUTABLE) + execute_process( + COMMAND ${SWIFTEN_CONFIG_EXECUTABLE} --libs + OUTPUT_VARIABLE SWIFTEN_LIB) + string(REGEX REPLACE "[\r\n]" " " SWIFTEN_LIB ${SWIFTEN_LIB}) + string(REGEX REPLACE " +$" "" SWIFTEN_LIB ${SWIFTEN_LIB}) + string(REGEX REPLACE " " ";" SWIFTEN_LIB ${SWIFTEN_LIB}) + set(SWIFTEN_LIBRARY "") + foreach(f ${SWIFTEN_LIB}) + STRING(SUBSTRING ${f} 0 2 f_out) + STRING(COMPARE EQUAL ${f_out} "/L" IS_PATH) + if(${IS_PATH}) + string(REGEX REPLACE "/LIBPATH:" "" f_replaced "${f}") + message("Added link directory: ${f_replaced}") + link_directories(${f_replaced}) + else() + list(APPEND SWIFTEN_LIBRARY ${f}) + endif() + endforeach(f) + set( SWIFTEN_FOUND 1 ) + else() + message( STATUS "Could NOT find swiften-config" ) + endif() + + if (SWIFTEN_FOUND) + set( SWIFTEN_INCLUDE_DIR ${SWIFTEN_INCLUDE_DIR} ) + message( STATUS "Found libSwiften: ${SWIFTEN_LIBRARY}, ${SWIFTEN_INCLUDE_DIR}") + endif() + +else( SWIFTEN_LIBRARY AND SWIFTEN_INCLUDE_DIR ) + message( STATUS "Could NOT find libSwiften" ) +endif( SWIFTEN_LIBRARY AND SWIFTEN_INCLUDE_DIR ) diff --git a/include/Swiften/Elements/PubSubItem.cpp b/include/Swiften/Elements/PubSubItem.cpp deleted file mode 100644 index 730ad89e..00000000 --- a/include/Swiften/Elements/PubSubItem.cpp +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include - -namespace Swift { - -PubSubItem::PubSubItem() { -} - -} diff --git a/include/Swiften/Elements/PubSubItem.h b/include/Swiften/Elements/PubSubItem.h deleted file mode 100644 index f0b3ef70..00000000 --- a/include/Swiften/Elements/PubSubItem.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include - -#include -#include - -namespace Swift { - class PubSubItem : public Payload { - public: - PubSubItem(); - - void addPayload(boost::shared_ptr payload) { - payloads.push_back(payload); - } - - const std::vector > getPayloads() const { - return payloads; - } - - template - const std::vector > getPayloads() const { - std::vector > matched_payloads; - for (std::vector >::const_iterator i = payloads.begin(); i != payloads.end(); ++i) { - boost::shared_ptr result = boost::dynamic_pointer_cast(*i); - if (result) { - matched_payloads.push_back(result); - } - } - - return matched_payloads; - - } - - template - const boost::shared_ptr getPayload() const { - boost::shared_ptr result; - for (std::vector >::const_iterator i = payloads.begin(); i != payloads.end(); ++i) { - result = boost::dynamic_pointer_cast(*i); - if (result) { - return result; - } - } - - return result; - } - - const std::string& getId() const { return id; } - - void setId(const std::string& id) { - this->id = id; - } - - private: - std::vector > payloads; - std::string id; - }; -} diff --git a/include/Swiften/Elements/PubSubPayload.cpp b/include/Swiften/Elements/PubSubPayload.cpp deleted file mode 100644 index 663e8eb7..00000000 --- a/include/Swiften/Elements/PubSubPayload.cpp +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include - -namespace Swift { - -PubSubPayload::PubSubPayload() { -} - -} diff --git a/include/Swiften/Elements/PubSubPayload.h b/include/Swiften/Elements/PubSubPayload.h deleted file mode 100644 index 46c8b82e..00000000 --- a/include/Swiften/Elements/PubSubPayload.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2012 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include - -#include -#include - -namespace Swift { - class PubSubPayload : public Payload { - public: - PubSubPayload(); - - void addPayload(boost::shared_ptr payload) { - payloads.push_back(payload); - } - - const std::vector > getPayloads() const { - return payloads; - } - - template - const std::vector > getPayloads() const { - std::vector > matched_payloads; - for (std::vector >::const_iterator i = payloads.begin(); i != payloads.end(); ++i) { - boost::shared_ptr result = boost::dynamic_pointer_cast(*i); - if (result) { - matched_payloads.push_back(result); - } - } - - return matched_payloads; - - } - - template - const boost::shared_ptr getPayload() const { - boost::shared_ptr result; - for (std::vector >::const_iterator i = payloads.begin(); i != payloads.end(); ++i) { - result = boost::dynamic_pointer_cast(*i); - if (result) { - return result; - } - } - - return result; - } - - private: - std::vector > payloads; - }; -} diff --git a/include/Swiften/Elements/PubSubPublishPayload.cpp b/include/Swiften/Elements/PubSubPublishPayload.cpp deleted file mode 100644 index dec34e92..00000000 --- a/include/Swiften/Elements/PubSubPublishPayload.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include - -namespace Swift { - -PubSubPublishPayload::PubSubPublishPayload(const std::string &node) : - node(node) { - - } - -} diff --git a/include/Swiften/Elements/PubSubPublishPayload.h b/include/Swiften/Elements/PubSubPublishPayload.h deleted file mode 100644 index 930cca6b..00000000 --- a/include/Swiften/Elements/PubSubPublishPayload.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include - -#include -#include -#include -#include - -namespace Swift { - class PubSubPublishPayload : public Payload { - public: - enum Type { None, Pending, Subscribed, Unconfigured }; - - PubSubPublishPayload(const std::string &node = ""); - - void setNode(const std::string &node) { - this->node = node; - } - - const std::string &getNode() const { - return node; - } - - void addItem(const boost::shared_ptr &item) { - items.push_back(item); - } - - const std::vector > &getItems() const { - return items; - } - - private: - std::string node; - std::vector > items; - }; -} diff --git a/include/Swiften/Elements/PubSubSubscribePayload.cpp b/include/Swiften/Elements/PubSubSubscribePayload.cpp deleted file mode 100644 index 24aa6fd6..00000000 --- a/include/Swiften/Elements/PubSubSubscribePayload.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include - -namespace Swift { - -PubSubSubscribePayload::PubSubSubscribePayload(const JID &jid, const std::string &node) : - jid(jid), node(node) { - - } - -} diff --git a/include/Swiften/Elements/PubSubSubscribePayload.h b/include/Swiften/Elements/PubSubSubscribePayload.h deleted file mode 100644 index f2b011a2..00000000 --- a/include/Swiften/Elements/PubSubSubscribePayload.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include - -#include -#include -#include - -namespace Swift { - class PubSubSubscribePayload : public Payload { - public: - PubSubSubscribePayload(const JID &jid = JID(), const std::string &node = ""); - - void setJID(const JID &jid) { - this->jid = jid; - } - - const JID &getJID() const { - return jid; - } - - void setNode(const std::string &node) { - this->node = node; - } - - const std::string &getNode() const { - return node; - } - - private: - JID jid; - std::string node; - }; -} diff --git a/include/Swiften/Elements/PubSubSubscriptionPayload.cpp b/include/Swiften/Elements/PubSubSubscriptionPayload.cpp deleted file mode 100644 index 293505ec..00000000 --- a/include/Swiften/Elements/PubSubSubscriptionPayload.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include - -namespace Swift { - -PubSubSubscriptionPayload::PubSubSubscriptionPayload(const JID &jid, const std::string &node) : - jid(jid), node(node), type(None) { - - } - -} diff --git a/include/Swiften/Elements/PubSubSubscriptionPayload.h b/include/Swiften/Elements/PubSubSubscriptionPayload.h deleted file mode 100644 index c404e6ff..00000000 --- a/include/Swiften/Elements/PubSubSubscriptionPayload.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include - -#include -#include -#include - -namespace Swift { - class PubSubSubscriptionPayload : public Payload { - public: - enum Type { None, Pending, Subscribed, Unconfigured }; - - PubSubSubscriptionPayload(const JID &jid = JID(), const std::string &node = ""); - - void setJID(const JID &jid) { - this->jid = jid; - } - - const JID &getJID() const { - return jid; - } - - void setNode(const std::string &node) { - this->node = node; - } - - const std::string &getNode() const { - return node; - } - - void setId(const std::string &id) { - this->id = id; - } - - const std::string &getId() const { - return id; - } - - void setType(const Type &type) { - this->type = type; - } - - const Type &getType() const { - return type; - } - - private: - JID jid; - std::string node; - std::string id; - Type type; - }; -} diff --git a/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.cpp b/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.cpp index ac06ab75..a5d46a6b 100644 --- a/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.cpp +++ b/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.cpp @@ -1,108 +1,110 @@ -/* - * Copyright (c) 2011 Tobias Markmann - * Licensed under the simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include "CombinedOutgoingFileTransferManager.h" - -#include - -#include -#include "Swiften/Disco/EntityCapsProvider.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace Swift { - -CombinedOutgoingFileTransferManager::CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, Transport::PresenceOracle *presOracle, SOCKS5BytestreamServer *bytestreamServer) : jsManager(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy), presenceOracle(presOracle), bytestreamServer(bytestreamServer) { - idGenerator = new IDGenerator(); -} - -CombinedOutgoingFileTransferManager::~CombinedOutgoingFileTransferManager() { - delete idGenerator; -} - -boost::shared_ptr CombinedOutgoingFileTransferManager::createOutgoingFileTransfer(const JID& from, const JID& receipient, boost::shared_ptr readBytestream, const StreamInitiationFileInfo& fileInfo) { - // check if receipient support Jingle FT - boost::optional fullJID = highestPriorityJIDSupportingJingle(receipient); - if (!fullJID.is_initialized()) { - fullJID = highestPriorityJIDSupportingSI(receipient); - } - else { - JingleSessionImpl::ref jingleSession = boost::make_shared(from, receipient, idGenerator->generateID(), iqRouter); - - //jsManager->getSession(receipient, idGenerator->generateID()); - assert(jingleSession); - jsManager->registerOutgoingSession(from, jingleSession); - boost::shared_ptr jingleFT = boost::shared_ptr(new OutgoingJingleFileTransfer(jingleSession, remoteFactory, localFactory, iqRouter, idGenerator, from, receipient, readBytestream, fileInfo, bytestreamRegistry, bytestreamProxy)); - return jingleFT; - } - - if (!fullJID.is_initialized()) { - return boost::shared_ptr(); - } - - // otherwise try SI - boost::shared_ptr jingleFT = boost::shared_ptr(new MyOutgoingSIFileTransfer(idGenerator->generateID(), from, fullJID.get(), fileInfo.getName(), fileInfo.getSize(), fileInfo.getDescription(), readBytestream, iqRouter, bytestreamServer, bytestreamRegistry)); - // else fail - - return jingleFT; -} - -boost::optional CombinedOutgoingFileTransferManager::highestPriorityJIDSupportingJingle(const JID& bareJID) { - JID fullReceipientJID; - int priority = INT_MIN; - - //getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11 - std::vector presences = presenceOracle->getAllPresence(bareJID); - - //iterate over them - foreach(Presence::ref pres, presences) { - if (pres->getPriority() > priority) { - // look up caps from the jid - DiscoInfo::ref info = capsProvider->getCaps(pres->getFrom()); - if (info && info->hasFeature(DiscoInfo::JingleFeature) && info->hasFeature(DiscoInfo::JingleFTFeature) && - info->hasFeature(DiscoInfo::JingleTransportsIBBFeature)) { - - priority = pres->getPriority(); - fullReceipientJID = pres->getFrom(); - } - } - } - - return fullReceipientJID.isValid() ? boost::optional(fullReceipientJID) : boost::optional(); -} - -boost::optional CombinedOutgoingFileTransferManager::highestPriorityJIDSupportingSI(const JID& bareJID) { - JID fullReceipientJID; - int priority = INT_MIN; - - //getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11 - std::vector presences = presenceOracle->getAllPresence(bareJID); - - //iterate over them - foreach(Presence::ref pres, presences) { - if (pres->getPriority() > priority) { - // look up caps from the jid - DiscoInfo::ref info = capsProvider->getCaps(pres->getFrom()); - if (info && info->hasFeature("http://jabber.org/protocol/si/profile/file-transfer")) { - - priority = pres->getPriority(); - fullReceipientJID = pres->getFrom(); - } - } - } - - return fullReceipientJID.isValid() ? boost::optional(fullReceipientJID) : boost::optional(); -} - -} +/* + * Copyright (c) 2011 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "CombinedOutgoingFileTransferManager.h" + +#include + +#include +#include "Swiften/Disco/EntityCapsProvider.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace Swift { + +CombinedOutgoingFileTransferManager::CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, Transport::PresenceOracle *presOracle, SOCKS5BytestreamServer *bytestreamServer) : jsManager(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy), presenceOracle(presOracle), bytestreamServer(bytestreamServer) { + idGenerator = new IDGenerator(); +} + +CombinedOutgoingFileTransferManager::~CombinedOutgoingFileTransferManager() { + delete idGenerator; +} + +boost::shared_ptr CombinedOutgoingFileTransferManager::createOutgoingFileTransfer(const JID& from, const JID& receipient, boost::shared_ptr readBytestream, const StreamInitiationFileInfo& fileInfo) { + // check if receipient support Jingle FT + boost::optional fullJID = highestPriorityJIDSupportingJingle(receipient); + if (!fullJID.is_initialized()) { + fullJID = highestPriorityJIDSupportingSI(receipient); + } + else { + JingleSessionImpl::ref jingleSession = boost::make_shared(from, receipient, idGenerator->generateID(), iqRouter); + + //jsManager->getSession(receipient, idGenerator->generateID()); + assert(jingleSession); + jsManager->registerOutgoingSession(from, jingleSession); +#if !HAVE_SWIFTEN_3 + boost::shared_ptr jingleFT = boost::shared_ptr(new OutgoingJingleFileTransfer(jingleSession, remoteFactory, localFactory, iqRouter, idGenerator, from, receipient, readBytestream, fileInfo, bytestreamRegistry, bytestreamProxy)); + return jingleFT; +#endif + } + + if (!fullJID.is_initialized()) { + return boost::shared_ptr(); + } + + // otherwise try SI + boost::shared_ptr jingleFT = boost::shared_ptr(new MyOutgoingSIFileTransfer(idGenerator->generateID(), from, fullJID.get(), fileInfo.getName(), fileInfo.getSize(), fileInfo.getDescription(), readBytestream, iqRouter, bytestreamServer, bytestreamRegistry)); + // else fail + + return jingleFT; +} + +boost::optional CombinedOutgoingFileTransferManager::highestPriorityJIDSupportingJingle(const JID& bareJID) { + JID fullReceipientJID; + int priority = INT_MIN; + + //getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11 + std::vector presences = presenceOracle->getAllPresence(bareJID); + + //iterate over them + foreach(Presence::ref pres, presences) { + if (pres->getPriority() > priority) { + // look up caps from the jid + DiscoInfo::ref info = capsProvider->getCaps(pres->getFrom()); + if (info && info->hasFeature(DiscoInfo::JingleFeature) && info->hasFeature(DiscoInfo::JingleFTFeature) && + info->hasFeature(DiscoInfo::JingleTransportsIBBFeature)) { + + priority = pres->getPriority(); + fullReceipientJID = pres->getFrom(); + } + } + } + + return fullReceipientJID.isValid() ? boost::optional(fullReceipientJID) : boost::optional(); +} + +boost::optional CombinedOutgoingFileTransferManager::highestPriorityJIDSupportingSI(const JID& bareJID) { + JID fullReceipientJID; + int priority = INT_MIN; + + //getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11 + std::vector presences = presenceOracle->getAllPresence(bareJID); + + //iterate over them + foreach(Presence::ref pres, presences) { + if (pres->getPriority() > priority) { + // look up caps from the jid + DiscoInfo::ref info = capsProvider->getCaps(pres->getFrom()); + if (info && info->hasFeature("http://jabber.org/protocol/si/profile/file-transfer")) { + + priority = pres->getPriority(); + fullReceipientJID = pres->getFrom(); + } + } + } + + return fullReceipientJID.isValid() ? boost::optional(fullReceipientJID) : boost::optional(); +} + +} diff --git a/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.h b/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.h index 17e7fc58..7cef426f 100644 --- a/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.h +++ b/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.h @@ -1,55 +1,58 @@ -/* - * Copyright (c) 2011 Tobias Markmann - * Licensed under the simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include -#include - -#include - -#include "transport/presenceoracle.h" - -namespace Swift { - -class JingleSessionManager; -class IQRouter; -class EntityCapsProvider; -class RemoteJingleTransportCandidateSelectorFactory; -class LocalJingleTransportCandidateGeneratorFactory; -class OutgoingFileTransfer; -class JID; -class IDGenerator; -class ReadBytestream; -class StreamInitiationFileInfo; -class SOCKS5BytestreamRegistry; -class SOCKS5BytestreamProxy; -class SOCKS5BytestreamServer; -class PresenceOracle; - -class CombinedOutgoingFileTransferManager { -public: - CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, Transport::PresenceOracle* presOracle, SOCKS5BytestreamServer *server); - ~CombinedOutgoingFileTransferManager(); - - boost::shared_ptr createOutgoingFileTransfer(const JID& from, const JID& to, boost::shared_ptr, const StreamInitiationFileInfo&); - -private: - boost::optional highestPriorityJIDSupportingJingle(const JID& bareJID); - boost::optional highestPriorityJIDSupportingSI(const JID& bareJID); - JingleSessionManager* jsManager; - IQRouter* iqRouter; - EntityCapsProvider* capsProvider; - RemoteJingleTransportCandidateSelectorFactory* remoteFactory; - LocalJingleTransportCandidateGeneratorFactory* localFactory; - IDGenerator *idGenerator; - SOCKS5BytestreamRegistry* bytestreamRegistry; - SOCKS5BytestreamProxy* bytestreamProxy; - Transport::PresenceOracle* presenceOracle; - SOCKS5BytestreamServer *bytestreamServer; -}; - -} +/* + * Copyright (c) 2011 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include +#include + +#include + +#include "transport/presenceoracle.h" +#include +#include +#define HAVE_SWIFTEN_3 SWIFTEN_VERSION >= 0x030000 + +namespace Swift { + +class JingleSessionManager; +class IQRouter; +class EntityCapsProvider; +class RemoteJingleTransportCandidateSelectorFactory; +class LocalJingleTransportCandidateGeneratorFactory; +class OutgoingFileTransfer; +class JID; +class IDGenerator; +class ReadBytestream; +class StreamInitiationFileInfo; +class SOCKS5BytestreamRegistry; +class SOCKS5BytestreamProxy; +class SOCKS5BytestreamServer; +class PresenceOracle; + +class CombinedOutgoingFileTransferManager { +public: + CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, Transport::PresenceOracle* presOracle, SOCKS5BytestreamServer *server); + ~CombinedOutgoingFileTransferManager(); + + boost::shared_ptr createOutgoingFileTransfer(const JID& from, const JID& to, boost::shared_ptr, const StreamInitiationFileInfo&); + +private: + boost::optional highestPriorityJIDSupportingJingle(const JID& bareJID); + boost::optional highestPriorityJIDSupportingSI(const JID& bareJID); + JingleSessionManager* jsManager; + IQRouter* iqRouter; + EntityCapsProvider* capsProvider; + RemoteJingleTransportCandidateSelectorFactory* remoteFactory; + LocalJingleTransportCandidateGeneratorFactory* localFactory; + IDGenerator *idGenerator; + SOCKS5BytestreamRegistry* bytestreamRegistry; + SOCKS5BytestreamProxy* bytestreamProxy; + Transport::PresenceOracle* presenceOracle; + SOCKS5BytestreamServer *bytestreamServer; +}; + +} diff --git a/include/Swiften/FileTransfer/MyOutgoingSIFileTransfer.cpp b/include/Swiften/FileTransfer/MyOutgoingSIFileTransfer.cpp index 394cec09..023d8096 100644 --- a/include/Swiften/FileTransfer/MyOutgoingSIFileTransfer.cpp +++ b/include/Swiften/FileTransfer/MyOutgoingSIFileTransfer.cpp @@ -1,111 +1,118 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#include - -#include - -#include -#include -#include -#include -#include - -namespace Swift { - -MyOutgoingSIFileTransfer::MyOutgoingSIFileTransfer(const std::string& id, const JID& from, const JID& to, const std::string& name, int size, const std::string& description, boost::shared_ptr bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer, SOCKS5BytestreamRegistry* registry) : id(id), from(from), to(to), name(name), size(size), description(description), bytestream(bytestream), iqRouter(iqRouter), socksServer(socksServer), registry(registry) { -} - -void MyOutgoingSIFileTransfer::start() { - StreamInitiation::ref streamInitiation(new StreamInitiation()); - streamInitiation->setID(id); - streamInitiation->setFileInfo(StreamInitiationFileInfo(name, description, size)); - streamInitiation->addProvidedMethod("http://jabber.org/protocol/bytestreams"); - streamInitiation->addProvidedMethod("http://jabber.org/protocol/ibb"); - StreamInitiationRequest::ref request = StreamInitiationRequest::create(from, to, streamInitiation, iqRouter); - request->onResponse.connect(boost::bind(&MyOutgoingSIFileTransfer::handleStreamInitiationRequestResponse, this, _1, _2)); - request->send(); -} - -void MyOutgoingSIFileTransfer::stop() { -} - -void MyOutgoingSIFileTransfer::cancel() { - // TODO -// session->sendTerminate(JinglePayload::Reason::Cancel); - - if (ibbSession) { - ibbSession->stop(); - } - SOCKS5BytestreamServerSession *serverSession = registry->getConnectedSession(SOCKS5BytestreamRegistry::getHostname(id, from, to)); - if (serverSession) { - serverSession->stop(); - } - - onStateChange(FileTransfer::State(FileTransfer::State::Canceled)); -} - -void MyOutgoingSIFileTransfer::handleStreamInitiationRequestResponse(StreamInitiation::ref response, ErrorPayload::ref error) { - if (error) { - finish(FileTransferError()); - } - else { - if (response->getRequestedMethod() == "http://jabber.org/protocol/bytestreams") { - registry->addReadBytestream(SOCKS5BytestreamRegistry::getHostname(id, from, to), bytestream); - socksServer->addReadBytestream(id, from, to, bytestream); - Bytestreams::ref bytestreams(new Bytestreams()); - bytestreams->setStreamID(id); - HostAddressPort addressPort = socksServer->getAddressPort(); - bytestreams->addStreamHost(Bytestreams::StreamHost(addressPort.getAddress().toString(), from, addressPort.getPort())); - BytestreamsRequest::ref request = BytestreamsRequest::create(from, to, bytestreams, iqRouter); - request->onResponse.connect(boost::bind(&MyOutgoingSIFileTransfer::handleBytestreamsRequestResponse, this, _1, _2)); - request->send(); - } - else if (response->getRequestedMethod() == "http://jabber.org/protocol/ibb") { - ibbSession = boost::shared_ptr(new IBBSendSession(id, from, to, bytestream, iqRouter)); - ibbSession->onFinished.connect(boost::bind(&MyOutgoingSIFileTransfer::handleIBBSessionFinished, this, _1)); - ibbSession->start(); - - onStateChange(FileTransfer::State(FileTransfer::State::Transferring)); - } - } -} - -void MyOutgoingSIFileTransfer::handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref error) { - if (error) { - finish(FileTransferError()); - return; - } - - SOCKS5BytestreamServerSession *serverSession = registry->getConnectedSession(SOCKS5BytestreamRegistry::getHostname(id, from, to)); -// serverSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1)); -// serverSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1)); - serverSession->startTransfer(); - onStateChange(FileTransfer::State(FileTransfer::State::Transferring)); - - //socksServer->onTransferFinished.connect(); -} - -void MyOutgoingSIFileTransfer::finish(boost::optional error) { - if (ibbSession) { - ibbSession->onFinished.disconnect(boost::bind(&MyOutgoingSIFileTransfer::handleIBBSessionFinished, this, _1)); - ibbSession.reset(); - } - socksServer->removeReadBytestream(id, from, to); - if(error) { - onStateChange(FileTransfer::State(FileTransfer::State::Canceled)); - } - else { - onStateChange(FileTransfer::State(FileTransfer::State::Finished)); - } - onFinished(error); -} - -void MyOutgoingSIFileTransfer::handleIBBSessionFinished(boost::optional error) { - finish(error); -} - -} +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include + +#include + +#include +#include +#include +#include +#include + +namespace Swift { + +MyOutgoingSIFileTransfer::MyOutgoingSIFileTransfer(const std::string& id, const JID& from, const JID& to, const std::string& name, int size, const std::string& description, boost::shared_ptr bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer, SOCKS5BytestreamRegistry* registry) : id(id), from(from), to(to), name(name), size(size), description(description), bytestream(bytestream), iqRouter(iqRouter), socksServer(socksServer), registry(registry) { +} + +void MyOutgoingSIFileTransfer::start() { + StreamInitiation::ref streamInitiation(new StreamInitiation()); + streamInitiation->setID(id); + streamInitiation->setFileInfo(StreamInitiationFileInfo(name, description, size)); + streamInitiation->addProvidedMethod("http://jabber.org/protocol/bytestreams"); + streamInitiation->addProvidedMethod("http://jabber.org/protocol/ibb"); + StreamInitiationRequest::ref request = StreamInitiationRequest::create(from, to, streamInitiation, iqRouter); + request->onResponse.connect(boost::bind(&MyOutgoingSIFileTransfer::handleStreamInitiationRequestResponse, this, _1, _2)); + request->send(); +} + +void MyOutgoingSIFileTransfer::stop() { +} + +void MyOutgoingSIFileTransfer::cancel() { + // TODO +// session->sendTerminate(JinglePayload::Reason::Cancel); + + if (ibbSession) { + ibbSession->stop(); + } +#if !HAVE_SWIFTEN_3 + SOCKS5BytestreamServerSession *serverSession = registry->getConnectedSession(SOCKS5BytestreamRegistry::getHostname(id, from, to)); + if (serverSession) { + serverSession->stop(); + } + + onStateChange(FileTransfer::State(FileTransfer::State::Canceled)); +#endif +} + +void MyOutgoingSIFileTransfer::handleStreamInitiationRequestResponse(StreamInitiation::ref response, ErrorPayload::ref error) { + if (error) { + finish(FileTransferError()); + } + else { + if (response->getRequestedMethod() == "http://jabber.org/protocol/bytestreams") { +#if !HAVE_SWIFTEN_3 + registry->addReadBytestream(SOCKS5BytestreamRegistry::getHostname(id, from, to), bytestream); + socksServer->addReadBytestream(id, from, to, bytestream); +#endif + Bytestreams::ref bytestreams(new Bytestreams()); + bytestreams->setStreamID(id); + HostAddressPort addressPort = socksServer->getAddressPort(); + bytestreams->addStreamHost(Bytestreams::StreamHost(addressPort.getAddress().toString(), from, addressPort.getPort())); + BytestreamsRequest::ref request = BytestreamsRequest::create(from, to, bytestreams, iqRouter); + request->onResponse.connect(boost::bind(&MyOutgoingSIFileTransfer::handleBytestreamsRequestResponse, this, _1, _2)); + request->send(); + } + else if (response->getRequestedMethod() == "http://jabber.org/protocol/ibb") { + ibbSession = boost::shared_ptr(new IBBSendSession(id, from, to, bytestream, iqRouter)); + ibbSession->onFinished.connect(boost::bind(&MyOutgoingSIFileTransfer::handleIBBSessionFinished, this, _1)); + ibbSession->start(); +#if !HAVE_SWIFTEN_3 + onStateChange(FileTransfer::State(FileTransfer::State::Transferring)); +#endif + } + } +} + +void MyOutgoingSIFileTransfer::handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref error) { + if (error) { + finish(FileTransferError()); + return; + } +#if !HAVE_SWIFTEN_3 + SOCKS5BytestreamServerSession *serverSession = registry->getConnectedSession(SOCKS5BytestreamRegistry::getHostname(id, from, to)); +// serverSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1)); +// serverSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1)); + serverSession->startTransfer(); + onStateChange(FileTransfer::State(FileTransfer::State::Transferring)); +#endif + //socksServer->onTransferFinished.connect(); +} + +void MyOutgoingSIFileTransfer::finish(boost::optional error) { + if (ibbSession) { + ibbSession->onFinished.disconnect(boost::bind(&MyOutgoingSIFileTransfer::handleIBBSessionFinished, this, _1)); + ibbSession.reset(); + } +#if !HAVE_SWIFTEN_3 + socksServer->removeReadBytestream(id, from, to); + if(error) { + onStateChange(FileTransfer::State(FileTransfer::State::Canceled)); + } + else { + onStateChange(FileTransfer::State(FileTransfer::State::Finished)); + } +#endif + onFinished(error); +} + +void MyOutgoingSIFileTransfer::handleIBBSessionFinished(boost::optional error) { + finish(error); +} + +} diff --git a/include/Swiften/FileTransfer/MyOutgoingSIFileTransfer.h b/include/Swiften/FileTransfer/MyOutgoingSIFileTransfer.h index 8fa88ea6..7e353830 100644 --- a/include/Swiften/FileTransfer/MyOutgoingSIFileTransfer.h +++ b/include/Swiften/FileTransfer/MyOutgoingSIFileTransfer.h @@ -1,56 +1,58 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Swift { - class IQRouter; - class SOCKS5BytestreamServer; - class SOCKS5BytestreamRegistry; - - class MyOutgoingSIFileTransfer : public OutgoingFileTransfer { - public: - MyOutgoingSIFileTransfer(const std::string& id, const JID& from, const JID& to, const std::string& name, int size, const std::string& description, boost::shared_ptr bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer, SOCKS5BytestreamRegistry* registry); - - virtual void start(); - virtual void stop(); - virtual void cancel(); - - boost::signal&)> onFinished; - - private: - void handleStreamInitiationRequestResponse(StreamInitiation::ref, ErrorPayload::ref); - void handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref); - void finish(boost::optional error); - void handleIBBSessionFinished(boost::optional error); - - private: - std::string id; - JID from; - JID to; - std::string name; - int size; - std::string description; - boost::shared_ptr bytestream; - IQRouter* iqRouter; - SOCKS5BytestreamServer* socksServer; - boost::shared_ptr ibbSession; - SOCKS5BytestreamRegistry *registry; - }; -} +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define HAVE_SWIFTEN_3 SWIFTEN_VERSION >= 0x030000 + +namespace Swift { + class IQRouter; + class SOCKS5BytestreamServer; + class SOCKS5BytestreamRegistry; + + class MyOutgoingSIFileTransfer : public OutgoingFileTransfer { + public: + MyOutgoingSIFileTransfer(const std::string& id, const JID& from, const JID& to, const std::string& name, int size, const std::string& description, boost::shared_ptr bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer, SOCKS5BytestreamRegistry* registry); + + virtual void start(); + virtual void stop(); + virtual void cancel(); + + boost::signal&)> onFinished; + + private: + void handleStreamInitiationRequestResponse(StreamInitiation::ref, ErrorPayload::ref); + void handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref); + void finish(boost::optional error); + void handleIBBSessionFinished(boost::optional error); + + private: + std::string id; + JID from; + JID to; + std::string name; + int size; + std::string description; + boost::shared_ptr bytestream; + IQRouter* iqRouter; + SOCKS5BytestreamServer* socksServer; + boost::shared_ptr ibbSession; + SOCKS5BytestreamRegistry *registry; + }; +} diff --git a/include/Swiften/Network/DummyConnectionServer.cpp b/include/Swiften/Network/DummyConnectionServer.cpp deleted file mode 100644 index 7250dce3..00000000 --- a/include/Swiften/Network/DummyConnectionServer.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#include - -#include -#include -#include - -#include - -namespace Swift { - -DummyConnectionServer::DummyConnectionServer(EventLoop* eventLoop) : eventLoop(eventLoop) { -} - -void DummyConnectionServer::start() { -} - - -void DummyConnectionServer::stop() { - -} - -void DummyConnectionServer::acceptConnection(boost::shared_ptr connection) { - eventLoop->postEvent( - boost::bind(boost::ref(onNewConnection), connection), - shared_from_this()); -// connection->listen(); -} - - -HostAddressPort DummyConnectionServer::getAddressPort() const { - return HostAddressPort(); -} - -} diff --git a/include/Swiften/Network/DummyConnectionServer.h b/include/Swiften/Network/DummyConnectionServer.h deleted file mode 100644 index 0e2a141c..00000000 --- a/include/Swiften/Network/DummyConnectionServer.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace Swift { - class DummyConnectionServer : public ConnectionServer, public EventOwner, public boost::enable_shared_from_this { - public: - typedef boost::shared_ptr ref; - - enum Error { - Conflict, - UnknownError - }; - - static ref create(EventLoop* eventLoop) { - return ref(new DummyConnectionServer(eventLoop)); - } - - void acceptConnection(boost::shared_ptr connection); - - virtual void start(); - virtual void stop(); - - virtual HostAddressPort getAddressPort() const; - - - private: - DummyConnectionServer(EventLoop* eventLoop); - - - private: - HostAddress address_; - EventLoop* eventLoop; - }; -} diff --git a/include/Swiften/Network/DummyConnectionServerFactory.cpp b/include/Swiften/Network/DummyConnectionServerFactory.cpp deleted file mode 100644 index 4fbabbc2..00000000 --- a/include/Swiften/Network/DummyConnectionServerFactory.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include -#include - -namespace Swift { - -DummyConnectionServerFactory::DummyConnectionServerFactory(EventLoop* eventLoop) : eventLoop(eventLoop) { -} - -boost::shared_ptr DummyConnectionServerFactory::createConnectionServer(int port) { - return DummyConnectionServer::create(eventLoop); -} - -boost::shared_ptr DummyConnectionServerFactory::createConnectionServer(const Swift::HostAddress &hostAddress, int port) { - return DummyConnectionServer::create(eventLoop); -} - -} diff --git a/include/Swiften/Network/DummyConnectionServerFactory.h b/include/Swiften/Network/DummyConnectionServerFactory.h deleted file mode 100644 index a4bdbfd2..00000000 --- a/include/Swiften/Network/DummyConnectionServerFactory.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include - -#include -#include - -namespace Swift { - class ConnectionServer; - - class DummyConnectionServerFactory : public ConnectionServerFactory { - public: - DummyConnectionServerFactory(EventLoop* eventLoop); - - virtual boost::shared_ptr createConnectionServer(int port); - - virtual boost::shared_ptr createConnectionServer(const Swift::HostAddress &hostAddress, int port); - - private: - EventLoop* eventLoop; - }; -} diff --git a/include/Swiften/Network/DummyNetworkFactories.h b/include/Swiften/Network/DummyNetworkFactories.h index 2edc85b0..78b17f7c 100644 --- a/include/Swiften/Network/DummyNetworkFactories.h +++ b/include/Swiften/Network/DummyNetworkFactories.h @@ -1,82 +1,80 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#pragma once - -#include -//#define HAVE_SWIFTEN_3 SWIFTEN_VERSION >= 0x030000 -// Swiften 3 was not released yet and these changes are not in 3.0alpha -#define HAVE_SWIFTEN_3 0 - -#include -#include -#if HAVE_SWIFTEN_3 -#include -#include -#endif - -namespace Swift { - class EventLoop; - - class DummyNetworkFactories : public NetworkFactories { - public: - DummyNetworkFactories(EventLoop *eventLoop); - ~DummyNetworkFactories(); - - virtual TimerFactory* getTimerFactory() const { - return timerFactory; - } - - virtual ConnectionFactory* getConnectionFactory() const { - return connectionFactory; - } - -#if HAVE_SWIFTEN_3 - IDNConverter* getIDNConverter() const { - return idnConverter.get(); - } -#endif - - DomainNameResolver* getDomainNameResolver() const { - return domainNameResolver; - } - - ConnectionServerFactory* getConnectionServerFactory() const { - return connectionServerFactory; - } - - virtual Swift::NATTraverser* getNATTraverser() const { - return 0; - } - - Swift::XMLParserFactory* getXMLParserFactory() const { - return m_platformXMLParserFactory; - } - - EventLoop *getEventLoop() const { - return eventLoop; - } - - Swift::TLSContextFactory* getTLSContextFactory() const { - return 0; - } - - Swift::ProxyProvider* getProxyProvider() const { - return 0; - } - - private: - PlatformXMLParserFactory *m_platformXMLParserFactory; - TimerFactory* timerFactory; - ConnectionFactory* connectionFactory; -#if HAVE_SWIFTEN_3 - boost::shared_ptr idnConverter; -#endif - DomainNameResolver* domainNameResolver; - ConnectionServerFactory* connectionServerFactory; - EventLoop *eventLoop; - }; -} +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include +#define HAVE_SWIFTEN_3 SWIFTEN_VERSION >= 0x030000 + +#include +#include +#if HAVE_SWIFTEN_3 +#include +#include +#endif + +namespace Swift { + class EventLoop; + + class DummyNetworkFactories : public NetworkFactories { + public: + DummyNetworkFactories(EventLoop *eventLoop); + ~DummyNetworkFactories(); + + virtual TimerFactory* getTimerFactory() const { + return timerFactory; + } + + virtual ConnectionFactory* getConnectionFactory() const { + return connectionFactory; + } + +#if HAVE_SWIFTEN_3 + IDNConverter* getIDNConverter() const { + return idnConverter.get(); + } +#endif + + DomainNameResolver* getDomainNameResolver() const { + return domainNameResolver; + } + + ConnectionServerFactory* getConnectionServerFactory() const { + return connectionServerFactory; + } + + virtual Swift::NATTraverser* getNATTraverser() const { + return 0; + } + + Swift::XMLParserFactory* getXMLParserFactory() const { + return m_platformXMLParserFactory; + } + + EventLoop *getEventLoop() const { + return eventLoop; + } + + Swift::TLSContextFactory* getTLSContextFactory() const { + return 0; + } + + Swift::ProxyProvider* getProxyProvider() const { + return 0; + } + + private: + PlatformXMLParserFactory *m_platformXMLParserFactory; + TimerFactory* timerFactory; + ConnectionFactory* connectionFactory; +#if HAVE_SWIFTEN_3 + boost::shared_ptr idnConverter; +#endif + DomainNameResolver* domainNameResolver; + ConnectionServerFactory* connectionServerFactory; + EventLoop *eventLoop; + }; +} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubItemParser.cpp b/include/Swiften/Parser/PayloadParsers/PubSubItemParser.cpp deleted file mode 100644 index 8f23c41a..00000000 --- a/include/Swiften/Parser/PayloadParsers/PubSubItemParser.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2012 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include - -#include - -#include -#include -#include -#include -#include - -namespace Swift { - -void PubSubItemParser::handleTree(ParserElement::ref root) { - std::string id = root->getAttributes().getAttribute("id"); - if (!id.empty()) { - getPayloadInternal()->setId(id); - } - - foreach (ParserElement::ref child, root->getAllChildren()) { - getPayloadInternal()->addPayload(TreeReparser::parseTree(child, factories)); - } -} - -} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubItemParser.h b/include/Swiften/Parser/PayloadParsers/PubSubItemParser.h deleted file mode 100644 index 9326f92d..00000000 --- a/include/Swiften/Parser/PayloadParsers/PubSubItemParser.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2012 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include - -#include -#include -#include - -namespace Swift { - class PayloadParserFactoryCollection; - class PubSubItemParser : public GenericPayloadTreeParser { - public: - PubSubItemParser(PayloadParserFactoryCollection* collection) : factories(collection) {} - virtual void handleTree(ParserElement::ref root); - private: - PayloadParserFactoryCollection* factories; - }; -} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubPayloadParser.cpp b/include/Swiften/Parser/PayloadParsers/PubSubPayloadParser.cpp deleted file mode 100644 index c3e151bb..00000000 --- a/include/Swiften/Parser/PayloadParsers/PubSubPayloadParser.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2012 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include - -#include - -#include -#include -#include -#include -#include - -namespace Swift { - -void PubSubPayloadParser::handleTree(ParserElement::ref root) { - foreach (ParserElement::ref child, root->getAllChildren()) { - getPayloadInternal()->addPayload(TreeReparser::parseTree(child, factories)); - } -} - -} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubPayloadParser.h b/include/Swiften/Parser/PayloadParsers/PubSubPayloadParser.h deleted file mode 100644 index 7bbb688c..00000000 --- a/include/Swiften/Parser/PayloadParsers/PubSubPayloadParser.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2012 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include - -#include -#include -#include - -namespace Swift { - class PayloadParserFactoryCollection; - class PubSubPayloadParser : public GenericPayloadTreeParser { - public: - PubSubPayloadParser(PayloadParserFactoryCollection* collection) : factories(collection) {} - virtual void handleTree(ParserElement::ref root); - private: - PayloadParserFactoryCollection* factories; - }; -} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubPublishPayloadParser.cpp b/include/Swiften/Parser/PayloadParsers/PubSubPublishPayloadParser.cpp deleted file mode 100644 index 949ceba4..00000000 --- a/include/Swiften/Parser/PayloadParsers/PubSubPublishPayloadParser.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2012 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include - -#include - -#include -#include -#include -#include -#include - -namespace Swift { - -void PubSubPublishPayloadParser::handleTree(ParserElement::ref root) { - std::string node = root->getAttributes().getAttribute("node"); - if (!node.empty()) { - getPayloadInternal()->setNode(node); - } - - foreach (ParserElement::ref child, root->getAllChildren()) { - getPayloadInternal()->addItem(TreeReparser::parseTree(child, factories)); - } -} - -} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubPublishPayloadParser.h b/include/Swiften/Parser/PayloadParsers/PubSubPublishPayloadParser.h deleted file mode 100644 index b0575b72..00000000 --- a/include/Swiften/Parser/PayloadParsers/PubSubPublishPayloadParser.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2012 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include - -#include -#include -#include - -namespace Swift { - class PayloadParserFactoryCollection; - class PubSubPublishPayloadParser : public GenericPayloadTreeParser { - public: - PubSubPublishPayloadParser(PayloadParserFactoryCollection* collection) : factories(collection) {} - virtual void handleTree(ParserElement::ref root); - private: - PayloadParserFactoryCollection* factories; - }; -} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubSubscribePayloadParser.cpp b/include/Swiften/Parser/PayloadParsers/PubSubSubscribePayloadParser.cpp deleted file mode 100644 index b7913d24..00000000 --- a/include/Swiften/Parser/PayloadParsers/PubSubSubscribePayloadParser.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2012 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include - -#include - -#include -#include -#include -#include -#include - -namespace Swift { - -void PubSubSubscribePayloadParser::handleTree(ParserElement::ref root) { - std::string node = root->getAttributes().getAttribute("node"); - if (!node.empty()) { - getPayloadInternal()->setNode(node); - } - - std::string jid = root->getAttributes().getAttribute("jid"); - if (!jid.empty()) { - getPayloadInternal()->setJID(jid); - } - -} - -} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubSubscribePayloadParser.h b/include/Swiften/Parser/PayloadParsers/PubSubSubscribePayloadParser.h deleted file mode 100644 index 54a2b05a..00000000 --- a/include/Swiften/Parser/PayloadParsers/PubSubSubscribePayloadParser.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2012 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include - -#include -#include -#include - -namespace Swift { - class PayloadParserFactoryCollection; - class PubSubSubscribePayloadParser : public GenericPayloadTreeParser { - public: - PubSubSubscribePayloadParser(PayloadParserFactoryCollection* collection) : factories(collection) {} - virtual void handleTree(ParserElement::ref root); - private: - PayloadParserFactoryCollection* factories; - }; -} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubSubscriptionPayloadParser.cpp b/include/Swiften/Parser/PayloadParsers/PubSubSubscriptionPayloadParser.cpp deleted file mode 100644 index 0c964762..00000000 --- a/include/Swiften/Parser/PayloadParsers/PubSubSubscriptionPayloadParser.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2012 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include - -#include - -#include -#include -#include -#include -#include - -namespace Swift { - -void PubSubSubscriptionPayloadParser::handleTree(ParserElement::ref root) { - std::string node = root->getAttributes().getAttribute("node"); - if (!node.empty()) { - getPayloadInternal()->setNode(node); - } - - std::string jid = root->getAttributes().getAttribute("jid"); - if (!jid.empty()) { - getPayloadInternal()->setJID(jid); - } - - std::string id = root->getAttributes().getAttribute("subid"); - if (!id.empty()) { - getPayloadInternal()->setId(id); - } - - std::string type = root->getAttributes().getAttribute("subscription"); - if (type == "none") { - getPayloadInternal()->setType(PubSubSubscriptionPayload::None); - } - else if (type == "subscribed") { - getPayloadInternal()->setType(PubSubSubscriptionPayload::Subscribed); - } - else if (type == "pending") { - getPayloadInternal()->setType(PubSubSubscriptionPayload::Pending); - } - else if (type == "unconfigured") { - getPayloadInternal()->setType(PubSubSubscriptionPayload::Unconfigured); - } - -} - -} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubSubscriptionPayloadParser.h b/include/Swiften/Parser/PayloadParsers/PubSubSubscriptionPayloadParser.h deleted file mode 100644 index 7bd1c2d1..00000000 --- a/include/Swiften/Parser/PayloadParsers/PubSubSubscriptionPayloadParser.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2012 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include - -#include -#include -#include - -namespace Swift { - class PayloadParserFactoryCollection; - class PubSubSubscriptionPayloadParser : public GenericPayloadTreeParser { - public: - PubSubSubscriptionPayloadParser(PayloadParserFactoryCollection* collection) : factories(collection) {} - virtual void handleTree(ParserElement::ref root); - private: - PayloadParserFactoryCollection* factories; - }; -} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubItemSerializer.cpp b/include/Swiften/Serializer/PayloadSerializers/PubSubItemSerializer.cpp deleted file mode 100644 index 47ab6492..00000000 --- a/include/Swiften/Serializer/PayloadSerializers/PubSubItemSerializer.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include -#include -#include -#include -#include -#include - -namespace Swift { - -PubSubItemSerializer::PubSubItemSerializer(PayloadSerializerCollection *serializers) : - GenericPayloadSerializer(), serializers(serializers) { -} - -std::string PubSubItemSerializer::serializePayload(boost::shared_ptr payload) const { - XMLElement item("item"); - if (!payload->getId().empty()) { - item.setAttribute("id", payload->getId()); - } - - if (!payload->getPayloads().empty()) { - foreach(boost::shared_ptr subPayload, payload->getPayloads()) { - PayloadSerializer* serializer = serializers->getPayloadSerializer(subPayload); - if (serializer) { - item.addNode(boost::shared_ptr(new XMLRawTextNode(serializer->serialize(subPayload)))); - } - } - } - - return item.serialize(); -} - -} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubItemSerializer.h b/include/Swiften/Serializer/PayloadSerializers/PubSubItemSerializer.h deleted file mode 100644 index 795c63ba..00000000 --- a/include/Swiften/Serializer/PayloadSerializers/PubSubItemSerializer.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include -#include - -namespace Swift { - class PayloadSerializerCollection; - class PubSubItemSerializer : public GenericPayloadSerializer { - public: - PubSubItemSerializer(PayloadSerializerCollection *serializers); - - virtual std::string serializePayload(boost::shared_ptr item) const; - private: - PayloadSerializerCollection *serializers; - }; -} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubPayloadSerializer.cpp b/include/Swiften/Serializer/PayloadSerializers/PubSubPayloadSerializer.cpp deleted file mode 100644 index 9706d681..00000000 --- a/include/Swiften/Serializer/PayloadSerializers/PubSubPayloadSerializer.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include -#include -#include -#include -#include -#include - -namespace Swift { - -PubSubPayloadSerializer::PubSubPayloadSerializer(PayloadSerializerCollection *serializers) - : GenericPayloadSerializer(), - serializers(serializers) { -} - -std::string PubSubPayloadSerializer::serializePayload(boost::shared_ptr payload) const { - XMLElement pubsub("pubsub", "http://jabber.org/protocol/pubsub"); - - if (!payload->getPayloads().empty()) { - foreach(boost::shared_ptr subPayload, payload->getPayloads()) { - PayloadSerializer* serializer = serializers->getPayloadSerializer(subPayload); - if (serializer) { - pubsub.addNode(boost::shared_ptr(new XMLRawTextNode(serializer->serialize(subPayload)))); - } - } - } - - return pubsub.serialize(); -} - -} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubPayloadSerializer.h b/include/Swiften/Serializer/PayloadSerializers/PubSubPayloadSerializer.h deleted file mode 100644 index 0f8f251a..00000000 --- a/include/Swiften/Serializer/PayloadSerializers/PubSubPayloadSerializer.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include -#include - -namespace Swift { - class PayloadSerializerCollection; - - class PubSubPayloadSerializer : public GenericPayloadSerializer { - public: - PubSubPayloadSerializer(PayloadSerializerCollection *serializers); - - virtual std::string serializePayload(boost::shared_ptr item) const; - private: - PayloadSerializerCollection *serializers; - }; -} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubPublishPayloadSerializer.cpp b/include/Swiften/Serializer/PayloadSerializers/PubSubPublishPayloadSerializer.cpp deleted file mode 100644 index 09cfbb5f..00000000 --- a/include/Swiften/Serializer/PayloadSerializers/PubSubPublishPayloadSerializer.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include -#include -#include -#include -#include -#include - -namespace Swift { - -PubSubPublishPayloadSerializer::PubSubPublishPayloadSerializer(PayloadSerializerCollection *serializers) - : GenericPayloadSerializer(), - serializers(serializers) { -} - -std::string PubSubPublishPayloadSerializer::serializePayload(boost::shared_ptr payload) const { - XMLElement publish("publish"); - - if (!payload->getNode().empty()) { - publish.setAttribute("node", payload->getNode()); - } - - if (!payload->getItems().empty()) { - foreach(boost::shared_ptr subPayload, payload->getItems()) { - PayloadSerializer* serializer = serializers->getPayloadSerializer(subPayload); - if (serializer) { - publish.addNode(boost::shared_ptr(new XMLRawTextNode(serializer->serialize(subPayload)))); - } - } - } - - return publish.serialize(); -} - -} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubPublishPayloadSerializer.h b/include/Swiften/Serializer/PayloadSerializers/PubSubPublishPayloadSerializer.h deleted file mode 100644 index ae290559..00000000 --- a/include/Swiften/Serializer/PayloadSerializers/PubSubPublishPayloadSerializer.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include -#include - -namespace Swift { - class PayloadSerializerCollection; - - class PubSubPublishPayloadSerializer : public GenericPayloadSerializer { - public: - PubSubPublishPayloadSerializer(PayloadSerializerCollection *serializers); - - virtual std::string serializePayload(boost::shared_ptr item) const; - private: - PayloadSerializerCollection *serializers; - }; -} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubSubscribePayloadSerializer.cpp b/include/Swiften/Serializer/PayloadSerializers/PubSubSubscribePayloadSerializer.cpp deleted file mode 100644 index f6a96626..00000000 --- a/include/Swiften/Serializer/PayloadSerializers/PubSubSubscribePayloadSerializer.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include -#include -#include -#include -#include -#include - -namespace Swift { - -PubSubSubscribePayloadSerializer::PubSubSubscribePayloadSerializer() - : GenericPayloadSerializer() { -} - -std::string PubSubSubscribePayloadSerializer::serializePayload(boost::shared_ptr payload) const { - XMLElement subscribe("subscribe"); - - if (!payload->getJID().isValid()) { - subscribe.setAttribute("jid", payload->getJID().toBare().toString()); - } - - if (!payload->getNode().empty()) { - subscribe.setAttribute("node", payload->getNode()); - } - - return subscribe.serialize(); -} - -} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubSubscribePayloadSerializer.h b/include/Swiften/Serializer/PayloadSerializers/PubSubSubscribePayloadSerializer.h deleted file mode 100644 index 327703be..00000000 --- a/include/Swiften/Serializer/PayloadSerializers/PubSubSubscribePayloadSerializer.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include -#include - -namespace Swift { - class PubSubSubscribePayloadSerializer : public GenericPayloadSerializer { - public: - PubSubSubscribePayloadSerializer(); - - virtual std::string serializePayload(boost::shared_ptr item) const; - }; -} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubSubscriptionPayloadSerializer.cpp b/include/Swiften/Serializer/PayloadSerializers/PubSubSubscriptionPayloadSerializer.cpp deleted file mode 100644 index 02a634a1..00000000 --- a/include/Swiften/Serializer/PayloadSerializers/PubSubSubscriptionPayloadSerializer.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include -#include -#include -#include -#include -#include - -namespace Swift { - -PubSubSubscriptionPayloadSerializer::PubSubSubscriptionPayloadSerializer() - : GenericPayloadSerializer() { -} - -std::string PubSubSubscriptionPayloadSerializer::serializePayload(boost::shared_ptr payload) const { - XMLElement subscription("subscription"); - - if (!payload->getJID().isValid()) { - subscription.setAttribute("jid", payload->getJID().toBare().toString()); - } - - if (!payload->getNode().empty()) { - subscription.setAttribute("node", payload->getNode()); - } - - switch (payload->getType()) { - case PubSubSubscriptionPayload::None: - subscription.setAttribute("subscription", "none"); - break; - case PubSubSubscriptionPayload::Subscribed: - subscription.setAttribute("subscription", "subscribed"); - break; - case PubSubSubscriptionPayload::Unconfigured: - subscription.setAttribute("subscription", "unconfigured"); - break; - case PubSubSubscriptionPayload::Pending: - subscription.setAttribute("subscription", "pending"); - break; - } - - return subscription.serialize(); -} - -} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubSubscriptionPayloadSerializer.h b/include/Swiften/Serializer/PayloadSerializers/PubSubSubscriptionPayloadSerializer.h deleted file mode 100644 index 0e06a34b..00000000 --- a/include/Swiften/Serializer/PayloadSerializers/PubSubSubscriptionPayloadSerializer.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2011 Jan Kaluza - * Licensed under the Simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include -#include - -namespace Swift { - class PubSubSubscriptionPayloadSerializer : public GenericPayloadSerializer { - public: - PubSubSubscriptionPayloadSerializer(); - - virtual std::string serializePayload(boost::shared_ptr item) const; - }; -} diff --git a/include/Swiften/Server/ServerFromClientSession.cpp b/include/Swiften/Server/ServerFromClientSession.cpp index de988fcd..9c61c6a5 100644 --- a/include/Swiften/Server/ServerFromClientSession.cpp +++ b/include/Swiften/Server/ServerFromClientSession.cpp @@ -1,183 +1,183 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Swift { - -ServerFromClientSession::ServerFromClientSession( - const std::string& id, - boost::shared_ptr connection, - PayloadParserFactoryCollection* payloadParserFactories, - PayloadSerializerCollection* payloadSerializers, - UserRegistry* userRegistry, - XMLParserFactory* factory, - Swift::JID remoteJID) : - Session(connection, payloadParserFactories, payloadSerializers, factory), - id_(id), - userRegistry_(userRegistry), - authenticated_(false), - initialized(false), - allowSASLEXTERNAL(false), - tlsLayer(0), - tlsConnected(false) { - setRemoteJID(remoteJID); -} - -ServerFromClientSession::~ServerFromClientSession() { - if (tlsLayer) { - delete tlsLayer; - } -} - -void ServerFromClientSession::handlePasswordValid() { - if (!isInitialized()) { - getXMPPLayer()->writeElement(boost::shared_ptr(new AuthSuccess())); - authenticated_ = true; - getXMPPLayer()->resetParser(); - } -} - -void ServerFromClientSession::handlePasswordInvalid(const std::string &error) { - if (!isInitialized()) { - getXMPPLayer()->writeElement(boost::shared_ptr(new AuthFailure)); - if (!error.empty()) { - boost::shared_ptr msg(new StreamError(StreamError::UndefinedCondition, error)); - getXMPPLayer()->writeElement(msg); - } - - finishSession(AuthenticationFailedError); - } -} - -void ServerFromClientSession::handleElement(boost::shared_ptr element) { - if (isInitialized()) { - onElementReceived(element); - } - else { - if (AuthRequest* authRequest = dynamic_cast(element.get())) { - if (authRequest->getMechanism() == "PLAIN" || (allowSASLEXTERNAL && authRequest->getMechanism() == "EXTERNAL")) { - if (authRequest->getMechanism() == "EXTERNAL") { - getXMPPLayer()->writeElement(boost::shared_ptr(new AuthSuccess())); - authenticated_ = true; - getXMPPLayer()->resetParser(); - } - else { - PLAINMessage plainMessage(authRequest->getMessage() ? *authRequest->getMessage() : createSafeByteArray("")); - user_ = plainMessage.getAuthenticationID(); - userRegistry_->isValidUserPassword(JID(plainMessage.getAuthenticationID(), getLocalJID().getDomain()), this, plainMessage.getPassword()); - } - } - else { - getXMPPLayer()->writeElement(boost::shared_ptr(new AuthFailure)); - finishSession(NoSupportedAuthMechanismsError); - } - } - else if (dynamic_cast(element.get()) != NULL) { - getXMPPLayer()->writeElement(boost::shared_ptr(new TLSProceed)); - getStreamStack()->addLayer(tlsLayer); - tlsLayer->connect(); - getXMPPLayer()->resetParser(); - } - else if (IQ* iq = dynamic_cast(element.get())) { - if (boost::shared_ptr resourceBind = iq->getPayload()) { - std::string bucket = "abcdefghijklmnopqrstuvwxyz"; - std::string uuid; - for (int i = 0; i < 10; i++) { - uuid += bucket[rand() % bucket.size()]; - } - setRemoteJID(JID(user_, getLocalJID().getDomain(), uuid)); - boost::shared_ptr resultResourceBind(new ResourceBind()); - resultResourceBind->setJID(getRemoteJID()); - getXMPPLayer()->writeElement(IQ::createResult(JID(), iq->getID(), resultResourceBind)); - } - else if (iq->getPayload()) { - getXMPPLayer()->writeElement(IQ::createResult(getRemoteJID(), iq->getID())); - setInitialized(); - } - } - } -} - -void ServerFromClientSession::handleStreamStart(const ProtocolHeader& incomingHeader) { - setLocalJID(JID("", incomingHeader.getTo())); - ProtocolHeader header; - header.setFrom(incomingHeader.getTo()); - header.setID(id_); - getXMPPLayer()->writeHeader(header); - - boost::shared_ptr features(new StreamFeatures()); - - if (!authenticated_) { - if (tlsLayer && !tlsConnected) { - features->setHasStartTLS(); - } - features->addAuthenticationMechanism("PLAIN"); - if (allowSASLEXTERNAL) { - features->addAuthenticationMechanism("EXTERNAL"); - } - } - else { - features->setHasResourceBind(); - features->setHasSession(); - } - getXMPPLayer()->writeElement(features); -} - -void ServerFromClientSession::setInitialized() { - initialized = true; - onSessionStarted(); -} - -void ServerFromClientSession::setAllowSASLEXTERNAL() { - allowSASLEXTERNAL = true; -} - -void ServerFromClientSession::handleSessionFinished(const boost::optional&) { - userRegistry_->stopLogin(JID(user_, getLocalJID().getDomain()), this); -} - -void ServerFromClientSession::addTLSEncryption(TLSServerContextFactory* tlsContextFactory, CertificateWithKey::ref cert) { - tlsLayer = new TLSServerLayer(tlsContextFactory); - if (!tlsLayer->setServerCertificate(cert)) { -// std::cout << "error\n"; - // TODO: -// onClosed(boost::shared_ptr(new Error(Error::InvalidTLSCertificateError))); - } - else { - tlsLayer->onError.connect(boost::bind(&ServerFromClientSession::handleTLSError, this)); - tlsLayer->onConnected.connect(boost::bind(&ServerFromClientSession::handleTLSConnected, this)); -// getStreamStack()->addLayer(tlsLayer); -// tlsLayer->onError.connect(boost::bind(&BasicSessionStream::handleTLSError, this)); -// tlsLayer->onConnected.connect(boost::bind(&BasicSessionStream::handleTLSConnected, this)); -// tlsLayer->connect(); - } -} - -} +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Swift { + +ServerFromClientSession::ServerFromClientSession( + const std::string& id, + boost::shared_ptr connection, + PayloadParserFactoryCollection* payloadParserFactories, + PayloadSerializerCollection* payloadSerializers, + UserRegistry* userRegistry, + XMLParserFactory* factory, + Swift::JID remoteJID) : + Session(connection, payloadParserFactories, payloadSerializers, factory), + id_(id), + userRegistry_(userRegistry), + authenticated_(false), + initialized(false), + allowSASLEXTERNAL(false), + tlsLayer(0), + tlsConnected(false) { + setRemoteJID(remoteJID); +} + +ServerFromClientSession::~ServerFromClientSession() { + if (tlsLayer) { + delete tlsLayer; + } +} + +void ServerFromClientSession::handlePasswordValid() { + if (!isInitialized()) { + getXMPPLayer()->writeElement(boost::shared_ptr(new AuthSuccess())); + authenticated_ = true; + getXMPPLayer()->resetParser(); + } +} + +void ServerFromClientSession::handlePasswordInvalid(const std::string &error) { + if (!isInitialized()) { + getXMPPLayer()->writeElement(boost::shared_ptr(new AuthFailure)); + if (!error.empty()) { + boost::shared_ptr msg(new StreamError(StreamError::UndefinedCondition, error)); + getXMPPLayer()->writeElement(msg); + } + + finishSession(AuthenticationFailedError); + } +} + +void ServerFromClientSession::handleElement(boost::shared_ptr element) { + if (isInitialized()) { + onElementReceived(element); + } + else { + if (AuthRequest* authRequest = dynamic_cast(element.get())) { + if (authRequest->getMechanism() == "PLAIN" || (allowSASLEXTERNAL && authRequest->getMechanism() == "EXTERNAL")) { + if (authRequest->getMechanism() == "EXTERNAL") { + getXMPPLayer()->writeElement(boost::shared_ptr(new AuthSuccess())); + authenticated_ = true; + getXMPPLayer()->resetParser(); + } + else { + PLAINMessage plainMessage(authRequest->getMessage() ? *authRequest->getMessage() : createSafeByteArray("")); + user_ = plainMessage.getAuthenticationID(); + userRegistry_->isValidUserPassword(JID(plainMessage.getAuthenticationID(), getLocalJID().getDomain()), this, plainMessage.getPassword()); + } + } + else { + getXMPPLayer()->writeElement(boost::shared_ptr(new AuthFailure)); + finishSession(NoSupportedAuthMechanismsError); + } + } + else if (dynamic_cast(element.get()) != NULL) { + getXMPPLayer()->writeElement(boost::shared_ptr(new TLSProceed)); + getStreamStack()->addLayer(tlsLayer); + tlsLayer->connect(); + getXMPPLayer()->resetParser(); + } + else if (IQ* iq = dynamic_cast(element.get())) { + if (boost::shared_ptr resourceBind = iq->getPayload()) { + std::string bucket = "abcdefghijklmnopqrstuvwxyz"; + std::string uuid; + for (int i = 0; i < 10; i++) { + uuid += bucket[rand() % bucket.size()]; + } + setRemoteJID(JID(user_, getLocalJID().getDomain(), uuid)); + boost::shared_ptr resultResourceBind(new ResourceBind()); + resultResourceBind->setJID(getRemoteJID()); + getXMPPLayer()->writeElement(IQ::createResult(JID(), iq->getID(), resultResourceBind)); + } + else if (iq->getPayload()) { + getXMPPLayer()->writeElement(IQ::createResult(getRemoteJID(), iq->getID())); + setInitialized(); + } + } + } +} + +void ServerFromClientSession::handleStreamStart(const ProtocolHeader& incomingHeader) { + setLocalJID(JID("", incomingHeader.getTo())); + ProtocolHeader header; + header.setFrom(incomingHeader.getTo()); + header.setID(id_); + getXMPPLayer()->writeHeader(header); + + boost::shared_ptr features(new StreamFeatures()); + + if (!authenticated_) { + if (tlsLayer && !tlsConnected) { + features->setHasStartTLS(); + } + features->addAuthenticationMechanism("PLAIN"); + if (allowSASLEXTERNAL) { + features->addAuthenticationMechanism("EXTERNAL"); + } + } + else { + features->setHasResourceBind(); + features->setHasSession(); + } + getXMPPLayer()->writeElement(features); +} + +void ServerFromClientSession::setInitialized() { + initialized = true; + onSessionStarted(); +} + +void ServerFromClientSession::setAllowSASLEXTERNAL() { + allowSASLEXTERNAL = true; +} + +void ServerFromClientSession::handleSessionFinished(const boost::optional&) { + userRegistry_->stopLogin(JID(user_, getLocalJID().getDomain()), this); +} + +void ServerFromClientSession::addTLSEncryption(TLSServerContextFactory* tlsContextFactory, CertificateWithKey::ref cert) { + tlsLayer = new TLSServerLayer(tlsContextFactory); + if (!tlsLayer->setServerCertificate(cert)) { +// std::cout << "error\n"; + // TODO: +// onClosed(boost::shared_ptr(new Error(Error::InvalidTLSCertificateError))); + } + else { + tlsLayer->onError.connect(boost::bind(&ServerFromClientSession::handleTLSError, this)); + tlsLayer->onConnected.connect(boost::bind(&ServerFromClientSession::handleTLSConnected, this)); +// getStreamStack()->addLayer(tlsLayer); +// tlsLayer->onError.connect(boost::bind(&BasicSessionStream::handleTLSError, this)); +// tlsLayer->onConnected.connect(boost::bind(&BasicSessionStream::handleTLSConnected, this)); +// tlsLayer->connect(); + } +} + +} diff --git a/include/Swiften/Server/ServerFromClientSession.h b/include/Swiften/Server/ServerFromClientSession.h index 121f3f9a..230bb119 100644 --- a/include/Swiften/Server/ServerFromClientSession.h +++ b/include/Swiften/Server/ServerFromClientSession.h @@ -1,85 +1,91 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#pragma once - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace Swift { - class ProtocolHeader; - class Element; - class Stanza; - class PayloadParserFactoryCollection; - class PayloadSerializerCollection; - class StreamStack; - class UserRegistry; - class XMPPLayer; - class ConnectionLayer; - class Connection; - class TLSServerLayer; - class TLSServerContextFactory; - class PKCS12Certificate; - - class ServerFromClientSession : public Session { - public: - ServerFromClientSession( - const std::string& id, - boost::shared_ptr connection, - PayloadParserFactoryCollection* payloadParserFactories, - PayloadSerializerCollection* payloadSerializers, - UserRegistry* userRegistry, - XMLParserFactory* factory, - Swift::JID remoteJID = Swift::JID()); - ~ServerFromClientSession(); - - boost::signal onSessionStarted; - void setAllowSASLEXTERNAL(); - const std::string &getUser() { - return user_; - } - - void addTLSEncryption(TLSServerContextFactory* tlsContextFactory, CertificateWithKey::ref cert); - - Swift::JID getBareJID() { - return Swift::JID(user_, getLocalJID().getDomain()); - } - - void handlePasswordValid(); - void handlePasswordInvalid(const std::string &error = ""); - - private: - void handleElement(boost::shared_ptr); - void handleStreamStart(const ProtocolHeader& header); - void handleSessionFinished(const boost::optional&); - - void setInitialized(); - bool isInitialized() const { - return initialized; - } - - void handleTLSError() { } - void handleTLSConnected() { tlsConnected = true; } - - private: - std::string id_; - UserRegistry* userRegistry_; - bool authenticated_; - bool initialized; - bool allowSASLEXTERNAL; - std::string user_; - TLSServerLayer* tlsLayer; - bool tlsConnected; - }; -} +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#define HAVE_SWIFTEN_3 SWIFTEN_VERSION >= 0x030000 + +namespace Swift { + class ProtocolHeader; + class Element; + class Stanza; + class PayloadParserFactoryCollection; + class PayloadSerializerCollection; + class StreamStack; + class UserRegistry; + class XMPPLayer; + class ConnectionLayer; + class Connection; + class TLSServerLayer; + class TLSServerContextFactory; + class PKCS12Certificate; + + class ServerFromClientSession : public Session { + public: + ServerFromClientSession( + const std::string& id, + boost::shared_ptr connection, + PayloadParserFactoryCollection* payloadParserFactories, + PayloadSerializerCollection* payloadSerializers, + UserRegistry* userRegistry, + XMLParserFactory* factory, + Swift::JID remoteJID = Swift::JID()); + ~ServerFromClientSession(); + + boost::signal onSessionStarted; + void setAllowSASLEXTERNAL(); + const std::string &getUser() { + return user_; + } + + void addTLSEncryption(TLSServerContextFactory* tlsContextFactory, CertificateWithKey::ref cert); + + Swift::JID getBareJID() { + return Swift::JID(user_, getLocalJID().getDomain()); + } + + void handlePasswordValid(); + void handlePasswordInvalid(const std::string &error = ""); + + private: +#if HAVE_SWIFTEN_3 + void handleElement(boost::shared_ptr); +#else + void handleElement(boost::shared_ptr); +#endif + void handleStreamStart(const ProtocolHeader& header); + void handleSessionFinished(const boost::optional&); + + void setInitialized(); + bool isInitialized() const { + return initialized; + } + + void handleTLSError() { } + void handleTLSConnected() { tlsConnected = true; } + + private: + std::string id_; + UserRegistry* userRegistry_; + bool authenticated_; + bool initialized; + bool allowSASLEXTERNAL; + std::string user_; + TLSServerLayer* tlsLayer; + bool tlsConnected; + }; +} diff --git a/include/Swiften/Server/ServerStanzaChannel.cpp b/include/Swiften/Server/ServerStanzaChannel.cpp index 9597cc1c..af7dc752 100644 --- a/include/Swiften/Server/ServerStanzaChannel.cpp +++ b/include/Swiften/Server/ServerStanzaChannel.cpp @@ -1,176 +1,179 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#include "Swiften/Server/ServerStanzaChannel.h" -#include "Swiften/Base/Error.h" -#include - -#include - -namespace Swift { - -namespace { -// struct PriorityLessThan { -// bool operator()(const ServerSession* s1, const ServerSession* s2) const { -// return s1->getPriority() < s2->getPriority(); -// } -// }; - - struct HasJID { - HasJID(const JID& jid) : jid(jid) {} - bool operator()(const boost::shared_ptr session) const { - return session->getRemoteJID().equals(jid, JID::WithResource); - } - JID jid; - }; -} - -void ServerStanzaChannel::addSession(boost::shared_ptr session) { - sessions[session->getRemoteJID().toBare().toString()].push_back(session); - session->onSessionFinished.connect(boost::bind(&ServerStanzaChannel::handleSessionFinished, this, _1, session)); - session->onElementReceived.connect(boost::bind(&ServerStanzaChannel::handleElement, this, _1, session)); - session->onDataRead.connect(boost::bind(&ServerStanzaChannel::handleDataRead, this, _1, session)); -} - -void ServerStanzaChannel::removeSession(boost::shared_ptr session) { - session->onSessionFinished.disconnect(boost::bind(&ServerStanzaChannel::handleSessionFinished, this, _1, session)); - session->onElementReceived.disconnect(boost::bind(&ServerStanzaChannel::handleElement, this, _1, session)); - session->onDataRead.disconnect(boost::bind(&ServerStanzaChannel::handleDataRead, this, _1, session)); - std::list > &lst = sessions[session->getRemoteJID().toBare().toString()]; - lst.erase(std::remove(lst.begin(), lst.end(), session), lst.end()); -} - -void ServerStanzaChannel::sendIQ(boost::shared_ptr iq) { - send(iq); -} - -void ServerStanzaChannel::sendMessage(boost::shared_ptr message) { - send(message); -} - -void ServerStanzaChannel::sendPresence(boost::shared_ptr presence) { - send(presence); -} - -void ServerStanzaChannel::handleDataRead(const SafeByteArray &data, const boost::shared_ptr &session) { - if (safeByteArrayToString(data).find("") != std::string::npos) { - Swift::Presence::ref presence = Swift::Presence::create(); - presence->setFrom(session->getRemoteJID()); - presence->setType(Swift::Presence::Unavailable); - onPresenceReceived(presence); - } -} - -void ServerStanzaChannel::finishSession(const JID& to, boost::shared_ptr element, bool last) { - std::vector > candidateSessions; - for (std::list >::const_iterator i = sessions[to.toBare().toString()].begin(); i != sessions[to.toBare().toString()].end(); ++i) { - candidateSessions.push_back(*i); - } - - for (std::vector >::const_iterator i = candidateSessions.begin(); i != candidateSessions.end(); ++i) { - removeSession(*i); - if (element) { - (*i)->sendElement(element); - } - - if (last && (*i)->getRemoteJID().isValid()) { - Swift::Presence::ref presence = Swift::Presence::create(); - presence->setFrom((*i)->getRemoteJID()); - presence->setType(Swift::Presence::Unavailable); - onPresenceReceived(presence); - } - - (*i)->finishSession(); -// std::cout << "FINISH SESSION " << sessions[to.toBare().toString()].size() << "\n"; - if (last) { - break; - } - } -} - -std::string ServerStanzaChannel::getNewIQID() { - return idGenerator.generateID(); -} - -void ServerStanzaChannel::send(boost::shared_ptr stanza) { - JID to = stanza->getTo(); - assert(to.isValid()); - - // For a full JID, first try to route to a session with the full JID - if (!to.isBare()) { - std::list >::const_iterator i = std::find_if(sessions[stanza->getTo().toBare().toString()].begin(), sessions[stanza->getTo().toBare().toString()].end(), HasJID(to)); - if (i != sessions[stanza->getTo().toBare().toString()].end()) { - (*i)->sendElement(stanza); - return; - } - } - - // Look for candidate sessions - to = to.toBare(); - std::vector > candidateSessions; - for (std::list >::const_iterator i = sessions[stanza->getTo().toBare().toString()].begin(); i != sessions[stanza->getTo().toBare().toString()].end(); ++i) { - if ((*i)->getRemoteJID().equals(to, JID::WithoutResource)) { - candidateSessions.push_back(*i); - (*i)->sendElement(stanza); - } - } - if (candidateSessions.empty()) { - return; - } - - // Find the session with the highest priority -// std::vector::const_iterator i = std::max_element(sessions.begin(), sessions.end(), PriorityLessThan()); -// (*i)->sendStanza(stanza); - return; -} - -void ServerStanzaChannel::handleSessionFinished(const boost::optional&, const boost::shared_ptr& session) { - removeSession(session); - -// if (!session->initiatedFinish()) { - Swift::Presence::ref presence = Swift::Presence::create(); - presence->setFrom(session->getRemoteJID()); - presence->setType(Swift::Presence::Unavailable); - onPresenceReceived(presence); -// } -} - -void ServerStanzaChannel::handleElement(boost::shared_ptr element, const boost::shared_ptr& session) { - boost::shared_ptr stanza = boost::dynamic_pointer_cast(element); - if (!stanza) { - return; - } - - stanza->setFrom(session->getRemoteJID()); - - if (!stanza->getFrom().isValid()) - return; - - - boost::shared_ptr message = boost::dynamic_pointer_cast(stanza); - if (message) { - onMessageReceived(message); - return; - } - - boost::shared_ptr presence = boost::dynamic_pointer_cast(stanza); - if (presence) { - onPresenceReceived(presence); - return; - } - - boost::shared_ptr iq = boost::dynamic_pointer_cast(stanza); - if (iq) { - onIQReceived(iq); - return; - } -} - -void ServerStanzaChannel::handleSessionInitialized() { - onAvailableChanged(true); -} - -} +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swiften/Server/ServerStanzaChannel.h" +#include "Swiften/Base/Error.h" +#include + +#include + +namespace Swift { + +namespace { +// struct PriorityLessThan { +// bool operator()(const ServerSession* s1, const ServerSession* s2) const { +// return s1->getPriority() < s2->getPriority(); +// } +// }; + + struct HasJID { + HasJID(const JID& jid) : jid(jid) {} + bool operator()(const boost::shared_ptr session) const { + return session->getRemoteJID().equals(jid, JID::WithResource); + } + JID jid; + }; +} + +void ServerStanzaChannel::addSession(boost::shared_ptr session) { + sessions[session->getRemoteJID().toBare().toString()].push_back(session); + session->onSessionFinished.connect(boost::bind(&ServerStanzaChannel::handleSessionFinished, this, _1, session)); + session->onElementReceived.connect(boost::bind(&ServerStanzaChannel::handleElement, this, _1, session)); + session->onDataRead.connect(boost::bind(&ServerStanzaChannel::handleDataRead, this, _1, session)); +} + +void ServerStanzaChannel::removeSession(boost::shared_ptr session) { + session->onSessionFinished.disconnect(boost::bind(&ServerStanzaChannel::handleSessionFinished, this, _1, session)); + session->onElementReceived.disconnect(boost::bind(&ServerStanzaChannel::handleElement, this, _1, session)); + session->onDataRead.disconnect(boost::bind(&ServerStanzaChannel::handleDataRead, this, _1, session)); + std::list > &lst = sessions[session->getRemoteJID().toBare().toString()]; + lst.erase(std::remove(lst.begin(), lst.end(), session), lst.end()); +} + +void ServerStanzaChannel::sendIQ(boost::shared_ptr iq) { + send(iq); +} + +void ServerStanzaChannel::sendMessage(boost::shared_ptr message) { + send(message); +} + +void ServerStanzaChannel::sendPresence(boost::shared_ptr presence) { + send(presence); +} + +void ServerStanzaChannel::handleDataRead(const SafeByteArray &data, const boost::shared_ptr &session) { + if (safeByteArrayToString(data).find("") != std::string::npos) { + Swift::Presence::ref presence = Swift::Presence::create(); + presence->setFrom(session->getRemoteJID()); + presence->setType(Swift::Presence::Unavailable); + onPresenceReceived(presence); + } +} +#if HAVE_SWIFTEN_3 +void ServerStanzaChannel::finishSession(const JID& to, boost::shared_ptr element, bool last) { +#else +void ServerStanzaChannel::finishSession(const JID& to, boost::shared_ptr element, bool last) { +#endif + std::vector > candidateSessions; + for (std::list >::const_iterator i = sessions[to.toBare().toString()].begin(); i != sessions[to.toBare().toString()].end(); ++i) { + candidateSessions.push_back(*i); + } + + for (std::vector >::const_iterator i = candidateSessions.begin(); i != candidateSessions.end(); ++i) { + removeSession(*i); + if (element) { + (*i)->sendElement(element); + } + + if (last && (*i)->getRemoteJID().isValid()) { + Swift::Presence::ref presence = Swift::Presence::create(); + presence->setFrom((*i)->getRemoteJID()); + presence->setType(Swift::Presence::Unavailable); + onPresenceReceived(presence); + } + + (*i)->finishSession(); +// std::cout << "FINISH SESSION " << sessions[to.toBare().toString()].size() << "\n"; + if (last) { + break; + } + } +} + +std::string ServerStanzaChannel::getNewIQID() { + return idGenerator.generateID(); +} + +void ServerStanzaChannel::send(boost::shared_ptr stanza) { + JID to = stanza->getTo(); + assert(to.isValid()); + + // For a full JID, first try to route to a session with the full JID + if (!to.isBare()) { + std::list >::const_iterator i = std::find_if(sessions[stanza->getTo().toBare().toString()].begin(), sessions[stanza->getTo().toBare().toString()].end(), HasJID(to)); + if (i != sessions[stanza->getTo().toBare().toString()].end()) { + (*i)->sendElement(stanza); + return; + } + } + + // Look for candidate sessions + to = to.toBare(); + std::vector > candidateSessions; + for (std::list >::const_iterator i = sessions[stanza->getTo().toBare().toString()].begin(); i != sessions[stanza->getTo().toBare().toString()].end(); ++i) { + if ((*i)->getRemoteJID().equals(to, JID::WithoutResource)) { + candidateSessions.push_back(*i); + (*i)->sendElement(stanza); + } + } + if (candidateSessions.empty()) { + return; + } + + // Find the session with the highest priority +// std::vector::const_iterator i = std::max_element(sessions.begin(), sessions.end(), PriorityLessThan()); +// (*i)->sendStanza(stanza); + return; +} + +void ServerStanzaChannel::handleSessionFinished(const boost::optional&, const boost::shared_ptr& session) { + removeSession(session); + +// if (!session->initiatedFinish()) { + Swift::Presence::ref presence = Swift::Presence::create(); + presence->setFrom(session->getRemoteJID()); + presence->setType(Swift::Presence::Unavailable); + onPresenceReceived(presence); +// } +} + +void ServerStanzaChannel::handleElement(boost::shared_ptr element, const boost::shared_ptr& session) { + boost::shared_ptr stanza = boost::dynamic_pointer_cast(element); + if (!stanza) { + return; + } + + stanza->setFrom(session->getRemoteJID()); + + if (!stanza->getFrom().isValid()) + return; + + + boost::shared_ptr message = boost::dynamic_pointer_cast(stanza); + if (message) { + onMessageReceived(message); + return; + } + + boost::shared_ptr presence = boost::dynamic_pointer_cast(stanza); + if (presence) { + onPresenceReceived(presence); + return; + } + + boost::shared_ptr iq = boost::dynamic_pointer_cast(stanza); + if (iq) { + onIQReceived(iq); + return; + } +} + +void ServerStanzaChannel::handleSessionInitialized() { + onAvailableChanged(true); +} + +} diff --git a/include/Swiften/Server/ServerStanzaChannel.h b/include/Swiften/Server/ServerStanzaChannel.h index 2a562761..3b9d4592 100644 --- a/include/Swiften/Server/ServerStanzaChannel.h +++ b/include/Swiften/Server/ServerStanzaChannel.h @@ -1,58 +1,62 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#pragma once - -#include - -#include "Swiften/Base/IDGenerator.h" -#include "Swiften/Server/ServerFromClientSession.h" -#include "Swiften/Client/StanzaChannel.h" -#include "Swiften/Elements/Message.h" -#include "Swiften/Elements/IQ.h" -#include "Swiften/Elements/Presence.h" -#include "Swiften/TLS/Certificate.h" - -namespace Swift { - class Error; - class ServerStanzaChannel : public StanzaChannel { - public: - void addSession(boost::shared_ptr session); - void removeSession(boost::shared_ptr session); - - void sendIQ(boost::shared_ptr iq); - void sendMessage(boost::shared_ptr message); - void sendPresence(boost::shared_ptr presence); - - void finishSession(const JID& to, boost::shared_ptr element, bool last = false); - - bool getStreamManagementEnabled() const { - return false; - } - - bool isAvailable() const { - return true; - } - - std::vector getPeerCertificateChain() const { - return std::vector(); - } - - private: - std::string getNewIQID(); - void send(boost::shared_ptr stanza); - void handleSessionFinished(const boost::optional&, const boost::shared_ptr &session); - void handleElement(boost::shared_ptr element, const boost::shared_ptr &session); - void handleDataRead(const SafeByteArray &data, const boost::shared_ptr &session); - void handleSessionInitialized(); - - private: - IDGenerator idGenerator; - // [JID][resources][ServerFromClientSession] - std::map > > sessions; - }; - -} +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include + +#include "Swiften/Base/IDGenerator.h" +#include "Swiften/Server/ServerFromClientSession.h" +#include "Swiften/Client/StanzaChannel.h" +#include "Swiften/Elements/Message.h" +#include "Swiften/Elements/IQ.h" +#include "Swiften/Elements/Presence.h" +#include "Swiften/TLS/Certificate.h" +#include +#define HAVE_SWIFTEN_3 SWIFTEN_VERSION >= 0x030000 + +namespace Swift { + class Error; + class ServerStanzaChannel : public StanzaChannel { + public: + void addSession(boost::shared_ptr session); + void removeSession(boost::shared_ptr session); + + void sendIQ(boost::shared_ptr iq); + void sendMessage(boost::shared_ptr message); + void sendPresence(boost::shared_ptr presence); +#if HAVE_SWIFTEN_3 + void finishSession(const JID& to, boost::shared_ptr element, bool last = false); +#else + void finishSession(const JID& to, boost::shared_ptr element, bool last = false); +#endif + bool getStreamManagementEnabled() const { + return false; + } + + bool isAvailable() const { + return true; + } + + std::vector getPeerCertificateChain() const { + return std::vector(); + } + + private: + std::string getNewIQID(); + void send(boost::shared_ptr stanza); + void handleSessionFinished(const boost::optional&, const boost::shared_ptr &session); + void handleElement(boost::shared_ptr element, const boost::shared_ptr &session); + void handleDataRead(const SafeByteArray &data, const boost::shared_ptr &session); + void handleSessionInitialized(); + + private: + IDGenerator idGenerator; + // [JID][resources][ServerFromClientSession] + std::map > > sessions; + }; + +} diff --git a/include/transport/filetransfermanager.h b/include/transport/filetransfermanager.h index 322962c5..cc4714a8 100644 --- a/include/transport/filetransfermanager.h +++ b/include/transport/filetransfermanager.h @@ -1,68 +1,83 @@ -/** - * 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 -#include -#include -#include -#include -#include -#include - -namespace Transport { - -class UserManager; -class User; -class Component; -class Buddy; - -class FileTransferManager { - public: - typedef struct Transfer { - boost::shared_ptr ft; - Swift::JID from; - Swift::JID to; - boost::shared_ptr readByteStream; - } Transfer; - - FileTransferManager(Component *component, UserManager *userManager); - virtual ~FileTransferManager(); - - FileTransferManager::Transfer sendFile(User *user, Buddy *buddy, boost::shared_ptr byteStream, const Swift::StreamInitiationFileInfo &info); - - private: - Component *m_component; - UserManager *m_userManager; - Swift::CombinedOutgoingFileTransferManager* m_outgoingFTManager; - Swift::RemoteJingleTransportCandidateSelectorFactory* m_remoteCandidateSelectorFactory; - Swift::LocalJingleTransportCandidateGeneratorFactory* m_localCandidateGeneratorFactory; - Swift::JingleSessionManager *m_jingleSessionManager; - Swift::SOCKS5BytestreamRegistry* m_bytestreamRegistry; - Swift::SOCKS5BytestreamServer* m_bytestreamServer; - Swift::SOCKS5BytestreamProxy* m_bytestreamProxy; - Swift::SOCKS5BytestreamServer *bytestreamServer; - Swift::ConnectivityManager* m_connectivityManager; -}; - -} +/** + * 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 +#define HAVE_SWIFTEN_3 SWIFTEN_VERSION >= 0x030000 +#include +#if !HAVE_SWIFTEN_3 +#include +#endif +#include +#include +#if !HAVE_SWIFTEN_3 +#include +#include +#else +#include +#include +#endif +#include +#include +#if !HAVE_SWIFTEN_3 +#include +#endif + +namespace Transport { + +class UserManager; +class User; +class Component; +class Buddy; + +class FileTransferManager { + public: + typedef struct Transfer { + boost::shared_ptr ft; + Swift::JID from; + Swift::JID to; + boost::shared_ptr readByteStream; + } Transfer; + + FileTransferManager(Component *component, UserManager *userManager); + virtual ~FileTransferManager(); + + FileTransferManager::Transfer sendFile(User *user, Buddy *buddy, boost::shared_ptr byteStream, const Swift::StreamInitiationFileInfo &info); + + private: + Component *m_component; + UserManager *m_userManager; + Swift::CombinedOutgoingFileTransferManager* m_outgoingFTManager; + Swift::RemoteJingleTransportCandidateSelectorFactory* m_remoteCandidateSelectorFactory; + Swift::LocalJingleTransportCandidateGeneratorFactory* m_localCandidateGeneratorFactory; + Swift::JingleSessionManager *m_jingleSessionManager; + Swift::SOCKS5BytestreamRegistry* m_bytestreamRegistry; +#if HAVE_SWIFTEN_3 + Swift::SOCKS5BytestreamServerManager* m_proxyServerManager; + Swift::SOCKS5BytestreamProxiesManager *m_proxyManager; +#else + Swift::SOCKS5BytestreamServer* m_bytestreamServer; + Swift::SOCKS5BytestreamProxy* m_bytestreamProxy; + Swift::SOCKS5BytestreamServer *bytestreamServer; + Swift::ConnectivityManager* m_connectivityManager; +#endif +}; + +} diff --git a/include/transport/networkpluginserver.h b/include/transport/networkpluginserver.h index 44a20b26..0c5d1160 100644 --- a/include/transport/networkpluginserver.h +++ b/include/transport/networkpluginserver.h @@ -1,195 +1,199 @@ -/** - * 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/Presence/PresenceOracle.h" -#include "Swiften/Disco/EntityCapsManager.h" -#include "Swiften/Network/BoostConnectionServer.h" -#include "Swiften/Network/Connection.h" -#include "Swiften/Elements/ChatState.h" -#include "Swiften/Elements/RosterItemPayload.h" -#include "Swiften/Elements/VCard.h" -#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" -#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h" -#include "Swiften/Parser/XMPPParser.h" -#include "Swiften/Parser/XMPPParserClient.h" -#include "Swiften/Serializer/XMPPSerializer.h" -#include "storagebackend.h" -#include "transport/filetransfermanager.h" - -namespace Transport { - -class UserManager; -class User; -class Component; -class Buddy; -class LocalBuddy; -class Config; -class NetworkConversation; -class VCardResponder; -class RosterResponder; -class BlockResponder; -class DummyReadBytestream; -class AdminInterface; -class DiscoItemsResponder; - -class NetworkPluginServer : Swift::XMPPParserClient { - public: - struct Backend { - int pongReceived; - std::list users; - Swift::SafeByteArray data; - boost::shared_ptr connection; - unsigned long res; - unsigned long init_res; - unsigned long shared; - bool acceptUsers; - bool longRun; - bool willDie; - std::string id; - }; - - NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager, DiscoItemsResponder *discoItemsResponder); - - virtual ~NetworkPluginServer(); - - void start(); - - void setAdminInterface(AdminInterface *adminInterface) { - m_adminInterface = adminInterface; - } - - int getBackendCount() { - return m_clients.size(); - } - - const std::list &getBackends() { - return m_clients; - } - - const std::vector &getCrashedBackends() { - return m_crashedBackends; - } - - void collectBackend(); - - bool moveToLongRunBackend(User *user); - - void handleMessageReceived(NetworkConversation *conv, boost::shared_ptr &message); - - public: - void handleNewClientConnection(boost::shared_ptr c); - void handleSessionFinished(Backend *c); - void handlePongReceived(Backend *c); - void handleDataRead(Backend *c, boost::shared_ptr data); - - void handleConnectedPayload(const std::string &payload); - void handleDisconnectedPayload(const std::string &payload); - void handleBuddyChangedPayload(const std::string &payload); - void handleBuddyRemovedPayload(const std::string &payload); - void handleConvMessagePayload(const std::string &payload, bool subject = false); - void handleConvMessageAckPayload(const std::string &payload); - void handleParticipantChangedPayload(const std::string &payload); - void handleRoomChangedPayload(const std::string &payload); - void handleVCardPayload(const std::string &payload); - void handleChatStatePayload(const std::string &payload, Swift::ChatState::ChatStateType type); - void handleAuthorizationPayload(const std::string &payload); - void handleAttentionPayload(const std::string &payload); - void handleStatsPayload(Backend *c, const std::string &payload); - void handleFTStartPayload(const std::string &payload); - void handleFTFinishPayload(const std::string &payload); - void handleFTDataPayload(Backend *b, const std::string &payload); - void handleQueryPayload(Backend *b, const std::string &payload); - void handleBackendConfigPayload(const std::string &payload); - void handleRoomListPayload(const std::string &payload); - void handleRawXML(const std::string &xml); - - void handleUserCreated(User *user); - void handleRoomJoined(User *user, const Swift::JID &who, const std::string &room, const std::string &nickname, const std::string &password); - void handleRoomLeft(User *user, const std::string &room); - void handleUserReadyToConnect(User *user); - void handleUserPresenceChanged(User *user, Swift::Presence::ref presence); - void handleUserDestroyed(User *user); - - void handleBuddyUpdated(Buddy *buddy, const Swift::RosterItemPayload &item); - void handleBuddyRemoved(Buddy *buddy); - void handleBuddyAdded(Buddy *buddy, const Swift::RosterItemPayload &item); - void handleUserBuddyAdded(User *user, Buddy *buddy); - void handleUserBuddyRemoved(User *user, Buddy *buddy); - - void handleBlockToggled(Buddy *buddy); - - void handleVCardUpdated(User *user, boost::shared_ptr vcard); - void handleVCardRequired(User *user, const std::string &name, unsigned int id); - - void handleFTStateChanged(Swift::FileTransfer::State state, const std::string &userName, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long id); - void handleFTAccepted(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID); - void handleFTRejected(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size); - void handleFTDataNeeded(Backend *b, unsigned long ftid); - - void handlePIDTerminated(unsigned long pid); - private: - void send(boost::shared_ptr &, const std::string &data); - - void pingTimeout(); - void sendPing(Backend *c); - Backend *getFreeClient(bool acceptUsers = true, bool longRun = false, bool check = false); - void connectWaitingUsers(); - void loginDelayFinished(); - void handleRawIQReceived(boost::shared_ptr iq); - void handleRawPresenceReceived(boost::shared_ptr presence); - - void handleStreamStart(const Swift::ProtocolHeader&) {} - - void handleElement(boost::shared_ptr element); - - void handleStreamEnd() {} - - UserManager *m_userManager; - VCardResponder *m_vcardResponder; - RosterResponder *m_rosterResponder; - BlockResponder *m_blockResponder; - Config *m_config; - boost::shared_ptr m_server; - std::list m_clients; - std::vector m_pids; - Swift::Timer::ref m_pingTimer; - Swift::Timer::ref m_collectTimer; - Swift::Timer::ref m_loginTimer; - Component *m_component; - std::list m_waitingUsers; - bool m_isNextLongRun; - std::map m_filetransfers; - FileTransferManager *m_ftManager; - std::vector m_crashedBackends; - AdminInterface *m_adminInterface; - bool m_startingBackend; - DiscoItemsResponder *m_discoItemsResponder; - time_t m_lastLogin; - Swift::XMPPParser *m_xmppParser; - Swift::FullPayloadParserFactoryCollection m_collection; - Swift::XMPPSerializer *m_serializer; - Swift::FullPayloadSerializerCollection m_collection2; - std::map m_id2resource; -}; - -} +/** + * 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/Presence/PresenceOracle.h" +#include "Swiften/Disco/EntityCapsManager.h" +#include "Swiften/Network/BoostConnectionServer.h" +#include "Swiften/Network/Connection.h" +#include "Swiften/Elements/ChatState.h" +#include "Swiften/Elements/RosterItemPayload.h" +#include "Swiften/Elements/VCard.h" +#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" +#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h" +#include "Swiften/Parser/XMPPParser.h" +#include "Swiften/Parser/XMPPParserClient.h" +#include "Swiften/Serializer/XMPPSerializer.h" +#include "storagebackend.h" +#include "transport/filetransfermanager.h" +#include +#define HAVE_SWIFTEN_3 SWIFTEN_VERSION >= 0x030000 + +namespace Transport { + +class UserManager; +class User; +class Component; +class Buddy; +class LocalBuddy; +class Config; +class NetworkConversation; +class VCardResponder; +class RosterResponder; +class BlockResponder; +class DummyReadBytestream; +class AdminInterface; +class DiscoItemsResponder; + +class NetworkPluginServer : Swift::XMPPParserClient { + public: + struct Backend { + int pongReceived; + std::list users; + Swift::SafeByteArray data; + boost::shared_ptr connection; + unsigned long res; + unsigned long init_res; + unsigned long shared; + bool acceptUsers; + bool longRun; + bool willDie; + std::string id; + }; + + NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager, DiscoItemsResponder *discoItemsResponder); + + virtual ~NetworkPluginServer(); + + void start(); + + void setAdminInterface(AdminInterface *adminInterface) { + m_adminInterface = adminInterface; + } + + int getBackendCount() { + return m_clients.size(); + } + + const std::list &getBackends() { + return m_clients; + } + + const std::vector &getCrashedBackends() { + return m_crashedBackends; + } + + void collectBackend(); + + bool moveToLongRunBackend(User *user); + + void handleMessageReceived(NetworkConversation *conv, boost::shared_ptr &message); + + public: + void handleNewClientConnection(boost::shared_ptr c); + void handleSessionFinished(Backend *c); + void handlePongReceived(Backend *c); + void handleDataRead(Backend *c, boost::shared_ptr data); + + void handleConnectedPayload(const std::string &payload); + void handleDisconnectedPayload(const std::string &payload); + void handleBuddyChangedPayload(const std::string &payload); + void handleBuddyRemovedPayload(const std::string &payload); + void handleConvMessagePayload(const std::string &payload, bool subject = false); + void handleConvMessageAckPayload(const std::string &payload); + void handleParticipantChangedPayload(const std::string &payload); + void handleRoomChangedPayload(const std::string &payload); + void handleVCardPayload(const std::string &payload); + void handleChatStatePayload(const std::string &payload, Swift::ChatState::ChatStateType type); + void handleAuthorizationPayload(const std::string &payload); + void handleAttentionPayload(const std::string &payload); + void handleStatsPayload(Backend *c, const std::string &payload); + void handleFTStartPayload(const std::string &payload); + void handleFTFinishPayload(const std::string &payload); + void handleFTDataPayload(Backend *b, const std::string &payload); + void handleQueryPayload(Backend *b, const std::string &payload); + void handleBackendConfigPayload(const std::string &payload); + void handleRoomListPayload(const std::string &payload); + void handleRawXML(const std::string &xml); + + void handleUserCreated(User *user); + void handleRoomJoined(User *user, const Swift::JID &who, const std::string &room, const std::string &nickname, const std::string &password); + void handleRoomLeft(User *user, const std::string &room); + void handleUserReadyToConnect(User *user); + void handleUserPresenceChanged(User *user, Swift::Presence::ref presence); + void handleUserDestroyed(User *user); + + void handleBuddyUpdated(Buddy *buddy, const Swift::RosterItemPayload &item); + void handleBuddyRemoved(Buddy *buddy); + void handleBuddyAdded(Buddy *buddy, const Swift::RosterItemPayload &item); + void handleUserBuddyAdded(User *user, Buddy *buddy); + void handleUserBuddyRemoved(User *user, Buddy *buddy); + + void handleBlockToggled(Buddy *buddy); + + void handleVCardUpdated(User *user, boost::shared_ptr vcard); + void handleVCardRequired(User *user, const std::string &name, unsigned int id); + + void handleFTStateChanged(Swift::FileTransfer::State state, const std::string &userName, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long id); + void handleFTAccepted(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID); + void handleFTRejected(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size); + void handleFTDataNeeded(Backend *b, unsigned long ftid); + + void handlePIDTerminated(unsigned long pid); + private: + void send(boost::shared_ptr &, const std::string &data); + + void pingTimeout(); + void sendPing(Backend *c); + Backend *getFreeClient(bool acceptUsers = true, bool longRun = false, bool check = false); + void connectWaitingUsers(); + void loginDelayFinished(); + void handleRawIQReceived(boost::shared_ptr iq); + void handleRawPresenceReceived(boost::shared_ptr presence); + + void handleStreamStart(const Swift::ProtocolHeader&) {} +#if HAVE_SWIFTEN_3 + void handleElement(boost::shared_ptr element); +#else + void handleElement(boost::shared_ptr element); +#endif + void handleStreamEnd() {} + + UserManager *m_userManager; + VCardResponder *m_vcardResponder; + RosterResponder *m_rosterResponder; + BlockResponder *m_blockResponder; + Config *m_config; + boost::shared_ptr m_server; + std::list m_clients; + std::vector m_pids; + Swift::Timer::ref m_pingTimer; + Swift::Timer::ref m_collectTimer; + Swift::Timer::ref m_loginTimer; + Component *m_component; + std::list m_waitingUsers; + bool m_isNextLongRun; + std::map m_filetransfers; + FileTransferManager *m_ftManager; + std::vector m_crashedBackends; + AdminInterface *m_adminInterface; + bool m_startingBackend; + DiscoItemsResponder *m_discoItemsResponder; + time_t m_lastLogin; + Swift::XMPPParser *m_xmppParser; + Swift::FullPayloadParserFactoryCollection m_collection; + Swift::XMPPSerializer *m_serializer; + Swift::FullPayloadSerializerCollection m_collection2; + std::map m_id2resource; +}; + +} diff --git a/include/transport/settingsadhoccommand.h b/include/transport/settingsadhoccommand.h index 02eff3d9..9e2708c0 100644 --- a/include/transport/settingsadhoccommand.h +++ b/include/transport/settingsadhoccommand.h @@ -1,75 +1,77 @@ -/** - * 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 - */ - -#pragma once - -#include -#include -#include -#include "transport/adhoccommand.h" -#include "transport/adhoccommandfactory.h" - - -namespace Transport { - -class Component; -class UserManager; -class StorageBackend; - -class SettingsAdHocCommand : public AdHocCommand { - public: - typedef enum { Init, WaitingForResponse } State; - - SettingsAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to); - - /// Destructor. - virtual ~SettingsAdHocCommand(); - - virtual boost::shared_ptr handleRequest(boost::shared_ptr payload); - - private: - boost::shared_ptr getForm(); - boost::shared_ptr handleResponse(boost::shared_ptr payload); - State m_state; -}; - -class SettingsAdHocCommandFactory : public AdHocCommandFactory { - public: - SettingsAdHocCommandFactory() { - m_userSettings["send_headlines"] = "0"; - m_userSettings["stay_connected"] = "0"; - } - - virtual ~SettingsAdHocCommandFactory() {} - - AdHocCommand *createAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to) { - return new SettingsAdHocCommand(component, userManager, storageBackend, initiator, to); - } - - std::string getNode() { - return "settings"; - } - - std::string getName() { - return "Transport settings"; - } -}; - -} +/** + * 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 + */ + +#pragma once + +#include +#include +#include +#include "transport/adhoccommand.h" +#include "transport/adhoccommandfactory.h" +#include +#define HAVE_SWIFTEN_3 SWIFTEN_VERSION >= 0x030000 + + +namespace Transport { + +class Component; +class UserManager; +class StorageBackend; + +class SettingsAdHocCommand : public AdHocCommand { + public: + typedef enum { Init, WaitingForResponse } State; + + SettingsAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to); + + /// Destructor. + virtual ~SettingsAdHocCommand(); + + virtual boost::shared_ptr handleRequest(boost::shared_ptr payload); + + private: + boost::shared_ptr getForm(); + boost::shared_ptr handleResponse(boost::shared_ptr payload); + State m_state; +}; + +class SettingsAdHocCommandFactory : public AdHocCommandFactory { + public: + SettingsAdHocCommandFactory() { + m_userSettings["send_headlines"] = "0"; + m_userSettings["stay_connected"] = "0"; + } + + virtual ~SettingsAdHocCommandFactory() {} + + AdHocCommand *createAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to) { + return new SettingsAdHocCommand(component, userManager, storageBackend, initiator, to); + } + + std::string getNode() { + return "settings"; + } + + std::string getName() { + return "Transport settings"; + } +}; + +} diff --git a/include/transport/userregistration.h b/include/transport/userregistration.h index 1c994423..d5fb8633 100644 --- a/include/transport/userregistration.h +++ b/include/transport/userregistration.h @@ -1,85 +1,87 @@ -/** - * 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/Queries/Responder.h" -#include "Swiften/Elements/InBandRegistrationPayload.h" -#include "Swiften/Elements/RosterPayload.h" -#include - -namespace Transport { - -struct UserInfo; -class Component; -class StorageBackend; -class UserManager; -class Config; - -/// Allows users to register the transport using service discovery. -class UserRegistration : public Swift::Responder { - public: - /// Creates new UserRegistration handler. - /// \param component Component associated with this class - /// \param userManager UserManager associated with this class - /// \param storageBackend StorageBackend where the registered users will be stored - UserRegistration(Component *component, UserManager *userManager, StorageBackend *storageBackend); - - /// Destroys UserRegistration. - ~UserRegistration(); - - /// Registers new user. This function stores user into database and subscribe user to transport. - /// \param userInfo UserInfo struct with informations about registered user - /// \return false if user is already registered - bool registerUser(const UserInfo &userInfo); - - /// Unregisters user. This function removes all data about user from databa, unsubscribe all buddies - /// managed by this transport and disconnects user if he's connected. - /// \param barejid bare JID of user to unregister - /// \return false if there is no such user registered - bool unregisterUser(const std::string &barejid); - - /// Called when new user has been registered. - /// \param userInfo UserInfo struct with informations about user - boost::signal onUserRegistered; - - /// Called when user has been unregistered. - /// \param userInfo UserInfo struct with informations about user - boost::signal onUserUnregistered; - - /// Called when user's registration has been updated. - /// \param userInfo UserInfo struct with informations about user - boost::signal onUserUpdated; - - private: - virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload); - virtual bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload); - - void handleRegisterRemoteRosterResponse(boost::shared_ptr payload, Swift::ErrorPayload::ref error, const UserInfo &row); - void handleUnregisterRemoteRosterResponse(boost::shared_ptr payload, Swift::ErrorPayload::ref error, const std::string &barejid); - - Component *m_component; - StorageBackend *m_storageBackend; - UserManager *m_userManager; - Config *m_config; - -}; - -} +/** + * 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/Queries/Responder.h" +#include "Swiften/Elements/InBandRegistrationPayload.h" +#include "Swiften/Elements/RosterPayload.h" +#include +#include +#define HAVE_SWIFTEN_3 SWIFTEN_VERSION >= 0x030000 + +namespace Transport { + +struct UserInfo; +class Component; +class StorageBackend; +class UserManager; +class Config; + +/// Allows users to register the transport using service discovery. +class UserRegistration : public Swift::Responder { + public: + /// Creates new UserRegistration handler. + /// \param component Component associated with this class + /// \param userManager UserManager associated with this class + /// \param storageBackend StorageBackend where the registered users will be stored + UserRegistration(Component *component, UserManager *userManager, StorageBackend *storageBackend); + + /// Destroys UserRegistration. + ~UserRegistration(); + + /// Registers new user. This function stores user into database and subscribe user to transport. + /// \param userInfo UserInfo struct with informations about registered user + /// \return false if user is already registered + bool registerUser(const UserInfo &userInfo); + + /// Unregisters user. This function removes all data about user from databa, unsubscribe all buddies + /// managed by this transport and disconnects user if he's connected. + /// \param barejid bare JID of user to unregister + /// \return false if there is no such user registered + bool unregisterUser(const std::string &barejid); + + /// Called when new user has been registered. + /// \param userInfo UserInfo struct with informations about user + boost::signal onUserRegistered; + + /// Called when user has been unregistered. + /// \param userInfo UserInfo struct with informations about user + boost::signal onUserUnregistered; + + /// Called when user's registration has been updated. + /// \param userInfo UserInfo struct with informations about user + boost::signal onUserUpdated; + + private: + virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload); + virtual bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload); + + void handleRegisterRemoteRosterResponse(boost::shared_ptr payload, Swift::ErrorPayload::ref error, const UserInfo &row); + void handleUnregisterRemoteRosterResponse(boost::shared_ptr payload, Swift::ErrorPayload::ref error, const std::string &barejid); + + Component *m_component; + StorageBackend *m_storageBackend; + UserManager *m_userManager; + Config *m_config; + +}; + +} diff --git a/msvc-deps/CMakeLists.txt b/msvc-deps/CMakeLists.txt index b9360e97..b2130fc9 100644 --- a/msvc-deps/CMakeLists.txt +++ b/msvc-deps/CMakeLists.txt @@ -1,2 +1 @@ -ADD_SUBDIRECTORY(sqlite3) -ADD_SUBDIRECTORY(curl) +ADD_SUBDIRECTORY(sqlite3) diff --git a/msvc-deps/curl b/msvc-deps/curl index 9ce2d700..f39b1c08 160000 --- a/msvc-deps/curl +++ b/msvc-deps/curl @@ -1 +1 @@ -Subproject commit 9ce2d7001939b795b45a8ce7700d1a3dcde0475d +Subproject commit f39b1c080129c01c8204d3a5a40aad038c7a57f3 diff --git a/src/discoinforesponder.cpp b/src/discoinforesponder.cpp index d3ea87da..93bf8388 100644 --- a/src/discoinforesponder.cpp +++ b/src/discoinforesponder.cpp @@ -1,161 +1,167 @@ -/** - * 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 -#include "Swiften/Disco/DiscoInfoResponder.h" -#include "Swiften/Queries/IQRouter.h" -#include "Swiften/Elements/DiscoInfo.h" -#include "transport/config.h" -#include "transport/logging.h" -#include "Swiften/Disco/CapsInfoGenerator.h" - -using namespace Swift; -using namespace boost; - -DEFINE_LOGGER(logger, "DiscoInfoResponder"); - -namespace Transport { - -DiscoInfoResponder::DiscoInfoResponder(Swift::IQRouter *router, Config *config) : Swift::GetResponder(router) { - m_config = config; - m_config->onBackendConfigUpdated.connect(boost::bind(&DiscoInfoResponder::updateFeatures, this)); - m_buddyInfo = NULL; - m_transportInfo.addIdentity(DiscoInfo::Identity(CONFIG_STRING(m_config, "identity.name"), - CONFIG_STRING(m_config, "identity.category"), - CONFIG_STRING(m_config, "identity.type"))); - - updateFeatures(); -} - -DiscoInfoResponder::~DiscoInfoResponder() { - delete m_buddyInfo; -} - -void DiscoInfoResponder::updateFeatures() { - std::list features2; - features2.push_back("jabber:iq:register"); - features2.push_back("jabber:iq:gateway"); - features2.push_back("jabber:iq:private"); - features2.push_back("http://jabber.org/protocol/disco#info"); - features2.push_back("http://jabber.org/protocol/commands"); - if (CONFIG_BOOL_DEFAULTED(m_config, "features.muc", false)) { - features2.push_back("http://jabber.org/protocol/muc"); - } - setTransportFeatures(features2); - - std::list features; - features.push_back("http://jabber.org/protocol/disco#items"); - features.push_back("http://jabber.org/protocol/disco#info"); - features.push_back("http://jabber.org/protocol/chatstates"); - features.push_back("http://jabber.org/protocol/xhtml-im"); - if (CONFIG_BOOL_DEFAULTED(m_config, "features.receipts", false)) { - features.push_back("urn:xmpp:receipts"); - } - setBuddyFeatures(features); -} - -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) { - delete m_buddyInfo; - m_buddyInfo = new Swift::DiscoInfo; - m_buddyInfo->addIdentity(DiscoInfo::Identity(CONFIG_STRING(m_config, "identity.name"), "client", "pc")); - - for (std::list::iterator it = f.begin(); it != f.end(); it++) { - if (!m_buddyInfo->hasFeature(*it)) { - m_buddyInfo->addFeature(*it); - } - } - - CapsInfoGenerator caps("spectrum"); - m_capsInfo = caps.generateCapsInfo(*m_buddyInfo); - onBuddyCapsInfoChanged(m_capsInfo); -} - -void DiscoInfoResponder::addRoom(const std::string &jid, const std::string &name) { - std::string j = jid; - boost::algorithm::to_lower(j); - m_rooms[j] = name; -} - -void DiscoInfoResponder::clearRooms() { - m_rooms.clear(); -} - -void DiscoInfoResponder::addAdHocCommand(const std::string &node, const std::string &name) { - m_commands[node] = node; -} - -bool DiscoInfoResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr info) { - // disco#info for transport - if (to.getNode().empty()) { - // Adhoc command - if (m_commands.find(info->getNode()) != m_commands.end()) { - boost::shared_ptr res(new DiscoInfo()); - res->addFeature("http://jabber.org/protocol/commands"); - res->addFeature("jabber:x:data"); - res->addIdentity(DiscoInfo::Identity(m_commands[info->getNode()], "automation", "command-node")); - res->setNode(info->getNode()); - sendResponse(from, to, id, res); - } - else if (info->getNode() == "http://jabber.org/protocol/commands") { - boost::shared_ptr res(new DiscoInfo()); - res->addIdentity(DiscoInfo::Identity("Commands", "automation", "command-list")); - res->setNode(info->getNode()); - sendResponse(from, to, id, res); - } - else { - if (!info->getNode().empty()) { - sendError(from, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel); - return true; - } - - boost::shared_ptr res(new DiscoInfo(m_transportInfo)); - res->setNode(info->getNode()); - sendResponse(from, id, res); - } - } - // disco#info for room - else if (m_rooms.find(to.toBare().toString()) != m_rooms.end()) { - boost::shared_ptr res(new DiscoInfo()); - res->addIdentity(DiscoInfo::Identity(m_rooms[to.toBare().toString()], "conference", "text")); - res->setNode(info->getNode()); - sendResponse(from, to, id, res); - } - // disco#info for buddy - else { - boost::shared_ptr res(new DiscoInfo(*m_buddyInfo)); - res->setNode(info->getNode()); - sendResponse(from, to, id, res); - } - return true; -} - -} +/** + * 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 +#include "Swiften/Disco/DiscoInfoResponder.h" +#include "Swiften/Queries/IQRouter.h" +#include "Swiften/Elements/DiscoInfo.h" +#include "transport/config.h" +#include "transport/logging.h" +#include "Swiften/Disco/CapsInfoGenerator.h" + +using namespace Swift; +using namespace boost; + +DEFINE_LOGGER(logger, "DiscoInfoResponder"); + +namespace Transport { + +DiscoInfoResponder::DiscoInfoResponder(Swift::IQRouter *router, Config *config) : Swift::GetResponder(router) { + m_config = config; + m_config->onBackendConfigUpdated.connect(boost::bind(&DiscoInfoResponder::updateFeatures, this)); + m_buddyInfo = NULL; + m_transportInfo.addIdentity(DiscoInfo::Identity(CONFIG_STRING(m_config, "identity.name"), + CONFIG_STRING(m_config, "identity.category"), + CONFIG_STRING(m_config, "identity.type"))); +#if HAVE_SWIFTEN_3 + crypto = boost::shared_ptr(PlatformCryptoProvider::create()); +#endif + + updateFeatures(); +} + +DiscoInfoResponder::~DiscoInfoResponder() { + delete m_buddyInfo; +} + +void DiscoInfoResponder::updateFeatures() { + std::list features2; + features2.push_back("jabber:iq:register"); + features2.push_back("jabber:iq:gateway"); + features2.push_back("jabber:iq:private"); + features2.push_back("http://jabber.org/protocol/disco#info"); + features2.push_back("http://jabber.org/protocol/commands"); + if (CONFIG_BOOL_DEFAULTED(m_config, "features.muc", false)) { + features2.push_back("http://jabber.org/protocol/muc"); + } + setTransportFeatures(features2); + + std::list features; + features.push_back("http://jabber.org/protocol/disco#items"); + features.push_back("http://jabber.org/protocol/disco#info"); + features.push_back("http://jabber.org/protocol/chatstates"); + features.push_back("http://jabber.org/protocol/xhtml-im"); + if (CONFIG_BOOL_DEFAULTED(m_config, "features.receipts", false)) { + features.push_back("urn:xmpp:receipts"); + } + setBuddyFeatures(features); +} + +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) { + delete m_buddyInfo; + m_buddyInfo = new Swift::DiscoInfo; + m_buddyInfo->addIdentity(DiscoInfo::Identity(CONFIG_STRING(m_config, "identity.name"), "client", "pc")); + + for (std::list::iterator it = f.begin(); it != f.end(); it++) { + if (!m_buddyInfo->hasFeature(*it)) { + m_buddyInfo->addFeature(*it); + } + } +#if HAVE_SWIFTEN_3 + CapsInfoGenerator caps("spectrum", crypto.get()); +#else + CapsInfoGenerator caps("spectrum"); +#endif + m_capsInfo = caps.generateCapsInfo(*m_buddyInfo); + onBuddyCapsInfoChanged(m_capsInfo); +} + +void DiscoInfoResponder::addRoom(const std::string &jid, const std::string &name) { + std::string j = jid; + boost::algorithm::to_lower(j); + m_rooms[j] = name; +} + +void DiscoInfoResponder::clearRooms() { + m_rooms.clear(); +} + +void DiscoInfoResponder::addAdHocCommand(const std::string &node, const std::string &name) { + m_commands[node] = node; +} + +bool DiscoInfoResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr info) { + // disco#info for transport + if (to.getNode().empty()) { + // Adhoc command + if (m_commands.find(info->getNode()) != m_commands.end()) { + boost::shared_ptr res(new DiscoInfo()); + res->addFeature("http://jabber.org/protocol/commands"); + res->addFeature("jabber:x:data"); + res->addIdentity(DiscoInfo::Identity(m_commands[info->getNode()], "automation", "command-node")); + res->setNode(info->getNode()); + sendResponse(from, to, id, res); + } + else if (info->getNode() == "http://jabber.org/protocol/commands") { + boost::shared_ptr res(new DiscoInfo()); + res->addIdentity(DiscoInfo::Identity("Commands", "automation", "command-list")); + res->setNode(info->getNode()); + sendResponse(from, to, id, res); + } + else { + if (!info->getNode().empty()) { + sendError(from, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel); + return true; + } + + boost::shared_ptr res(new DiscoInfo(m_transportInfo)); + res->setNode(info->getNode()); + sendResponse(from, id, res); + } + } + // disco#info for room + else if (m_rooms.find(to.toBare().toString()) != m_rooms.end()) { + boost::shared_ptr res(new DiscoInfo()); + res->addIdentity(DiscoInfo::Identity(m_rooms[to.toBare().toString()], "conference", "text")); + res->setNode(info->getNode()); + sendResponse(from, to, id, res); + } + // disco#info for buddy + else { + boost::shared_ptr res(new DiscoInfo(*m_buddyInfo)); + res->setNode(info->getNode()); + sendResponse(from, to, id, res); + } + return true; +} + +} diff --git a/src/discoinforesponder.h b/src/discoinforesponder.h index 08b0656e..6887f6a6 100644 --- a/src/discoinforesponder.h +++ b/src/discoinforesponder.h @@ -1,65 +1,75 @@ -/** - * 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 -#include "Swiften/Queries/GetResponder.h" -#include "Swiften/Elements/DiscoInfo.h" -#include "Swiften/Elements/CapsInfo.h" - -namespace Transport { - -class Config; - -class DiscoInfoResponder : public Swift::GetResponder { - public: - DiscoInfoResponder(Swift::IQRouter *router, Config *config); - ~DiscoInfoResponder(); - - void setTransportFeatures(std::list &features); - void setBuddyFeatures(std::list &features); - - void addRoom(const std::string &jid, const std::string &name); - void clearRooms(); - - void addAdHocCommand(const std::string &node, const std::string &name); - - boost::signal onBuddyCapsInfoChanged; - - Swift::CapsInfo &getBuddyCapsInfo() { - return m_capsInfo; - } - - private: - virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload); - void updateFeatures(); - - Swift::DiscoInfo m_transportInfo; - Swift::DiscoInfo *m_buddyInfo; - Config *m_config; - Swift::CapsInfo m_capsInfo; - std::map m_rooms; - std::map m_commands; -}; - -} +/** + * 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 +#include "Swiften/Queries/GetResponder.h" +#include "Swiften/Elements/DiscoInfo.h" +#include "Swiften/Elements/CapsInfo.h" +#include +#define HAVE_SWIFTEN_3 SWIFTEN_VERSION >= 0x030000 +#if HAVE_SWIFTEN_3 +#include +#include +#endif + + +namespace Transport { + +class Config; + +class DiscoInfoResponder : public Swift::GetResponder { + public: + DiscoInfoResponder(Swift::IQRouter *router, Config *config); + ~DiscoInfoResponder(); + + void setTransportFeatures(std::list &features); + void setBuddyFeatures(std::list &features); + + void addRoom(const std::string &jid, const std::string &name); + void clearRooms(); + + void addAdHocCommand(const std::string &node, const std::string &name); + + boost::signal onBuddyCapsInfoChanged; + + Swift::CapsInfo &getBuddyCapsInfo() { + return m_capsInfo; + } + + private: + virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload); + void updateFeatures(); + + Swift::DiscoInfo m_transportInfo; + Swift::DiscoInfo *m_buddyInfo; + Config *m_config; + Swift::CapsInfo m_capsInfo; + std::map m_rooms; + std::map m_commands; +#if HAVE_SWIFTEN_3 + boost::shared_ptr crypto; +#endif +}; + +} diff --git a/src/filetransfermanager.cpp b/src/filetransfermanager.cpp index dd893fb1..04b7b67b 100644 --- a/src/filetransfermanager.cpp +++ b/src/filetransfermanager.cpp @@ -1,89 +1,102 @@ -/** - * 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/filetransfermanager.h" -#include "transport/transport.h" -#include "transport/usermanager.h" -#include "transport/user.h" -#include "transport/buddy.h" -#include "transport/logging.h" -#include "Swiften/Network/ConnectionServerFactory.h" - -namespace Transport { - -DEFINE_LOGGER(logger, "FileTransferManager"); - -FileTransferManager::FileTransferManager(Component *component, UserManager *userManager) { - m_component = component; - m_userManager = userManager; - - m_jingleSessionManager = new Swift::JingleSessionManager(m_component->getIQRouter()); - m_connectivityManager = new Swift::ConnectivityManager(m_component->getNetworkFactories()->getNATTraverser()); - m_bytestreamRegistry = new Swift::SOCKS5BytestreamRegistry(); - m_bytestreamProxy = new Swift::SOCKS5BytestreamProxy(m_component->getNetworkFactories()->getConnectionFactory(), m_component->getNetworkFactories()->getTimerFactory()); - - m_localCandidateGeneratorFactory = new Swift::DefaultLocalJingleTransportCandidateGeneratorFactory(m_connectivityManager, m_bytestreamRegistry, m_bytestreamProxy, "thishouldnotbeused"); - m_remoteCandidateSelectorFactory = new Swift::DefaultRemoteJingleTransportCandidateSelectorFactory(m_component->getNetworkFactories()->getConnectionFactory(), m_component->getNetworkFactories()->getTimerFactory()); - - boost::shared_ptr server = m_component->getNetworkFactories()->getConnectionServerFactory()->createConnectionServer(19645); - server->start(); - m_bytestreamServer = new Swift::SOCKS5BytestreamServer(server, m_bytestreamRegistry); - m_bytestreamServer->start(); - - m_outgoingFTManager = new Swift::CombinedOutgoingFileTransferManager(m_jingleSessionManager, m_component->getIQRouter(), - m_userManager, m_remoteCandidateSelectorFactory, - m_localCandidateGeneratorFactory, m_bytestreamRegistry, - m_bytestreamProxy, m_component->getPresenceOracle(), - m_bytestreamServer); - -// WARNING: Swiften crashes when this is uncommented... But we probably need it for working Jingle FT -// m_connectivityManager->addListeningPort(19645); -} - -FileTransferManager::~FileTransferManager() { - m_bytestreamServer->stop(); - delete m_outgoingFTManager; - delete m_remoteCandidateSelectorFactory; - delete m_localCandidateGeneratorFactory; - delete m_jingleSessionManager; - delete m_bytestreamRegistry; - delete m_bytestreamServer; - delete m_bytestreamProxy; - delete m_connectivityManager; -} - -FileTransferManager::Transfer FileTransferManager::sendFile(User *user, Buddy *buddy, boost::shared_ptr byteStream, const Swift::StreamInitiationFileInfo &info) { - FileTransferManager::Transfer transfer; - transfer.from = buddy->getJID(); - transfer.to = user->getJID(); - transfer.readByteStream = byteStream; - - LOG4CXX_INFO(logger, "Starting FT from '" << transfer.from << "' to '" << transfer.to << "'") - - transfer.ft = m_outgoingFTManager->createOutgoingFileTransfer(transfer.from, transfer.to, transfer.readByteStream, info); -// if (transfer.ft) { -// m_filetransfers.push_back(ft); -// ft->onStateChange.connect(boost::bind(&User::handleFTStateChanged, this, _1, Buddy::JIDToLegacyName(from), info.getName(), info.getSize(), id)); -// transfer.ft->start(); -// } - return transfer; -} - -} +/** + * 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/filetransfermanager.h" +#include "transport/transport.h" +#include "transport/usermanager.h" +#include "transport/user.h" +#include "transport/buddy.h" +#include "transport/logging.h" +#include "Swiften/Network/ConnectionServerFactory.h" + +namespace Transport { + +DEFINE_LOGGER(logger, "FileTransferManager"); + +FileTransferManager::FileTransferManager(Component *component, UserManager *userManager) { + m_component = component; + m_userManager = userManager; + + m_jingleSessionManager = new Swift::JingleSessionManager(m_component->getIQRouter()); +#if !HAVE_SWIFTEN_3 + m_connectivityManager = new Swift::ConnectivityManager(m_component->getNetworkFactories()->getNATTraverser()); +#endif + m_bytestreamRegistry = new Swift::SOCKS5BytestreamRegistry(); +#if !HAVE_SWIFTEN_3 + m_bytestreamProxy = new Swift::SOCKS5BytestreamProxy(m_component->getNetworkFactories()->getConnectionFactory(), m_component->getNetworkFactories()->getTimerFactory()); + m_localCandidateGeneratorFactory = new Swift::DefaultLocalJingleTransportCandidateGeneratorFactory(m_connectivityManager, m_bytestreamRegistry, m_bytestreamProxy, "thishouldnotbeused"); + m_remoteCandidateSelectorFactory = new Swift::DefaultRemoteJingleTransportCandidateSelectorFactory(m_component->getNetworkFactories()->getConnectionFactory(), m_component->getNetworkFactories()->getTimerFactory()); +#else + m_proxyManager = new Swift::SOCKS5BytestreamProxiesManager(m_component->getNetworkFactories()->getConnectionFactory(), m_component->getNetworkFactories()->getTimerFactory(), m_component->getNetworkFactories()->getDomainNameResolver(), m_component->getIQRouter(), "bar.com"); +#endif + boost::shared_ptr server = m_component->getNetworkFactories()->getConnectionServerFactory()->createConnectionServer(19645); + server->start(); +#if HAVE_SWIFTEN_3 + m_proxyServerManager = new Swift::SOCKS5BytestreamServerManager(m_bytestreamRegistry, m_component->getNetworkFactories()->getConnectionServerFactory(), m_component->getNetworkFactories()->getNetworkEnvironment(), m_component->getNetworkFactories()->getNATTraverser()); +#else + m_bytestreamServer = new Swift::SOCKS5BytestreamServer(server, m_bytestreamRegistry); + m_bytestreamServer->start(); + m_outgoingFTManager = new Swift::CombinedOutgoingFileTransferManager(m_jingleSessionManager, m_component->getIQRouter(), + m_userManager, m_remoteCandidateSelectorFactory, + m_localCandidateGeneratorFactory, m_bytestreamRegistry, + m_bytestreamProxy, m_component->getPresenceOracle(), + m_bytestreamServer); +#endif + + + +// WARNING: Swiften crashes when this is uncommented... But we probably need it for working Jingle FT +// m_connectivityManager->addListeningPort(19645); +} + +FileTransferManager::~FileTransferManager() { +#if !HAVE_SWIFTEN_3 + m_bytestreamServer->stop(); + delete m_remoteCandidateSelectorFactory; + delete m_localCandidateGeneratorFactory; +#endif + delete m_outgoingFTManager; + delete m_jingleSessionManager; + delete m_bytestreamRegistry; +#if !HAVE_SWIFTEN_3 + delete m_bytestreamServer; + delete m_bytestreamProxy; + delete m_connectivityManager; +#endif +} + +FileTransferManager::Transfer FileTransferManager::sendFile(User *user, Buddy *buddy, boost::shared_ptr byteStream, const Swift::StreamInitiationFileInfo &info) { + FileTransferManager::Transfer transfer; + transfer.from = buddy->getJID(); + transfer.to = user->getJID(); + transfer.readByteStream = byteStream; + + LOG4CXX_INFO(logger, "Starting FT from '" << transfer.from << "' to '" << transfer.to << "'") + + transfer.ft = m_outgoingFTManager->createOutgoingFileTransfer(transfer.from, transfer.to, transfer.readByteStream, info); +// if (transfer.ft) { +// m_filetransfers.push_back(ft); +// ft->onStateChange.connect(boost::bind(&User::handleFTStateChanged, this, _1, Buddy::JIDToLegacyName(from), info.getName(), info.getSize(), id)); +// transfer.ft->start(); +// } + return transfer; +} + +} diff --git a/src/networkpluginserver.cpp b/src/networkpluginserver.cpp index 28cea64b..f9947335 100644 --- a/src/networkpluginserver.cpp +++ b/src/networkpluginserver.cpp @@ -1,2018 +1,2029 @@ -/** - * 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/networkpluginserver.h" -#include "transport/user.h" -#include "transport/transport.h" -#include "transport/storagebackend.h" -#include "transport/rostermanager.h" -#include "transport/usermanager.h" -#include "transport/conversationmanager.h" -#include "transport/localbuddy.h" -#include "transport/config.h" -#include "transport/conversation.h" -#include "transport/vcardresponder.h" -#include "transport/rosterresponder.h" -#include "transport/memoryreadbytestream.h" -#include "transport/logging.h" -#include "transport/admininterface.h" -#include "blockresponder.h" -#include "Swiften/Server/ServerStanzaChannel.h" -#include "Swiften/Elements/StreamError.h" -#include "Swiften/Network/BoostConnectionServer.h" -#include "Swiften/Network/ConnectionServerFactory.h" -#include "Swiften/Elements/AttentionPayload.h" -#include "Swiften/Elements/XHTMLIMPayload.h" -#include "Swiften/Elements/Delay.h" -#include "Swiften/Elements/DeliveryReceipt.h" -#include "Swiften/Elements/DeliveryReceiptRequest.h" -#include "Swiften/Elements/InvisiblePayload.h" -#include "Swiften/Elements/SpectrumErrorPayload.h" -#include "transport/protocol.pb.h" -#include "transport/util.h" -#include "transport/discoitemsresponder.h" - -#include "boost/date_time/posix_time/posix_time.hpp" -#include "boost/signal.hpp" - -#include "transport/utf8.h" - -#include -#include - -#ifdef _WIN32 -#include "windows.h" -#include -#else -#include "sys/wait.h" -#include "sys/signal.h" -#include -#include -#include "popt.h" -#endif - -using namespace Transport::Util; - -namespace Transport { - -static unsigned long backend_id; -static unsigned long bytestream_id; - -DEFINE_LOGGER(logger, "NetworkPluginServer"); - -static NetworkPluginServer *_server; - -class NetworkConversation : public Conversation { - public: - NetworkConversation(ConversationManager *conversationManager, const std::string &legacyName, bool muc = false) : Conversation(conversationManager, legacyName, muc) { - } - - // Called when there's new message to legacy network from XMPP network - void sendMessage(boost::shared_ptr &message) { - onMessageToSend(this, message); - } - - boost::signal &)> onMessageToSend; -}; - -class NetworkFactory : public Factory { - public: - NetworkFactory(NetworkPluginServer *nps) { - m_nps = nps; - } - - virtual ~NetworkFactory() {} - - // Creates new conversation (NetworkConversation in this case) - Conversation *createConversation(ConversationManager *conversationManager, const std::string &legacyName, bool isMuc) { - NetworkConversation *nc = new NetworkConversation(conversationManager, legacyName, isMuc); - nc->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, m_nps, _1, _2)); - return nc; - } - - // Creates new LocalBuddy - Buddy *createBuddy(RosterManager *rosterManager, const BuddyInfo &buddyInfo) { - LocalBuddy *buddy = new LocalBuddy(rosterManager, buddyInfo.id, buddyInfo.legacyName, buddyInfo.alias, buddyInfo.groups, (BuddyFlag) buddyInfo.flags); - if (!buddy->isValid()) { - delete buddy; - return NULL; - } - if (buddyInfo.subscription == "both") { - buddy->setSubscription(Buddy::Both); - } - else { - buddy->setSubscription(Buddy::Ask); - } - if (buddyInfo.settings.find("icon_hash") != buddyInfo.settings.end()) - buddy->setIconHash(buddyInfo.settings.find("icon_hash")->second.s); - return buddy; - } - - private: - NetworkPluginServer *m_nps; -}; - -// Wraps google protobuf payload into WrapperMessage and serialize it to string -#define WRAP(MESSAGE, TYPE) pbnetwork::WrapperMessage wrap; \ - wrap.set_type(TYPE); \ - wrap.set_payload(MESSAGE); \ - wrap.SerializeToString(&MESSAGE); - -// Executes new backend -static unsigned long exec_(const std::string& exePath, const char *host, const char *port, const char *log_id, const char *cmdlineArgs) { - // BACKEND_ID is replaced with unique ID. The ID is increasing for every backend. - std::string finalExePath = boost::replace_all_copy(exePath, "BACKEND_ID", boost::lexical_cast(backend_id++)); - -#ifdef _WIN32 - // Add host and port. - std::ostringstream fullCmdLine; - fullCmdLine << "\"" << finalExePath << "\" --host " << host << " --port " << port; - - if (cmdlineArgs) - fullCmdLine << " " << cmdlineArgs; - - LOG4CXX_INFO(logger, "Starting new backend " << fullCmdLine.str()); - - // We must provide a non-const buffer to CreateProcess below - std::vector rawCommandLineArgs( fullCmdLine.str().size() + 1 ); - wcscpy_s(&rawCommandLineArgs[0], rawCommandLineArgs.size(), utf8ToUtf16(fullCmdLine.str()).c_str()); - - STARTUPINFO si; - PROCESS_INFORMATION pi; - - ZeroMemory (&si, sizeof(si)); - si.cb=sizeof (si); - - if (! CreateProcess( - utf8ToUtf16(finalExePath).c_str(), - &rawCommandLineArgs[0], - 0, // process attributes - 0, // thread attributes - 0, // inherit handles - 0, // creation flags - 0, // environment - 0, // cwd - &si, - &pi - ) - ) { - LOG4CXX_ERROR(logger, "Could not start process"); - } - - return 0; -#else - // Add host and port. - finalExePath += std::string(" --host ") + host + " --port " + port + " --service.backend_id=" + log_id + " " + cmdlineArgs; - LOG4CXX_INFO(logger, "Starting new backend " << finalExePath); - - // Create array of char * from string using -lpopt library - char *p = (char *) malloc(finalExePath.size() + 1); - strcpy(p, finalExePath.c_str()); - int argc; - char **argv; - poptParseArgvString(p, &argc, (const char ***) &argv); - - // fork and exec - pid_t pid = fork(); - if ( pid == 0 ) { - setsid(); - // close all files - int maxfd=sysconf(_SC_OPEN_MAX); - for(int fd=3; fd 0) { - if (result != 0) { - _server->handlePIDTerminated((unsigned long)result); - if (WIFEXITED(status)) { - if (WEXITSTATUS(status) != 0) { -// LOG4CXX_ERROR(logger, "Backend can not be started, exit_code=" << WEXITSTATUS(status)); - } - } - else { -// LOG4CXX_ERROR(logger, "Backend can not be started"); - } - } - } -} -#endif - -static void handleBuddyPayload(LocalBuddy *buddy, const pbnetwork::Buddy &payload) { - // Set alias only if it's not empty. Backends are allowed to send empty alias if it has - // not changed. - if (!payload.alias().empty()) { - buddy->setAlias(payload.alias()); - } - - // Change groups if it's not empty. The same as above... - std::vector groups; - for (int i = 0; i < payload.group_size(); i++) { - std::string group; - utf8::replace_invalid(payload.group(i).begin(), payload.group(i).end(), std::back_inserter(group), '_'); - groups.push_back(group); - } - if (!groups.empty()) { - buddy->setGroups(groups); - } - - buddy->setStatus(Swift::StatusShow((Swift::StatusShow::Type) payload.status()), payload.statusmessage()); - buddy->setIconHash(payload.iconhash()); - buddy->setBlocked(payload.blocked()); -} - -NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager, DiscoItemsResponder *discoItemsResponder) { - _server = this; - m_ftManager = ftManager; - m_userManager = userManager; - m_config = config; - m_component = component; - m_isNextLongRun = false; - m_adminInterface = NULL; - m_startingBackend = false; - m_lastLogin = 0; - m_xmppParser = new Swift::XMPPParser(this, &m_collection, component->getNetworkFactories()->getXMLParserFactory()); - m_xmppParser->parse(""); - m_serializer = new Swift::XMPPSerializer(&m_collection2, Swift::ClientStreamType); - m_discoItemsResponder = discoItemsResponder; - m_component->m_factory = new NetworkFactory(this); - m_userManager->onUserCreated.connect(boost::bind(&NetworkPluginServer::handleUserCreated, this, _1)); - m_userManager->onUserDestroyed.connect(boost::bind(&NetworkPluginServer::handleUserDestroyed, this, _1)); - - m_component->onRawIQReceived.connect(boost::bind(&NetworkPluginServer::handleRawIQReceived, this, _1)); - - m_pingTimer = component->getNetworkFactories()->getTimerFactory()->createTimer(20000); - m_pingTimer->onTick.connect(boost::bind(&NetworkPluginServer::pingTimeout, this)); - m_pingTimer->start(); - - m_loginTimer = component->getNetworkFactories()->getTimerFactory()->createTimer(CONFIG_INT(config, "service.login_delay") * 1000); - m_loginTimer->onTick.connect(boost::bind(&NetworkPluginServer::loginDelayFinished, this)); - m_loginTimer->start(); - - if (CONFIG_INT(m_config, "service.memory_collector_time") != 0) { - m_collectTimer = component->getNetworkFactories()->getTimerFactory()->createTimer(CONFIG_INT(m_config, "service.memory_collector_time")); - m_collectTimer->onTick.connect(boost::bind(&NetworkPluginServer::collectBackend, this)); - m_collectTimer->start(); - } - - m_vcardResponder = new VCardResponder(component->getIQRouter(), component->getNetworkFactories(), userManager); - m_vcardResponder->onVCardRequired.connect(boost::bind(&NetworkPluginServer::handleVCardRequired, this, _1, _2, _3)); - m_vcardResponder->onVCardUpdated.connect(boost::bind(&NetworkPluginServer::handleVCardUpdated, this, _1, _2)); - m_vcardResponder->start(); - - m_rosterResponder = new RosterResponder(component->getIQRouter(), userManager); - m_rosterResponder->onBuddyAdded.connect(boost::bind(&NetworkPluginServer::handleBuddyAdded, this, _1, _2)); - m_rosterResponder->onBuddyRemoved.connect(boost::bind(&NetworkPluginServer::handleBuddyRemoved, this, _1)); - m_rosterResponder->onBuddyUpdated.connect(boost::bind(&NetworkPluginServer::handleBuddyUpdated, this, _1, _2)); - m_rosterResponder->start(); - - m_blockResponder = new BlockResponder(component->getIQRouter(), userManager); - m_blockResponder->onBlockToggled.connect(boost::bind(&NetworkPluginServer::handleBlockToggled, this, _1)); - m_blockResponder->start(); - - m_server = component->getNetworkFactories()->getConnectionServerFactory()->createConnectionServer(Swift::HostAddress(CONFIG_STRING(m_config, "service.backend_host")), boost::lexical_cast(CONFIG_STRING(m_config, "service.backend_port"))); - m_server->onNewConnection.connect(boost::bind(&NetworkPluginServer::handleNewClientConnection, this, _1)); -} - -NetworkPluginServer::~NetworkPluginServer() { - for (std::list::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) { - LOG4CXX_INFO(logger, "Stopping backend " << *it); - std::string message; - pbnetwork::WrapperMessage wrap; - wrap.set_type(pbnetwork::WrapperMessage_Type_TYPE_EXIT); - wrap.SerializeToString(&message); - - Backend *c = (Backend *) *it; - send(c->connection, message); - } - - m_pingTimer->stop(); - m_server->stop(); - m_server.reset(); - delete m_component->m_factory; - delete m_vcardResponder; - delete m_rosterResponder; - delete m_blockResponder; -} - -void NetworkPluginServer::start() { - m_server->start(); - - LOG4CXX_INFO(logger, "Listening on host " << CONFIG_STRING(m_config, "service.backend_host") << " port " << CONFIG_STRING(m_config, "service.backend_port")); - - while (true) { - unsigned long pid = exec_(CONFIG_STRING(m_config, "service.backend"), CONFIG_STRING(m_config, "service.backend_host").c_str(), CONFIG_STRING(m_config, "service.backend_port").c_str(), "1", m_config->getCommandLineArgs().c_str()); - LOG4CXX_INFO(logger, "Tried to spawn first backend with pid " << pid); - LOG4CXX_INFO(logger, "Backend should now connect to Spectrum2 instance. Spectrum2 won't accept any connection before backend connects"); - -#ifndef _WIN32 - // wait if the backend process will still be alive after 1 second - sleep(1); - pid_t result; - int status; - result = waitpid(-1, &status, WNOHANG); - if (result != 0) { - if (WIFEXITED(status)) { - if (WEXITSTATUS(status) != 0) { - if (status == 254) { - LOG4CXX_ERROR(logger, "Backend can not be started, because it needs database to store data, but the database backend is not configured."); - } - else { - LOG4CXX_ERROR(logger, "Backend can not be started, exit_code=" << WEXITSTATUS(status) << ", possible error: " << strerror(WEXITSTATUS(status))); - if (WEXITSTATUS(status) == ENOENT) { - LOG4CXX_ERROR(logger, "This usually means the path to backend executable defined in config file as '[service] backend=\"...\"' is wrong or the executable does not exists."); - } - - } - LOG4CXX_ERROR(logger, "Check backend log for more details"); - continue; - } - } - else { - LOG4CXX_ERROR(logger, "Backend can not be started"); - continue; - } - } - - m_pids.push_back(pid); - - signal(SIGCHLD, SigCatcher); -#endif - // quit the while loop - break; - } -} - -void NetworkPluginServer::loginDelayFinished() { - m_loginTimer->stop(); - connectWaitingUsers(); -} - -void NetworkPluginServer::handleNewClientConnection(boost::shared_ptr c) { - // Create new Backend instance - Backend *client = new Backend; - client->pongReceived = -1; - client->connection = c; - client->res = 0; - client->init_res = 0; - client->shared = 0; - // Until we receive first PONG from backend, backend is in willDie state. - client->willDie = true; - // Backend does not accept new clients automatically if it's long-running - client->acceptUsers = !m_isNextLongRun; - client->longRun = m_isNextLongRun; - - m_startingBackend = false; - - LOG4CXX_INFO(logger, "New" + (client->longRun ? std::string(" long-running") : "") + " backend " << client << " connected. Current backend count=" << (m_clients.size() + 1)); - - m_clients.push_front(client); - - c->onDisconnected.connect(boost::bind(&NetworkPluginServer::handleSessionFinished, this, client)); - c->onDataRead.connect(boost::bind(&NetworkPluginServer::handleDataRead, this, client, _1)); - sendPing(client); - - // sendPing sets pongReceived to 0, but we want to have it -1 to ignore this backend - // in first ::pingTimeout call, because it can be called right after this function - // and backend wouldn't have any time to response to ping. - client->pongReceived = -1; -} - -void NetworkPluginServer::handleSessionFinished(Backend *c) { - LOG4CXX_INFO(logger, "Backend " << c << " (ID=" << c->id << ") disconnected. Current backend count=" << (m_clients.size() - 1)); - - // This backend will do, so we can't reconnect users to it in User::handleDisconnected call - c->willDie = true; - - // If there are users associated with this backend, it must have crashed, so print error output - // and disconnect users - if (!c->users.empty()) { - m_crashedBackends.push_back(c->id); - } - - for (std::list::const_iterator it = c->users.begin(); it != c->users.end(); it++) { - LOG4CXX_ERROR(logger, "Backend " << c << " (ID=" << c->id << ") disconnected (probably crashed) with active user " << (*it)->getJID().toString()); - (*it)->setData(NULL); - (*it)->handleDisconnected("Internal Server Error, please reconnect."); - } - - std::string message; - pbnetwork::WrapperMessage wrap; - wrap.set_type(pbnetwork::WrapperMessage_Type_TYPE_EXIT); - wrap.SerializeToString(&message); - - send(c->connection, message); - - c->connection->onDisconnected.disconnect_all_slots(); - c->connection->onDataRead.disconnect_all_slots(); - c->connection->disconnect(); - c->connection.reset(); - - m_clients.remove(c); - delete c; -} - -void NetworkPluginServer::handleConnectedPayload(const std::string &data) { - pbnetwork::Connected payload; - if (payload.ParseFromString(data) == false) { - // TODO: ERROR - return; - } - - User *user = m_userManager->getUser(payload.user()); - if (!user) { - LOG4CXX_ERROR(logger, "Connected payload received for unknown user " << payload.user()); - return; - } - - user->setConnected(true); - m_component->m_userRegistry->onPasswordValid(payload.user()); -} - -void NetworkPluginServer::handleDisconnectedPayload(const std::string &data) { - pbnetwork::Disconnected payload; - if (payload.ParseFromString(data) == false) { - // TODO: ERROR - return; - } - - m_component->m_userRegistry->onPasswordInvalid(payload.user(), payload.message()); - - User *user = m_userManager->getUser(payload.user()); - if (!user) { - return; - } - user->handleDisconnected(payload.message(), (Swift::SpectrumErrorPayload::Error) payload.error()); -} - -void NetworkPluginServer::handleVCardPayload(const std::string &data) { - pbnetwork::VCard payload; - if (payload.ParseFromString(data) == false) { - std::cout << "PARSING ERROR\n"; - // TODO: ERROR - return; - } - std::string field; - - boost::shared_ptr vcard(new Swift::VCard()); - - utf8::replace_invalid(payload.fullname().begin(), payload.fullname().end(), std::back_inserter(field), '_'); - vcard->setFullName(field); - - field.clear(); - - utf8::replace_invalid(payload.nickname().begin(), payload.nickname().end(), std::back_inserter(field), '_'); - vcard->setNickname(field); - - vcard->setPhoto(Swift::createByteArray(payload.photo())); - - m_vcardResponder->sendVCard(payload.id(), vcard); -} - -void NetworkPluginServer::handleAuthorizationPayload(const std::string &data) { - pbnetwork::Buddy payload; - if (payload.ParseFromString(data) == false) { - // TODO: ERROR - return; - } - - User *user = m_userManager->getUser(payload.username()); - if (!user) - return; - - // Create subscribe presence and forward it to XMPP side - Swift::Presence::ref response = Swift::Presence::create(); - response->setTo(user->getJID()); - std::string name = payload.buddyname(); - - if (CONFIG_BOOL_DEFAULTED(m_config, "service.jid_escaping", true)) { - name = Swift::JID::getEscapedNode(name); - } - else { - if (name.find_last_of("@") != std::string::npos) { - name.replace(name.find_last_of("@"), 1, "%"); - } - } - - response->setFrom(Swift::JID(name, m_component->getJID().toString())); - response->setType(Swift::Presence::Subscribe); - m_component->getStanzaChannel()->sendPresence(response); -} - -void NetworkPluginServer::handleChatStatePayload(const std::string &data, Swift::ChatState::ChatStateType type) { - pbnetwork::Buddy payload; - if (payload.ParseFromString(data) == false) { - // TODO: ERROR - return; - } - - User *user = m_userManager->getUser(payload.username()); - if (!user) - return; - - // We're not creating new Conversation just because of chatstates. - // Some networks/clients spams with chatstates a lot and it leads to bigger memory usage. - NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname()); - if (!conv) { - return; - } - - // Forward chatstate - boost::shared_ptr msg(new Swift::Message()); - msg->addPayload(boost::make_shared(type)); - - conv->handleMessage(msg); -} - -void NetworkPluginServer::handleBuddyChangedPayload(const std::string &data) { - pbnetwork::Buddy payload; - if (payload.ParseFromString(data) == false) { - // TODO: ERROR - return; - } - - User *user = m_userManager->getUser(payload.username()); - if (!user) - return; - - LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(payload.buddyname()); - if (buddy) { - handleBuddyPayload(buddy, payload); - } - else { - if (payload.buddyname() == user->getUserInfo().uin) { - return; - } - - std::vector groups; - for (int i = 0; i < payload.group_size(); i++) { - groups.push_back(payload.group(i)); - } - if (CONFIG_BOOL_DEFAULTED(m_config, "service.jid_escaping", true)) { - buddy = new LocalBuddy(user->getRosterManager(), -1, payload.buddyname(), payload.alias(), groups, BUDDY_JID_ESCAPING); - } - else { - buddy = new LocalBuddy(user->getRosterManager(), -1, payload.buddyname(), payload.alias(), groups, BUDDY_NO_FLAG); - } - if (!buddy->isValid()) { - delete buddy; - return; - } - - buddy->setBlocked(payload.blocked()); - user->getRosterManager()->setBuddy(buddy); - buddy->setStatus(Swift::StatusShow((Swift::StatusShow::Type) payload.status()), payload.statusmessage()); - buddy->setIconHash(payload.iconhash()); - } -} - -void NetworkPluginServer::handleBuddyRemovedPayload(const std::string &data) { - pbnetwork::Buddy payload; - if (payload.ParseFromString(data) == false) { - // TODO: ERROR - return; - } - - User *user = m_userManager->getUser(payload.username()); - if (!user) - return; - - user->getRosterManager()->removeBuddy(payload.buddyname()); -} - -void NetworkPluginServer::handleParticipantChangedPayload(const std::string &data) { - pbnetwork::Participant payload; - if (payload.ParseFromString(data) == false) { - // TODO: ERROR - return; - } - - User *user = m_userManager->getUser(payload.username()); - if (!user) - return; - - NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.room()); - if (!conv) { - return; - } - - conv->handleParticipantChanged(payload.nickname(), (Conversation::ParticipantFlag) payload.flag(), payload.status(), payload.statusmessage(), payload.newname()); -} - -void NetworkPluginServer::handleRoomChangedPayload(const std::string &data) { - pbnetwork::Room payload; - if (payload.ParseFromString(data) == false) { - return; - } - - User *user = m_userManager->getUser(payload.username()); - if (!user) - return; - - NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.room()); - if (!conv) { - return; - } - - conv->setNickname(payload.nickname()); -} - -void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool subject) { - pbnetwork::ConversationMessage payload; - - if (payload.ParseFromString(data) == false) { - // TODO: ERROR - return; - } - - User *user = m_userManager->getUser(payload.username()); - if (!user) - return; - - // Message from legacy network triggers network acticity - user->updateLastActivity(); - - // Set proper body. - boost::shared_ptr msg(new Swift::Message()); - if (subject) { - msg->setSubject(payload.message()); - } - else { - msg->setBody(payload.message()); - } - - if (payload.headline()) { - msg->setType(Swift::Message::Headline); - } - - // Add xhtml-im payload. - if (CONFIG_BOOL(m_config, "service.enable_xhtml") && !payload.xhtml().empty()) { - msg->addPayload(boost::make_shared(payload.xhtml())); - } - - if (!payload.timestamp().empty()) { - boost::posix_time::ptime timestamp = boost::posix_time::from_iso_string(payload.timestamp()); - boost::shared_ptr delay(boost::make_shared()); - delay->setStamp(timestamp); - msg->addPayload(delay); - } - - NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname()); - - // We can't create Conversation for payload with nickname, because this means the message is from room, - // but this user is not in any room, so it's OK to just reject this message - if (!conv && !payload.nickname().empty()) { - return; - } - - if (conv && payload.pm()) { - conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname() + "/" + payload.nickname()); - if (!conv) { - conv = new NetworkConversation(user->getConversationManager(), payload.nickname()); - std::string name = payload.buddyname(); - conv->setRoom(name); - conv->setNickname(payload.buddyname() + "/" + payload.nickname()); - - user->getConversationManager()->addConversation(conv); - conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2)); - } - } - - // Create new Conversation if it does not exist - if (!conv) { - conv = new NetworkConversation(user->getConversationManager(), payload.buddyname()); - user->getConversationManager()->addConversation(conv); - conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2)); - } - - // Forward it - conv->handleMessage(msg, payload.nickname()); - m_userManager->messageToXMPPSent(); -} - -void NetworkPluginServer::handleConvMessageAckPayload(const std::string &data) { - pbnetwork::ConversationMessage payload; - - if (payload.ParseFromString(data) == false) { - // TODO: ERROR - return; - } - - User *user = m_userManager->getUser(payload.username()); - if (!user) - return; - - if (payload.id().empty()) { - LOG4CXX_WARN(logger, "Received message ack with empty ID, not forwarding to XMPP."); - return; - } - - boost::shared_ptr msg(new Swift::Message()); - msg->addPayload(boost::make_shared(payload.id())); - - NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname()); - - // Receipts don't create conversation - if (!conv) { - return; - } - - // Forward it - conv->handleMessage(msg); -} - -void NetworkPluginServer::handleAttentionPayload(const std::string &data) { - pbnetwork::ConversationMessage payload; - if (payload.ParseFromString(data) == false) { - // TODO: ERROR - return; - } - - User *user = m_userManager->getUser(payload.username()); - if (!user) - return; - - boost::shared_ptr msg(new Swift::Message()); - msg->setBody(payload.message()); - msg->addPayload(boost::make_shared()); - - // Attentions trigger new Conversation creation - NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname()); - if (!conv) { - conv = new NetworkConversation(user->getConversationManager(), payload.buddyname()); - user->getConversationManager()->addConversation(conv); - conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2)); - } - - conv->handleMessage(msg); -} - -void NetworkPluginServer::handleStatsPayload(Backend *c, const std::string &data) { - pbnetwork::Stats payload; - if (payload.ParseFromString(data) == false) { - // TODO: ERROR - return; - } - - c->res = payload.res(); - c->init_res = payload.init_res(); - c->shared = payload.shared(); - c->id = payload.id(); -} - -void NetworkPluginServer::handleFTStartPayload(const std::string &data) { - pbnetwork::File payload; - if (payload.ParseFromString(data) == false) { - // TODO: ERROR - return; - } - - User *user = m_userManager->getUser(payload.username()); - if (!user) - return; - - LOG4CXX_INFO(logger, "handleFTStartPayload " << payload.filename() << " " << payload.buddyname()); - - LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(payload.buddyname()); - if (!buddy) { - // TODO: escape? reject? - return; - } - - Swift::StreamInitiationFileInfo fileInfo; - fileInfo.setSize(payload.size()); - fileInfo.setName(payload.filename()); - - Backend *c = (Backend *) user->getData(); - boost::shared_ptr bytestream(new MemoryReadBytestream(payload.size())); - bytestream->onDataNeeded.connect(boost::bind(&NetworkPluginServer::handleFTDataNeeded, this, c, bytestream_id + 1)); - - LOG4CXX_INFO(logger, "jid=" << buddy->getJID()); - - FileTransferManager::Transfer transfer = m_ftManager->sendFile(user, buddy, bytestream, fileInfo); - if (!transfer.ft) { - handleFTRejected(user, payload.buddyname(), payload.filename(), payload.size()); - return; - } - - m_filetransfers[++bytestream_id] = transfer; - transfer.ft->onStateChange.connect(boost::bind(&NetworkPluginServer::handleFTStateChanged, this, _1, payload.username(), payload.buddyname(), payload.filename(), payload.size(), bytestream_id)); - transfer.ft->start(); -} - -void NetworkPluginServer::handleFTFinishPayload(const std::string &data) { - pbnetwork::File payload; - if (payload.ParseFromString(data) == false) { - // TODO: ERROR - return; - } - - if (payload.has_ftid()) { - if (m_filetransfers.find(payload.ftid()) != m_filetransfers.end()) { - FileTransferManager::Transfer &transfer = m_filetransfers[payload.ftid()]; - transfer.ft->cancel(); - } - else { - LOG4CXX_ERROR(logger, "FTFinishPayload for unknown ftid=" << payload.ftid()); - } - } - -} - -void NetworkPluginServer::handleFTDataPayload(Backend *b, const std::string &data) { - pbnetwork::FileTransferData payload; - if (payload.ParseFromString(data) == false) { - // TODO: ERROR - return; - } - -// User *user = m_userManager->getUser(payload.username()); -// if (!user) -// return; - - if (m_filetransfers.find(payload.ftid()) == m_filetransfers.end()) { - LOG4CXX_ERROR(logger, "Uknown filetransfer with id " << payload.ftid()); - return; - } - - FileTransferManager::Transfer &transfer = m_filetransfers[payload.ftid()]; - MemoryReadBytestream *bytestream = (MemoryReadBytestream *) transfer.readByteStream.get(); - - if (bytestream->appendData(payload.data()) > 5000000) { - pbnetwork::FileTransferData f; - f.set_ftid(payload.ftid()); - f.set_data(""); - - std::string message; - f.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_PAUSE); - - send(b->connection, message); - } -} - -void NetworkPluginServer::handleFTDataNeeded(Backend *b, unsigned long ftid) { - pbnetwork::FileTransferData f; - f.set_ftid(ftid); - f.set_data(""); - - std::string message; - f.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_CONTINUE); - - send(b->connection, message); -} - -void NetworkPluginServer::connectWaitingUsers() { - // some users are in queue waiting for this backend - while(!m_waitingUsers.empty()) { - // There's no new backend, so stop associating users and wait for new backend, - // which has been already spawned in getFreeClient() call. - if (getFreeClient(true, false, true) == NULL) - break; - - User *u = m_waitingUsers.front(); - m_waitingUsers.pop_front(); - - LOG4CXX_INFO(logger, "Associating " << u->getJID().toString() << " with this backend"); - - // associate backend with user - handleUserCreated(u); - - // connect user if it's ready - if (u->isReadyToConnect()) { - handleUserReadyToConnect(u); - } - } -} - -void NetworkPluginServer::handlePongReceived(Backend *c) { - // This could be first PONG from the backend - if (c->pongReceived == -1) { - // Backend is fully ready to handle requests - c->willDie = false; - - if (m_clients.size() == 1) { - // first backend connected, start the server, we're ready. - m_component->start(); - } - - connectWaitingUsers(); - } - - c->pongReceived = true; -} - -void NetworkPluginServer::handleQueryPayload(Backend *b, const std::string &data) { - pbnetwork::BackendConfig payload; - if (payload.ParseFromString(data) == false) { - // TODO: ERROR - return; - } - - if (!m_adminInterface) { - return; - } - - boost::shared_ptr msg(new Swift::Message()); - msg->setBody(payload.config()); - m_adminInterface->handleQuery(msg); - - pbnetwork::BackendConfig response; - response.set_config(msg->getBody()); - - std::string message; - response.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_QUERY); - - send(b->connection, message); -} - -void NetworkPluginServer::handleBackendConfigPayload(const std::string &data) { - pbnetwork::BackendConfig payload; - if (payload.ParseFromString(data) == false) { - // TODO: ERROR - return; - } - - m_config->updateBackendConfig(payload.config()); -} - -void NetworkPluginServer::handleRoomListPayload(const std::string &data) { - pbnetwork::RoomList payload; - if (payload.ParseFromString(data) == false) { - // TODO: ERROR - return; - } - - m_discoItemsResponder->clearRooms(); - for (int i = 0; i < payload.room_size() && i < payload.name_size(); i++) { - m_discoItemsResponder->addRoom(Swift::JID::getEscapedNode(payload.room(i)) + "@" + m_component->getJID().toString(), payload.name(i)); - } -} - -void NetworkPluginServer::handleElement(boost::shared_ptr element) { - boost::shared_ptr stanza = boost::dynamic_pointer_cast(element); - if (!stanza) { - return; - } - - User *user = m_userManager->getUser(stanza->getTo().toBare()); - if (!user) - return; - - Swift::JID originalJID = stanza->getFrom(); - NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(originalJID.toBare()); - - LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(stanza->getFrom().toBare()); - if (buddy) { - const Swift::JID &jid = buddy->getJID(); - if (stanza->getFrom().getResource().empty()) { - stanza->setFrom(Swift::JID(jid.getNode(), jid.getDomain())); - } - else { - stanza->setFrom(Swift::JID(jid.getNode(), jid.getDomain(), stanza->getFrom().getResource())); - } - } - else { - std::string name = stanza->getFrom().toBare(); - if (conv && conv->isMUC()) { - if (name.find_last_of("@") != std::string::npos) { - name.replace(name.find_last_of("@"), 1, "%"); - } - } - else { - if (CONFIG_BOOL_DEFAULTED(m_config, "service.jid_escaping", true)) { - name = Swift::JID::getEscapedNode(name); - } - else { - if (name.find_last_of("@") != std::string::npos) { - name.replace(name.find_last_of("@"), 1, "%"); - } - } - } - if (stanza->getFrom().getResource().empty()) { - stanza->setFrom(Swift::JID(name, m_component->getJID().toString())); - } - else { - stanza->setFrom(Swift::JID(name, m_component->getJID().toString(), stanza->getFrom().getResource())); - } - } - - boost::shared_ptr message = boost::dynamic_pointer_cast(stanza); - if (message) { - if (conv) { - conv->handleRawMessage(message); - return; - } - - m_component->getStanzaChannel()->sendMessage(message); - return; - } - - boost::shared_ptr presence = boost::dynamic_pointer_cast(stanza); - if (presence) { - if (buddy) { - if (!buddy->isAvailable() && presence->getType() != Swift::Presence::Unavailable) { - buddy->m_status.setType(Swift::StatusShow::Online); - } - buddy->handleRawPresence(presence); - } - else if (conv) { - conv->handleRawPresence(presence); - } - else { - m_component->getStanzaChannel()->sendPresence(presence); - } - - return; - } - - boost::shared_ptr iq = boost::dynamic_pointer_cast(stanza); - if (iq) { - if (m_id2resource.find(stanza->getTo().toBare().toString() + stanza->getID()) != m_id2resource.end()) { - iq->setTo(Swift::JID(iq->getTo().getNode(), iq->getTo().getDomain(), m_id2resource[stanza->getTo().toBare().toString() + stanza->getID()])); - m_id2resource.erase(stanza->getTo().toBare().toString() + stanza->getID()); - } - else { - Swift::Presence::ref highest = m_component->getPresenceOracle()->getHighestPriorityPresence(user->getJID()); - if (highest) { - iq->setTo(highest->getFrom()); - } else { - iq->setTo(user->getJID()); - } - } - m_component->getIQRouter()->sendIQ(iq); - return; - } -} - -void NetworkPluginServer::handleRawXML(const std::string &xml) { - m_xmppParser->parse(xml); -} - -void NetworkPluginServer::handleRawPresenceReceived(boost::shared_ptr presence) { - User *user = m_userManager->getUser(presence->getFrom().toBare()); - if (!user) - return; - - Backend *c = (Backend *) user->getData(); - if (!c) { - return; - } - - Swift::JID legacyname = Swift::JID(Buddy::JIDToLegacyName(presence->getTo())); - if (!presence->getTo().getResource().empty()) { - presence->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain(), presence->getTo().getResource())); - } - else { - presence->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain())); - } - - std::string xml = safeByteArrayToString(m_serializer->serializeElement(presence)); - WRAP(xml, pbnetwork::WrapperMessage_Type_TYPE_RAW_XML); - send(c->connection, xml); -} - -void NetworkPluginServer::handleRawIQReceived(boost::shared_ptr iq) { - User *user = m_userManager->getUser(iq->getFrom().toBare()); - if (!user) - return; - - Backend *c = (Backend *) user->getData(); - if (!c) { - return; - } - - if (iq->getType() == Swift::IQ::Get) { - m_id2resource[iq->getFrom().toBare().toString() + iq->getID()] = iq->getFrom().getResource(); - } - - Swift::JID legacyname = Swift::JID(Buddy::JIDToLegacyName(iq->getTo())); - if (!iq->getTo().getResource().empty()) { - iq->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain(), iq->getTo().getResource())); - } - else { - iq->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain())); - } - - std::string xml = safeByteArrayToString(m_serializer->serializeElement(iq)); - WRAP(xml, pbnetwork::WrapperMessage_Type_TYPE_RAW_XML); - send(c->connection, xml); -} - -void NetworkPluginServer::handleDataRead(Backend *c, boost::shared_ptr data) { - // Append data to buffer - c->data.insert(c->data.end(), data->begin(), data->end()); - - // Parse data while there are some - while (c->data.size() != 0) { - // expected_size of wrapper message - unsigned int expected_size; - - // if data is >= 4, we have whole header and we can - // read expected_size. - if (c->data.size() >= 4) { - expected_size = *((unsigned int*) &c->data[0]); - expected_size = ntohl(expected_size); - // If we don't have whole wrapper message, wait for next - // handleDataRead call. - if (c->data.size() - 4 < expected_size) - return; - } - else { - return; - } - - // Parse wrapper message and erase it from buffer. - pbnetwork::WrapperMessage wrapper; - if (wrapper.ParseFromArray(&c->data[4], expected_size) == false) { - std::cout << "PARSING ERROR " << expected_size << "\n"; - c->data.erase(c->data.begin(), c->data.begin() + 4 + expected_size); - continue; - } - c->data.erase(c->data.begin(), c->data.begin() + 4 + expected_size); - - // If backend is slow and it is sending us lot of message, there is possibility - // that we don't receive PONG response before timeout. However, if we received - // at least some data, it means backend is not dead and we can treat it as - // PONG received event. - if (c->pongReceived == false) { - c->pongReceived = true; - } - - // Handle payload in wrapper message - switch(wrapper.type()) { - case pbnetwork::WrapperMessage_Type_TYPE_CONNECTED: - handleConnectedPayload(wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_DISCONNECTED: - handleDisconnectedPayload(wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED: - handleBuddyChangedPayload(wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE: - handleConvMessagePayload(wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_ROOM_SUBJECT_CHANGED: - handleConvMessagePayload(wrapper.payload(), true); - break; - case pbnetwork::WrapperMessage_Type_TYPE_PONG: - handlePongReceived(c); - break; - case pbnetwork::WrapperMessage_Type_TYPE_PARTICIPANT_CHANGED: - handleParticipantChangedPayload(wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_ROOM_NICKNAME_CHANGED: - handleRoomChangedPayload(wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_VCARD: - handleVCardPayload(wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPING: - handleChatStatePayload(wrapper.payload(), Swift::ChatState::Composing); - break; - case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPED: - handleChatStatePayload(wrapper.payload(), Swift::ChatState::Paused); - break; - case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_STOPPED_TYPING: - handleChatStatePayload(wrapper.payload(), Swift::ChatState::Active); - break; - case pbnetwork::WrapperMessage_Type_TYPE_AUTH_REQUEST: - handleAuthorizationPayload(wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_ATTENTION: - handleAttentionPayload(wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_STATS: - handleStatsPayload(c, wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_FT_START: - handleFTStartPayload(wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_FT_FINISH: - handleFTFinishPayload(wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_FT_DATA: - handleFTDataPayload(c, wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_REMOVED: - handleBuddyRemovedPayload(wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_QUERY: - handleQueryPayload(c, wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_BACKEND_CONFIG: - handleBackendConfigPayload(wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_ROOM_LIST: - handleRoomListPayload(wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE_ACK: - handleConvMessageAckPayload(wrapper.payload()); - break; - case pbnetwork::WrapperMessage_Type_TYPE_RAW_XML: - handleRawXML(wrapper.payload()); - break; - default: - return; - } - } -} - -void NetworkPluginServer::send(boost::shared_ptr &c, const std::string &data) { - // generate header - size of wrapper message - uint32_t size = htonl(data.size()); - char *header = (char *) &size; - - // send header together with wrapper message - c->write(Swift::createSafeByteArray(std::string(header, 4) + data)); -} - -void NetworkPluginServer::pingTimeout() { - // TODO: move to separate timer, those 2 loops could be expensive - // Some users are connected for weeks and they are blocking backend to be destroyed and its memory - // to be freed. We are finding users who are inactive for more than "idle_reconnect_time" seconds and - // reconnect them to long-running backend, where they can idle hapilly till the end of ages. - time_t now = time(NULL); - std::vector usersToMove; - unsigned long diff = CONFIG_INT(m_config, "service.idle_reconnect_time"); - if (diff != 0) { - for (std::list::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) { - // Users from long-running backends can't be moved - if ((*it)->longRun) { - continue; - } - - // Find users which are inactive for more than 'diff' - BOOST_FOREACH(User *u, (*it)->users) { - if (now - u->getLastActivity() > diff) { - usersToMove.push_back(u); - } - } - } - - // Move inactive users to long-running backend. - BOOST_FOREACH(User *u, usersToMove) { - LOG4CXX_INFO(logger, "Moving user " << u->getJID().toString() << " to long-running backend"); - if (!moveToLongRunBackend(u)) - break; - } - } - - // We have to remove startingBackend flag otherwise 1 broken backend start could - // block the backend. - m_startingBackend = false; - - // check ping responses - std::vector toRemove; - for (std::list::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) { - // pong has been received OR backend just connected and did not have time to answer the ping - // request. - if ((*it)->pongReceived || (*it)->pongReceived == -1) { - // Don't send another ping if pongReceived == -1, because we've already sent one - // when registering backend. - if ((*it)->pongReceived) { - sendPing((*it)); - } - } - else { - LOG4CXX_INFO(logger, "Disconnecting backend " << (*it) << " (ID=" << (*it)->id << "). PING response not received."); - toRemove.push_back(*it); - -#ifndef WIN32 - // generate coredump for this backend to find out why it wasn't able to respond to PING - std::string pid = (*it)->id; - if (!pid.empty()) { - try { - kill(boost::lexical_cast(pid), SIGABRT); - } - catch (...) { } - } -#endif - } - - if ((*it)->users.size() == 0) { - LOG4CXX_INFO(logger, "Disconnecting backend " << (*it) << " (ID=" << (*it)->id << "). There are no users."); - toRemove.push_back(*it); - } - } - - BOOST_FOREACH(Backend *b, toRemove) { - handleSessionFinished(b); - } - - m_pingTimer->start(); -} - -void NetworkPluginServer::collectBackend() { - // Stop accepting new users to backend with the biggest memory usage. This prevents backends - // which are leaking to eat whole memory by connectin new users to legacy network. - LOG4CXX_INFO(logger, "Collect backend called, finding backend which will be set to die"); - unsigned long max = 0; - Backend *backend = NULL; - for (std::list::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) { - if ((*it)->res > max) { - max = (*it)->res; - backend = (*it); - } - } - - if (backend) { - if (m_collectTimer) { - m_collectTimer->start(); - } - LOG4CXX_INFO(logger, "Backend " << backend << " (ID=" << backend->id << ") is set to die"); - backend->acceptUsers = false; - } -} - -bool NetworkPluginServer::moveToLongRunBackend(User *user) { - // Check if user has already some backend - Backend *old = (Backend *) user->getData(); - if (!old) { - LOG4CXX_INFO(logger, "User " << user->getJID().toString() << " does not have old backend. Not moving."); - return true; - } - - // if he's already on long run, do nothing - if (old->longRun) { - LOG4CXX_INFO(logger, "User " << user->getJID().toString() << " is already on long-running backend. Not moving."); - return true; - } - - // Get free longrun backend, if there's no longrun backend, create one and wait - // for its connection - Backend *backend = getFreeClient(false, true); - if (!backend) { - LOG4CXX_INFO(logger, "No free long-running backend for user " << user->getJID().toString() << ". Will try later"); - return false; - } - - // old backend will trigger disconnection which has to be ignored to keep user online - user->setIgnoreDisconnect(true); - - // remove user from the old backend - // If backend is empty, it will be collected by pingTimeout - old->users.remove(user); - - // switch to new backend and connect - user->setData(backend); - backend->users.push_back(user); - - // connect him - handleUserReadyToConnect(user); - return true; -} - -void NetworkPluginServer::handleUserCreated(User *user) { - // Get free backend to handle this user or spawn new one if there's no free one. - Backend *c = getFreeClient(); - - // Add user to queue if there's no free backend to handle him so far. - if (!c) { - LOG4CXX_INFO(logger, "There is no backend to handle user " << user->getJID().toString() << ". Adding him to queue."); - m_waitingUsers.push_back(user); - return; - } - - // Associate users with backend - user->setData(c); - c->users.push_back(user); - - // Don't forget to disconnect these in handleUserDestroyed!!! - user->onReadyToConnect.connect(boost::bind(&NetworkPluginServer::handleUserReadyToConnect, this, user)); - user->onPresenceChanged.connect(boost::bind(&NetworkPluginServer::handleUserPresenceChanged, this, user, _1)); - user->onRawPresenceReceived.connect(boost::bind(&NetworkPluginServer::handleRawPresenceReceived, this, _1)); - user->onRoomJoined.connect(boost::bind(&NetworkPluginServer::handleRoomJoined, this, user, _1, _2, _3, _4)); - user->onRoomLeft.connect(boost::bind(&NetworkPluginServer::handleRoomLeft, this, user, _1)); - - user->getRosterManager()->onBuddyAdded.connect(boost::bind(&NetworkPluginServer::handleUserBuddyAdded, this, user, _1)); - user->getRosterManager()->onBuddyRemoved.connect(boost::bind(&NetworkPluginServer::handleUserBuddyRemoved, this, user, _1)); -} - -void NetworkPluginServer::handleUserReadyToConnect(User *user) { - UserInfo userInfo = user->getUserInfo(); - - pbnetwork::Login login; - login.set_user(user->getJID().toBare()); - login.set_legacyname(userInfo.uin); - login.set_password(userInfo.password); - - std::string message; - login.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LOGIN); - - Backend *c = (Backend *) user->getData(); - if (!c) { - return; - } - send(c->connection, message); -} - -void NetworkPluginServer::handleUserPresenceChanged(User *user, Swift::Presence::ref presence) { - if (presence->getShow() == Swift::StatusShow::None) - return; - - handleRawPresenceReceived(presence); - - UserInfo userInfo = user->getUserInfo(); - - pbnetwork::Status status; - status.set_username(user->getJID().toBare()); - - bool isInvisible = presence->getPayload() != NULL; - if (isInvisible) { - LOG4CXX_INFO(logger, "This presence is invisible"); - status.set_status((pbnetwork::STATUS_INVISIBLE)); - } - else { - status.set_status((pbnetwork::StatusType) presence->getShow()); - } - - status.set_statusmessage(presence->getStatus()); - - std::string message; - status.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_STATUS_CHANGED); - - Backend *c = (Backend *) user->getData(); - if (!c) { - return; - } - send(c->connection, message); -} - -void NetworkPluginServer::handleRoomJoined(User *user, const Swift::JID &who, const std::string &r, const std::string &nickname, const std::string &password) { - UserInfo userInfo = user->getUserInfo(); - - pbnetwork::Room room; - room.set_username(user->getJID().toBare()); - room.set_nickname(nickname); - room.set_room(r); - room.set_password(password); - - std::string message; - room.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_JOIN_ROOM); - - Backend *c = (Backend *) user->getData(); - if (!c) { - return; - } - send(c->connection, message); -} - -void NetworkPluginServer::handleRoomLeft(User *user, const std::string &r) { - UserInfo userInfo = user->getUserInfo(); - - pbnetwork::Room room; - room.set_username(user->getJID().toBare()); - room.set_nickname(""); - room.set_room(r); - room.set_password(""); - - std::string message; - room.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LEAVE_ROOM); - - Backend *c = (Backend *) user->getData(); - if (!c) { - return; - } - send(c->connection, message); -} - -void NetworkPluginServer::handleUserDestroyed(User *user) { - m_waitingUsers.remove(user); - UserInfo userInfo = user->getUserInfo(); - - user->onReadyToConnect.disconnect(boost::bind(&NetworkPluginServer::handleUserReadyToConnect, this, user)); - user->onPresenceChanged.disconnect(boost::bind(&NetworkPluginServer::handleUserPresenceChanged, this, user, _1)); - user->onRawPresenceReceived.disconnect(boost::bind(&NetworkPluginServer::handleRawPresenceReceived, this, _1)); - user->onRoomJoined.disconnect(boost::bind(&NetworkPluginServer::handleRoomJoined, this, user, _1, _2, _3, _4)); - user->onRoomLeft.disconnect(boost::bind(&NetworkPluginServer::handleRoomLeft, this, user, _1)); - - user->getRosterManager()->onBuddyAdded.disconnect(boost::bind(&NetworkPluginServer::handleUserBuddyAdded, this, user, _1)); - user->getRosterManager()->onBuddyRemoved.disconnect(boost::bind(&NetworkPluginServer::handleUserBuddyRemoved, this, user, _1)); - - pbnetwork::Logout logout; - logout.set_user(user->getJID().toBare()); - logout.set_legacyname(userInfo.uin); - - std::string message; - logout.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LOGOUT); - - Backend *c = (Backend *) user->getData(); - if (!c) { - return; - } - send(c->connection, message); - c->users.remove(user); - - // If backend should handle only one user, it must not accept another one before - // we kill it, so set up willDie to true - if (c->users.size() == 0 && CONFIG_INT(m_config, "service.users_per_backend") == 1) { - LOG4CXX_INFO(logger, "Backend " << c->id << " will die, because the last user disconnected"); - c->willDie = true; - } -} - -void NetworkPluginServer::handleMessageReceived(NetworkConversation *conv, boost::shared_ptr &msg) { - conv->getConversationManager()->getUser()->updateLastActivity(); - - if (CONFIG_BOOL_DEFAULTED(m_config, "features.rawxml", false)) { - Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData(); - if (!c) { - return; - } - Swift::JID legacyname = Swift::JID(Buddy::JIDToLegacyName(msg->getTo())); - if (!msg->getTo().getResource().empty()) { - msg->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain(), msg->getTo().getResource())); - } - else { - msg->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain())); - } - std::string xml = safeByteArrayToString(m_serializer->serializeElement(msg)); - WRAP(xml, pbnetwork::WrapperMessage_Type_TYPE_RAW_XML); - send(c->connection, xml); - return; - } - - boost::shared_ptr statePayload = msg->getPayload(); - if (statePayload) { - pbnetwork::WrapperMessage_Type type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED; - switch (statePayload->getChatState()) { - case Swift::ChatState::Active: - type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_STOPPED_TYPING; - break; - case Swift::ChatState::Composing: - type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPING; - break; - case Swift::ChatState::Paused: - type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPED; - break; - default: - break; - } - if (type != pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED) { - pbnetwork::Buddy buddy; - buddy.set_username(conv->getConversationManager()->getUser()->getJID().toBare()); - buddy.set_buddyname(conv->getLegacyName()); - - std::string message; - buddy.SerializeToString(&message); - - WRAP(message, type); - - Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData(); - if (!c) { - return; - } - send(c->connection, message); - } - } - - boost::shared_ptr attentionPayload = msg->getPayload(); - if (attentionPayload) { - pbnetwork::ConversationMessage m; - m.set_username(conv->getConversationManager()->getUser()->getJID().toBare()); - m.set_buddyname(conv->getLegacyName()); - m.set_message(msg->getBody()); - - std::string message; - m.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_ATTENTION); - - Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData(); - send(c->connection, message); - return; - } - - if (!msg->getSubject().empty()) { - pbnetwork::ConversationMessage m; - m.set_username(conv->getConversationManager()->getUser()->getJID().toBare()); - m.set_buddyname(conv->getLegacyName()); - m.set_message(msg->getSubject()); - - std::string message; - m.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_ROOM_SUBJECT_CHANGED); - - Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData(); - send(c->connection, message); - return; - } - - - std::string xhtml; - boost::shared_ptr xhtmlPayload = msg->getPayload(); - if (xhtmlPayload) { - xhtml = xhtmlPayload->getBody(); - } - - // Send normal message - if (!msg->getBody().empty() || !xhtml.empty()) { - pbnetwork::ConversationMessage m; - m.set_username(conv->getConversationManager()->getUser()->getJID().toBare()); - m.set_buddyname(conv->getLegacyName()); - m.set_message(msg->getBody()); - m.set_xhtml(xhtml); - boost::shared_ptr receiptPayload = msg->getPayload(); - if (receiptPayload && !msg->getID().empty()) { - m.set_id(msg->getID()); - } - - std::string message; - m.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE); - - Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData(); - if (!c) { - return; - } - send(c->connection, message); - } -} - -void NetworkPluginServer::handleBuddyRemoved(Buddy *b) { - User *user = b->getRosterManager()->getUser(); - - pbnetwork::Buddy buddy; - buddy.set_username(user->getJID().toBare()); - buddy.set_buddyname(b->getName()); - buddy.set_alias(b->getAlias()); - BOOST_FOREACH(const std::string &g, b->getGroups()) { - buddy.add_group(g); - } - buddy.set_status(pbnetwork::STATUS_NONE); - - std::string message; - buddy.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_REMOVED); - - Backend *c = (Backend *) user->getData(); - if (!c) { - return; - } - send(c->connection, message); -} - -void NetworkPluginServer::handleBuddyUpdated(Buddy *b, const Swift::RosterItemPayload &item) { - User *user = b->getRosterManager()->getUser(); - - dynamic_cast(b)->setAlias(item.getName()); - dynamic_cast(b)->setGroups(item.getGroups()); - - pbnetwork::Buddy buddy; - buddy.set_username(user->getJID().toBare()); - buddy.set_buddyname(b->getName()); - buddy.set_alias(b->getAlias()); - BOOST_FOREACH(const std::string &g, b->getGroups()) { - buddy.add_group(g); - } - buddy.set_status(pbnetwork::STATUS_NONE); - - std::string message; - buddy.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED); - - Backend *c = (Backend *) user->getData(); - if (!c) { - return; - } - send(c->connection, message); -} - -void NetworkPluginServer::handleBuddyAdded(Buddy *buddy, const Swift::RosterItemPayload &item) { - handleBuddyUpdated(buddy, item); -} - -void NetworkPluginServer::handleUserBuddyAdded(User *user, Buddy *b) { - pbnetwork::Buddy buddy; - buddy.set_username(user->getJID().toBare()); - buddy.set_buddyname(b->getName()); - buddy.set_alias(b->getAlias()); - BOOST_FOREACH(const std::string &g, b->getGroups()) { - buddy.add_group(g); - } - buddy.set_status(pbnetwork::STATUS_NONE); - - std::string message; - buddy.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED); - - Backend *c = (Backend *) user->getData(); - if (!c) { - return; - } - send(c->connection, message); -} - -void NetworkPluginServer::handleUserBuddyRemoved(User *user, Buddy *b) { - handleBuddyRemoved(b); -} - -void NetworkPluginServer::handleBlockToggled(Buddy *b) { - User *user = b->getRosterManager()->getUser(); - - pbnetwork::Buddy buddy; - buddy.set_username(user->getJID().toBare()); - buddy.set_buddyname(b->getName()); - buddy.set_alias(b->getAlias()); - BOOST_FOREACH(const std::string &g, b->getGroups()) { - buddy.add_group(g); - } - buddy.set_status(pbnetwork::STATUS_NONE); - buddy.set_blocked(!b->isBlocked()); - - std::string message; - buddy.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED); - - Backend *c = (Backend *) user->getData(); - if (!c) { - return; - } - send(c->connection, message); -} - - -void NetworkPluginServer::handleVCardUpdated(User *user, boost::shared_ptr v) { - pbnetwork::VCard vcard; - vcard.set_username(user->getJID().toBare()); - vcard.set_buddyname(""); - vcard.set_id(0); - vcard.set_photo(&v->getPhoto()[0], v->getPhoto().size()); - vcard.set_nickname(v->getNickname()); - - std::string message; - vcard.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_VCARD); - - Backend *c = (Backend *) user->getData(); - if (!c) { - return; - } - send(c->connection, message); -} - -void NetworkPluginServer::handleVCardRequired(User *user, const std::string &name, unsigned int id) { - pbnetwork::VCard vcard; - vcard.set_username(user->getJID().toBare()); - vcard.set_buddyname(name); - vcard.set_id(id); - - std::string message; - vcard.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_VCARD); - - Backend *c = (Backend *) user->getData(); - if (!c) { - return; - } - send(c->connection, message); -} - -void NetworkPluginServer::handleFTAccepted(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID) { - pbnetwork::File f; - f.set_username(user->getJID().toBare()); - f.set_buddyname(buddyName); - f.set_filename(fileName); - f.set_size(size); - f.set_ftid(ftID); - - std::string message; - f.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_START); - - Backend *c = (Backend *) user->getData(); - if (!c) { - return; - } - send(c->connection, message); -} - -void NetworkPluginServer::handleFTRejected(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size) { - pbnetwork::File f; - f.set_username(user->getJID().toBare()); - f.set_buddyname(buddyName); - f.set_filename(fileName); - f.set_size(size); - f.set_ftid(0); - - std::string message; - f.SerializeToString(&message); - - WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_FINISH); - - Backend *c = (Backend *) user->getData(); - if (!c) { - return; - } - send(c->connection, message); -} - -void NetworkPluginServer::handleFTStateChanged(Swift::FileTransfer::State state, const std::string &userName, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long id) { - User *user = m_userManager->getUser(userName); - if (!user) { - // TODO: FIXME We have to remove filetransfer when use disconnects - return; - } - if (state.state == Swift::FileTransfer::State::Transferring) { - handleFTAccepted(user, buddyName, fileName, size, id); - } - else if (state.state == Swift::FileTransfer::State::Canceled) { - handleFTRejected(user, buddyName, fileName, size); - } -} - -void NetworkPluginServer::sendPing(Backend *c) { - - std::string message; - pbnetwork::WrapperMessage wrap; - wrap.set_type(pbnetwork::WrapperMessage_Type_TYPE_PING); - wrap.SerializeToString(&message); - - if (c->connection) { - LOG4CXX_INFO(logger, "PING to " << c << " (ID=" << c->id << ")"); - send(c->connection, message); - c->pongReceived = false; - } -// LOG4CXX_INFO(logger, "PING to " << c); -} - -void NetworkPluginServer::handlePIDTerminated(unsigned long pid) { - std::vector::iterator log_id_it; - log_id_it = std::find(m_pids.begin(), m_pids.end(), pid); - if (log_id_it != m_pids.end()) { - *log_id_it = 0; - } -} - -#ifndef _WIN32 - -static int sig_block_count = 0; -static sigset_t block_mask; - -static void __block_signals ( void ) -{ - static int init_done = 0; - - if ( (sig_block_count++) != 1 ) return; - - if ( init_done == 0 ) { - sigemptyset ( &block_mask ); - sigaddset ( &block_mask, SIGPIPE ); - sigaddset ( &block_mask, SIGHUP ); - sigaddset ( &block_mask, SIGINT ); - sigaddset ( &block_mask, SIGQUIT ); - sigaddset ( &block_mask, SIGTERM ); - sigaddset ( &block_mask, SIGABRT ); - sigaddset ( &block_mask, SIGCHLD ); - init_done = 1; - } - - sigprocmask ( SIG_BLOCK, &block_mask, NULL ); - return; -} - -static void __unblock_signals ( void ) -{ - sigset_t sigset; - - if ( (sig_block_count--) != 0 ) return; - sigprocmask ( SIG_UNBLOCK, &block_mask, NULL ); - - if ( sigpending ( &sigset ) == 0 ) { - if ( sigismember ( &sigset, SIGCHLD ) ) { - raise ( SIGCHLD ); - } - } -} - -#endif - -NetworkPluginServer::Backend *NetworkPluginServer::getFreeClient(bool acceptUsers, bool longRun, bool check) { - NetworkPluginServer::Backend *c = NULL; - - unsigned long diff = CONFIG_INT(m_config, "service.login_delay"); - time_t now = time(NULL); - if (diff && (now - m_lastLogin < diff)) { - m_loginTimer->stop(); - m_loginTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer((diff - (now - m_lastLogin)) * 1000); - m_loginTimer->onTick.connect(boost::bind(&NetworkPluginServer::loginDelayFinished, this)); - m_loginTimer->start(); - LOG4CXX_INFO(logger, "Postponing login because of service.login_delay setting"); - return NULL; - } - - if (!check) { - m_lastLogin = time(NULL); - } - - // Check all backends and find free one - for (std::list::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) { - if ((*it)->willDie == false && (*it)->acceptUsers == acceptUsers && (*it)->users.size() < CONFIG_INT(m_config, "service.users_per_backend") && (*it)->connection && (*it)->longRun == longRun) { - c = *it; - // if we're not reusing all backends and backend is full, stop accepting new users on this backend - if (!CONFIG_BOOL(m_config, "service.reuse_old_backends")) { - if (!check && c->users.size() + 1 >= CONFIG_INT(m_config, "service.users_per_backend")) { - c->acceptUsers = false; - } - } - break; - } - } - - // there's no free backend, so spawn one. - if (c == NULL && !m_startingBackend) { - m_isNextLongRun = longRun; - m_startingBackend = true; - -#ifndef _WIN32 - __block_signals(); -#endif - std::vector::iterator log_id_it; - log_id_it = std::find(m_pids.begin(), m_pids.end(), 0); - std::string log_id = ""; - if (log_id_it == m_pids.end()) { - log_id = boost::lexical_cast(m_pids.size() + 1); - } - else { - log_id = boost::lexical_cast(log_id_it - m_pids.begin() + 1); - } - unsigned long pid = exec_(CONFIG_STRING(m_config, "service.backend"), CONFIG_STRING(m_config, "service.backend_host").c_str(), CONFIG_STRING(m_config, "service.backend_port").c_str(), log_id.c_str(), m_config->getCommandLineArgs().c_str()); - if (log_id_it == m_pids.end()) { - m_pids.push_back(pid); - } - else { - *log_id_it = pid; - } -#ifndef _WIN32 - __unblock_signals(); -#endif - } - - return c; -} - -} +/** + * 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/networkpluginserver.h" +#include "transport/user.h" +#include "transport/transport.h" +#include "transport/storagebackend.h" +#include "transport/rostermanager.h" +#include "transport/usermanager.h" +#include "transport/conversationmanager.h" +#include "transport/localbuddy.h" +#include "transport/config.h" +#include "transport/conversation.h" +#include "transport/vcardresponder.h" +#include "transport/rosterresponder.h" +#include "transport/memoryreadbytestream.h" +#include "transport/logging.h" +#include "transport/admininterface.h" +#include "blockresponder.h" +#include "Swiften/Server/ServerStanzaChannel.h" +#include "Swiften/Elements/StreamError.h" +#include "Swiften/Network/BoostConnectionServer.h" +#include "Swiften/Network/ConnectionServerFactory.h" +#include "Swiften/Elements/AttentionPayload.h" +#include "Swiften/Elements/XHTMLIMPayload.h" +#include "Swiften/Elements/Delay.h" +#include "Swiften/Elements/DeliveryReceipt.h" +#include "Swiften/Elements/DeliveryReceiptRequest.h" +#include "Swiften/Elements/InvisiblePayload.h" +#include "Swiften/Elements/SpectrumErrorPayload.h" +#include "transport/protocol.pb.h" +#include "transport/util.h" +#include "transport/discoitemsresponder.h" + +#include "boost/date_time/posix_time/posix_time.hpp" +#include "boost/signal.hpp" + +#include "transport/utf8.h" + +#include +#include + +#ifdef _WIN32 +#include "windows.h" +#include +#else +#include "sys/wait.h" +#include "sys/signal.h" +#include +#include +#include "popt.h" +#endif + +using namespace Transport::Util; + +namespace Transport { + +static unsigned long backend_id; +static unsigned long bytestream_id; + +DEFINE_LOGGER(logger, "NetworkPluginServer"); + +static NetworkPluginServer *_server; + +class NetworkConversation : public Conversation { + public: + NetworkConversation(ConversationManager *conversationManager, const std::string &legacyName, bool muc = false) : Conversation(conversationManager, legacyName, muc) { + } + + // Called when there's new message to legacy network from XMPP network + void sendMessage(boost::shared_ptr &message) { + onMessageToSend(this, message); + } + + boost::signal &)> onMessageToSend; +}; + +class NetworkFactory : public Factory { + public: + NetworkFactory(NetworkPluginServer *nps) { + m_nps = nps; + } + + virtual ~NetworkFactory() {} + + // Creates new conversation (NetworkConversation in this case) + Conversation *createConversation(ConversationManager *conversationManager, const std::string &legacyName, bool isMuc) { + NetworkConversation *nc = new NetworkConversation(conversationManager, legacyName, isMuc); + nc->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, m_nps, _1, _2)); + return nc; + } + + // Creates new LocalBuddy + Buddy *createBuddy(RosterManager *rosterManager, const BuddyInfo &buddyInfo) { + LocalBuddy *buddy = new LocalBuddy(rosterManager, buddyInfo.id, buddyInfo.legacyName, buddyInfo.alias, buddyInfo.groups, (BuddyFlag) buddyInfo.flags); + if (!buddy->isValid()) { + delete buddy; + return NULL; + } + if (buddyInfo.subscription == "both") { + buddy->setSubscription(Buddy::Both); + } + else { + buddy->setSubscription(Buddy::Ask); + } + if (buddyInfo.settings.find("icon_hash") != buddyInfo.settings.end()) + buddy->setIconHash(buddyInfo.settings.find("icon_hash")->second.s); + return buddy; + } + + private: + NetworkPluginServer *m_nps; +}; + +// Wraps google protobuf payload into WrapperMessage and serialize it to string +#define WRAP(MESSAGE, TYPE) pbnetwork::WrapperMessage wrap; \ + wrap.set_type(TYPE); \ + wrap.set_payload(MESSAGE); \ + wrap.SerializeToString(&MESSAGE); + +// Executes new backend +static unsigned long exec_(const std::string& exePath, const char *host, const char *port, const char *log_id, const char *cmdlineArgs) { + // BACKEND_ID is replaced with unique ID. The ID is increasing for every backend. + std::string finalExePath = boost::replace_all_copy(exePath, "BACKEND_ID", boost::lexical_cast(backend_id++)); + +#ifdef _WIN32 + // Add host and port. + std::ostringstream fullCmdLine; + fullCmdLine << "\"" << finalExePath << "\" --host " << host << " --port " << port; + + if (cmdlineArgs) + fullCmdLine << " " << cmdlineArgs; + + LOG4CXX_INFO(logger, "Starting new backend " << fullCmdLine.str()); + + // We must provide a non-const buffer to CreateProcess below + std::vector rawCommandLineArgs( fullCmdLine.str().size() + 1 ); + wcscpy_s(&rawCommandLineArgs[0], rawCommandLineArgs.size(), utf8ToUtf16(fullCmdLine.str()).c_str()); + + STARTUPINFO si; + PROCESS_INFORMATION pi; + + ZeroMemory (&si, sizeof(si)); + si.cb=sizeof (si); + + if (! CreateProcess( + utf8ToUtf16(finalExePath).c_str(), + &rawCommandLineArgs[0], + 0, // process attributes + 0, // thread attributes + 0, // inherit handles + 0, // creation flags + 0, // environment + 0, // cwd + &si, + &pi + ) + ) { + LOG4CXX_ERROR(logger, "Could not start process"); + } + + return 0; +#else + // Add host and port. + finalExePath += std::string(" --host ") + host + " --port " + port + " --service.backend_id=" + log_id + " " + cmdlineArgs; + LOG4CXX_INFO(logger, "Starting new backend " << finalExePath); + + // Create array of char * from string using -lpopt library + char *p = (char *) malloc(finalExePath.size() + 1); + strcpy(p, finalExePath.c_str()); + int argc; + char **argv; + poptParseArgvString(p, &argc, (const char ***) &argv); + + // fork and exec + pid_t pid = fork(); + if ( pid == 0 ) { + setsid(); + // close all files + int maxfd=sysconf(_SC_OPEN_MAX); + for(int fd=3; fd 0) { + if (result != 0) { + _server->handlePIDTerminated((unsigned long)result); + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) { +// LOG4CXX_ERROR(logger, "Backend can not be started, exit_code=" << WEXITSTATUS(status)); + } + } + else { +// LOG4CXX_ERROR(logger, "Backend can not be started"); + } + } + } +} +#endif + +static void handleBuddyPayload(LocalBuddy *buddy, const pbnetwork::Buddy &payload) { + // Set alias only if it's not empty. Backends are allowed to send empty alias if it has + // not changed. + if (!payload.alias().empty()) { + buddy->setAlias(payload.alias()); + } + + // Change groups if it's not empty. The same as above... + std::vector groups; + for (int i = 0; i < payload.group_size(); i++) { + std::string group; + utf8::replace_invalid(payload.group(i).begin(), payload.group(i).end(), std::back_inserter(group), '_'); + groups.push_back(group); + } + if (!groups.empty()) { + buddy->setGroups(groups); + } + + buddy->setStatus(Swift::StatusShow((Swift::StatusShow::Type) payload.status()), payload.statusmessage()); + buddy->setIconHash(payload.iconhash()); + buddy->setBlocked(payload.blocked()); +} + +NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager, DiscoItemsResponder *discoItemsResponder) { + _server = this; + m_ftManager = ftManager; + m_userManager = userManager; + m_config = config; + m_component = component; + m_isNextLongRun = false; + m_adminInterface = NULL; + m_startingBackend = false; + m_lastLogin = 0; + m_xmppParser = new Swift::XMPPParser(this, &m_collection, component->getNetworkFactories()->getXMLParserFactory()); + m_xmppParser->parse(""); +#if HAVE_SWIFTEN_3 + m_serializer = new Swift::XMPPSerializer(&m_collection2, Swift::ClientStreamType, false); +#else + m_serializer = new Swift::XMPPSerializer(&m_collection2, Swift::ClientStreamType); +#endif + m_discoItemsResponder = discoItemsResponder; + m_component->m_factory = new NetworkFactory(this); + m_userManager->onUserCreated.connect(boost::bind(&NetworkPluginServer::handleUserCreated, this, _1)); + m_userManager->onUserDestroyed.connect(boost::bind(&NetworkPluginServer::handleUserDestroyed, this, _1)); + + m_component->onRawIQReceived.connect(boost::bind(&NetworkPluginServer::handleRawIQReceived, this, _1)); + + m_pingTimer = component->getNetworkFactories()->getTimerFactory()->createTimer(20000); + m_pingTimer->onTick.connect(boost::bind(&NetworkPluginServer::pingTimeout, this)); + m_pingTimer->start(); + + m_loginTimer = component->getNetworkFactories()->getTimerFactory()->createTimer(CONFIG_INT(config, "service.login_delay") * 1000); + m_loginTimer->onTick.connect(boost::bind(&NetworkPluginServer::loginDelayFinished, this)); + m_loginTimer->start(); + + if (CONFIG_INT(m_config, "service.memory_collector_time") != 0) { + m_collectTimer = component->getNetworkFactories()->getTimerFactory()->createTimer(CONFIG_INT(m_config, "service.memory_collector_time")); + m_collectTimer->onTick.connect(boost::bind(&NetworkPluginServer::collectBackend, this)); + m_collectTimer->start(); + } + + m_vcardResponder = new VCardResponder(component->getIQRouter(), component->getNetworkFactories(), userManager); + m_vcardResponder->onVCardRequired.connect(boost::bind(&NetworkPluginServer::handleVCardRequired, this, _1, _2, _3)); + m_vcardResponder->onVCardUpdated.connect(boost::bind(&NetworkPluginServer::handleVCardUpdated, this, _1, _2)); + m_vcardResponder->start(); + + m_rosterResponder = new RosterResponder(component->getIQRouter(), userManager); + m_rosterResponder->onBuddyAdded.connect(boost::bind(&NetworkPluginServer::handleBuddyAdded, this, _1, _2)); + m_rosterResponder->onBuddyRemoved.connect(boost::bind(&NetworkPluginServer::handleBuddyRemoved, this, _1)); + m_rosterResponder->onBuddyUpdated.connect(boost::bind(&NetworkPluginServer::handleBuddyUpdated, this, _1, _2)); + m_rosterResponder->start(); + + m_blockResponder = new BlockResponder(component->getIQRouter(), userManager); + m_blockResponder->onBlockToggled.connect(boost::bind(&NetworkPluginServer::handleBlockToggled, this, _1)); + m_blockResponder->start(); + + m_server = component->getNetworkFactories()->getConnectionServerFactory()->createConnectionServer(Swift::HostAddress(CONFIG_STRING(m_config, "service.backend_host")), boost::lexical_cast(CONFIG_STRING(m_config, "service.backend_port"))); + m_server->onNewConnection.connect(boost::bind(&NetworkPluginServer::handleNewClientConnection, this, _1)); +} + +NetworkPluginServer::~NetworkPluginServer() { + for (std::list::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) { + LOG4CXX_INFO(logger, "Stopping backend " << *it); + std::string message; + pbnetwork::WrapperMessage wrap; + wrap.set_type(pbnetwork::WrapperMessage_Type_TYPE_EXIT); + wrap.SerializeToString(&message); + + Backend *c = (Backend *) *it; + send(c->connection, message); + } + + m_pingTimer->stop(); + m_server->stop(); + m_server.reset(); + delete m_component->m_factory; + delete m_vcardResponder; + delete m_rosterResponder; + delete m_blockResponder; +} + +void NetworkPluginServer::start() { + m_server->start(); + + LOG4CXX_INFO(logger, "Listening on host " << CONFIG_STRING(m_config, "service.backend_host") << " port " << CONFIG_STRING(m_config, "service.backend_port")); + + while (true) { + unsigned long pid = exec_(CONFIG_STRING(m_config, "service.backend"), CONFIG_STRING(m_config, "service.backend_host").c_str(), CONFIG_STRING(m_config, "service.backend_port").c_str(), "1", m_config->getCommandLineArgs().c_str()); + LOG4CXX_INFO(logger, "Tried to spawn first backend with pid " << pid); + LOG4CXX_INFO(logger, "Backend should now connect to Spectrum2 instance. Spectrum2 won't accept any connection before backend connects"); + +#ifndef _WIN32 + // wait if the backend process will still be alive after 1 second + sleep(1); + pid_t result; + int status; + result = waitpid(-1, &status, WNOHANG); + if (result != 0) { + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) { + if (status == 254) { + LOG4CXX_ERROR(logger, "Backend can not be started, because it needs database to store data, but the database backend is not configured."); + } + else { + LOG4CXX_ERROR(logger, "Backend can not be started, exit_code=" << WEXITSTATUS(status) << ", possible error: " << strerror(WEXITSTATUS(status))); + if (WEXITSTATUS(status) == ENOENT) { + LOG4CXX_ERROR(logger, "This usually means the path to backend executable defined in config file as '[service] backend=\"...\"' is wrong or the executable does not exists."); + } + + } + LOG4CXX_ERROR(logger, "Check backend log for more details"); + continue; + } + } + else { + LOG4CXX_ERROR(logger, "Backend can not be started"); + continue; + } + } + + m_pids.push_back(pid); + + signal(SIGCHLD, SigCatcher); +#endif + // quit the while loop + break; + } +} + +void NetworkPluginServer::loginDelayFinished() { + m_loginTimer->stop(); + connectWaitingUsers(); +} + +void NetworkPluginServer::handleNewClientConnection(boost::shared_ptr c) { + // Create new Backend instance + Backend *client = new Backend; + client->pongReceived = -1; + client->connection = c; + client->res = 0; + client->init_res = 0; + client->shared = 0; + // Until we receive first PONG from backend, backend is in willDie state. + client->willDie = true; + // Backend does not accept new clients automatically if it's long-running + client->acceptUsers = !m_isNextLongRun; + client->longRun = m_isNextLongRun; + + m_startingBackend = false; + + LOG4CXX_INFO(logger, "New" + (client->longRun ? std::string(" long-running") : "") + " backend " << client << " connected. Current backend count=" << (m_clients.size() + 1)); + + m_clients.push_front(client); + + c->onDisconnected.connect(boost::bind(&NetworkPluginServer::handleSessionFinished, this, client)); + c->onDataRead.connect(boost::bind(&NetworkPluginServer::handleDataRead, this, client, _1)); + sendPing(client); + + // sendPing sets pongReceived to 0, but we want to have it -1 to ignore this backend + // in first ::pingTimeout call, because it can be called right after this function + // and backend wouldn't have any time to response to ping. + client->pongReceived = -1; +} + +void NetworkPluginServer::handleSessionFinished(Backend *c) { + LOG4CXX_INFO(logger, "Backend " << c << " (ID=" << c->id << ") disconnected. Current backend count=" << (m_clients.size() - 1)); + + // This backend will do, so we can't reconnect users to it in User::handleDisconnected call + c->willDie = true; + + // If there are users associated with this backend, it must have crashed, so print error output + // and disconnect users + if (!c->users.empty()) { + m_crashedBackends.push_back(c->id); + } + + for (std::list::const_iterator it = c->users.begin(); it != c->users.end(); it++) { + LOG4CXX_ERROR(logger, "Backend " << c << " (ID=" << c->id << ") disconnected (probably crashed) with active user " << (*it)->getJID().toString()); + (*it)->setData(NULL); + (*it)->handleDisconnected("Internal Server Error, please reconnect."); + } + + std::string message; + pbnetwork::WrapperMessage wrap; + wrap.set_type(pbnetwork::WrapperMessage_Type_TYPE_EXIT); + wrap.SerializeToString(&message); + + send(c->connection, message); + + c->connection->onDisconnected.disconnect_all_slots(); + c->connection->onDataRead.disconnect_all_slots(); + c->connection->disconnect(); + c->connection.reset(); + + m_clients.remove(c); + delete c; +} + +void NetworkPluginServer::handleConnectedPayload(const std::string &data) { + pbnetwork::Connected payload; + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + User *user = m_userManager->getUser(payload.user()); + if (!user) { + LOG4CXX_ERROR(logger, "Connected payload received for unknown user " << payload.user()); + return; + } + + user->setConnected(true); + m_component->m_userRegistry->onPasswordValid(payload.user()); +} + +void NetworkPluginServer::handleDisconnectedPayload(const std::string &data) { + pbnetwork::Disconnected payload; + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + m_component->m_userRegistry->onPasswordInvalid(payload.user(), payload.message()); + + User *user = m_userManager->getUser(payload.user()); + if (!user) { + return; + } + user->handleDisconnected(payload.message(), (Swift::SpectrumErrorPayload::Error) payload.error()); +} + +void NetworkPluginServer::handleVCardPayload(const std::string &data) { + pbnetwork::VCard payload; + if (payload.ParseFromString(data) == false) { + std::cout << "PARSING ERROR\n"; + // TODO: ERROR + return; + } + std::string field; + + boost::shared_ptr vcard(new Swift::VCard()); + + utf8::replace_invalid(payload.fullname().begin(), payload.fullname().end(), std::back_inserter(field), '_'); + vcard->setFullName(field); + + field.clear(); + + utf8::replace_invalid(payload.nickname().begin(), payload.nickname().end(), std::back_inserter(field), '_'); + vcard->setNickname(field); + + vcard->setPhoto(Swift::createByteArray(payload.photo())); + + m_vcardResponder->sendVCard(payload.id(), vcard); +} + +void NetworkPluginServer::handleAuthorizationPayload(const std::string &data) { + pbnetwork::Buddy payload; + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + User *user = m_userManager->getUser(payload.username()); + if (!user) + return; + + // Create subscribe presence and forward it to XMPP side + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo(user->getJID()); + std::string name = payload.buddyname(); + + if (CONFIG_BOOL_DEFAULTED(m_config, "service.jid_escaping", true)) { + name = Swift::JID::getEscapedNode(name); + } + else { + if (name.find_last_of("@") != std::string::npos) { + name.replace(name.find_last_of("@"), 1, "%"); + } + } + + response->setFrom(Swift::JID(name, m_component->getJID().toString())); + response->setType(Swift::Presence::Subscribe); + m_component->getStanzaChannel()->sendPresence(response); +} + +void NetworkPluginServer::handleChatStatePayload(const std::string &data, Swift::ChatState::ChatStateType type) { + pbnetwork::Buddy payload; + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + User *user = m_userManager->getUser(payload.username()); + if (!user) + return; + + // We're not creating new Conversation just because of chatstates. + // Some networks/clients spams with chatstates a lot and it leads to bigger memory usage. + NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname()); + if (!conv) { + return; + } + + // Forward chatstate + boost::shared_ptr msg(new Swift::Message()); + msg->addPayload(boost::make_shared(type)); + + conv->handleMessage(msg); +} + +void NetworkPluginServer::handleBuddyChangedPayload(const std::string &data) { + pbnetwork::Buddy payload; + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + User *user = m_userManager->getUser(payload.username()); + if (!user) + return; + + LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(payload.buddyname()); + if (buddy) { + handleBuddyPayload(buddy, payload); + } + else { + if (payload.buddyname() == user->getUserInfo().uin) { + return; + } + + std::vector groups; + for (int i = 0; i < payload.group_size(); i++) { + groups.push_back(payload.group(i)); + } + if (CONFIG_BOOL_DEFAULTED(m_config, "service.jid_escaping", true)) { + buddy = new LocalBuddy(user->getRosterManager(), -1, payload.buddyname(), payload.alias(), groups, BUDDY_JID_ESCAPING); + } + else { + buddy = new LocalBuddy(user->getRosterManager(), -1, payload.buddyname(), payload.alias(), groups, BUDDY_NO_FLAG); + } + if (!buddy->isValid()) { + delete buddy; + return; + } + + buddy->setBlocked(payload.blocked()); + user->getRosterManager()->setBuddy(buddy); + buddy->setStatus(Swift::StatusShow((Swift::StatusShow::Type) payload.status()), payload.statusmessage()); + buddy->setIconHash(payload.iconhash()); + } +} + +void NetworkPluginServer::handleBuddyRemovedPayload(const std::string &data) { + pbnetwork::Buddy payload; + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + User *user = m_userManager->getUser(payload.username()); + if (!user) + return; + + user->getRosterManager()->removeBuddy(payload.buddyname()); +} + +void NetworkPluginServer::handleParticipantChangedPayload(const std::string &data) { + pbnetwork::Participant payload; + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + User *user = m_userManager->getUser(payload.username()); + if (!user) + return; + + NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.room()); + if (!conv) { + return; + } + + conv->handleParticipantChanged(payload.nickname(), (Conversation::ParticipantFlag) payload.flag(), payload.status(), payload.statusmessage(), payload.newname()); +} + +void NetworkPluginServer::handleRoomChangedPayload(const std::string &data) { + pbnetwork::Room payload; + if (payload.ParseFromString(data) == false) { + return; + } + + User *user = m_userManager->getUser(payload.username()); + if (!user) + return; + + NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.room()); + if (!conv) { + return; + } + + conv->setNickname(payload.nickname()); +} + +void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool subject) { + pbnetwork::ConversationMessage payload; + + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + User *user = m_userManager->getUser(payload.username()); + if (!user) + return; + + // Message from legacy network triggers network acticity + user->updateLastActivity(); + + // Set proper body. + boost::shared_ptr msg(new Swift::Message()); + if (subject) { + msg->setSubject(payload.message()); + } + else { + msg->setBody(payload.message()); + } + + if (payload.headline()) { + msg->setType(Swift::Message::Headline); + } + + // Add xhtml-im payload. + if (CONFIG_BOOL(m_config, "service.enable_xhtml") && !payload.xhtml().empty()) { + msg->addPayload(boost::make_shared(payload.xhtml())); + } + + if (!payload.timestamp().empty()) { + boost::posix_time::ptime timestamp = boost::posix_time::from_iso_string(payload.timestamp()); + boost::shared_ptr delay(boost::make_shared()); + delay->setStamp(timestamp); + msg->addPayload(delay); + } + + NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname()); + + // We can't create Conversation for payload with nickname, because this means the message is from room, + // but this user is not in any room, so it's OK to just reject this message + if (!conv && !payload.nickname().empty()) { + return; + } + + if (conv && payload.pm()) { + conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname() + "/" + payload.nickname()); + if (!conv) { + conv = new NetworkConversation(user->getConversationManager(), payload.nickname()); + std::string name = payload.buddyname(); + conv->setRoom(name); + conv->setNickname(payload.buddyname() + "/" + payload.nickname()); + + user->getConversationManager()->addConversation(conv); + conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2)); + } + } + + // Create new Conversation if it does not exist + if (!conv) { + conv = new NetworkConversation(user->getConversationManager(), payload.buddyname()); + user->getConversationManager()->addConversation(conv); + conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2)); + } + + // Forward it + conv->handleMessage(msg, payload.nickname()); + m_userManager->messageToXMPPSent(); +} + +void NetworkPluginServer::handleConvMessageAckPayload(const std::string &data) { + pbnetwork::ConversationMessage payload; + + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + User *user = m_userManager->getUser(payload.username()); + if (!user) + return; + + if (payload.id().empty()) { + LOG4CXX_WARN(logger, "Received message ack with empty ID, not forwarding to XMPP."); + return; + } + + boost::shared_ptr msg(new Swift::Message()); + msg->addPayload(boost::make_shared(payload.id())); + + NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname()); + + // Receipts don't create conversation + if (!conv) { + return; + } + + // Forward it + conv->handleMessage(msg); +} + +void NetworkPluginServer::handleAttentionPayload(const std::string &data) { + pbnetwork::ConversationMessage payload; + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + User *user = m_userManager->getUser(payload.username()); + if (!user) + return; + + boost::shared_ptr msg(new Swift::Message()); + msg->setBody(payload.message()); + msg->addPayload(boost::make_shared()); + + // Attentions trigger new Conversation creation + NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname()); + if (!conv) { + conv = new NetworkConversation(user->getConversationManager(), payload.buddyname()); + user->getConversationManager()->addConversation(conv); + conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2)); + } + + conv->handleMessage(msg); +} + +void NetworkPluginServer::handleStatsPayload(Backend *c, const std::string &data) { + pbnetwork::Stats payload; + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + c->res = payload.res(); + c->init_res = payload.init_res(); + c->shared = payload.shared(); + c->id = payload.id(); +} + +void NetworkPluginServer::handleFTStartPayload(const std::string &data) { + pbnetwork::File payload; + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + User *user = m_userManager->getUser(payload.username()); + if (!user) + return; + + LOG4CXX_INFO(logger, "handleFTStartPayload " << payload.filename() << " " << payload.buddyname()); + + LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(payload.buddyname()); + if (!buddy) { + // TODO: escape? reject? + return; + } + + Swift::StreamInitiationFileInfo fileInfo; + fileInfo.setSize(payload.size()); + fileInfo.setName(payload.filename()); + + Backend *c = (Backend *) user->getData(); + boost::shared_ptr bytestream(new MemoryReadBytestream(payload.size())); + bytestream->onDataNeeded.connect(boost::bind(&NetworkPluginServer::handleFTDataNeeded, this, c, bytestream_id + 1)); + + LOG4CXX_INFO(logger, "jid=" << buddy->getJID()); + + FileTransferManager::Transfer transfer = m_ftManager->sendFile(user, buddy, bytestream, fileInfo); + if (!transfer.ft) { + handleFTRejected(user, payload.buddyname(), payload.filename(), payload.size()); + return; + } + + m_filetransfers[++bytestream_id] = transfer; +#if !HAVE_SWIFTEN_3 + transfer.ft->onStateChange.connect(boost::bind(&NetworkPluginServer::handleFTStateChanged, this, _1, payload.username(), payload.buddyname(), payload.filename(), payload.size(), bytestream_id)); + transfer.ft->start(); +#endif +} + +void NetworkPluginServer::handleFTFinishPayload(const std::string &data) { + pbnetwork::File payload; + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + if (payload.has_ftid()) { + if (m_filetransfers.find(payload.ftid()) != m_filetransfers.end()) { + FileTransferManager::Transfer &transfer = m_filetransfers[payload.ftid()]; + transfer.ft->cancel(); + } + else { + LOG4CXX_ERROR(logger, "FTFinishPayload for unknown ftid=" << payload.ftid()); + } + } + +} + +void NetworkPluginServer::handleFTDataPayload(Backend *b, const std::string &data) { + pbnetwork::FileTransferData payload; + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + +// User *user = m_userManager->getUser(payload.username()); +// if (!user) +// return; + + if (m_filetransfers.find(payload.ftid()) == m_filetransfers.end()) { + LOG4CXX_ERROR(logger, "Uknown filetransfer with id " << payload.ftid()); + return; + } + + FileTransferManager::Transfer &transfer = m_filetransfers[payload.ftid()]; + MemoryReadBytestream *bytestream = (MemoryReadBytestream *) transfer.readByteStream.get(); + + if (bytestream->appendData(payload.data()) > 5000000) { + pbnetwork::FileTransferData f; + f.set_ftid(payload.ftid()); + f.set_data(""); + + std::string message; + f.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_PAUSE); + + send(b->connection, message); + } +} + +void NetworkPluginServer::handleFTDataNeeded(Backend *b, unsigned long ftid) { + pbnetwork::FileTransferData f; + f.set_ftid(ftid); + f.set_data(""); + + std::string message; + f.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_CONTINUE); + + send(b->connection, message); +} + +void NetworkPluginServer::connectWaitingUsers() { + // some users are in queue waiting for this backend + while(!m_waitingUsers.empty()) { + // There's no new backend, so stop associating users and wait for new backend, + // which has been already spawned in getFreeClient() call. + if (getFreeClient(true, false, true) == NULL) + break; + + User *u = m_waitingUsers.front(); + m_waitingUsers.pop_front(); + + LOG4CXX_INFO(logger, "Associating " << u->getJID().toString() << " with this backend"); + + // associate backend with user + handleUserCreated(u); + + // connect user if it's ready + if (u->isReadyToConnect()) { + handleUserReadyToConnect(u); + } + } +} + +void NetworkPluginServer::handlePongReceived(Backend *c) { + // This could be first PONG from the backend + if (c->pongReceived == -1) { + // Backend is fully ready to handle requests + c->willDie = false; + + if (m_clients.size() == 1) { + // first backend connected, start the server, we're ready. + m_component->start(); + } + + connectWaitingUsers(); + } + + c->pongReceived = true; +} + +void NetworkPluginServer::handleQueryPayload(Backend *b, const std::string &data) { + pbnetwork::BackendConfig payload; + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + if (!m_adminInterface) { + return; + } + + boost::shared_ptr msg(new Swift::Message()); + msg->setBody(payload.config()); + m_adminInterface->handleQuery(msg); + + pbnetwork::BackendConfig response; + response.set_config(msg->getBody()); + + std::string message; + response.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_QUERY); + + send(b->connection, message); +} + +void NetworkPluginServer::handleBackendConfigPayload(const std::string &data) { + pbnetwork::BackendConfig payload; + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + m_config->updateBackendConfig(payload.config()); +} + +void NetworkPluginServer::handleRoomListPayload(const std::string &data) { + pbnetwork::RoomList payload; + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + m_discoItemsResponder->clearRooms(); + for (int i = 0; i < payload.room_size() && i < payload.name_size(); i++) { + m_discoItemsResponder->addRoom(Swift::JID::getEscapedNode(payload.room(i)) + "@" + m_component->getJID().toString(), payload.name(i)); + } +} +#if HAVE_SWIFTEN_3 +void NetworkPluginServer::handleElement(boost::shared_ptr element) { +#else +void NetworkPluginServer::handleElement(boost::shared_ptr element) { +#endif + boost::shared_ptr stanza = boost::dynamic_pointer_cast(element); + if (!stanza) { + return; + } + + User *user = m_userManager->getUser(stanza->getTo().toBare()); + if (!user) + return; + + Swift::JID originalJID = stanza->getFrom(); + NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(originalJID.toBare()); + + LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(stanza->getFrom().toBare()); + if (buddy) { + const Swift::JID &jid = buddy->getJID(); + if (stanza->getFrom().getResource().empty()) { + stanza->setFrom(Swift::JID(jid.getNode(), jid.getDomain())); + } + else { + stanza->setFrom(Swift::JID(jid.getNode(), jid.getDomain(), stanza->getFrom().getResource())); + } + } + else { + std::string name = stanza->getFrom().toBare(); + if (conv && conv->isMUC()) { + if (name.find_last_of("@") != std::string::npos) { + name.replace(name.find_last_of("@"), 1, "%"); + } + } + else { + if (CONFIG_BOOL_DEFAULTED(m_config, "service.jid_escaping", true)) { + name = Swift::JID::getEscapedNode(name); + } + else { + if (name.find_last_of("@") != std::string::npos) { + name.replace(name.find_last_of("@"), 1, "%"); + } + } + } + if (stanza->getFrom().getResource().empty()) { + stanza->setFrom(Swift::JID(name, m_component->getJID().toString())); + } + else { + stanza->setFrom(Swift::JID(name, m_component->getJID().toString(), stanza->getFrom().getResource())); + } + } + + boost::shared_ptr message = boost::dynamic_pointer_cast(stanza); + if (message) { + if (conv) { + conv->handleRawMessage(message); + return; + } + + m_component->getStanzaChannel()->sendMessage(message); + return; + } + + boost::shared_ptr presence = boost::dynamic_pointer_cast(stanza); + if (presence) { + if (buddy) { + if (!buddy->isAvailable() && presence->getType() != Swift::Presence::Unavailable) { + buddy->m_status.setType(Swift::StatusShow::Online); + } + buddy->handleRawPresence(presence); + } + else if (conv) { + conv->handleRawPresence(presence); + } + else { + m_component->getStanzaChannel()->sendPresence(presence); + } + + return; + } + + boost::shared_ptr iq = boost::dynamic_pointer_cast(stanza); + if (iq) { + if (m_id2resource.find(stanza->getTo().toBare().toString() + stanza->getID()) != m_id2resource.end()) { + iq->setTo(Swift::JID(iq->getTo().getNode(), iq->getTo().getDomain(), m_id2resource[stanza->getTo().toBare().toString() + stanza->getID()])); + m_id2resource.erase(stanza->getTo().toBare().toString() + stanza->getID()); + } + else { + Swift::Presence::ref highest = m_component->getPresenceOracle()->getHighestPriorityPresence(user->getJID()); + if (highest) { + iq->setTo(highest->getFrom()); + } else { + iq->setTo(user->getJID()); + } + } + m_component->getIQRouter()->sendIQ(iq); + return; + } +} + +void NetworkPluginServer::handleRawXML(const std::string &xml) { + m_xmppParser->parse(xml); +} + +void NetworkPluginServer::handleRawPresenceReceived(boost::shared_ptr presence) { + User *user = m_userManager->getUser(presence->getFrom().toBare()); + if (!user) + return; + + Backend *c = (Backend *) user->getData(); + if (!c) { + return; + } + + Swift::JID legacyname = Swift::JID(Buddy::JIDToLegacyName(presence->getTo())); + if (!presence->getTo().getResource().empty()) { + presence->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain(), presence->getTo().getResource())); + } + else { + presence->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain())); + } + + std::string xml = safeByteArrayToString(m_serializer->serializeElement(presence)); + WRAP(xml, pbnetwork::WrapperMessage_Type_TYPE_RAW_XML); + send(c->connection, xml); +} + +void NetworkPluginServer::handleRawIQReceived(boost::shared_ptr iq) { + User *user = m_userManager->getUser(iq->getFrom().toBare()); + if (!user) + return; + + Backend *c = (Backend *) user->getData(); + if (!c) { + return; + } + + if (iq->getType() == Swift::IQ::Get) { + m_id2resource[iq->getFrom().toBare().toString() + iq->getID()] = iq->getFrom().getResource(); + } + + Swift::JID legacyname = Swift::JID(Buddy::JIDToLegacyName(iq->getTo())); + if (!iq->getTo().getResource().empty()) { + iq->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain(), iq->getTo().getResource())); + } + else { + iq->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain())); + } + + std::string xml = safeByteArrayToString(m_serializer->serializeElement(iq)); + WRAP(xml, pbnetwork::WrapperMessage_Type_TYPE_RAW_XML); + send(c->connection, xml); +} + +void NetworkPluginServer::handleDataRead(Backend *c, boost::shared_ptr data) { + // Append data to buffer + c->data.insert(c->data.end(), data->begin(), data->end()); + + // Parse data while there are some + while (c->data.size() != 0) { + // expected_size of wrapper message + unsigned int expected_size; + + // if data is >= 4, we have whole header and we can + // read expected_size. + if (c->data.size() >= 4) { + expected_size = *((unsigned int*) &c->data[0]); + expected_size = ntohl(expected_size); + // If we don't have whole wrapper message, wait for next + // handleDataRead call. + if (c->data.size() - 4 < expected_size) + return; + } + else { + return; + } + + // Parse wrapper message and erase it from buffer. + pbnetwork::WrapperMessage wrapper; + if (wrapper.ParseFromArray(&c->data[4], expected_size) == false) { + std::cout << "PARSING ERROR " << expected_size << "\n"; + c->data.erase(c->data.begin(), c->data.begin() + 4 + expected_size); + continue; + } + c->data.erase(c->data.begin(), c->data.begin() + 4 + expected_size); + + // If backend is slow and it is sending us lot of message, there is possibility + // that we don't receive PONG response before timeout. However, if we received + // at least some data, it means backend is not dead and we can treat it as + // PONG received event. + if (c->pongReceived == false) { + c->pongReceived = true; + } + + // Handle payload in wrapper message + switch(wrapper.type()) { + case pbnetwork::WrapperMessage_Type_TYPE_CONNECTED: + handleConnectedPayload(wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_DISCONNECTED: + handleDisconnectedPayload(wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED: + handleBuddyChangedPayload(wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE: + handleConvMessagePayload(wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_ROOM_SUBJECT_CHANGED: + handleConvMessagePayload(wrapper.payload(), true); + break; + case pbnetwork::WrapperMessage_Type_TYPE_PONG: + handlePongReceived(c); + break; + case pbnetwork::WrapperMessage_Type_TYPE_PARTICIPANT_CHANGED: + handleParticipantChangedPayload(wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_ROOM_NICKNAME_CHANGED: + handleRoomChangedPayload(wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_VCARD: + handleVCardPayload(wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPING: + handleChatStatePayload(wrapper.payload(), Swift::ChatState::Composing); + break; + case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPED: + handleChatStatePayload(wrapper.payload(), Swift::ChatState::Paused); + break; + case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_STOPPED_TYPING: + handleChatStatePayload(wrapper.payload(), Swift::ChatState::Active); + break; + case pbnetwork::WrapperMessage_Type_TYPE_AUTH_REQUEST: + handleAuthorizationPayload(wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_ATTENTION: + handleAttentionPayload(wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_STATS: + handleStatsPayload(c, wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_FT_START: + handleFTStartPayload(wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_FT_FINISH: + handleFTFinishPayload(wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_FT_DATA: + handleFTDataPayload(c, wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_REMOVED: + handleBuddyRemovedPayload(wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_QUERY: + handleQueryPayload(c, wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_BACKEND_CONFIG: + handleBackendConfigPayload(wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_ROOM_LIST: + handleRoomListPayload(wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE_ACK: + handleConvMessageAckPayload(wrapper.payload()); + break; + case pbnetwork::WrapperMessage_Type_TYPE_RAW_XML: + handleRawXML(wrapper.payload()); + break; + default: + return; + } + } +} + +void NetworkPluginServer::send(boost::shared_ptr &c, const std::string &data) { + // generate header - size of wrapper message + uint32_t size = htonl(data.size()); + char *header = (char *) &size; + + // send header together with wrapper message + c->write(Swift::createSafeByteArray(std::string(header, 4) + data)); +} + +void NetworkPluginServer::pingTimeout() { + // TODO: move to separate timer, those 2 loops could be expensive + // Some users are connected for weeks and they are blocking backend to be destroyed and its memory + // to be freed. We are finding users who are inactive for more than "idle_reconnect_time" seconds and + // reconnect them to long-running backend, where they can idle hapilly till the end of ages. + time_t now = time(NULL); + std::vector usersToMove; + unsigned long diff = CONFIG_INT(m_config, "service.idle_reconnect_time"); + if (diff != 0) { + for (std::list::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) { + // Users from long-running backends can't be moved + if ((*it)->longRun) { + continue; + } + + // Find users which are inactive for more than 'diff' + BOOST_FOREACH(User *u, (*it)->users) { + if (now - u->getLastActivity() > diff) { + usersToMove.push_back(u); + } + } + } + + // Move inactive users to long-running backend. + BOOST_FOREACH(User *u, usersToMove) { + LOG4CXX_INFO(logger, "Moving user " << u->getJID().toString() << " to long-running backend"); + if (!moveToLongRunBackend(u)) + break; + } + } + + // We have to remove startingBackend flag otherwise 1 broken backend start could + // block the backend. + m_startingBackend = false; + + // check ping responses + std::vector toRemove; + for (std::list::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) { + // pong has been received OR backend just connected and did not have time to answer the ping + // request. + if ((*it)->pongReceived || (*it)->pongReceived == -1) { + // Don't send another ping if pongReceived == -1, because we've already sent one + // when registering backend. + if ((*it)->pongReceived) { + sendPing((*it)); + } + } + else { + LOG4CXX_INFO(logger, "Disconnecting backend " << (*it) << " (ID=" << (*it)->id << "). PING response not received."); + toRemove.push_back(*it); + +#ifndef WIN32 + // generate coredump for this backend to find out why it wasn't able to respond to PING + std::string pid = (*it)->id; + if (!pid.empty()) { + try { + kill(boost::lexical_cast(pid), SIGABRT); + } + catch (...) { } + } +#endif + } + + if ((*it)->users.size() == 0) { + LOG4CXX_INFO(logger, "Disconnecting backend " << (*it) << " (ID=" << (*it)->id << "). There are no users."); + toRemove.push_back(*it); + } + } + + BOOST_FOREACH(Backend *b, toRemove) { + handleSessionFinished(b); + } + + m_pingTimer->start(); +} + +void NetworkPluginServer::collectBackend() { + // Stop accepting new users to backend with the biggest memory usage. This prevents backends + // which are leaking to eat whole memory by connectin new users to legacy network. + LOG4CXX_INFO(logger, "Collect backend called, finding backend which will be set to die"); + unsigned long max = 0; + Backend *backend = NULL; + for (std::list::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) { + if ((*it)->res > max) { + max = (*it)->res; + backend = (*it); + } + } + + if (backend) { + if (m_collectTimer) { + m_collectTimer->start(); + } + LOG4CXX_INFO(logger, "Backend " << backend << " (ID=" << backend->id << ") is set to die"); + backend->acceptUsers = false; + } +} + +bool NetworkPluginServer::moveToLongRunBackend(User *user) { + // Check if user has already some backend + Backend *old = (Backend *) user->getData(); + if (!old) { + LOG4CXX_INFO(logger, "User " << user->getJID().toString() << " does not have old backend. Not moving."); + return true; + } + + // if he's already on long run, do nothing + if (old->longRun) { + LOG4CXX_INFO(logger, "User " << user->getJID().toString() << " is already on long-running backend. Not moving."); + return true; + } + + // Get free longrun backend, if there's no longrun backend, create one and wait + // for its connection + Backend *backend = getFreeClient(false, true); + if (!backend) { + LOG4CXX_INFO(logger, "No free long-running backend for user " << user->getJID().toString() << ". Will try later"); + return false; + } + + // old backend will trigger disconnection which has to be ignored to keep user online + user->setIgnoreDisconnect(true); + + // remove user from the old backend + // If backend is empty, it will be collected by pingTimeout + old->users.remove(user); + + // switch to new backend and connect + user->setData(backend); + backend->users.push_back(user); + + // connect him + handleUserReadyToConnect(user); + return true; +} + +void NetworkPluginServer::handleUserCreated(User *user) { + // Get free backend to handle this user or spawn new one if there's no free one. + Backend *c = getFreeClient(); + + // Add user to queue if there's no free backend to handle him so far. + if (!c) { + LOG4CXX_INFO(logger, "There is no backend to handle user " << user->getJID().toString() << ". Adding him to queue."); + m_waitingUsers.push_back(user); + return; + } + + // Associate users with backend + user->setData(c); + c->users.push_back(user); + + // Don't forget to disconnect these in handleUserDestroyed!!! + user->onReadyToConnect.connect(boost::bind(&NetworkPluginServer::handleUserReadyToConnect, this, user)); + user->onPresenceChanged.connect(boost::bind(&NetworkPluginServer::handleUserPresenceChanged, this, user, _1)); + user->onRawPresenceReceived.connect(boost::bind(&NetworkPluginServer::handleRawPresenceReceived, this, _1)); + user->onRoomJoined.connect(boost::bind(&NetworkPluginServer::handleRoomJoined, this, user, _1, _2, _3, _4)); + user->onRoomLeft.connect(boost::bind(&NetworkPluginServer::handleRoomLeft, this, user, _1)); + + user->getRosterManager()->onBuddyAdded.connect(boost::bind(&NetworkPluginServer::handleUserBuddyAdded, this, user, _1)); + user->getRosterManager()->onBuddyRemoved.connect(boost::bind(&NetworkPluginServer::handleUserBuddyRemoved, this, user, _1)); +} + +void NetworkPluginServer::handleUserReadyToConnect(User *user) { + UserInfo userInfo = user->getUserInfo(); + + pbnetwork::Login login; + login.set_user(user->getJID().toBare()); + login.set_legacyname(userInfo.uin); + login.set_password(userInfo.password); + + std::string message; + login.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LOGIN); + + Backend *c = (Backend *) user->getData(); + if (!c) { + return; + } + send(c->connection, message); +} + +void NetworkPluginServer::handleUserPresenceChanged(User *user, Swift::Presence::ref presence) { + if (presence->getShow() == Swift::StatusShow::None) + return; + + handleRawPresenceReceived(presence); + + UserInfo userInfo = user->getUserInfo(); + + pbnetwork::Status status; + status.set_username(user->getJID().toBare()); + + bool isInvisible = presence->getPayload() != NULL; + if (isInvisible) { + LOG4CXX_INFO(logger, "This presence is invisible"); + status.set_status((pbnetwork::STATUS_INVISIBLE)); + } + else { + status.set_status((pbnetwork::StatusType) presence->getShow()); + } + + status.set_statusmessage(presence->getStatus()); + + std::string message; + status.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_STATUS_CHANGED); + + Backend *c = (Backend *) user->getData(); + if (!c) { + return; + } + send(c->connection, message); +} + +void NetworkPluginServer::handleRoomJoined(User *user, const Swift::JID &who, const std::string &r, const std::string &nickname, const std::string &password) { + UserInfo userInfo = user->getUserInfo(); + + pbnetwork::Room room; + room.set_username(user->getJID().toBare()); + room.set_nickname(nickname); + room.set_room(r); + room.set_password(password); + + std::string message; + room.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_JOIN_ROOM); + + Backend *c = (Backend *) user->getData(); + if (!c) { + return; + } + send(c->connection, message); +} + +void NetworkPluginServer::handleRoomLeft(User *user, const std::string &r) { + UserInfo userInfo = user->getUserInfo(); + + pbnetwork::Room room; + room.set_username(user->getJID().toBare()); + room.set_nickname(""); + room.set_room(r); + room.set_password(""); + + std::string message; + room.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LEAVE_ROOM); + + Backend *c = (Backend *) user->getData(); + if (!c) { + return; + } + send(c->connection, message); +} + +void NetworkPluginServer::handleUserDestroyed(User *user) { + m_waitingUsers.remove(user); + UserInfo userInfo = user->getUserInfo(); + + user->onReadyToConnect.disconnect(boost::bind(&NetworkPluginServer::handleUserReadyToConnect, this, user)); + user->onPresenceChanged.disconnect(boost::bind(&NetworkPluginServer::handleUserPresenceChanged, this, user, _1)); + user->onRawPresenceReceived.disconnect(boost::bind(&NetworkPluginServer::handleRawPresenceReceived, this, _1)); + user->onRoomJoined.disconnect(boost::bind(&NetworkPluginServer::handleRoomJoined, this, user, _1, _2, _3, _4)); + user->onRoomLeft.disconnect(boost::bind(&NetworkPluginServer::handleRoomLeft, this, user, _1)); + + user->getRosterManager()->onBuddyAdded.disconnect(boost::bind(&NetworkPluginServer::handleUserBuddyAdded, this, user, _1)); + user->getRosterManager()->onBuddyRemoved.disconnect(boost::bind(&NetworkPluginServer::handleUserBuddyRemoved, this, user, _1)); + + pbnetwork::Logout logout; + logout.set_user(user->getJID().toBare()); + logout.set_legacyname(userInfo.uin); + + std::string message; + logout.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LOGOUT); + + Backend *c = (Backend *) user->getData(); + if (!c) { + return; + } + send(c->connection, message); + c->users.remove(user); + + // If backend should handle only one user, it must not accept another one before + // we kill it, so set up willDie to true + if (c->users.size() == 0 && CONFIG_INT(m_config, "service.users_per_backend") == 1) { + LOG4CXX_INFO(logger, "Backend " << c->id << " will die, because the last user disconnected"); + c->willDie = true; + } +} + +void NetworkPluginServer::handleMessageReceived(NetworkConversation *conv, boost::shared_ptr &msg) { + conv->getConversationManager()->getUser()->updateLastActivity(); + + if (CONFIG_BOOL_DEFAULTED(m_config, "features.rawxml", false)) { + Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData(); + if (!c) { + return; + } + Swift::JID legacyname = Swift::JID(Buddy::JIDToLegacyName(msg->getTo())); + if (!msg->getTo().getResource().empty()) { + msg->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain(), msg->getTo().getResource())); + } + else { + msg->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain())); + } + std::string xml = safeByteArrayToString(m_serializer->serializeElement(msg)); + WRAP(xml, pbnetwork::WrapperMessage_Type_TYPE_RAW_XML); + send(c->connection, xml); + return; + } + + boost::shared_ptr statePayload = msg->getPayload(); + if (statePayload) { + pbnetwork::WrapperMessage_Type type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED; + switch (statePayload->getChatState()) { + case Swift::ChatState::Active: + type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_STOPPED_TYPING; + break; + case Swift::ChatState::Composing: + type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPING; + break; + case Swift::ChatState::Paused: + type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPED; + break; + default: + break; + } + if (type != pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED) { + pbnetwork::Buddy buddy; + buddy.set_username(conv->getConversationManager()->getUser()->getJID().toBare()); + buddy.set_buddyname(conv->getLegacyName()); + + std::string message; + buddy.SerializeToString(&message); + + WRAP(message, type); + + Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData(); + if (!c) { + return; + } + send(c->connection, message); + } + } + + boost::shared_ptr attentionPayload = msg->getPayload(); + if (attentionPayload) { + pbnetwork::ConversationMessage m; + m.set_username(conv->getConversationManager()->getUser()->getJID().toBare()); + m.set_buddyname(conv->getLegacyName()); + m.set_message(msg->getBody()); + + std::string message; + m.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_ATTENTION); + + Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData(); + send(c->connection, message); + return; + } + + if (!msg->getSubject().empty()) { + pbnetwork::ConversationMessage m; + m.set_username(conv->getConversationManager()->getUser()->getJID().toBare()); + m.set_buddyname(conv->getLegacyName()); + m.set_message(msg->getSubject()); + + std::string message; + m.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_ROOM_SUBJECT_CHANGED); + + Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData(); + send(c->connection, message); + return; + } + + + std::string xhtml; + boost::shared_ptr xhtmlPayload = msg->getPayload(); + if (xhtmlPayload) { + xhtml = xhtmlPayload->getBody(); + } + + // Send normal message + if (!msg->getBody().empty() || !xhtml.empty()) { + pbnetwork::ConversationMessage m; + m.set_username(conv->getConversationManager()->getUser()->getJID().toBare()); + m.set_buddyname(conv->getLegacyName()); + m.set_message(msg->getBody()); + m.set_xhtml(xhtml); + boost::shared_ptr receiptPayload = msg->getPayload(); + if (receiptPayload && !msg->getID().empty()) { + m.set_id(msg->getID()); + } + + std::string message; + m.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE); + + Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData(); + if (!c) { + return; + } + send(c->connection, message); + } +} + +void NetworkPluginServer::handleBuddyRemoved(Buddy *b) { + User *user = b->getRosterManager()->getUser(); + + pbnetwork::Buddy buddy; + buddy.set_username(user->getJID().toBare()); + buddy.set_buddyname(b->getName()); + buddy.set_alias(b->getAlias()); + BOOST_FOREACH(const std::string &g, b->getGroups()) { + buddy.add_group(g); + } + buddy.set_status(pbnetwork::STATUS_NONE); + + std::string message; + buddy.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_REMOVED); + + Backend *c = (Backend *) user->getData(); + if (!c) { + return; + } + send(c->connection, message); +} + +void NetworkPluginServer::handleBuddyUpdated(Buddy *b, const Swift::RosterItemPayload &item) { + User *user = b->getRosterManager()->getUser(); + + dynamic_cast(b)->setAlias(item.getName()); + dynamic_cast(b)->setGroups(item.getGroups()); + + pbnetwork::Buddy buddy; + buddy.set_username(user->getJID().toBare()); + buddy.set_buddyname(b->getName()); + buddy.set_alias(b->getAlias()); + BOOST_FOREACH(const std::string &g, b->getGroups()) { + buddy.add_group(g); + } + buddy.set_status(pbnetwork::STATUS_NONE); + + std::string message; + buddy.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED); + + Backend *c = (Backend *) user->getData(); + if (!c) { + return; + } + send(c->connection, message); +} + +void NetworkPluginServer::handleBuddyAdded(Buddy *buddy, const Swift::RosterItemPayload &item) { + handleBuddyUpdated(buddy, item); +} + +void NetworkPluginServer::handleUserBuddyAdded(User *user, Buddy *b) { + pbnetwork::Buddy buddy; + buddy.set_username(user->getJID().toBare()); + buddy.set_buddyname(b->getName()); + buddy.set_alias(b->getAlias()); + BOOST_FOREACH(const std::string &g, b->getGroups()) { + buddy.add_group(g); + } + buddy.set_status(pbnetwork::STATUS_NONE); + + std::string message; + buddy.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED); + + Backend *c = (Backend *) user->getData(); + if (!c) { + return; + } + send(c->connection, message); +} + +void NetworkPluginServer::handleUserBuddyRemoved(User *user, Buddy *b) { + handleBuddyRemoved(b); +} + +void NetworkPluginServer::handleBlockToggled(Buddy *b) { + User *user = b->getRosterManager()->getUser(); + + pbnetwork::Buddy buddy; + buddy.set_username(user->getJID().toBare()); + buddy.set_buddyname(b->getName()); + buddy.set_alias(b->getAlias()); + BOOST_FOREACH(const std::string &g, b->getGroups()) { + buddy.add_group(g); + } + buddy.set_status(pbnetwork::STATUS_NONE); + buddy.set_blocked(!b->isBlocked()); + + std::string message; + buddy.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED); + + Backend *c = (Backend *) user->getData(); + if (!c) { + return; + } + send(c->connection, message); +} + + +void NetworkPluginServer::handleVCardUpdated(User *user, boost::shared_ptr v) { + pbnetwork::VCard vcard; + vcard.set_username(user->getJID().toBare()); + vcard.set_buddyname(""); + vcard.set_id(0); + vcard.set_photo(&v->getPhoto()[0], v->getPhoto().size()); + vcard.set_nickname(v->getNickname()); + + std::string message; + vcard.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_VCARD); + + Backend *c = (Backend *) user->getData(); + if (!c) { + return; + } + send(c->connection, message); +} + +void NetworkPluginServer::handleVCardRequired(User *user, const std::string &name, unsigned int id) { + pbnetwork::VCard vcard; + vcard.set_username(user->getJID().toBare()); + vcard.set_buddyname(name); + vcard.set_id(id); + + std::string message; + vcard.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_VCARD); + + Backend *c = (Backend *) user->getData(); + if (!c) { + return; + } + send(c->connection, message); +} + +void NetworkPluginServer::handleFTAccepted(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID) { + pbnetwork::File f; + f.set_username(user->getJID().toBare()); + f.set_buddyname(buddyName); + f.set_filename(fileName); + f.set_size(size); + f.set_ftid(ftID); + + std::string message; + f.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_START); + + Backend *c = (Backend *) user->getData(); + if (!c) { + return; + } + send(c->connection, message); +} + +void NetworkPluginServer::handleFTRejected(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size) { + pbnetwork::File f; + f.set_username(user->getJID().toBare()); + f.set_buddyname(buddyName); + f.set_filename(fileName); + f.set_size(size); + f.set_ftid(0); + + std::string message; + f.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_FINISH); + + Backend *c = (Backend *) user->getData(); + if (!c) { + return; + } + send(c->connection, message); +} + +void NetworkPluginServer::handleFTStateChanged(Swift::FileTransfer::State state, const std::string &userName, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long id) { + User *user = m_userManager->getUser(userName); + if (!user) { + // TODO: FIXME We have to remove filetransfer when use disconnects + return; + } +#if !HAVE_SWIFTEN_3 + if (state.state == Swift::FileTransfer::State::Transferring) { + handleFTAccepted(user, buddyName, fileName, size, id); + } + else if (state.state == Swift::FileTransfer::State::Canceled) { + handleFTRejected(user, buddyName, fileName, size); + } +#endif +} + +void NetworkPluginServer::sendPing(Backend *c) { + + std::string message; + pbnetwork::WrapperMessage wrap; + wrap.set_type(pbnetwork::WrapperMessage_Type_TYPE_PING); + wrap.SerializeToString(&message); + + if (c->connection) { + LOG4CXX_INFO(logger, "PING to " << c << " (ID=" << c->id << ")"); + send(c->connection, message); + c->pongReceived = false; + } +// LOG4CXX_INFO(logger, "PING to " << c); +} + +void NetworkPluginServer::handlePIDTerminated(unsigned long pid) { + std::vector::iterator log_id_it; + log_id_it = std::find(m_pids.begin(), m_pids.end(), pid); + if (log_id_it != m_pids.end()) { + *log_id_it = 0; + } +} + +#ifndef _WIN32 + +static int sig_block_count = 0; +static sigset_t block_mask; + +static void __block_signals ( void ) +{ + static int init_done = 0; + + if ( (sig_block_count++) != 1 ) return; + + if ( init_done == 0 ) { + sigemptyset ( &block_mask ); + sigaddset ( &block_mask, SIGPIPE ); + sigaddset ( &block_mask, SIGHUP ); + sigaddset ( &block_mask, SIGINT ); + sigaddset ( &block_mask, SIGQUIT ); + sigaddset ( &block_mask, SIGTERM ); + sigaddset ( &block_mask, SIGABRT ); + sigaddset ( &block_mask, SIGCHLD ); + init_done = 1; + } + + sigprocmask ( SIG_BLOCK, &block_mask, NULL ); + return; +} + +static void __unblock_signals ( void ) +{ + sigset_t sigset; + + if ( (sig_block_count--) != 0 ) return; + sigprocmask ( SIG_UNBLOCK, &block_mask, NULL ); + + if ( sigpending ( &sigset ) == 0 ) { + if ( sigismember ( &sigset, SIGCHLD ) ) { + raise ( SIGCHLD ); + } + } +} + +#endif + +NetworkPluginServer::Backend *NetworkPluginServer::getFreeClient(bool acceptUsers, bool longRun, bool check) { + NetworkPluginServer::Backend *c = NULL; + + unsigned long diff = CONFIG_INT(m_config, "service.login_delay"); + time_t now = time(NULL); + if (diff && (now - m_lastLogin < diff)) { + m_loginTimer->stop(); + m_loginTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer((diff - (now - m_lastLogin)) * 1000); + m_loginTimer->onTick.connect(boost::bind(&NetworkPluginServer::loginDelayFinished, this)); + m_loginTimer->start(); + LOG4CXX_INFO(logger, "Postponing login because of service.login_delay setting"); + return NULL; + } + + if (!check) { + m_lastLogin = time(NULL); + } + + // Check all backends and find free one + for (std::list::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) { + if ((*it)->willDie == false && (*it)->acceptUsers == acceptUsers && (*it)->users.size() < CONFIG_INT(m_config, "service.users_per_backend") && (*it)->connection && (*it)->longRun == longRun) { + c = *it; + // if we're not reusing all backends and backend is full, stop accepting new users on this backend + if (!CONFIG_BOOL(m_config, "service.reuse_old_backends")) { + if (!check && c->users.size() + 1 >= CONFIG_INT(m_config, "service.users_per_backend")) { + c->acceptUsers = false; + } + } + break; + } + } + + // there's no free backend, so spawn one. + if (c == NULL && !m_startingBackend) { + m_isNextLongRun = longRun; + m_startingBackend = true; + +#ifndef _WIN32 + __block_signals(); +#endif + std::vector::iterator log_id_it; + log_id_it = std::find(m_pids.begin(), m_pids.end(), 0); + std::string log_id = ""; + if (log_id_it == m_pids.end()) { + log_id = boost::lexical_cast(m_pids.size() + 1); + } + else { + log_id = boost::lexical_cast(log_id_it - m_pids.begin() + 1); + } + unsigned long pid = exec_(CONFIG_STRING(m_config, "service.backend"), CONFIG_STRING(m_config, "service.backend_host").c_str(), CONFIG_STRING(m_config, "service.backend_port").c_str(), log_id.c_str(), m_config->getCommandLineArgs().c_str()); + if (log_id_it == m_pids.end()) { + m_pids.push_back(pid); + } + else { + *log_id_it = pid; + } +#ifndef _WIN32 + __unblock_signals(); +#endif + } + + return c; +} + +} diff --git a/src/settingsadhoccommand.cpp b/src/settingsadhoccommand.cpp index 51ddca42..9d630e58 100644 --- a/src/settingsadhoccommand.cpp +++ b/src/settingsadhoccommand.cpp @@ -1,146 +1,181 @@ -/** - * XMPP - libpurple transport - * - * Copyright (C) 2012, 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/settingsadhoccommand.h" -#include "transport/conversation.h" -#include "transport/usermanager.h" -#include "transport/buddy.h" -#include "transport/factory.h" -#include "transport/user.h" -#include "transport/logging.h" -#include "transport/storagebackend.h" - - -namespace Transport { - -DEFINE_LOGGER(logger, "SettingsAdHocCommand"); - -SettingsAdHocCommand::SettingsAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to) : AdHocCommand(component, userManager, storageBackend, initiator, to) { - m_state = Init; - Swift::BooleanFormField::ref field; - - field = Swift::BooleanFormField::create(true); - field->setName("enable_transport"); - field->setLabel("Enable transport"); - addFormField(field); - - field = Swift::BooleanFormField::create(CONFIG_STRING_DEFAULTED(component->getConfig(), "settings.send_headlines", "0") == "1"); - field->setName("send_headlines"); - field->setLabel("Allow sending messages as headlines"); - addFormField(field); - - field = Swift::BooleanFormField::create(CONFIG_STRING_DEFAULTED(component->getConfig(), "settings.stay_connected", "0") == "1"); - field->setName("stay_connected"); - field->setLabel("Stay connected to legacy network when offline on XMPP"); - addFormField(field); -} - -SettingsAdHocCommand::~SettingsAdHocCommand() { -} - -boost::shared_ptr SettingsAdHocCommand::getForm() { - if (!m_storageBackend) { - boost::shared_ptr response(new Swift::Command("settings", m_id, Swift::Command::Completed)); - boost::shared_ptr form(new Swift::Form()); - form->addField(Swift::FixedFormField::create("This server does not support transport settings. There is no storage backend configured")); - response->setForm(form); - return response; - } - - UserInfo user; - if (m_storageBackend->getUser(m_initiator.toBare().toString(), user) == false) { - boost::shared_ptr response(new Swift::Command("settings", m_id, Swift::Command::Completed)); - boost::shared_ptr form(new Swift::Form()); - form->addField(Swift::FixedFormField::create("You are not registered.")); - response->setForm(form); - return response; - } - - boost::shared_ptr response(new Swift::Command("settings", m_id, Swift::Command::Executing)); - boost::shared_ptr form(new Swift::Form()); - - BOOST_FOREACH(Swift::FormField::ref field, m_fields) { - // FIXME: Support for more types than boolean - if (boost::dynamic_pointer_cast(field)) { - Swift::BooleanFormField::ref f(boost::dynamic_pointer_cast(field)); - std::string value = f->getValue() ? "1" : "0"; - int type = (int) TYPE_BOOLEAN; - m_storageBackend->getUserSetting(user.id, f->getName(), type, value); - f->setValue(value == "1"); - } - - form->addField(field); - } - - response->setForm(form); - return response; -} - -boost::shared_ptr SettingsAdHocCommand::handleResponse(boost::shared_ptr payload) { - UserInfo user; - bool registered = m_storageBackend->getUser(m_initiator.toBare().toString(), user); - - if (registered && payload->getForm()) { - BOOST_FOREACH(Swift::FormField::ref field, m_fields) { - Swift::FormField::ref received = payload->getForm()->getField(field->getName()); - if (!received) { - continue; - } - - // FIXME: Support for more types than boolean - if (boost::dynamic_pointer_cast(received)) { - Swift::BooleanFormField::ref f(boost::dynamic_pointer_cast(received)); - std::string value = f->getValue() ? "1" : "0"; - m_storageBackend->updateUserSetting(user.id, f->getName(), value); - } - else if (boost::dynamic_pointer_cast(received)) { - Swift::TextSingleFormField::ref f(boost::dynamic_pointer_cast(received)); - m_storageBackend->updateUserSetting(user.id, f->getName(), f->getValue()); - } - } - } - - boost::shared_ptr response(new Swift::Command("settings", m_id, Swift::Command::Completed)); - return response; -} - -boost::shared_ptr SettingsAdHocCommand::handleRequest(boost::shared_ptr payload) { - boost::shared_ptr response; - if (payload->getAction() == Swift::Command::Cancel) { - response = boost::shared_ptr(new Swift::Command("settings", m_id, Swift::Command::Canceled)); - return response; - } - - switch (m_state) { - case Init: - response = getForm(); - m_state = WaitingForResponse; - break; - case WaitingForResponse: - response = handleResponse(payload); - break; - default: - break; - } - - return response; -} - -} +/** + * XMPP - libpurple transport + * + * Copyright (C) 2012, 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/settingsadhoccommand.h" +#include "transport/conversation.h" +#include "transport/usermanager.h" +#include "transport/buddy.h" +#include "transport/factory.h" +#include "transport/user.h" +#include "transport/logging.h" +#include "transport/storagebackend.h" + + +namespace Transport { + +DEFINE_LOGGER(logger, "SettingsAdHocCommand"); + +SettingsAdHocCommand::SettingsAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to) : AdHocCommand(component, userManager, storageBackend, initiator, to) { + m_state = Init; +#if HAVE_SWIFTEN_3 + Swift::FormField::ref field = boost::make_shared(Swift::FormField::BooleanType, "1"); +#else + Swift::BooleanFormField::ref field; + + field = Swift::BooleanFormField::create(true); +#endif + field->setName("enable_transport"); + field->setLabel("Enable transport"); + addFormField(field); +#if HAVE_SWIFTEN_3 + field = boost::make_shared(Swift::FormField::BooleanType, CONFIG_STRING_DEFAULTED(component->getConfig(), "settings.send_headlines", "0")); +#else + field = Swift::BooleanFormField::create(CONFIG_STRING_DEFAULTED(component->getConfig(), "settings.send_headlines", "0") == "1"); +#endif + field->setName("send_headlines"); + field->setLabel("Allow sending messages as headlines"); + addFormField(field); +#if HAVE_SWIFTEN_3 + field = boost::make_shared(Swift::FormField::BooleanType, CONFIG_STRING_DEFAULTED(component->getConfig(), "settings.stay_connected", "0")); +#else + field = Swift::BooleanFormField::create(CONFIG_STRING_DEFAULTED(component->getConfig(), "settings.stay_connected", "0") == "1"); +#endif + field->setName("stay_connected"); + field->setLabel("Stay connected to legacy network when offline on XMPP"); + addFormField(field); +} + +SettingsAdHocCommand::~SettingsAdHocCommand() { +} + +boost::shared_ptr SettingsAdHocCommand::getForm() { + if (!m_storageBackend) { + boost::shared_ptr response(new Swift::Command("settings", m_id, Swift::Command::Completed)); + boost::shared_ptr form(new Swift::Form()); +#if HAVE_SWIFTEN_3 + form->addField(boost::make_shared(Swift::FormField::FixedType, "This server does not support transport settings. There is no storage backend configured")); +#else + form->addField(Swift::FixedFormField::create("This server does not support transport settings. There is no storage backend configured")); +#endif + response->setForm(form); + return response; + } + + UserInfo user; + if (m_storageBackend->getUser(m_initiator.toBare().toString(), user) == false) { + boost::shared_ptr response(new Swift::Command("settings", m_id, Swift::Command::Completed)); + boost::shared_ptr form(new Swift::Form()); +#if HAVE_SWIFTEN_3 + form->addField(boost::make_shared(Swift::FormField::FixedType, "You are not registered.")); +#else + form->addField(Swift::FixedFormField::create("You are not registered.")); +#endif + response->setForm(form); + return response; + } + + boost::shared_ptr response(new Swift::Command("settings", m_id, Swift::Command::Executing)); + boost::shared_ptr form(new Swift::Form()); + + BOOST_FOREACH(Swift::FormField::ref field, m_fields) { + // FIXME: Support for more types than boolean +#if HAVE_SWIFTEN_3 + if (field->getType() == Swift::FormField::BooleanType) { + std::string value = field->getBoolValue() ? "1" : "0"; + int type = (int) TYPE_BOOLEAN; + m_storageBackend->getUserSetting(user.id, field->getName(), type, value); + field->setBoolValue(value == "1"); + } +#else + if (boost::dynamic_pointer_cast(field)) { + Swift::BooleanFormField::ref f(boost::dynamic_pointer_cast(field)); + std::string value = f->getValue() ? "1" : "0"; + int type = (int)TYPE_BOOLEAN; + m_storageBackend->getUserSetting(user.id, f->getName(), type, value); + f->setValue(value == "1"); + } +#endif + + form->addField(field); + } + + response->setForm(form); + return response; +} + +boost::shared_ptr SettingsAdHocCommand::handleResponse(boost::shared_ptr payload) { + UserInfo user; + bool registered = m_storageBackend->getUser(m_initiator.toBare().toString(), user); + + if (registered && payload->getForm()) { + BOOST_FOREACH(Swift::FormField::ref field, m_fields) { + Swift::FormField::ref received = payload->getForm()->getField(field->getName()); + if (!received) { + continue; + } +#if HAVE_SWIFTEN_3 + if (received->getType() == Swift::FormField::BooleanType) { + std::string value = received->getBoolValue() ? "1" : "0"; + m_storageBackend->updateUserSetting(user.id, received->getName(), value); + } else if (received->getType() == Swift::FormField::TextSingleType) { + m_storageBackend->updateUserSetting(user.id, received->getName(), received->getTextSingleValue()); + } +#else + // FIXME: Support for more types than boolean + if (boost::dynamic_pointer_cast(received)) { + Swift::BooleanFormField::ref f(boost::dynamic_pointer_cast(received)); + std::string value = f->getValue() ? "1" : "0"; + m_storageBackend->updateUserSetting(user.id, f->getName(), value); + } + else if (boost::dynamic_pointer_cast(received)) { + Swift::TextSingleFormField::ref f(boost::dynamic_pointer_cast(received)); + m_storageBackend->updateUserSetting(user.id, f->getName(), f->getValue()); + } +#endif + } + } + + boost::shared_ptr response(new Swift::Command("settings", m_id, Swift::Command::Completed)); + return response; +} + +boost::shared_ptr SettingsAdHocCommand::handleRequest(boost::shared_ptr payload) { + boost::shared_ptr response; + if (payload->getAction() == Swift::Command::Cancel) { + response = boost::shared_ptr(new Swift::Command("settings", m_id, Swift::Command::Canceled)); + return response; + } + + switch (m_state) { + case Init: + response = getForm(); + m_state = WaitingForResponse; + break; + case WaitingForResponse: + response = handleResponse(payload); + break; + default: + break; + } + + return response; +} + +} diff --git a/src/transport.cpp b/src/transport.cpp index 18fc4830..76afb0be 100644 --- a/src/transport.cpp +++ b/src/transport.cpp @@ -1,355 +1,363 @@ -/** - * 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/transport.h" -#include -#include -#include -#include "transport/storagebackend.h" -#include "transport/factory.h" -#include "transport/userregistry.h" -#include "transport/logging.h" -#include "storageparser.h" -#ifdef _WIN32 -#include -#include "Swiften/TLS/Schannel/SchannelServerContext.h" -#include "Swiften/TLS/Schannel/SchannelServerContextFactory.h" -#else -#include "Swiften/TLS/PKCS12Certificate.h" -#include "Swiften/TLS/CertificateWithKey.h" -#include "Swiften/TLS/OpenSSL/OpenSSLServerContext.h" -#include "Swiften/TLS/OpenSSL/OpenSSLServerContextFactory.h" -#endif -#include "Swiften/Parser/PayloadParsers/AttentionParser.h" -#include "Swiften/Serializer/PayloadSerializers/AttentionSerializer.h" -#include "Swiften/Parser/PayloadParsers/XHTMLIMParser.h" -#include "Swiften/Serializer/PayloadSerializers/XHTMLIMSerializer.h" -#include "Swiften/Parser/PayloadParsers/StatsParser.h" -#include "Swiften/Serializer/PayloadSerializers/StatsSerializer.h" -#include "Swiften/Parser/PayloadParsers/GatewayPayloadParser.h" -#include "Swiften/Serializer/PayloadSerializers/GatewayPayloadSerializer.h" -#include "Swiften/Serializer/PayloadSerializers/SpectrumErrorSerializer.h" -#include "Swiften/Parser/PayloadParsers/MUCPayloadParser.h" -#include "transport/BlockParser.h" -#include "transport/BlockSerializer.h" -#include "Swiften/Parser/PayloadParsers/InvisibleParser.h" -#include "Swiften/Serializer/PayloadSerializers/InvisibleSerializer.h" -#include "Swiften/Parser/GenericPayloadParserFactory.h" -#include "Swiften/Queries/IQRouter.h" -#include "Swiften/Elements/RosterPayload.h" -#include "Swiften/Elements/InBandRegistrationPayload.h" - -using namespace Swift; -using namespace boost; - -namespace Transport { - -DEFINE_LOGGER(logger, "Component"); -DEFINE_LOGGER(logger_xml, "Component.XML"); - -Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, Config *config, Factory *factory, Transport::UserRegistry *userRegistry) { - m_component = NULL; - m_userRegistry = NULL; - m_server = NULL; - m_reconnectCount = 0; - m_config = config; - m_config->onBackendConfigUpdated.connect(boost::bind(&Component::handleBackendConfigChanged, this)); - m_factory = factory; - m_loop = loop; - m_userRegistry = userRegistry; - m_rawXML = false; - - m_jid = Swift::JID(CONFIG_STRING(m_config, "service.jid")); - - m_factories = factories; - - m_reconnectTimer = m_factories->getTimerFactory()->createTimer(3000); - m_reconnectTimer->onTick.connect(bind(&Component::start, this)); - - if (CONFIG_BOOL(m_config, "service.server_mode")) { - LOG4CXX_INFO(logger, "Creating component in server mode on port " << CONFIG_INT(m_config, "service.port")); - m_server = new Swift::Server(loop, m_factories, m_userRegistry, m_jid, CONFIG_STRING(m_config, "service.server"), CONFIG_INT(m_config, "service.port")); - if (!CONFIG_STRING(m_config, "service.cert").empty()) { -#ifndef _WIN32 -//TODO: fix - LOG4CXX_INFO(logger, "Using PKCS#12 certificate " << CONFIG_STRING(m_config, "service.cert")); - LOG4CXX_INFO(logger, "SSLv23_server_method used."); - TLSServerContextFactory *f = new OpenSSLServerContextFactory(); - CertificateWithKey::ref certificate = boost::make_shared(CONFIG_STRING(m_config, "service.cert"), createSafeByteArray(CONFIG_STRING(m_config, "service.cert_password"))); - m_server->addTLSEncryption(f, certificate); -#endif - - } - else { - LOG4CXX_WARN(logger, "No PKCS#12 certificate used. TLS is disabled."); - } -// m_server->start(); - m_stanzaChannel = m_server->getStanzaChannel(); - m_iqRouter = m_server->getIQRouter(); - - m_server->addPayloadParserFactory(new GenericPayloadParserFactory("private", "jabber:iq:private")); - m_server->addPayloadParserFactory(new GenericPayloadParserFactory("attention", "urn:xmpp:attention:0")); - m_server->addPayloadParserFactory(new GenericPayloadParserFactory("html", "http://jabber.org/protocol/xhtml-im")); - m_server->addPayloadParserFactory(new GenericPayloadParserFactory("block", "urn:xmpp:block:0")); - m_server->addPayloadParserFactory(new GenericPayloadParserFactory("invisible", "urn:xmpp:invisible:0")); - m_server->addPayloadParserFactory(new GenericPayloadParserFactory("query", "http://jabber.org/protocol/stats")); - m_server->addPayloadParserFactory(new GenericPayloadParserFactory("query", "jabber:iq:gateway")); - m_server->addPayloadParserFactory(new GenericPayloadParserFactory("x", "http://jabber.org/protocol/muc")); - - m_server->addPayloadSerializer(new Swift::AttentionSerializer()); - m_server->addPayloadSerializer(new Swift::XHTMLIMSerializer()); - m_server->addPayloadSerializer(new Transport::BlockSerializer()); - m_server->addPayloadSerializer(new Swift::InvisibleSerializer()); - m_server->addPayloadSerializer(new Swift::StatsSerializer()); - m_server->addPayloadSerializer(new Swift::SpectrumErrorSerializer()); - m_server->addPayloadSerializer(new Swift::GatewayPayloadSerializer()); - - m_server->onDataRead.connect(boost::bind(&Component::handleDataRead, this, _1)); - m_server->onDataWritten.connect(boost::bind(&Component::handleDataWritten, this, _1)); - } - else { - LOG4CXX_INFO(logger, "Creating component in gateway mode"); - m_component = new Swift::Component(loop, m_factories, m_jid, CONFIG_STRING(m_config, "service.password")); - m_component->setSoftwareVersion("Spectrum", SPECTRUM_VERSION); - m_component->onConnected.connect(bind(&Component::handleConnected, this)); - m_component->onError.connect(boost::bind(&Component::handleConnectionError, this, _1)); - m_component->onDataRead.connect(boost::bind(&Component::handleDataRead, this, _1)); - m_component->onDataWritten.connect(boost::bind(&Component::handleDataWritten, this, _1)); - - m_component->addPayloadParserFactory(new GenericPayloadParserFactory("private", "jabber:iq:private")); - m_component->addPayloadParserFactory(new GenericPayloadParserFactory("attention", "urn:xmpp:attention:0")); - m_component->addPayloadParserFactory(new GenericPayloadParserFactory("html", "http://jabber.org/protocol/xhtml-im")); - m_component->addPayloadParserFactory(new GenericPayloadParserFactory("block", "urn:xmpp:block:0")); - m_component->addPayloadParserFactory(new GenericPayloadParserFactory("invisible", "urn:xmpp:invisible:0")); - m_component->addPayloadParserFactory(new GenericPayloadParserFactory("query", "http://jabber.org/protocol/stats")); - m_component->addPayloadParserFactory(new GenericPayloadParserFactory("query", "jabber:iq:gateway")); - m_component->addPayloadParserFactory(new GenericPayloadParserFactory("x", "http://jabber.org/protocol/muc")); - - m_component->addPayloadSerializer(new Swift::AttentionSerializer()); - m_component->addPayloadSerializer(new Swift::XHTMLIMSerializer()); - m_component->addPayloadSerializer(new Transport::BlockSerializer()); - m_component->addPayloadSerializer(new Swift::InvisibleSerializer()); - m_component->addPayloadSerializer(new Swift::StatsSerializer()); - m_component->addPayloadSerializer(new Swift::SpectrumErrorSerializer()); - m_component->addPayloadSerializer(new Swift::GatewayPayloadSerializer()); - - m_stanzaChannel = m_component->getStanzaChannel(); - m_iqRouter = m_component->getIQRouter(); - } - - m_capsMemoryStorage = new CapsMemoryStorage(); - m_capsManager = new CapsManager(m_capsMemoryStorage, m_stanzaChannel, m_iqRouter); - m_entityCapsManager = new EntityCapsManager(m_capsManager, m_stanzaChannel); - m_entityCapsManager->onCapsChanged.connect(boost::bind(&Component::handleCapsChanged, this, _1)); - - m_presenceOracle = new Transport::PresenceOracle(m_stanzaChannel); - m_presenceOracle->onPresenceChange.connect(bind(&Component::handlePresence, this, _1)); - - - -// -// m_registerHandler = new SpectrumRegisterHandler(m_component); -// m_registerHandler->start(); -} - -Component::~Component() { - delete m_presenceOracle; - delete m_entityCapsManager; - delete m_capsManager; - delete m_capsMemoryStorage; - if (m_component) - delete m_component; - if (m_server) { - m_server->stop(); - delete m_server; - } -} - -bool Component::handleIQ(boost::shared_ptr iq) { - if (!m_rawXML) { - return false; - } - - if (iq->getPayload() != NULL) { return false; } - if (iq->getPayload() != NULL) { return false; } - if (iq->getPayload() != NULL) { return false; } - - if (iq->getTo().getNode().empty()) { - return false; - } - - onRawIQReceived(iq); - return true; -} - -void Component::handleBackendConfigChanged() { - if (!m_rawXML && CONFIG_BOOL_DEFAULTED(m_config, "features.rawxml", false)) { - m_rawXML = true; - m_iqRouter->addHandler(this); - } -} - -Swift::StanzaChannel *Component::getStanzaChannel() { - return m_stanzaChannel; -} - -Transport::PresenceOracle *Component::getPresenceOracle() { - return m_presenceOracle; -} - -void Component::start() { - if (m_component && !m_component->isAvailable()) { - LOG4CXX_INFO(logger, "Connecting XMPP server " << CONFIG_STRING(m_config, "service.server") << " port " << CONFIG_INT(m_config, "service.port")); - if (CONFIG_INT(m_config, "service.port") == 5222) { - LOG4CXX_WARN(logger, "Port 5222 is usually used for client connections, not for component connections! Are you sure you are using right port?"); - } - m_reconnectCount++; - m_component->connect(CONFIG_STRING(m_config, "service.server"), CONFIG_INT(m_config, "service.port")); - m_reconnectTimer->stop(); - } - else if (m_server) { - LOG4CXX_INFO(logger, "Starting component in server mode on port " << CONFIG_INT(m_config, "service.port")); - m_server->start(); - - //Type casting to BoostConnectionServer since onStopped signal is not defined in ConnectionServer - //Ideally, onStopped must be defined in ConnectionServer - if (boost::dynamic_pointer_cast(m_server->getConnectionServer())) { - boost::dynamic_pointer_cast(m_server->getConnectionServer())->onStopped.connect(boost::bind(&Component::handleServerStopped, this, _1)); - } - - // We're connected right here, because we're in server mode... - handleConnected(); - } -} - -void Component::stop() { - if (m_component) { - m_reconnectCount = 0; - // TODO: Call this once swiften will fix assert(!session_); -// m_component->disconnect(); - m_reconnectTimer->stop(); - } - else if (m_server) { - LOG4CXX_INFO(logger, "Stopping component in server mode on port " << CONFIG_INT(m_config, "service.port")); - m_server->stop(); - } -} - -void Component::handleConnected() { - onConnected(); - m_reconnectCount = 0; - m_reconnectTimer->stop(); -} - -void Component::handleServerStopped(boost::optional e) { - if(e != NULL ) { - if(*e == Swift::BoostConnectionServer::Conflict) { - LOG4CXX_INFO(logger, "Port "<< CONFIG_INT(m_config, "service.port") << " already in use! Stopping server.."); - if (CONFIG_INT(m_config, "service.port") == 5347) { - LOG4CXX_INFO(logger, "Port 5347 is usually used for components. You are using server_mode=1. Are you sure you don't want to use server_mode=0 and run spectrum as component?"); - } - } - if(*e == Swift::BoostConnectionServer::UnknownError) - LOG4CXX_INFO(logger, "Unknown error occured! Stopping server.."); - exit(1); - } -} - - -void Component::handleConnectionError(const ComponentError &error) { - onConnectionError(error); -// if (m_reconnectCount == 2) -// Component::instance()->userManager()->removeAllUsers(); - std::string str = "Unknown error"; - switch (error.getType()) { - case ComponentError::UnknownError: str = "Unknown error"; break; - case ComponentError::ConnectionError: str = "Connection error"; break; - case ComponentError::ConnectionReadError: str = "Connection read error"; break; - case ComponentError::ConnectionWriteError: str = "Connection write error"; break; - case ComponentError::XMLError: str = "XML Error"; break; - case ComponentError::AuthenticationFailedError: str = "Authentication failed error"; break; - case ComponentError::UnexpectedElementError: str = "Unexpected element error"; break; - } - LOG4CXX_INFO(logger, "Disconnected from XMPP server. Error: " << str); - - m_reconnectTimer->start(); -} - -void Component::handleDataRead(const Swift::SafeByteArray &data) { - std::string d = safeByteArrayToString(data); - if (!boost::starts_with(d, "getTo().getNode().empty()) - return; - - // filter out bad presences - if (!presence->getFrom().isValid()) { - return; - } - - switch (presence->getType()) { - case Presence::Error: - case Presence::Subscribe: - case Presence::Subscribed: - case Presence::Unsubscribe: - case Presence::Unsubscribed: - return; - default: - break; - }; - - // check if we have this client's capabilities and ask for them - 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(); - } -#ifdef SUPPORT_LEGACY_CAPS - else { - GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(presence->getFrom(), m_iqRouter); - discoInfoRequest->onResponse.connect(boost::bind(&Component::handleDiscoInfoResponse, this, _1, _2, presence->getFrom())); - discoInfoRequest->send(); - } -#endif - } - - onUserPresenceReceived(presence); -} - -void Component::handleDiscoInfoResponse(boost::shared_ptr info, Swift::ErrorPayload::ref error, const Swift::JID& jid) { -#ifdef SUPPORT_LEGACY_CAPS - onUserDiscoInfoReceived(jid, info); -#endif -} - -void Component::handleCapsChanged(const Swift::JID& jid) { - onUserDiscoInfoReceived(jid, m_entityCapsManager->getCaps(jid)); -} - -} +/** + * 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/transport.h" +#include +#include +#include +#include "transport/storagebackend.h" +#include "transport/factory.h" +#include "transport/userregistry.h" +#include "transport/logging.h" +#include "storageparser.h" +#ifdef _WIN32 +#include +#include "Swiften/TLS/Schannel/SchannelServerContext.h" +#include "Swiften/TLS/Schannel/SchannelServerContextFactory.h" +#else +#include "Swiften/TLS/PKCS12Certificate.h" +#include "Swiften/TLS/CertificateWithKey.h" +#include "Swiften/TLS/OpenSSL/OpenSSLServerContext.h" +#include "Swiften/TLS/OpenSSL/OpenSSLServerContextFactory.h" +#endif +#include "Swiften/Parser/PayloadParsers/AttentionParser.h" +#include "Swiften/Serializer/PayloadSerializers/AttentionSerializer.h" +#include "Swiften/Parser/PayloadParsers/XHTMLIMParser.h" +#include "Swiften/Serializer/PayloadSerializers/XHTMLIMSerializer.h" +#include "Swiften/Parser/PayloadParsers/StatsParser.h" +#include "Swiften/Serializer/PayloadSerializers/StatsSerializer.h" +#include "Swiften/Parser/PayloadParsers/GatewayPayloadParser.h" +#include "Swiften/Serializer/PayloadSerializers/GatewayPayloadSerializer.h" +#include "Swiften/Serializer/PayloadSerializers/SpectrumErrorSerializer.h" +#include "Swiften/Parser/PayloadParsers/MUCPayloadParser.h" +#include "transport/BlockParser.h" +#include "transport/BlockSerializer.h" +#include "Swiften/Parser/PayloadParsers/InvisibleParser.h" +#include "Swiften/Serializer/PayloadSerializers/InvisibleSerializer.h" +#include "Swiften/Parser/GenericPayloadParserFactory.h" +#include "Swiften/Queries/IQRouter.h" +#include "Swiften/Elements/RosterPayload.h" +#include "Swiften/Elements/InBandRegistrationPayload.h" + +using namespace Swift; +using namespace boost; + +namespace Transport { + +DEFINE_LOGGER(logger, "Component"); +DEFINE_LOGGER(logger_xml, "Component.XML"); + +Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, Config *config, Factory *factory, Transport::UserRegistry *userRegistry) { + m_component = NULL; + m_userRegistry = NULL; + m_server = NULL; + m_reconnectCount = 0; + m_config = config; + m_config->onBackendConfigUpdated.connect(boost::bind(&Component::handleBackendConfigChanged, this)); + m_factory = factory; + m_loop = loop; + m_userRegistry = userRegistry; + m_rawXML = false; + + m_jid = Swift::JID(CONFIG_STRING(m_config, "service.jid")); + + m_factories = factories; + + m_reconnectTimer = m_factories->getTimerFactory()->createTimer(3000); + m_reconnectTimer->onTick.connect(bind(&Component::start, this)); + + if (CONFIG_BOOL(m_config, "service.server_mode")) { + LOG4CXX_INFO(logger, "Creating component in server mode on port " << CONFIG_INT(m_config, "service.port")); + m_server = new Swift::Server(loop, m_factories, m_userRegistry, m_jid, CONFIG_STRING(m_config, "service.server"), CONFIG_INT(m_config, "service.port")); + if (!CONFIG_STRING(m_config, "service.cert").empty()) { +#ifndef _WIN32 +//TODO: fix + LOG4CXX_INFO(logger, "Using PKCS#12 certificate " << CONFIG_STRING(m_config, "service.cert")); + LOG4CXX_INFO(logger, "SSLv23_server_method used."); + TLSServerContextFactory *f = new OpenSSLServerContextFactory(); + CertificateWithKey::ref certificate = boost::make_shared(CONFIG_STRING(m_config, "service.cert"), createSafeByteArray(CONFIG_STRING(m_config, "service.cert_password"))); + m_server->addTLSEncryption(f, certificate); +#endif + + } + else { + LOG4CXX_WARN(logger, "No PKCS#12 certificate used. TLS is disabled."); + } +// m_server->start(); + m_stanzaChannel = m_server->getStanzaChannel(); + m_iqRouter = m_server->getIQRouter(); + + m_server->addPayloadParserFactory(new GenericPayloadParserFactory("private", "jabber:iq:private")); + m_server->addPayloadParserFactory(new GenericPayloadParserFactory("attention", "urn:xmpp:attention:0")); + m_server->addPayloadParserFactory(new GenericPayloadParserFactory("html", "http://jabber.org/protocol/xhtml-im")); + m_server->addPayloadParserFactory(new GenericPayloadParserFactory("block", "urn:xmpp:block:0")); + m_server->addPayloadParserFactory(new GenericPayloadParserFactory("invisible", "urn:xmpp:invisible:0")); + m_server->addPayloadParserFactory(new GenericPayloadParserFactory("query", "http://jabber.org/protocol/stats")); + m_server->addPayloadParserFactory(new GenericPayloadParserFactory("query", "jabber:iq:gateway")); + m_server->addPayloadParserFactory(new GenericPayloadParserFactory("x", "http://jabber.org/protocol/muc")); + + m_server->addPayloadSerializer(new Swift::AttentionSerializer()); + m_server->addPayloadSerializer(new Swift::XHTMLIMSerializer()); + m_server->addPayloadSerializer(new Transport::BlockSerializer()); + m_server->addPayloadSerializer(new Swift::InvisibleSerializer()); + m_server->addPayloadSerializer(new Swift::StatsSerializer()); + m_server->addPayloadSerializer(new Swift::SpectrumErrorSerializer()); + m_server->addPayloadSerializer(new Swift::GatewayPayloadSerializer()); + + m_server->onDataRead.connect(boost::bind(&Component::handleDataRead, this, _1)); + m_server->onDataWritten.connect(boost::bind(&Component::handleDataWritten, this, _1)); + } + else { + LOG4CXX_INFO(logger, "Creating component in gateway mode"); +#if HAVE_SWIFTEN_3 + m_component = new Swift::Component(m_jid, CONFIG_STRING(m_config, "service.password"), m_factories); +#else + m_component = new Swift::Component(loop, m_factories, m_jid, CONFIG_STRING(m_config, "service.password")); +#endif + m_component->setSoftwareVersion("Spectrum", SPECTRUM_VERSION); + m_component->onConnected.connect(bind(&Component::handleConnected, this)); + m_component->onError.connect(boost::bind(&Component::handleConnectionError, this, _1)); + m_component->onDataRead.connect(boost::bind(&Component::handleDataRead, this, _1)); + m_component->onDataWritten.connect(boost::bind(&Component::handleDataWritten, this, _1)); + + m_component->addPayloadParserFactory(new GenericPayloadParserFactory("private", "jabber:iq:private")); + m_component->addPayloadParserFactory(new GenericPayloadParserFactory("attention", "urn:xmpp:attention:0")); + m_component->addPayloadParserFactory(new GenericPayloadParserFactory("html", "http://jabber.org/protocol/xhtml-im")); + m_component->addPayloadParserFactory(new GenericPayloadParserFactory("block", "urn:xmpp:block:0")); + m_component->addPayloadParserFactory(new GenericPayloadParserFactory("invisible", "urn:xmpp:invisible:0")); + m_component->addPayloadParserFactory(new GenericPayloadParserFactory("query", "http://jabber.org/protocol/stats")); + m_component->addPayloadParserFactory(new GenericPayloadParserFactory("query", "jabber:iq:gateway")); + m_component->addPayloadParserFactory(new GenericPayloadParserFactory("x", "http://jabber.org/protocol/muc")); + + m_component->addPayloadSerializer(new Swift::AttentionSerializer()); + m_component->addPayloadSerializer(new Swift::XHTMLIMSerializer()); + m_component->addPayloadSerializer(new Transport::BlockSerializer()); + m_component->addPayloadSerializer(new Swift::InvisibleSerializer()); + m_component->addPayloadSerializer(new Swift::StatsSerializer()); + m_component->addPayloadSerializer(new Swift::SpectrumErrorSerializer()); + m_component->addPayloadSerializer(new Swift::GatewayPayloadSerializer()); + + m_stanzaChannel = m_component->getStanzaChannel(); + m_iqRouter = m_component->getIQRouter(); + } + + m_capsMemoryStorage = new CapsMemoryStorage(); +#if HAVE_SWIFTEN_3 + m_capsManager = new CapsManager(m_capsMemoryStorage, m_stanzaChannel, m_iqRouter, m_factories->getCryptoProvider()); +#else + m_capsManager = new CapsManager(m_capsMemoryStorage, m_stanzaChannel, m_iqRouter); +#endif + m_entityCapsManager = new EntityCapsManager(m_capsManager, m_stanzaChannel); + m_entityCapsManager->onCapsChanged.connect(boost::bind(&Component::handleCapsChanged, this, _1)); + + m_presenceOracle = new Transport::PresenceOracle(m_stanzaChannel); + m_presenceOracle->onPresenceChange.connect(bind(&Component::handlePresence, this, _1)); + + + +// +// m_registerHandler = new SpectrumRegisterHandler(m_component); +// m_registerHandler->start(); +} + +Component::~Component() { + delete m_presenceOracle; + delete m_entityCapsManager; + delete m_capsManager; + delete m_capsMemoryStorage; + if (m_component) + delete m_component; + if (m_server) { + m_server->stop(); + delete m_server; + } +} + +bool Component::handleIQ(boost::shared_ptr iq) { + if (!m_rawXML) { + return false; + } + + if (iq->getPayload() != NULL) { return false; } + if (iq->getPayload() != NULL) { return false; } + if (iq->getPayload() != NULL) { return false; } + + if (iq->getTo().getNode().empty()) { + return false; + } + + onRawIQReceived(iq); + return true; +} + +void Component::handleBackendConfigChanged() { + if (!m_rawXML && CONFIG_BOOL_DEFAULTED(m_config, "features.rawxml", false)) { + m_rawXML = true; + m_iqRouter->addHandler(this); + } +} + +Swift::StanzaChannel *Component::getStanzaChannel() { + return m_stanzaChannel; +} + +Transport::PresenceOracle *Component::getPresenceOracle() { + return m_presenceOracle; +} + +void Component::start() { + if (m_component && !m_component->isAvailable()) { + LOG4CXX_INFO(logger, "Connecting XMPP server " << CONFIG_STRING(m_config, "service.server") << " port " << CONFIG_INT(m_config, "service.port")); + if (CONFIG_INT(m_config, "service.port") == 5222) { + LOG4CXX_WARN(logger, "Port 5222 is usually used for client connections, not for component connections! Are you sure you are using right port?"); + } + m_reconnectCount++; + m_component->connect(CONFIG_STRING(m_config, "service.server"), CONFIG_INT(m_config, "service.port")); + m_reconnectTimer->stop(); + } + else if (m_server) { + LOG4CXX_INFO(logger, "Starting component in server mode on port " << CONFIG_INT(m_config, "service.port")); + m_server->start(); + + //Type casting to BoostConnectionServer since onStopped signal is not defined in ConnectionServer + //Ideally, onStopped must be defined in ConnectionServer + if (boost::dynamic_pointer_cast(m_server->getConnectionServer())) { + boost::dynamic_pointer_cast(m_server->getConnectionServer())->onStopped.connect(boost::bind(&Component::handleServerStopped, this, _1)); + } + + // We're connected right here, because we're in server mode... + handleConnected(); + } +} + +void Component::stop() { + if (m_component) { + m_reconnectCount = 0; + // TODO: Call this once swiften will fix assert(!session_); +// m_component->disconnect(); + m_reconnectTimer->stop(); + } + else if (m_server) { + LOG4CXX_INFO(logger, "Stopping component in server mode on port " << CONFIG_INT(m_config, "service.port")); + m_server->stop(); + } +} + +void Component::handleConnected() { + onConnected(); + m_reconnectCount = 0; + m_reconnectTimer->stop(); +} + +void Component::handleServerStopped(boost::optional e) { + if(e != NULL ) { + if(*e == Swift::BoostConnectionServer::Conflict) { + LOG4CXX_INFO(logger, "Port "<< CONFIG_INT(m_config, "service.port") << " already in use! Stopping server.."); + if (CONFIG_INT(m_config, "service.port") == 5347) { + LOG4CXX_INFO(logger, "Port 5347 is usually used for components. You are using server_mode=1. Are you sure you don't want to use server_mode=0 and run spectrum as component?"); + } + } + if(*e == Swift::BoostConnectionServer::UnknownError) + LOG4CXX_INFO(logger, "Unknown error occured! Stopping server.."); + exit(1); + } +} + + +void Component::handleConnectionError(const ComponentError &error) { + onConnectionError(error); +// if (m_reconnectCount == 2) +// Component::instance()->userManager()->removeAllUsers(); + std::string str = "Unknown error"; + switch (error.getType()) { + case ComponentError::UnknownError: str = "Unknown error"; break; + case ComponentError::ConnectionError: str = "Connection error"; break; + case ComponentError::ConnectionReadError: str = "Connection read error"; break; + case ComponentError::ConnectionWriteError: str = "Connection write error"; break; + case ComponentError::XMLError: str = "XML Error"; break; + case ComponentError::AuthenticationFailedError: str = "Authentication failed error"; break; + case ComponentError::UnexpectedElementError: str = "Unexpected element error"; break; + } + LOG4CXX_INFO(logger, "Disconnected from XMPP server. Error: " << str); + + m_reconnectTimer->start(); +} + +void Component::handleDataRead(const Swift::SafeByteArray &data) { + std::string d = safeByteArrayToString(data); + if (!boost::starts_with(d, "getTo().getNode().empty()) + return; + + // filter out bad presences + if (!presence->getFrom().isValid()) { + return; + } + + switch (presence->getType()) { + case Presence::Error: + case Presence::Subscribe: + case Presence::Subscribed: + case Presence::Unsubscribe: + case Presence::Unsubscribed: + return; + default: + break; + }; + + // check if we have this client's capabilities and ask for them + 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(); + } +#ifdef SUPPORT_LEGACY_CAPS + else { + GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(presence->getFrom(), m_iqRouter); + discoInfoRequest->onResponse.connect(boost::bind(&Component::handleDiscoInfoResponse, this, _1, _2, presence->getFrom())); + discoInfoRequest->send(); + } +#endif + } + + onUserPresenceReceived(presence); +} + +void Component::handleDiscoInfoResponse(boost::shared_ptr info, Swift::ErrorPayload::ref error, const Swift::JID& jid) { +#ifdef SUPPORT_LEGACY_CAPS + onUserDiscoInfoReceived(jid, info); +#endif +} + +void Component::handleCapsChanged(const Swift::JID& jid) { + onUserDiscoInfoReceived(jid, m_entityCapsManager->getCaps(jid)); +} + +} diff --git a/src/user.cpp b/src/user.cpp index 301031b8..51ba38f1 100644 --- a/src/user.cpp +++ b/src/user.cpp @@ -1,500 +1,508 @@ -/** - * 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 "transport/storagebackend.h" -#include "transport/rostermanager.h" -#include "transport/usermanager.h" -#include "transport/conversationmanager.h" -#include "transport/presenceoracle.h" -#include "transport/logging.h" -#include "Swiften/Server/ServerStanzaChannel.h" -#include "Swiften/Elements/StreamError.h" -#include "Swiften/Elements/MUCPayload.h" -#include "Swiften/Elements/SpectrumErrorPayload.h" -#include -#include -#include - -using namespace boost; - -#define foreach BOOST_FOREACH - -namespace Transport { - -DEFINE_LOGGER(logger, "User"); - -User::User(const Swift::JID &jid, UserInfo &userInfo, Component *component, UserManager *userManager) { - m_jid = jid.toBare(); - m_data = NULL; - - m_cacheMessages = false; - m_component = component; - m_presenceOracle = component->m_presenceOracle; - m_entityCapsManager = component->m_entityCapsManager; - m_userManager = userManager; - m_userInfo = userInfo; - m_connected = false; - m_readyForConnect = false; - m_ignoreDisconnect = false; - m_resources = 0; - m_reconnectCounter = 0; - - m_reconnectTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(5000); - m_reconnectTimer->onTick.connect(boost::bind(&User::onConnectingTimeout, this)); - - m_rosterManager = new RosterManager(this, m_component); - m_conversationManager = new ConversationManager(this, m_component); - LOG4CXX_INFO(logger, m_jid.toString() << ": Created"); - updateLastActivity(); -} - -User::~User(){ - LOG4CXX_INFO(logger, m_jid.toString() << ": Destroying"); - if (m_component->inServerMode()) { - dynamic_cast(m_component->getStanzaChannel())->finishSession(m_jid, boost::shared_ptr()); - } - - m_reconnectTimer->stop(); - delete m_rosterManager; - delete m_conversationManager; -} - -const Swift::JID &User::getJID() { - return m_jid; -} - -std::vector User::getJIDWithFeature(const std::string &feature) { - std::vector jid; - std::vector presences = m_presenceOracle->getAllPresence(m_jid); - - foreach(Swift::Presence::ref presence, presences) { - if (presence->getType() == Swift::Presence::Unavailable) - continue; - - Swift::DiscoInfo::ref discoInfo = m_entityCapsManager->getCaps(presence->getFrom()); - if (!discoInfo) { -#ifdef SUPPORT_LEGACY_CAPS - if (m_legacyCaps.find(presence->getFrom()) != m_legacyCaps.end()) { - discoInfo = m_legacyCaps[presence->getFrom()]; - } - else { - continue; - } - - if (!discoInfo) { - continue; - } -#else - continue; -#endif - } - - if (discoInfo->hasFeature(feature)) { - LOG4CXX_INFO(logger, m_jid.toString() << ": Found JID with " << feature << " feature: " << presence->getFrom().toString()); - jid.push_back(presence->getFrom()); - } - } - - if (jid.empty()) { - LOG4CXX_INFO(logger, m_jid.toString() << ": No JID with " << feature << " feature " << m_legacyCaps.size()); - } - return jid; -} - -Swift::DiscoInfo::ref User::getCaps(const Swift::JID &jid) const { - Swift::DiscoInfo::ref discoInfo = m_entityCapsManager->getCaps(jid); -#ifdef SUPPORT_LEGACY_CAPS - if (!discoInfo) { - std::map::const_iterator it = m_legacyCaps.find(jid); - if (it != m_legacyCaps.end()) { - discoInfo = it->second; - } - } -#endif - return discoInfo; -} - -void User::sendCurrentPresence() { - if (m_component->inServerMode()) { - return; - } - - std::vector presences = m_presenceOracle->getAllPresence(m_jid); - foreach(Swift::Presence::ref presence, presences) { - if (presence->getType() == Swift::Presence::Unavailable) { - continue; - } - - if (m_connected) { - Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare()); - if (highest) { - Swift::Presence::ref response = Swift::Presence::create(highest); - response->setTo(presence->getFrom()); - response->setFrom(m_component->getJID()); - m_component->getStanzaChannel()->sendPresence(response); - } - else { - Swift::Presence::ref response = Swift::Presence::create(); - response->setTo(presence->getFrom()); - response->setFrom(m_component->getJID()); - response->setType(Swift::Presence::Unavailable); - m_component->getStanzaChannel()->sendPresence(response); - } - } - else { - Swift::Presence::ref response = Swift::Presence::create(); - response->setTo(presence->getFrom()); - response->setFrom(m_component->getJID()); - response->setType(Swift::Presence::Unavailable); - response->setStatus("Connecting"); - m_component->getStanzaChannel()->sendPresence(response); - } - } -} - -void User::setConnected(bool connected) { - m_connected = connected; - m_reconnectCounter = 0; - setIgnoreDisconnect(false); - updateLastActivity(); - - sendCurrentPresence(); - - if (m_connected) { - BOOST_FOREACH(Swift::Presence::ref &presence, m_joinedRooms) { - handlePresence(presence, true); - } - } -} - -void User::setCacheMessages(bool cacheMessages) { - if (m_component->inServerMode() && !m_cacheMessages && cacheMessages) { - m_conversationManager->sendCachedChatMessages(); - } - m_cacheMessages = cacheMessages; -} - -void User::handlePresence(Swift::Presence::ref presence, bool forceJoin) { - LOG4CXX_INFO(logger, "PRESENCE " << presence->getFrom().toString() << " " << presence->getTo().toString()); - if (!m_connected) { - // we are not connected to legacy network, so we should do it when disco#info arrive :) - if (m_readyForConnect == false) { - - // Forward status message to legacy network, but only if it's sent from active resource -// if (m_activeResource == presence->getFrom().getResource().getUTF8String()) { -// forwardStatus(presenceShow, stanzaStatus); -// } - boost::shared_ptr capsInfo = presence->getPayload(); - if (capsInfo && capsInfo->getHash() == "sha-1") { - if (m_entityCapsManager->getCaps(presence->getFrom()) != Swift::DiscoInfo::ref()) { - LOG4CXX_INFO(logger, m_jid.toString() << ": Ready to be connected to legacy network"); - m_readyForConnect = true; - onReadyToConnect(); - } - else { - m_reconnectTimer->start(); - } - } - else if (m_component->inServerMode()) { - LOG4CXX_INFO(logger, m_jid.toString() << ": Ready to be connected to legacy network"); - m_readyForConnect = true; - onReadyToConnect(); - } - else { - m_reconnectTimer->start(); - } - } - } - - - if (!presence->getTo().getNode().empty()) { - bool isMUC = presence->getPayload() != NULL || *presence->getTo().getNode().c_str() == '#'; - if (presence->getType() == Swift::Presence::Unavailable) { - std::string room = Buddy::JIDToLegacyName(presence->getTo()); - Conversation *conv = m_conversationManager->getConversation(room); - if (conv) { - conv->removeJID(presence->getFrom()); - if (!conv->getJIDs().empty()) { - return; - } - } - else { - return; - } - - if (getUserSetting("stay_connected") != "1") { - LOG4CXX_INFO(logger, m_jid.toString() << ": Going to left room " << room); - onRawPresenceReceived(presence); - onRoomLeft(room); - - BOOST_FOREACH(Swift::Presence::ref &p, m_joinedRooms) { - if (p->getTo() == presence->getTo()) { - m_joinedRooms.remove(p); - break; - } - } - - if (conv) { - m_conversationManager->removeConversation(conv); - delete conv; - } - } - - return; - } - else if (isMUC) { - // force connection to legacy network to let backend to handle auto-join on connect. - if (!m_readyForConnect) { - LOG4CXX_INFO(logger, m_jid.toString() << ": Ready to be connected to legacy network"); - m_readyForConnect = true; - onReadyToConnect(); - } - - std::string room = Buddy::JIDToLegacyName(presence->getTo()); - std::string password = ""; - if (presence->getPayload() != NULL) { - password = presence->getPayload()->getPassword() ? *presence->getPayload()->getPassword() : ""; - } - - Conversation *conv = m_conversationManager->getConversation(room); - if (conv != NULL) { - if (std::find(conv->getJIDs().begin(), conv->getJIDs().end(), presence->getFrom()) != conv->getJIDs().end()) { - LOG4CXX_INFO(logger, m_jid.toString() << ": User has already tried to join room " << room << " as " << presence->getTo().getResource()); - } - else { - conv->addJID(presence->getFrom()); - conv->sendParticipants(presence->getFrom()); - conv->sendCachedMessages(presence->getFrom()); - } - - if (forceJoin) { - onRawPresenceReceived(presence); - onRoomJoined(presence->getFrom(), room, presence->getTo().getResource(), password); - } - return; - } - - bool isInJoined = false; - BOOST_FOREACH(Swift::Presence::ref &p, m_joinedRooms) { - if (p->getTo() == presence->getTo()) { - isInJoined = true; - } - } - if (!isInJoined) { - m_joinedRooms.push_back(presence); - } - - if (!m_connected) { - LOG4CXX_INFO(logger, m_jid.toString() << ": Joining room " << room << " postponed, because use is not connected to legacy network yet."); - return; - } - - LOG4CXX_INFO(logger, m_jid.toString() << ": Going to join room " << room << " as " << presence->getTo().getResource()); - - conv = m_component->getFactory()->createConversation(m_conversationManager, room, true); - m_conversationManager->addConversation(conv); - conv->setNickname(presence->getTo().getResource()); - conv->addJID(presence->getFrom()); - - onRawPresenceReceived(presence); - onRoomJoined(presence->getFrom(), room, presence->getTo().getResource(), password); - - return; - } - - onRawPresenceReceived(presence); - } - - int currentResourcesCount = m_presenceOracle->getAllPresence(m_jid).size(); - - m_conversationManager->resetResources(); - - - if (presence->getType() == Swift::Presence::Unavailable) { - m_conversationManager->removeJID(presence->getFrom()); - - std::string presences; - std::vector ps = m_presenceOracle->getAllPresence(m_jid); - BOOST_FOREACH(Swift::Presence::ref p, ps) { - if (p != presence) { - presences += p->getFrom().toString() + " "; - } - }; - - if (!presences.empty()) { - LOG4CXX_INFO(logger, m_jid.toString() << ": User is still connected from following clients: " << presences); - } - else { - LOG4CXX_INFO(logger, m_jid.toString() << ": Last client disconnected"); - } - } - - - // User wants to disconnect this resource - if (!m_component->inServerMode()) { - if (presence->getType() == Swift::Presence::Unavailable) { - // Send unavailable presences for online contacts - m_rosterManager->sendUnavailablePresences(presence->getFrom()); - - // Send unavailable presence for transport contact itself - Swift::Presence::ref response = Swift::Presence::create(); - response->setTo(presence->getFrom()); - response->setFrom(m_component->getJID()); - response->setType(Swift::Presence::Unavailable); - m_component->getStanzaChannel()->sendPresence(response); - } - else { - sendCurrentPresence(); - } - } - - // This resource is new, so we have to send buddies presences - if (presence->getType() != Swift::Presence::Unavailable && currentResourcesCount != m_resources) { - m_rosterManager->sendCurrentPresences(presence->getFrom()); - } - - m_resources = currentResourcesCount; - - // Change legacy network presence - if (m_readyForConnect) { - Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare()); - if (highest) { - if (highest->getType() == Swift::Presence::Unavailable && getUserSetting("stay_connected") == "1") { - m_resources = 0; - m_conversationManager->clearJIDs(); - setCacheMessages(true); - return; - } - Swift::Presence::ref response = Swift::Presence::create(highest); - response->setTo(m_jid); - response->setFrom(m_component->getJID()); - LOG4CXX_INFO(logger, m_jid.toString() << ": Changing legacy network presence to " << response->getType()); - onPresenceChanged(highest); - setCacheMessages(false); - } - else { - if (getUserSetting("stay_connected") == "1") { - m_resources = 0; - m_conversationManager->clearJIDs(); - setCacheMessages(true); - return; - } - Swift::Presence::ref response = Swift::Presence::create(); - response->setTo(m_jid.toBare()); - response->setFrom(m_component->getJID()); - response->setType(Swift::Presence::Unavailable); - onPresenceChanged(response); - } - } -} - -void User::handleSubscription(Swift::Presence::ref presence) { - m_rosterManager->handleSubscription(presence); -} - -void User::handleDiscoInfo(const Swift::JID& jid, boost::shared_ptr info) { - LOG4CXX_INFO(logger, jid.toString() << ": got disco#info"); -#ifdef SUPPORT_LEGACY_CAPS - Swift::DiscoInfo::ref discoInfo = m_entityCapsManager->getCaps(jid); - // This is old legacy cap which is not stored in entityCapsManager, - // we have to store it in our user class. - if (!discoInfo) { - LOG4CXX_INFO(logger, jid.toString() << ": LEGACY"); - m_legacyCaps[jid] = info; - } -#endif - - onConnectingTimeout(); -} - -void User::onConnectingTimeout() { - if (m_connected || m_readyForConnect) - return; - m_reconnectTimer->stop(); - m_readyForConnect = true; - onReadyToConnect(); - - Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare()); - if (highest) { - LOG4CXX_INFO(logger, m_jid.toString() << ": Changing legacy network presence to " << highest->getType()); - onPresenceChanged(highest); - } -} - -void User::setIgnoreDisconnect(bool ignoreDisconnect) { - m_ignoreDisconnect = ignoreDisconnect; - LOG4CXX_INFO(logger, m_jid.toString() << ": Setting ignoreDisconnect=" << m_ignoreDisconnect); -} - -void User::handleDisconnected(const std::string &error, Swift::SpectrumErrorPayload::Error e) { - if (m_ignoreDisconnect) { - LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnecting from legacy network ignored (probably moving between backends)"); - return; - } - - if (e == Swift::SpectrumErrorPayload::CONNECTION_ERROR_OTHER_ERROR || e == Swift::SpectrumErrorPayload::CONNECTION_ERROR_NETWORK_ERROR) { - if (m_reconnectCounter < 3) { - m_reconnectCounter++; - LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnecting from legacy network " << error << ", trying to reconnect automatically."); - // Simulate destruction/resurrection :) - // TODO: If this stops working, create onReconnect signal - m_userManager->onUserDestroyed(this); - m_userManager->onUserCreated(this); - onReadyToConnect(); - return; - } - } - - if (error.empty()) { - LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnected from legacy network"); - } - else { - LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnected from legacy network with error " << error); - } - onDisconnected(); - - boost::shared_ptr msg(new Swift::Message()); - msg->setBody(error); - msg->setTo(m_jid.toBare()); - msg->setFrom(m_component->getJID()); - msg->addPayload(boost::make_shared(e)); - m_component->getStanzaChannel()->sendMessage(msg); - - // In server mode, server finishes the session and pass unavailable session to userManager if we're connected to legacy network, - // so we can't removeUser() in server mode, because it would be removed twice. - // Once in finishSession and once in m_userManager->removeUser. - if (m_component->inServerMode()) { - // Remove user later just to be sure there won't be double-free. - // We can't be sure finishSession sends unavailable presence everytime, so check if user gets removed - // in finishSession(...) call and if not, remove it here. - std::string jid = m_jid.toBare().toString(); - dynamic_cast(m_component->getStanzaChannel())->finishSession(m_jid, boost::shared_ptr(new Swift::StreamError(Swift::StreamError::UndefinedCondition, error))); - if (m_userManager->getUser(jid) != NULL) { - m_userManager->removeUser(this); - } - } - else { - m_userManager->removeUser(this); - } -} - -} +/** + * 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 "transport/storagebackend.h" +#include "transport/rostermanager.h" +#include "transport/usermanager.h" +#include "transport/conversationmanager.h" +#include "transport/presenceoracle.h" +#include "transport/logging.h" +#include "Swiften/Server/ServerStanzaChannel.h" +#include "Swiften/Elements/StreamError.h" +#include "Swiften/Elements/MUCPayload.h" +#include "Swiften/Elements/SpectrumErrorPayload.h" +#include +#include +#include + +using namespace boost; + +#define foreach BOOST_FOREACH + +namespace Transport { + +DEFINE_LOGGER(logger, "User"); + +User::User(const Swift::JID &jid, UserInfo &userInfo, Component *component, UserManager *userManager) { + m_jid = jid.toBare(); + m_data = NULL; + + m_cacheMessages = false; + m_component = component; + m_presenceOracle = component->m_presenceOracle; + m_entityCapsManager = component->m_entityCapsManager; + m_userManager = userManager; + m_userInfo = userInfo; + m_connected = false; + m_readyForConnect = false; + m_ignoreDisconnect = false; + m_resources = 0; + m_reconnectCounter = 0; + + m_reconnectTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(5000); + m_reconnectTimer->onTick.connect(boost::bind(&User::onConnectingTimeout, this)); + + m_rosterManager = new RosterManager(this, m_component); + m_conversationManager = new ConversationManager(this, m_component); + LOG4CXX_INFO(logger, m_jid.toString() << ": Created"); + updateLastActivity(); +} + +User::~User(){ + LOG4CXX_INFO(logger, m_jid.toString() << ": Destroying"); + if (m_component->inServerMode()) { +#if HAVE_SWIFTEN_3 + dynamic_cast(m_component->getStanzaChannel())->finishSession(m_jid, boost::shared_ptr()); +#else + dynamic_cast(m_component->getStanzaChannel())->finishSession(m_jid, boost::shared_ptr()); +#endif + } + + m_reconnectTimer->stop(); + delete m_rosterManager; + delete m_conversationManager; +} + +const Swift::JID &User::getJID() { + return m_jid; +} + +std::vector User::getJIDWithFeature(const std::string &feature) { + std::vector jid; + std::vector presences = m_presenceOracle->getAllPresence(m_jid); + + foreach(Swift::Presence::ref presence, presences) { + if (presence->getType() == Swift::Presence::Unavailable) + continue; + + Swift::DiscoInfo::ref discoInfo = m_entityCapsManager->getCaps(presence->getFrom()); + if (!discoInfo) { +#ifdef SUPPORT_LEGACY_CAPS + if (m_legacyCaps.find(presence->getFrom()) != m_legacyCaps.end()) { + discoInfo = m_legacyCaps[presence->getFrom()]; + } + else { + continue; + } + + if (!discoInfo) { + continue; + } +#else + continue; +#endif + } + + if (discoInfo->hasFeature(feature)) { + LOG4CXX_INFO(logger, m_jid.toString() << ": Found JID with " << feature << " feature: " << presence->getFrom().toString()); + jid.push_back(presence->getFrom()); + } + } + + if (jid.empty()) { + LOG4CXX_INFO(logger, m_jid.toString() << ": No JID with " << feature << " feature " << m_legacyCaps.size()); + } + return jid; +} + +Swift::DiscoInfo::ref User::getCaps(const Swift::JID &jid) const { + Swift::DiscoInfo::ref discoInfo = m_entityCapsManager->getCaps(jid); +#ifdef SUPPORT_LEGACY_CAPS + if (!discoInfo) { + std::map::const_iterator it = m_legacyCaps.find(jid); + if (it != m_legacyCaps.end()) { + discoInfo = it->second; + } + } +#endif + return discoInfo; +} + +void User::sendCurrentPresence() { + if (m_component->inServerMode()) { + return; + } + + std::vector presences = m_presenceOracle->getAllPresence(m_jid); + foreach(Swift::Presence::ref presence, presences) { + if (presence->getType() == Swift::Presence::Unavailable) { + continue; + } + + if (m_connected) { + Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare()); + if (highest) { + Swift::Presence::ref response = Swift::Presence::create(highest); + response->setTo(presence->getFrom()); + response->setFrom(m_component->getJID()); + m_component->getStanzaChannel()->sendPresence(response); + } + else { + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo(presence->getFrom()); + response->setFrom(m_component->getJID()); + response->setType(Swift::Presence::Unavailable); + m_component->getStanzaChannel()->sendPresence(response); + } + } + else { + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo(presence->getFrom()); + response->setFrom(m_component->getJID()); + response->setType(Swift::Presence::Unavailable); + response->setStatus("Connecting"); + m_component->getStanzaChannel()->sendPresence(response); + } + } +} + +void User::setConnected(bool connected) { + m_connected = connected; + m_reconnectCounter = 0; + setIgnoreDisconnect(false); + updateLastActivity(); + + sendCurrentPresence(); + + if (m_connected) { + BOOST_FOREACH(Swift::Presence::ref &presence, m_joinedRooms) { + handlePresence(presence, true); + } + } +} + +void User::setCacheMessages(bool cacheMessages) { + if (m_component->inServerMode() && !m_cacheMessages && cacheMessages) { + m_conversationManager->sendCachedChatMessages(); + } + m_cacheMessages = cacheMessages; +} + +void User::handlePresence(Swift::Presence::ref presence, bool forceJoin) { + LOG4CXX_INFO(logger, "PRESENCE " << presence->getFrom().toString() << " " << presence->getTo().toString()); + if (!m_connected) { + // we are not connected to legacy network, so we should do it when disco#info arrive :) + if (m_readyForConnect == false) { + + // Forward status message to legacy network, but only if it's sent from active resource +// if (m_activeResource == presence->getFrom().getResource().getUTF8String()) { +// forwardStatus(presenceShow, stanzaStatus); +// } + boost::shared_ptr capsInfo = presence->getPayload(); + if (capsInfo && capsInfo->getHash() == "sha-1") { + if (m_entityCapsManager->getCaps(presence->getFrom()) != Swift::DiscoInfo::ref()) { + LOG4CXX_INFO(logger, m_jid.toString() << ": Ready to be connected to legacy network"); + m_readyForConnect = true; + onReadyToConnect(); + } + else { + m_reconnectTimer->start(); + } + } + else if (m_component->inServerMode()) { + LOG4CXX_INFO(logger, m_jid.toString() << ": Ready to be connected to legacy network"); + m_readyForConnect = true; + onReadyToConnect(); + } + else { + m_reconnectTimer->start(); + } + } + } + + + if (!presence->getTo().getNode().empty()) { + bool isMUC = presence->getPayload() != NULL || *presence->getTo().getNode().c_str() == '#'; + if (presence->getType() == Swift::Presence::Unavailable) { + std::string room = Buddy::JIDToLegacyName(presence->getTo()); + Conversation *conv = m_conversationManager->getConversation(room); + if (conv) { + conv->removeJID(presence->getFrom()); + if (!conv->getJIDs().empty()) { + return; + } + } + else { + return; + } + + if (getUserSetting("stay_connected") != "1") { + LOG4CXX_INFO(logger, m_jid.toString() << ": Going to left room " << room); + onRawPresenceReceived(presence); + onRoomLeft(room); + + BOOST_FOREACH(Swift::Presence::ref &p, m_joinedRooms) { + if (p->getTo() == presence->getTo()) { + m_joinedRooms.remove(p); + break; + } + } + + if (conv) { + m_conversationManager->removeConversation(conv); + delete conv; + } + } + + return; + } + else if (isMUC) { + // force connection to legacy network to let backend to handle auto-join on connect. + if (!m_readyForConnect) { + LOG4CXX_INFO(logger, m_jid.toString() << ": Ready to be connected to legacy network"); + m_readyForConnect = true; + onReadyToConnect(); + } + + std::string room = Buddy::JIDToLegacyName(presence->getTo()); + std::string password = ""; + if (presence->getPayload() != NULL) { + password = presence->getPayload()->getPassword() ? *presence->getPayload()->getPassword() : ""; + } + + Conversation *conv = m_conversationManager->getConversation(room); + if (conv != NULL) { + if (std::find(conv->getJIDs().begin(), conv->getJIDs().end(), presence->getFrom()) != conv->getJIDs().end()) { + LOG4CXX_INFO(logger, m_jid.toString() << ": User has already tried to join room " << room << " as " << presence->getTo().getResource()); + } + else { + conv->addJID(presence->getFrom()); + conv->sendParticipants(presence->getFrom()); + conv->sendCachedMessages(presence->getFrom()); + } + + if (forceJoin) { + onRawPresenceReceived(presence); + onRoomJoined(presence->getFrom(), room, presence->getTo().getResource(), password); + } + return; + } + + bool isInJoined = false; + BOOST_FOREACH(Swift::Presence::ref &p, m_joinedRooms) { + if (p->getTo() == presence->getTo()) { + isInJoined = true; + } + } + if (!isInJoined) { + m_joinedRooms.push_back(presence); + } + + if (!m_connected) { + LOG4CXX_INFO(logger, m_jid.toString() << ": Joining room " << room << " postponed, because use is not connected to legacy network yet."); + return; + } + + LOG4CXX_INFO(logger, m_jid.toString() << ": Going to join room " << room << " as " << presence->getTo().getResource()); + + conv = m_component->getFactory()->createConversation(m_conversationManager, room, true); + m_conversationManager->addConversation(conv); + conv->setNickname(presence->getTo().getResource()); + conv->addJID(presence->getFrom()); + + onRawPresenceReceived(presence); + onRoomJoined(presence->getFrom(), room, presence->getTo().getResource(), password); + + return; + } + + onRawPresenceReceived(presence); + } + + int currentResourcesCount = m_presenceOracle->getAllPresence(m_jid).size(); + + m_conversationManager->resetResources(); + + + if (presence->getType() == Swift::Presence::Unavailable) { + m_conversationManager->removeJID(presence->getFrom()); + + std::string presences; + std::vector ps = m_presenceOracle->getAllPresence(m_jid); + BOOST_FOREACH(Swift::Presence::ref p, ps) { + if (p != presence) { + presences += p->getFrom().toString() + " "; + } + }; + + if (!presences.empty()) { + LOG4CXX_INFO(logger, m_jid.toString() << ": User is still connected from following clients: " << presences); + } + else { + LOG4CXX_INFO(logger, m_jid.toString() << ": Last client disconnected"); + } + } + + + // User wants to disconnect this resource + if (!m_component->inServerMode()) { + if (presence->getType() == Swift::Presence::Unavailable) { + // Send unavailable presences for online contacts + m_rosterManager->sendUnavailablePresences(presence->getFrom()); + + // Send unavailable presence for transport contact itself + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo(presence->getFrom()); + response->setFrom(m_component->getJID()); + response->setType(Swift::Presence::Unavailable); + m_component->getStanzaChannel()->sendPresence(response); + } + else { + sendCurrentPresence(); + } + } + + // This resource is new, so we have to send buddies presences + if (presence->getType() != Swift::Presence::Unavailable && currentResourcesCount != m_resources) { + m_rosterManager->sendCurrentPresences(presence->getFrom()); + } + + m_resources = currentResourcesCount; + + // Change legacy network presence + if (m_readyForConnect) { + Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare()); + if (highest) { + if (highest->getType() == Swift::Presence::Unavailable && getUserSetting("stay_connected") == "1") { + m_resources = 0; + m_conversationManager->clearJIDs(); + setCacheMessages(true); + return; + } + Swift::Presence::ref response = Swift::Presence::create(highest); + response->setTo(m_jid); + response->setFrom(m_component->getJID()); + LOG4CXX_INFO(logger, m_jid.toString() << ": Changing legacy network presence to " << response->getType()); + onPresenceChanged(highest); + setCacheMessages(false); + } + else { + if (getUserSetting("stay_connected") == "1") { + m_resources = 0; + m_conversationManager->clearJIDs(); + setCacheMessages(true); + return; + } + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo(m_jid.toBare()); + response->setFrom(m_component->getJID()); + response->setType(Swift::Presence::Unavailable); + onPresenceChanged(response); + } + } +} + +void User::handleSubscription(Swift::Presence::ref presence) { + m_rosterManager->handleSubscription(presence); +} + +void User::handleDiscoInfo(const Swift::JID& jid, boost::shared_ptr info) { + LOG4CXX_INFO(logger, jid.toString() << ": got disco#info"); +#ifdef SUPPORT_LEGACY_CAPS + Swift::DiscoInfo::ref discoInfo = m_entityCapsManager->getCaps(jid); + // This is old legacy cap which is not stored in entityCapsManager, + // we have to store it in our user class. + if (!discoInfo) { + LOG4CXX_INFO(logger, jid.toString() << ": LEGACY"); + m_legacyCaps[jid] = info; + } +#endif + + onConnectingTimeout(); +} + +void User::onConnectingTimeout() { + if (m_connected || m_readyForConnect) + return; + m_reconnectTimer->stop(); + m_readyForConnect = true; + onReadyToConnect(); + + Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare()); + if (highest) { + LOG4CXX_INFO(logger, m_jid.toString() << ": Changing legacy network presence to " << highest->getType()); + onPresenceChanged(highest); + } +} + +void User::setIgnoreDisconnect(bool ignoreDisconnect) { + m_ignoreDisconnect = ignoreDisconnect; + LOG4CXX_INFO(logger, m_jid.toString() << ": Setting ignoreDisconnect=" << m_ignoreDisconnect); +} + +void User::handleDisconnected(const std::string &error, Swift::SpectrumErrorPayload::Error e) { + if (m_ignoreDisconnect) { + LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnecting from legacy network ignored (probably moving between backends)"); + return; + } + + if (e == Swift::SpectrumErrorPayload::CONNECTION_ERROR_OTHER_ERROR || e == Swift::SpectrumErrorPayload::CONNECTION_ERROR_NETWORK_ERROR) { + if (m_reconnectCounter < 3) { + m_reconnectCounter++; + LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnecting from legacy network " << error << ", trying to reconnect automatically."); + // Simulate destruction/resurrection :) + // TODO: If this stops working, create onReconnect signal + m_userManager->onUserDestroyed(this); + m_userManager->onUserCreated(this); + onReadyToConnect(); + return; + } + } + + if (error.empty()) { + LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnected from legacy network"); + } + else { + LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnected from legacy network with error " << error); + } + onDisconnected(); + + boost::shared_ptr msg(new Swift::Message()); + msg->setBody(error); + msg->setTo(m_jid.toBare()); + msg->setFrom(m_component->getJID()); + msg->addPayload(boost::make_shared(e)); + m_component->getStanzaChannel()->sendMessage(msg); + + // In server mode, server finishes the session and pass unavailable session to userManager if we're connected to legacy network, + // so we can't removeUser() in server mode, because it would be removed twice. + // Once in finishSession and once in m_userManager->removeUser. + if (m_component->inServerMode()) { + // Remove user later just to be sure there won't be double-free. + // We can't be sure finishSession sends unavailable presence everytime, so check if user gets removed + // in finishSession(...) call and if not, remove it here. + std::string jid = m_jid.toBare().toString(); +#if HAVE_SWIFTEN_3 + dynamic_cast(m_component->getStanzaChannel())->finishSession(m_jid, boost::shared_ptr(new Swift::StreamError(Swift::StreamError::UndefinedCondition, error))); +#else + dynamic_cast(m_component->getStanzaChannel())->finishSession(m_jid, boost::shared_ptr(new Swift::StreamError(Swift::StreamError::UndefinedCondition, error))); +#endif + if (m_userManager->getUser(jid) != NULL) { + m_userManager->removeUser(this); + } + } + else { + m_userManager->removeUser(this); + } +} + +} diff --git a/src/usermanager.cpp b/src/usermanager.cpp index 2d9a5fbf..67d29ca4 100644 --- a/src/usermanager.cpp +++ b/src/usermanager.cpp @@ -1,602 +1,606 @@ -/** - * 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" -#include "transport/storagebackend.h" -#include "transport/conversationmanager.h" -#include "transport/rostermanager.h" -#include "transport/userregistry.h" -#include "transport/logging.h" -#include "transport/discoitemsresponder.h" -#include "storageresponder.h" - -#include "Swiften/Server/ServerStanzaChannel.h" -#include "Swiften/Elements/StreamError.h" -#include "Swiften/Elements/MUCPayload.h" -#include "Swiften/Elements/ChatState.h" -#ifndef __FreeBSD__ -#ifndef __MACH__ -#include "malloc.h" -#endif -#endif -// #include "valgrind/memcheck.h" - -namespace Transport { - -DEFINE_LOGGER(logger, "UserManager"); - -UserManager::UserManager(Component *component, UserRegistry *userRegistry, DiscoItemsResponder *discoItemsResponder, StorageBackend *storageBackend) { - m_cachedUser = NULL; - m_onlineBuddies = 0; - m_sentToXMPP = 0; - m_sentToBackend = 0; - m_component = component; - m_storageBackend = storageBackend; - m_storageResponder = NULL; - m_userRegistry = userRegistry; - m_discoItemsResponder = discoItemsResponder; - - if (m_storageBackend) { - m_storageResponder = new StorageResponder(component->getIQRouter(), m_storageBackend, this); - m_storageResponder->start(); - } - - component->onUserPresenceReceived.connect(bind(&UserManager::handlePresence, this, _1)); - component->onUserDiscoInfoReceived.connect(bind(&UserManager::handleDiscoInfo, this, _1, _2)); - m_component->getStanzaChannel()->onMessageReceived.connect(bind(&UserManager::handleMessageReceived, this, _1)); - m_component->getStanzaChannel()->onPresenceReceived.connect(bind(&UserManager::handleGeneralPresenceReceived, this, _1)); - - m_userRegistry->onConnectUser.connect(bind(&UserManager::connectUser, this, _1)); - m_userRegistry->onDisconnectUser.connect(bind(&UserManager::disconnectUser, this, _1)); - - m_removeTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(1); -} - -UserManager::~UserManager(){ - if (m_storageResponder) { - m_storageResponder->stop(); - delete m_storageResponder; - } -} - -void UserManager::addUser(User *user) { - m_users[user->getJID().toBare().toString()] = user; - if (m_storageBackend) { - m_storageBackend->setUserOnline(user->getUserInfo().id, true); - } - onUserCreated(user); -} - -User *UserManager::getUser(const std::string &barejid){ - if (m_cachedUser && barejid == m_cachedUser->getJID().toBare().toString()) { - return m_cachedUser; - } - - if (m_users.find(barejid) != m_users.end()) { - User *user = m_users[barejid]; - m_cachedUser = user; - return user; - } - return NULL; -} - -Swift::DiscoInfo::ref UserManager::getCaps(const Swift::JID &jid) const { - std::map::const_iterator it = m_users.find(jid.toBare().toString()); - if (it == m_users.end()) { - return Swift::DiscoInfo::ref(); - } - - User *user = it->second; - return user->getCaps(jid); -} - -void UserManager::removeUser(User *user, bool onUserBehalf) { - m_users.erase(user->getJID().toBare().toString()); - if (m_cachedUser == user) - m_cachedUser = NULL; - - if (m_component->inServerMode()) { - disconnectUser(user->getJID()); - } - else { - // User could be disconnected by User::handleDisconnect() method, but - // Transport::PresenceOracle could still contain his last presence. - // We have to clear all received presences for this user in PresenceOracle. - m_component->getPresenceOracle()->clearPresences(user->getJID().toBare()); - } - - if (m_storageBackend && onUserBehalf) { - m_storageBackend->setUserOnline(user->getUserInfo().id, false); - } - - onUserDestroyed(user); - delete user; -#ifndef WIN32 -#ifndef __FreeBSD__ -#ifndef __MACH__ - malloc_trim(0); -#endif -#endif -#endif -// VALGRIND_DO_LEAK_CHECK; -} - -void UserManager::removeAllUsers(bool onUserBehalf) { - while(m_users.begin() != m_users.end()) { - removeUser((*m_users.begin()).second, onUserBehalf); - } -} - -int UserManager::getUserCount() { - return m_users.size(); -} - -void UserManager::handleDiscoInfo(const Swift::JID& jid, boost::shared_ptr info) { - User *user = getUser(jid.toBare().toString()); - if (!user) { - return; - } - - user->handleDiscoInfo(jid, info); -} - -void UserManager::handlePresence(Swift::Presence::ref presence) { - std::string barejid = presence->getTo().toBare().toString(); - std::string userkey = presence->getFrom().toBare().toString(); - - User *user = getUser(userkey); - // Create user class if it's not there - if (!user) { - // Admin user is not legacy network user, so do not create User class instance for him - if (m_component->inServerMode()) { - std::vector const &x = CONFIG_VECTOR(m_component->getConfig(),"service.admin_jid"); - if (std::find(x.begin(), x.end(), presence->getFrom().toBare().toString()) != x.end()) { - // Send admin contact to the user. - Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload()); - Swift::RosterItemPayload item; - item.setJID(m_component->getJID()); - item.setName("Admin"); - item.setSubscription(Swift::RosterItemPayload::Both); - payload->addItem(item); - - Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, presence->getFrom(), m_component->getIQRouter()); - request->send(); - - Swift::Presence::ref response = Swift::Presence::create(); - response->setTo(presence->getFrom()); - response->setFrom(m_component->getJID()); - m_component->getStanzaChannel()->sendPresence(response); - return; - } - } - - UserInfo res; - bool registered = m_storageBackend ? m_storageBackend->getUser(userkey, res) : false; - - // No user and unavailable presence -> answer with unavailable - if (presence->getType() == Swift::Presence::Unavailable || presence->getType() == Swift::Presence::Probe) { - Swift::Presence::ref response = Swift::Presence::create(); - response->setTo(presence->getFrom()); - response->setFrom(presence->getTo()); - response->setType(Swift::Presence::Unavailable); - m_component->getStanzaChannel()->sendPresence(response); - - // bother him with probe presence, just to be - // sure he is subscribed to us. - if (/*registered && */presence->getType() == Swift::Presence::Probe) { - Swift::Presence::ref response = Swift::Presence::create(); - response->setTo(presence->getFrom()); - response->setFrom(presence->getTo()); - response->setType(Swift::Presence::Probe); - m_component->getStanzaChannel()->sendPresence(response); - } - - // Set user offline in database - if (m_storageBackend) { - UserInfo res; - bool registered = m_storageBackend->getUser(userkey, res); - if (registered) { - m_storageBackend->setUserOnline(res.id, false); - } - } - return; - } - - // In server mode, we don't need registration normally, but for networks like IRC - // or Twitter where there's no real authorization using password, we have to force - // registration otherwise some data (like bookmarked rooms) could leak. - if (m_component->inServerMode()) { - if (!registered) { - // If we need registration, stop login process because user is not registered - if (CONFIG_BOOL_DEFAULTED(m_component->getConfig(), "registration.needRegistration", false)) { - m_userRegistry->onPasswordInvalid(presence->getFrom()); - return; - } - res.password = ""; - res.uin = presence->getFrom().getNode(); - res.jid = userkey; - while (res.uin.find_last_of("%") != std::string::npos) { // OK - res.uin.replace(res.uin.find_last_of("%"), 1, "@"); // OK - } - if (m_storageBackend) { - // store user and getUser again to get user ID. - m_storageBackend->setUser(res); - registered = m_storageBackend->getUser(userkey, res); - } - else { - registered = true; - } - } - res.password = m_userRegistry->getUserPassword(userkey); - } - - // We allow auto_register feature in gateway-mode. This allows IRC user to register - // the transport just by joining the room. - if (!m_component->inServerMode()) { - if (!registered && (CONFIG_BOOL(m_component->getConfig(), "registration.auto_register") - /*!CONFIG_BOOL_DEFAULTED(m_component->getConfig(), "registration.needRegistration", true)*/)) { - res.password = ""; - res.jid = userkey; - - bool isMUC = presence->getPayload() != NULL || *presence->getTo().getNode().c_str() == '#'; - if (isMUC) { - res.uin = presence->getTo().getResource(); - } - else { - res.uin = presence->getFrom().toString(); - } - LOG4CXX_INFO(logger, "Auto-registering user " << userkey << " with uin=" << res.uin); - - if (m_storageBackend) { - // store user and getUser again to get user ID. - m_storageBackend->setUser(res); - registered = m_storageBackend->getUser(userkey, res); - } - else { - registered = true; - } - } - } - - // Unregistered users are not able to login - if (!registered) { - LOG4CXX_WARN(logger, "Unregistered user " << userkey << " tried to login"); - return; - } - - if (CONFIG_BOOL(m_component->getConfig(), "service.vip_only") && res.vip == false) { - if (!CONFIG_STRING(m_component->getConfig(), "service.vip_message").empty()) { - boost::shared_ptr msg(new Swift::Message()); - msg->setBody(CONFIG_STRING(m_component->getConfig(), "service.vip_message")); - msg->setTo(presence->getFrom()); - msg->setFrom(m_component->getJID()); - m_component->getStanzaChannel()->sendMessage(msg); - } - - LOG4CXX_WARN(logger, "Non VIP user " << userkey << " tried to login"); - if (m_component->inServerMode()) { - m_userRegistry->onPasswordInvalid(presence->getFrom()); - } - return; - } - - bool transport_enabled = true; - if (m_storageBackend) { - std::string value = "1"; - int type = (int) TYPE_BOOLEAN; - m_storageBackend->getUserSetting(res.id, "enable_transport", type, value); - transport_enabled = value == "1"; - } - // User can disabled the transport using adhoc commands - if (!transport_enabled) { - LOG4CXX_INFO(logger, "User " << userkey << " has disabled transport, not logging"); - return; - } - - // Create new user class and set storagebackend - user = new User(presence->getFrom(), res, m_component, this); - user->getRosterManager()->setStorageBackend(m_storageBackend); - addUser(user); - } - - // User can be handleDisconnected in addUser callback, so refresh the pointer - user = getUser(userkey); - if (!user) { - m_userRegistry->onPasswordInvalid(presence->getFrom()); - return; - } - - // Handle this presence - user->handlePresence(presence); - - // Unavailable MUC presence should not trigger whole account disconnection, so block it here. - bool isMUC = presence->getPayload() != NULL || *presence->getTo().getNode().c_str() == '#'; - if (isMUC) - return; - - // Unavailable presence could remove this user, because he could be unavailable - if (presence->getType() == Swift::Presence::Unavailable) { - if (user) { - if (user->getUserSetting("stay_connected") == "1") { - return; - } - - Swift::Presence::ref highest = m_component->getPresenceOracle()->getHighestPriorityPresence(presence->getFrom().toBare()); - // There's no presence for this user, so disconnect - if (!highest || (highest && highest->getType() == Swift::Presence::Unavailable)) { - m_removeTimer->onTick.connect(boost::bind(&UserManager::handleRemoveTimeout, this, user->getJID().toBare().toString(), user, false)); - m_removeTimer->start(); - } - } - } -} - -void UserManager::handleRemoveTimeout(const std::string jid, User *u, bool reconnect) { - m_removeTimer->onTick.disconnect(boost::bind(&UserManager::handleRemoveTimeout, this, jid, u, reconnect)); - - // Maybe this User instance has been deleted in mean time and we would remove new one, - // so better check for it and ignore deletion if "u" does not exist anymore. - User *user = getUser(jid); - if (user != u) { - return; - } - - // Remove user - if (user) { - removeUser(user); - } - - // Connect the user again when we're reconnecting. - if (reconnect) { - connectUser(jid); - } -} - -void UserManager::handleMessageReceived(Swift::Message::ref message) { - if (message->getType() == Swift::Message::Error) { - return; - } - - // Do not count chatstate notification... - boost::shared_ptr statePayload = message->getPayload(); - if (!statePayload) { - messageToBackendSent(); - } - - if (message->getBody().empty() && !statePayload && message->getSubject().empty()) { - return; - } - - User *user = getUser(message->getFrom().toBare().toString()); - if (!user){ - return; - } - - user->getConversationManager()->handleMessageReceived(message); -} - -void UserManager::handleGeneralPresenceReceived(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: - handleMUCPresence(presence); - break; - case Swift::Presence::Probe: - handleProbePresence(presence); - break; - case Swift::Presence::Error: - handleErrorPresence(presence); - break; - default: - break; - }; -} - -void UserManager::handleMUCPresence(Swift::Presence::ref presence) { - // Don't let RosterManager to handle presences for us - if (presence->getTo().getNode().empty()) { - return; - } - - if (presence->getType() == Swift::Presence::Available) { - handlePresence(presence); - } - else if (presence->getType() == Swift::Presence::Unavailable) { - std::string userkey = presence->getFrom().toBare().toString(); - User *user = getUser(userkey); - if (user) { - user->handlePresence(presence); - } - } -} - -void UserManager::handleProbePresence(Swift::Presence::ref presence) { - // Don't let RosterManager to handle presences for us - if (presence->getTo().getNode().empty()) { - return; - } - - User *user = getUser(presence->getFrom().toBare().toString()); - - if (user) { - user->getRosterManager()->sendCurrentPresence(presence->getTo(), presence->getFrom()); - } - else { - Swift::Presence::ref response = Swift::Presence::create(); - response->setFrom(presence->getTo()); - response->setTo(presence->getFrom()); - response->setType(Swift::Presence::Unavailable); - m_component->getStanzaChannel()->sendPresence(response); - } -} - -void UserManager::handleErrorPresence(Swift::Presence::ref presence) { - // Don't let RosterManager to handle presences for us - if (!presence->getTo().getNode().empty()) { - return; - } - - if (!presence->getPayload()) { - return; - } - - if (presence->getPayload()->getCondition() != Swift::ErrorPayload::SubscriptionRequired) { - return; - } - - std::string userkey = presence->getFrom().toBare().toString(); - UserInfo res; - bool registered = m_storageBackend ? m_storageBackend->getUser(userkey, res) : false; - if (registered) { - Swift::Presence::ref response = Swift::Presence::create(); - response->setFrom(presence->getTo().toBare()); - response->setTo(presence->getFrom().toBare()); - response->setType(Swift::Presence::Subscribe); - m_component->getStanzaChannel()->sendPresence(response); - } -} - -void UserManager::handleSubscription(Swift::Presence::ref presence) { - - // answer to subscibe for transport itself - if (presence->getType() == Swift::Presence::Subscribe && presence->getTo().getNode().empty()) { - Swift::Presence::ref response = Swift::Presence::create(); - response->setFrom(presence->getTo().toBare()); - response->setTo(presence->getFrom().toBare()); - response->setType(Swift::Presence::Subscribed); - m_component->getStanzaChannel()->sendPresence(response); - -// response = Swift::Presence::create(); -// response->setFrom(presence->getTo()); -// response->setTo(presence->getFrom()); -// response->setType(Swift::Presence::Subscribe); -// m_component->getStanzaChannel()->sendPresence(response); - return; - } - else if (presence->getType() == Swift::Presence::Unsubscribed && presence->getTo().getNode().empty()) { - std::string userkey = presence->getFrom().toBare().toString(); - UserInfo res; - bool registered = m_storageBackend ? m_storageBackend->getUser(userkey, res) : false; - if (registered) { - Swift::Presence::ref response = Swift::Presence::create(); - response->setFrom(presence->getTo().toBare()); - response->setTo(presence->getFrom().toBare()); - response->setType(Swift::Presence::Subscribe); - m_component->getStanzaChannel()->sendPresence(response); - } - return; - } - - // Don't let RosterManager to handle presences for us - if (presence->getTo().getNode().empty()) { - return; - } - - User *user = getUser(presence->getFrom().toBare().toString()); - - 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->getStanzaChannel()->sendPresence(response); - } -// else { -// // Log(presence->getFrom().toString().getUTF8String(), "Subscribe presence received, but this user is not logged in"); -// } -} - -void UserManager::connectUser(const Swift::JID &user) { - // Called by UserRegistry in server mode when user connects the server and wants - // to connect legacy network - if (m_users.find(user.toBare().toString()) != m_users.end()) { - if (!m_component->inServerMode()) { - return; - } - - User *u = m_users[user.toBare().toString()]; - if (u->isConnected()) { - // User is already logged in, so his password is OK, but this new user has different password => bad password. - // We can't call m_userRegistry->onPasswordInvalid() here, because this fuction is called from Swift::Parser - // and onPasswordInvalid destroys whole session together with parser itself, which leads to crash. - if (m_userRegistry->getUserPassword(user.toBare().toString()) != u->getUserInfo().password) { - m_userRegistry->removeLater(user); - return; - } - if (CONFIG_BOOL(m_component->getConfig(), "service.more_resources")) { - m_userRegistry->onPasswordValid(user); - } - else { - // Send message to currently logged in session - boost::shared_ptr msg(new Swift::Message()); - msg->setBody("You have signed on from another location."); - msg->setTo(user); - msg->setFrom(m_component->getJID()); - m_component->getStanzaChannel()->sendMessage(msg); - - // Switch the session = accept new one, disconnect old one. - // Unavailable presence from old session has to be ignored, otherwise it would disconnect the user from legacy network. - m_userRegistry->onPasswordValid(user); - m_component->onUserPresenceReceived.disconnect(bind(&UserManager::handlePresence, this, _1)); - dynamic_cast(m_component->getStanzaChannel())->finishSession(user, boost::shared_ptr(new Swift::StreamError()), true); - m_component->onUserPresenceReceived.connect(bind(&UserManager::handlePresence, this, _1)); - } - } - else { - // User is created, but not connected => he's loggin in or he just logged out, but hasn't been deleted yet. - // Stop deletion process if there's any - m_removeTimer->onTick.disconnect(boost::bind(&UserManager::handleRemoveTimeout, this, user.toBare().toString(), m_users[user.toBare().toString()], false)); - - // Delete old User instance but create new one immediatelly - m_removeTimer->onTick.connect(boost::bind(&UserManager::handleRemoveTimeout, this, user.toBare().toString(), m_users[user.toBare().toString()], true)); - m_removeTimer->start(); - } - } - else { - // simulate initial available presence to start connecting this user. - Swift::Presence::ref response = Swift::Presence::create(); - response->setTo(m_component->getJID()); - response->setFrom(user); - response->setType(Swift::Presence::Available); - dynamic_cast(m_component->getStanzaChannel())->onPresenceReceived(response); - } -} - - -void UserManager::disconnectUser(const Swift::JID &user) { - Swift::Presence::ref response = Swift::Presence::create(); - response->setTo(m_component->getJID()); - response->setFrom(user); - response->setType(Swift::Presence::Unavailable); - dynamic_cast(m_component->getStanzaChannel())->onPresenceReceived(response); -} - -} +/** + * 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" +#include "transport/storagebackend.h" +#include "transport/conversationmanager.h" +#include "transport/rostermanager.h" +#include "transport/userregistry.h" +#include "transport/logging.h" +#include "transport/discoitemsresponder.h" +#include "storageresponder.h" + +#include "Swiften/Server/ServerStanzaChannel.h" +#include "Swiften/Elements/StreamError.h" +#include "Swiften/Elements/MUCPayload.h" +#include "Swiften/Elements/ChatState.h" +#ifndef __FreeBSD__ +#ifndef __MACH__ +#include "malloc.h" +#endif +#endif +// #include "valgrind/memcheck.h" + +namespace Transport { + +DEFINE_LOGGER(logger, "UserManager"); + +UserManager::UserManager(Component *component, UserRegistry *userRegistry, DiscoItemsResponder *discoItemsResponder, StorageBackend *storageBackend) { + m_cachedUser = NULL; + m_onlineBuddies = 0; + m_sentToXMPP = 0; + m_sentToBackend = 0; + m_component = component; + m_storageBackend = storageBackend; + m_storageResponder = NULL; + m_userRegistry = userRegistry; + m_discoItemsResponder = discoItemsResponder; + + if (m_storageBackend) { + m_storageResponder = new StorageResponder(component->getIQRouter(), m_storageBackend, this); + m_storageResponder->start(); + } + + component->onUserPresenceReceived.connect(bind(&UserManager::handlePresence, this, _1)); + component->onUserDiscoInfoReceived.connect(bind(&UserManager::handleDiscoInfo, this, _1, _2)); + m_component->getStanzaChannel()->onMessageReceived.connect(bind(&UserManager::handleMessageReceived, this, _1)); + m_component->getStanzaChannel()->onPresenceReceived.connect(bind(&UserManager::handleGeneralPresenceReceived, this, _1)); + + m_userRegistry->onConnectUser.connect(bind(&UserManager::connectUser, this, _1)); + m_userRegistry->onDisconnectUser.connect(bind(&UserManager::disconnectUser, this, _1)); + + m_removeTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(1); +} + +UserManager::~UserManager(){ + if (m_storageResponder) { + m_storageResponder->stop(); + delete m_storageResponder; + } +} + +void UserManager::addUser(User *user) { + m_users[user->getJID().toBare().toString()] = user; + if (m_storageBackend) { + m_storageBackend->setUserOnline(user->getUserInfo().id, true); + } + onUserCreated(user); +} + +User *UserManager::getUser(const std::string &barejid){ + if (m_cachedUser && barejid == m_cachedUser->getJID().toBare().toString()) { + return m_cachedUser; + } + + if (m_users.find(barejid) != m_users.end()) { + User *user = m_users[barejid]; + m_cachedUser = user; + return user; + } + return NULL; +} + +Swift::DiscoInfo::ref UserManager::getCaps(const Swift::JID &jid) const { + std::map::const_iterator it = m_users.find(jid.toBare().toString()); + if (it == m_users.end()) { + return Swift::DiscoInfo::ref(); + } + + User *user = it->second; + return user->getCaps(jid); +} + +void UserManager::removeUser(User *user, bool onUserBehalf) { + m_users.erase(user->getJID().toBare().toString()); + if (m_cachedUser == user) + m_cachedUser = NULL; + + if (m_component->inServerMode()) { + disconnectUser(user->getJID()); + } + else { + // User could be disconnected by User::handleDisconnect() method, but + // Transport::PresenceOracle could still contain his last presence. + // We have to clear all received presences for this user in PresenceOracle. + m_component->getPresenceOracle()->clearPresences(user->getJID().toBare()); + } + + if (m_storageBackend && onUserBehalf) { + m_storageBackend->setUserOnline(user->getUserInfo().id, false); + } + + onUserDestroyed(user); + delete user; +#ifndef WIN32 +#ifndef __FreeBSD__ +#ifndef __MACH__ + malloc_trim(0); +#endif +#endif +#endif +// VALGRIND_DO_LEAK_CHECK; +} + +void UserManager::removeAllUsers(bool onUserBehalf) { + while(m_users.begin() != m_users.end()) { + removeUser((*m_users.begin()).second, onUserBehalf); + } +} + +int UserManager::getUserCount() { + return m_users.size(); +} + +void UserManager::handleDiscoInfo(const Swift::JID& jid, boost::shared_ptr info) { + User *user = getUser(jid.toBare().toString()); + if (!user) { + return; + } + + user->handleDiscoInfo(jid, info); +} + +void UserManager::handlePresence(Swift::Presence::ref presence) { + std::string barejid = presence->getTo().toBare().toString(); + std::string userkey = presence->getFrom().toBare().toString(); + + User *user = getUser(userkey); + // Create user class if it's not there + if (!user) { + // Admin user is not legacy network user, so do not create User class instance for him + if (m_component->inServerMode()) { + std::vector const &x = CONFIG_VECTOR(m_component->getConfig(),"service.admin_jid"); + if (std::find(x.begin(), x.end(), presence->getFrom().toBare().toString()) != x.end()) { + // Send admin contact to the user. + Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload()); + Swift::RosterItemPayload item; + item.setJID(m_component->getJID()); + item.setName("Admin"); + item.setSubscription(Swift::RosterItemPayload::Both); + payload->addItem(item); + + Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, presence->getFrom(), m_component->getIQRouter()); + request->send(); + + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo(presence->getFrom()); + response->setFrom(m_component->getJID()); + m_component->getStanzaChannel()->sendPresence(response); + return; + } + } + + UserInfo res; + bool registered = m_storageBackend ? m_storageBackend->getUser(userkey, res) : false; + + // No user and unavailable presence -> answer with unavailable + if (presence->getType() == Swift::Presence::Unavailable || presence->getType() == Swift::Presence::Probe) { + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo(presence->getFrom()); + response->setFrom(presence->getTo()); + response->setType(Swift::Presence::Unavailable); + m_component->getStanzaChannel()->sendPresence(response); + + // bother him with probe presence, just to be + // sure he is subscribed to us. + if (/*registered && */presence->getType() == Swift::Presence::Probe) { + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo(presence->getFrom()); + response->setFrom(presence->getTo()); + response->setType(Swift::Presence::Probe); + m_component->getStanzaChannel()->sendPresence(response); + } + + // Set user offline in database + if (m_storageBackend) { + UserInfo res; + bool registered = m_storageBackend->getUser(userkey, res); + if (registered) { + m_storageBackend->setUserOnline(res.id, false); + } + } + return; + } + + // In server mode, we don't need registration normally, but for networks like IRC + // or Twitter where there's no real authorization using password, we have to force + // registration otherwise some data (like bookmarked rooms) could leak. + if (m_component->inServerMode()) { + if (!registered) { + // If we need registration, stop login process because user is not registered + if (CONFIG_BOOL_DEFAULTED(m_component->getConfig(), "registration.needRegistration", false)) { + m_userRegistry->onPasswordInvalid(presence->getFrom()); + return; + } + res.password = ""; + res.uin = presence->getFrom().getNode(); + res.jid = userkey; + while (res.uin.find_last_of("%") != std::string::npos) { // OK + res.uin.replace(res.uin.find_last_of("%"), 1, "@"); // OK + } + if (m_storageBackend) { + // store user and getUser again to get user ID. + m_storageBackend->setUser(res); + registered = m_storageBackend->getUser(userkey, res); + } + else { + registered = true; + } + } + res.password = m_userRegistry->getUserPassword(userkey); + } + + // We allow auto_register feature in gateway-mode. This allows IRC user to register + // the transport just by joining the room. + if (!m_component->inServerMode()) { + if (!registered && (CONFIG_BOOL(m_component->getConfig(), "registration.auto_register") + /*!CONFIG_BOOL_DEFAULTED(m_component->getConfig(), "registration.needRegistration", true)*/)) { + res.password = ""; + res.jid = userkey; + + bool isMUC = presence->getPayload() != NULL || *presence->getTo().getNode().c_str() == '#'; + if (isMUC) { + res.uin = presence->getTo().getResource(); + } + else { + res.uin = presence->getFrom().toString(); + } + LOG4CXX_INFO(logger, "Auto-registering user " << userkey << " with uin=" << res.uin); + + if (m_storageBackend) { + // store user and getUser again to get user ID. + m_storageBackend->setUser(res); + registered = m_storageBackend->getUser(userkey, res); + } + else { + registered = true; + } + } + } + + // Unregistered users are not able to login + if (!registered) { + LOG4CXX_WARN(logger, "Unregistered user " << userkey << " tried to login"); + return; + } + + if (CONFIG_BOOL(m_component->getConfig(), "service.vip_only") && res.vip == false) { + if (!CONFIG_STRING(m_component->getConfig(), "service.vip_message").empty()) { + boost::shared_ptr msg(new Swift::Message()); + msg->setBody(CONFIG_STRING(m_component->getConfig(), "service.vip_message")); + msg->setTo(presence->getFrom()); + msg->setFrom(m_component->getJID()); + m_component->getStanzaChannel()->sendMessage(msg); + } + + LOG4CXX_WARN(logger, "Non VIP user " << userkey << " tried to login"); + if (m_component->inServerMode()) { + m_userRegistry->onPasswordInvalid(presence->getFrom()); + } + return; + } + + bool transport_enabled = true; + if (m_storageBackend) { + std::string value = "1"; + int type = (int) TYPE_BOOLEAN; + m_storageBackend->getUserSetting(res.id, "enable_transport", type, value); + transport_enabled = value == "1"; + } + // User can disabled the transport using adhoc commands + if (!transport_enabled) { + LOG4CXX_INFO(logger, "User " << userkey << " has disabled transport, not logging"); + return; + } + + // Create new user class and set storagebackend + user = new User(presence->getFrom(), res, m_component, this); + user->getRosterManager()->setStorageBackend(m_storageBackend); + addUser(user); + } + + // User can be handleDisconnected in addUser callback, so refresh the pointer + user = getUser(userkey); + if (!user) { + m_userRegistry->onPasswordInvalid(presence->getFrom()); + return; + } + + // Handle this presence + user->handlePresence(presence); + + // Unavailable MUC presence should not trigger whole account disconnection, so block it here. + bool isMUC = presence->getPayload() != NULL || *presence->getTo().getNode().c_str() == '#'; + if (isMUC) + return; + + // Unavailable presence could remove this user, because he could be unavailable + if (presence->getType() == Swift::Presence::Unavailable) { + if (user) { + if (user->getUserSetting("stay_connected") == "1") { + return; + } + + Swift::Presence::ref highest = m_component->getPresenceOracle()->getHighestPriorityPresence(presence->getFrom().toBare()); + // There's no presence for this user, so disconnect + if (!highest || (highest && highest->getType() == Swift::Presence::Unavailable)) { + m_removeTimer->onTick.connect(boost::bind(&UserManager::handleRemoveTimeout, this, user->getJID().toBare().toString(), user, false)); + m_removeTimer->start(); + } + } + } +} + +void UserManager::handleRemoveTimeout(const std::string jid, User *u, bool reconnect) { + m_removeTimer->onTick.disconnect(boost::bind(&UserManager::handleRemoveTimeout, this, jid, u, reconnect)); + + // Maybe this User instance has been deleted in mean time and we would remove new one, + // so better check for it and ignore deletion if "u" does not exist anymore. + User *user = getUser(jid); + if (user != u) { + return; + } + + // Remove user + if (user) { + removeUser(user); + } + + // Connect the user again when we're reconnecting. + if (reconnect) { + connectUser(jid); + } +} + +void UserManager::handleMessageReceived(Swift::Message::ref message) { + if (message->getType() == Swift::Message::Error) { + return; + } + + // Do not count chatstate notification... + boost::shared_ptr statePayload = message->getPayload(); + if (!statePayload) { + messageToBackendSent(); + } + + if (message->getBody().empty() && !statePayload && message->getSubject().empty()) { + return; + } + + User *user = getUser(message->getFrom().toBare().toString()); + if (!user){ + return; + } + + user->getConversationManager()->handleMessageReceived(message); +} + +void UserManager::handleGeneralPresenceReceived(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: + handleMUCPresence(presence); + break; + case Swift::Presence::Probe: + handleProbePresence(presence); + break; + case Swift::Presence::Error: + handleErrorPresence(presence); + break; + default: + break; + }; +} + +void UserManager::handleMUCPresence(Swift::Presence::ref presence) { + // Don't let RosterManager to handle presences for us + if (presence->getTo().getNode().empty()) { + return; + } + + if (presence->getType() == Swift::Presence::Available) { + handlePresence(presence); + } + else if (presence->getType() == Swift::Presence::Unavailable) { + std::string userkey = presence->getFrom().toBare().toString(); + User *user = getUser(userkey); + if (user) { + user->handlePresence(presence); + } + } +} + +void UserManager::handleProbePresence(Swift::Presence::ref presence) { + // Don't let RosterManager to handle presences for us + if (presence->getTo().getNode().empty()) { + return; + } + + User *user = getUser(presence->getFrom().toBare().toString()); + + if (user) { + user->getRosterManager()->sendCurrentPresence(presence->getTo(), presence->getFrom()); + } + else { + Swift::Presence::ref response = Swift::Presence::create(); + response->setFrom(presence->getTo()); + response->setTo(presence->getFrom()); + response->setType(Swift::Presence::Unavailable); + m_component->getStanzaChannel()->sendPresence(response); + } +} + +void UserManager::handleErrorPresence(Swift::Presence::ref presence) { + // Don't let RosterManager to handle presences for us + if (!presence->getTo().getNode().empty()) { + return; + } + + if (!presence->getPayload()) { + return; + } + + if (presence->getPayload()->getCondition() != Swift::ErrorPayload::SubscriptionRequired) { + return; + } + + std::string userkey = presence->getFrom().toBare().toString(); + UserInfo res; + bool registered = m_storageBackend ? m_storageBackend->getUser(userkey, res) : false; + if (registered) { + Swift::Presence::ref response = Swift::Presence::create(); + response->setFrom(presence->getTo().toBare()); + response->setTo(presence->getFrom().toBare()); + response->setType(Swift::Presence::Subscribe); + m_component->getStanzaChannel()->sendPresence(response); + } +} + +void UserManager::handleSubscription(Swift::Presence::ref presence) { + + // answer to subscibe for transport itself + if (presence->getType() == Swift::Presence::Subscribe && presence->getTo().getNode().empty()) { + Swift::Presence::ref response = Swift::Presence::create(); + response->setFrom(presence->getTo().toBare()); + response->setTo(presence->getFrom().toBare()); + response->setType(Swift::Presence::Subscribed); + m_component->getStanzaChannel()->sendPresence(response); + +// response = Swift::Presence::create(); +// response->setFrom(presence->getTo()); +// response->setTo(presence->getFrom()); +// response->setType(Swift::Presence::Subscribe); +// m_component->getStanzaChannel()->sendPresence(response); + return; + } + else if (presence->getType() == Swift::Presence::Unsubscribed && presence->getTo().getNode().empty()) { + std::string userkey = presence->getFrom().toBare().toString(); + UserInfo res; + bool registered = m_storageBackend ? m_storageBackend->getUser(userkey, res) : false; + if (registered) { + Swift::Presence::ref response = Swift::Presence::create(); + response->setFrom(presence->getTo().toBare()); + response->setTo(presence->getFrom().toBare()); + response->setType(Swift::Presence::Subscribe); + m_component->getStanzaChannel()->sendPresence(response); + } + return; + } + + // Don't let RosterManager to handle presences for us + if (presence->getTo().getNode().empty()) { + return; + } + + User *user = getUser(presence->getFrom().toBare().toString()); + + 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->getStanzaChannel()->sendPresence(response); + } +// else { +// // Log(presence->getFrom().toString().getUTF8String(), "Subscribe presence received, but this user is not logged in"); +// } +} + +void UserManager::connectUser(const Swift::JID &user) { + // Called by UserRegistry in server mode when user connects the server and wants + // to connect legacy network + if (m_users.find(user.toBare().toString()) != m_users.end()) { + if (!m_component->inServerMode()) { + return; + } + + User *u = m_users[user.toBare().toString()]; + if (u->isConnected()) { + // User is already logged in, so his password is OK, but this new user has different password => bad password. + // We can't call m_userRegistry->onPasswordInvalid() here, because this fuction is called from Swift::Parser + // and onPasswordInvalid destroys whole session together with parser itself, which leads to crash. + if (m_userRegistry->getUserPassword(user.toBare().toString()) != u->getUserInfo().password) { + m_userRegistry->removeLater(user); + return; + } + if (CONFIG_BOOL(m_component->getConfig(), "service.more_resources")) { + m_userRegistry->onPasswordValid(user); + } + else { + // Send message to currently logged in session + boost::shared_ptr msg(new Swift::Message()); + msg->setBody("You have signed on from another location."); + msg->setTo(user); + msg->setFrom(m_component->getJID()); + m_component->getStanzaChannel()->sendMessage(msg); + + // Switch the session = accept new one, disconnect old one. + // Unavailable presence from old session has to be ignored, otherwise it would disconnect the user from legacy network. + m_userRegistry->onPasswordValid(user); + m_component->onUserPresenceReceived.disconnect(bind(&UserManager::handlePresence, this, _1)); +#if HAVE_SWIFTEN_3 + dynamic_cast(m_component->getStanzaChannel())->finishSession(user, boost::shared_ptr(new Swift::StreamError()), true); +#else + dynamic_cast(m_component->getStanzaChannel())->finishSession(user, boost::shared_ptr(new Swift::StreamError()), true); +#endif + m_component->onUserPresenceReceived.connect(bind(&UserManager::handlePresence, this, _1)); + } + } + else { + // User is created, but not connected => he's loggin in or he just logged out, but hasn't been deleted yet. + // Stop deletion process if there's any + m_removeTimer->onTick.disconnect(boost::bind(&UserManager::handleRemoveTimeout, this, user.toBare().toString(), m_users[user.toBare().toString()], false)); + + // Delete old User instance but create new one immediatelly + m_removeTimer->onTick.connect(boost::bind(&UserManager::handleRemoveTimeout, this, user.toBare().toString(), m_users[user.toBare().toString()], true)); + m_removeTimer->start(); + } + } + else { + // simulate initial available presence to start connecting this user. + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo(m_component->getJID()); + response->setFrom(user); + response->setType(Swift::Presence::Available); + dynamic_cast(m_component->getStanzaChannel())->onPresenceReceived(response); + } +} + + +void UserManager::disconnectUser(const Swift::JID &user) { + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo(m_component->getJID()); + response->setFrom(user); + response->setType(Swift::Presence::Unavailable); + dynamic_cast(m_component->getStanzaChannel())->onPresenceReceived(response); +} + +} diff --git a/src/userregistration.cpp b/src/userregistration.cpp index 75f2d942..5730659b 100644 --- a/src/userregistration.cpp +++ b/src/userregistration.cpp @@ -1,534 +1,627 @@ -/** - * 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/rostermanager.h" -#include "transport/user.h" -#include "transport/logging.h" -#include "Swiften/Elements/ErrorPayload.h" -#include "Swiften/EventLoop/SimpleEventLoop.h" -#include "Swiften/Network/BoostNetworkFactories.h" -#include "Swiften/Client/Client.h" -#include -#include -#include -#include - -using namespace Swift; - -namespace Transport { - -DEFINE_LOGGER(logger, "UserRegistration"); - -UserRegistration::UserRegistration(Component *component, UserManager *userManager, StorageBackend *storageBackend) : Swift::Responder(component->m_iqRouter) { - m_component = component; - m_config = m_component->m_config; - m_storageBackend = storageBackend; - m_userManager = userManager; -} - -UserRegistration::~UserRegistration(){ -} - -bool UserRegistration::registerUser(const UserInfo &row) { - UserInfo user; - bool registered = m_storageBackend->getUser(row.jid, user); - // This user is already registered - if (registered) - return false; - - m_storageBackend->setUser(row); - - //same as in unregisterUser but here we have to pass UserInfo to handleRegisterRRResponse - AddressedRosterRequest::ref request = AddressedRosterRequest::ref(new AddressedRosterRequest(m_component->getIQRouter(),row.jid)); - request->onResponse.connect(boost::bind(&UserRegistration::handleRegisterRemoteRosterResponse, this, _1, _2, row)); - request->send(); - - 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); - - // We have to check if server supports remoteroster XEP and use it if it's supported or fallback to unsubscribe otherwise - AddressedRosterRequest::ref request = AddressedRosterRequest::ref(new AddressedRosterRequest(m_component->getIQRouter(), barejid)); - request->onResponse.connect(boost::bind(&UserRegistration::handleUnregisterRemoteRosterResponse, this, _1, _2, barejid)); - request->send(); - - return true; -} - -void UserRegistration::handleRegisterRemoteRosterResponse(boost::shared_ptr payload, Swift::ErrorPayload::ref remoteRosterNotSupported /*error*/, const UserInfo &row){ - if (remoteRosterNotSupported || !payload) { - 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->getStanzaChannel()->sendPresence(response); - } - else{ - Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload()); - Swift::RosterItemPayload item; - item.setJID(m_component->getJID()); - item.setSubscription(Swift::RosterItemPayload::Both); - payload->addItem(item); - Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, row.jid, m_component->getIQRouter()); - request->send(); - } - onUserRegistered(row); - - std::vector const &x = CONFIG_VECTOR(m_component->getConfig(),"registration.notify_jid"); - BOOST_FOREACH(const std::string ¬ify_jid, x) { - boost::shared_ptr msg(new Swift::Message()); - msg->setBody(std::string("registered: ") + row.jid); - msg->setTo(notify_jid); - msg->setFrom(m_component->getJID()); - m_component->getStanzaChannel()->sendMessage(msg); - } -} - -void UserRegistration::handleUnregisterRemoteRosterResponse(boost::shared_ptr payload, Swift::ErrorPayload::ref remoteRosterNotSupported /*error*/, const std::string &barejid) { - UserInfo userInfo; - bool registered = m_storageBackend->getUser(barejid, userInfo); - // This user is not registered - if (!registered) - return; - - if (remoteRosterNotSupported || !payload) { - std::list roster; - m_storageBackend->getBuddies(userInfo.id, roster); - for(std::list::iterator u = roster.begin(); u != roster.end() ; u++){ - std::string name = (*u).legacyName; - if ((*u).flags & BUDDY_JID_ESCAPING) { - name = Swift::JID::getEscapedNode((*u).legacyName); - } - else { - if (name.find_last_of("@") != std::string::npos) { - name.replace(name.find_last_of("@"), 1, "%"); - } - } - - Swift::Presence::ref response; - 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->getStanzaChannel()->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->getStanzaChannel()->sendPresence(response); - } - } - else { - BOOST_FOREACH(Swift::RosterItemPayload it, payload->getItems()) { - Swift::RosterPayload::ref p = Swift::RosterPayload::ref(new Swift::RosterPayload()); - Swift::RosterItemPayload item; - item.setJID(it.getJID()); - item.setSubscription(Swift::RosterItemPayload::Remove); - - p->addItem(item); - - Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(p, barejid, m_component->getIQRouter()); - request->send(); - } - } - - // Remove user from database - m_storageBackend->removeUser(userInfo.id); - - // Disconnect the user - User *user = m_userManager->getUser(barejid); - if (user) { - m_userManager->removeUser(user); - } - - if (remoteRosterNotSupported || !payload) { - Swift::Presence::ref response; - response = Swift::Presence::create(); - response->setTo(Swift::JID(barejid)); - response->setFrom(m_component->getJID()); - response->setType(Swift::Presence::Unsubscribe); - m_component->getStanzaChannel()->sendPresence(response); - - response = Swift::Presence::create(); - response->setTo(Swift::JID(barejid)); - response->setFrom(m_component->getJID()); - response->setType(Swift::Presence::Unsubscribed); - m_component->getStanzaChannel()->sendPresence(response); - } - else { - Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload()); - Swift::RosterItemPayload item; - item.setJID(m_component->getJID()); - item.setSubscription(Swift::RosterItemPayload::Remove); - payload->addItem(item); - - Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, barejid, m_component->getIQRouter()); - request->send(); - } - - std::vector const &x = CONFIG_VECTOR(m_component->getConfig(),"registration.notify_jid"); - BOOST_FOREACH(const std::string ¬ify_jid, x) { - boost::shared_ptr msg(new Swift::Message()); - msg->setBody(std::string("unregistered: ") + barejid); - msg->setTo(notify_jid); - msg->setFrom(m_component->getJID()); - m_component->getStanzaChannel()->sendMessage(msg); - } -} - -bool UserRegistration::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload) { - // TODO: backend should say itself if registration is needed or not... - if (CONFIG_STRING(m_config, "service.protocol") == "irc") { - sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify); - return true; - } - - std::string barejid = from.toBare().toString(); - - if (!CONFIG_BOOL(m_config,"registration.enable_public_registration")) { - std::vector const &x = CONFIG_VECTOR(m_config,"service.allowed_servers"); - if (std::find(x.begin(), x.end(), from.getDomain()) == x.end()) { - LOG4CXX_INFO(logger, barejid << ": This user has no permissions to register an account") - sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify); - return true; - } - } - - boost::shared_ptr reg(new InBandRegistrationPayload()); - - UserInfo res; - bool registered = m_storageBackend->getUser(barejid, res); - - std::string instructions = CONFIG_STRING(m_config, "registration.instructions"); - std::string usernameField = CONFIG_STRING(m_config, "registration.username_label"); - - // normal jabber:iq:register - reg->setInstructions(instructions); - reg->setRegistered(registered); - reg->setUsername(res.uin); - if (CONFIG_BOOL_DEFAULTED(m_config, "registration.needPassword", true)) { - reg->setPassword(""); - } - - - // form - Form::ref form(new Form(Form::FormType)); - form->setTitle((("Registration"))); - form->setInstructions((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((usernameField)); - username->setValue(res.uin); - username->setRequired(true); - form->addField(username); - - if (CONFIG_BOOL_DEFAULTED(m_config, "registration.needPassword", true)) { - TextPrivateFormField::ref password = TextPrivateFormField::create(); - password->setName("password"); - password->setLabel((("Password"))); - password->setRequired(true); - form->addField(password); - } - - ListSingleFormField::ref language = ListSingleFormField::create(); - language->setName("language"); - language->setLabel((("Language"))); - language->addOption(Swift::FormField::Option(CONFIG_STRING(m_config, "registration.language"), CONFIG_STRING(m_config, "registration.language"))); - if (registered) - language->setValue(res.language); - else - language->setValue(CONFIG_STRING(m_config, "registration.language")); - form->addField(language); - -// TextSingleFormField::ref encoding = TextSingleFormField::create(); -// encoding->setName("encoding"); -// encoding->setLabel((("Encoding"))); -// if (registered) -// encoding->setValue(res.encoding); -// else -// encoding->setValue(CONFIG_STRING(m_config, "registration.encoding")); -// form->addField(encoding); - - if (registered) { - BooleanFormField::ref boolean = BooleanFormField::create(); - boolean->setName("unregister"); - boolean->setLabel((("Remove your registration"))); - boolean->setValue(0); - form->addField(boolean); - } else { - if (CONFIG_BOOL(m_config,"registration.require_local_account")) { - std::string localUsernameField = CONFIG_STRING(m_config, "registration.local_username_label"); - TextSingleFormField::ref local_username = TextSingleFormField::create(); - local_username->setName("local_username"); - local_username->setLabel((localUsernameField)); - local_username->setRequired(true); - form->addField(local_username); - TextPrivateFormField::ref local_password = TextPrivateFormField::create(); - local_password->setName("local_password"); - local_password->setLabel((("Local Password"))); - local_password->setRequired(true); - form->addField(local_password); - } - } - - reg->setForm(form); - - sendResponse(from, id, reg); - - return true; -} - -bool UserRegistration::handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload) { - // TODO: backend should say itself if registration is needed or not... - if (CONFIG_STRING(m_config, "service.protocol") == "irc") { - sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify); - return true; - } - - std::string barejid = from.toBare().toString(); - - if (!CONFIG_BOOL(m_config,"registration.enable_public_registration")) { - std::vector const &x = CONFIG_VECTOR(m_config,"service.allowed_servers"); - if (std::find(x.begin(), x.end(), from.getDomain()) == x.end()) { - LOG4CXX_INFO(logger, barejid << ": This user has no permissions to register an account") - sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify); - return true; - } - } - - UserInfo res; - bool registered = m_storageBackend->getUser(barejid, res); - - std::string encoding; - std::string language; - std::string local_username(""); - std::string local_password(""); - - 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(); - } - // Pidgin sends it as textSingle, not sure why... - else if (textSingle->getName() == "password") { - payload->setPassword(textSingle->getValue()); - } - else if (textSingle->getName() == "local_username") { - local_username = textSingle->getValue(); - } - // Pidgin sends it as textSingle, not sure why... - else if (textSingle->getName() == "local_password") { - local_password = textSingle->getValue(); - } - // Pidgin sends it as textSingle, not sure why... - else if (textSingle->getName() == "unregister") { - if (textSingle->getValue() == "1" || textSingle->getValue() == "true") { - payload->setRemove(true); - } - } - continue; - } - - TextPrivateFormField::ref textPrivate = boost::dynamic_pointer_cast(*it); - if (textPrivate) { - if (textPrivate->getName() == "password") { - payload->setPassword(textPrivate->getValue()); - } - else if (textPrivate->getName() == "local_password") { - local_password = textPrivate->getValue(); - } - continue; - } - - ListSingleFormField::ref listSingle = boost::dynamic_pointer_cast(*it); - if (listSingle) { - if (listSingle->getName() == "language") { - language = listSingle->getValue(); - } - 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); - sendResponse(from, id, InBandRegistrationPayload::ref()); - return true; - } - - if (CONFIG_BOOL(m_config,"registration.require_local_account")) { - /* if (!local_username || !local_password) { - sendResponse(from, id, InBandRegistrationPayload::ref()); - return true - } else */ if (local_username == "" || local_password == "") { - sendResponse(from, id, InBandRegistrationPayload::ref()); - return true; - } -// Swift::logging = true; - bool validLocal = false; - std::string localLookupServer = CONFIG_STRING(m_config, "registration.local_account_server"); - std::string localLookupJID = local_username + std::string("@") + localLookupServer; - SimpleEventLoop localLookupEventLoop; - BoostNetworkFactories localLookupNetworkFactories(&localLookupEventLoop); - Client localLookupClient(localLookupJID, local_password, &localLookupNetworkFactories); - - // TODO: this is neccessary on my server ... but should maybe omitted - localLookupClient.setAlwaysTrustCertificates(); - localLookupClient.connect(); - - class SimpleLoopRunner { - public: - SimpleLoopRunner() {}; - - static void run(SimpleEventLoop * loop) { - loop->run(); - }; - }; - - // TODO: Really ugly and hacky solution, any other ideas more than welcome! - boost::thread thread(boost::bind(&(SimpleLoopRunner::run), &localLookupEventLoop)); - thread.timed_join(boost::posix_time::millisec(CONFIG_INT(m_config, "registration.local_account_server_timeout"))); - localLookupEventLoop.stop(); - thread.join(); - validLocal = localLookupClient.isAvailable(); - localLookupClient.disconnect(); - if (!validLocal) { - sendError(from, id, ErrorPayload::NotAuthorized, ErrorPayload::Modify); - return true; - } - } - - if (!payload->getUsername() || (!payload->getPassword() && CONFIG_BOOL_DEFAULTED(m_config, "registration.needPassword", true))) { - sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); - return true; - } - - if (!payload->getPassword()) { - payload->setPassword(""); - } - - // Register or change password - if (payload->getUsername()->empty()) { - sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); - return true; - } - - // TODO: Move this check to backend somehow - if (CONFIG_STRING(m_config, "service.protocol") == "prpl-jabber") { - // User tries to register himself. - if ((Swift::JID(*payload->getUsername()).toBare() == from.toBare())) { - 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(), user_row); - if (registered) { - sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); - return true; - } - } - - std::string username = *payload->getUsername(); - - std::string newUsername(username); - if (!CONFIG_STRING(m_config, "registration.username_mask").empty()) { - newUsername = CONFIG_STRING(m_config, "registration.username_mask"); - boost::replace_all(newUsername, "$username", username); - } - -//TODO: Part of spectrum1 registration stuff, this should be potentially rewritten for S2 too -// if (!m_component->protocol()->isValidUsername(newUsername)) { -// Log("UserRegistration", "This is not valid username: "<< newUsername); -// sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); -// return true; -// } - - if (!CONFIG_STRING(m_config, "registration.allowed_usernames").empty()) { - boost::regex expression(CONFIG_STRING(m_config, "registration.allowed_usernames")); - if (!regex_match(newUsername, expression)) { - LOG4CXX_INFO(logger, "This is not valid username: " << newUsername); - sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); - return true; - } - } - - if (!registered) { - res.jid = barejid; - res.uin = newUsername; - res.password = *payload->getPassword(); - res.language = language; - res.encoding = encoding; - res.vip = 0; - res.id = 0; - registerUser(res); - } - else { - res.jid = barejid; - res.uin = newUsername; - res.password = *payload->getPassword(); - res.language = language; - res.encoding = encoding; - m_storageBackend->setUser(res); - onUserUpdated(res); - } - - sendResponse(from, id, InBandRegistrationPayload::ref()); - return true; -} - -} +/** + * 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/rostermanager.h" +#include "transport/user.h" +#include "transport/logging.h" +#include "Swiften/Elements/ErrorPayload.h" +#include "Swiften/EventLoop/SimpleEventLoop.h" +#include "Swiften/Network/BoostNetworkFactories.h" +#include "Swiften/Client/Client.h" +#include +#include +#include +#include +#if HAVE_SWIFTEN_3 +#include +#endif + +using namespace Swift; + +namespace Transport { + +DEFINE_LOGGER(logger, "UserRegistration"); + +UserRegistration::UserRegistration(Component *component, UserManager *userManager, StorageBackend *storageBackend) : Swift::Responder(component->m_iqRouter) { + m_component = component; + m_config = m_component->m_config; + m_storageBackend = storageBackend; + m_userManager = userManager; +} + +UserRegistration::~UserRegistration(){ +} + +bool UserRegistration::registerUser(const UserInfo &row) { + UserInfo user; + bool registered = m_storageBackend->getUser(row.jid, user); + // This user is already registered + if (registered) + return false; + + m_storageBackend->setUser(row); + + //same as in unregisterUser but here we have to pass UserInfo to handleRegisterRRResponse + AddressedRosterRequest::ref request = AddressedRosterRequest::ref(new AddressedRosterRequest(m_component->getIQRouter(),row.jid)); + request->onResponse.connect(boost::bind(&UserRegistration::handleRegisterRemoteRosterResponse, this, _1, _2, row)); + request->send(); + + 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); + + // We have to check if server supports remoteroster XEP and use it if it's supported or fallback to unsubscribe otherwise + AddressedRosterRequest::ref request = AddressedRosterRequest::ref(new AddressedRosterRequest(m_component->getIQRouter(), barejid)); + request->onResponse.connect(boost::bind(&UserRegistration::handleUnregisterRemoteRosterResponse, this, _1, _2, barejid)); + request->send(); + + return true; +} + +void UserRegistration::handleRegisterRemoteRosterResponse(boost::shared_ptr payload, Swift::ErrorPayload::ref remoteRosterNotSupported /*error*/, const UserInfo &row){ + if (remoteRosterNotSupported || !payload) { + 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->getStanzaChannel()->sendPresence(response); + } + else{ + Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload()); + Swift::RosterItemPayload item; + item.setJID(m_component->getJID()); + item.setSubscription(Swift::RosterItemPayload::Both); + payload->addItem(item); + Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, row.jid, m_component->getIQRouter()); + request->send(); + } + onUserRegistered(row); + + std::vector const &x = CONFIG_VECTOR(m_component->getConfig(),"registration.notify_jid"); + BOOST_FOREACH(const std::string ¬ify_jid, x) { + boost::shared_ptr msg(new Swift::Message()); + msg->setBody(std::string("registered: ") + row.jid); + msg->setTo(notify_jid); + msg->setFrom(m_component->getJID()); + m_component->getStanzaChannel()->sendMessage(msg); + } +} + +void UserRegistration::handleUnregisterRemoteRosterResponse(boost::shared_ptr payload, Swift::ErrorPayload::ref remoteRosterNotSupported /*error*/, const std::string &barejid) { + UserInfo userInfo; + bool registered = m_storageBackend->getUser(barejid, userInfo); + // This user is not registered + if (!registered) + return; + + if (remoteRosterNotSupported || !payload) { + std::list roster; + m_storageBackend->getBuddies(userInfo.id, roster); + for(std::list::iterator u = roster.begin(); u != roster.end() ; u++){ + std::string name = (*u).legacyName; + if ((*u).flags & BUDDY_JID_ESCAPING) { + name = Swift::JID::getEscapedNode((*u).legacyName); + } + else { + if (name.find_last_of("@") != std::string::npos) { + name.replace(name.find_last_of("@"), 1, "%"); + } + } + + Swift::Presence::ref response; + 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->getStanzaChannel()->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->getStanzaChannel()->sendPresence(response); + } + } + else { + BOOST_FOREACH(Swift::RosterItemPayload it, payload->getItems()) { + Swift::RosterPayload::ref p = Swift::RosterPayload::ref(new Swift::RosterPayload()); + Swift::RosterItemPayload item; + item.setJID(it.getJID()); + item.setSubscription(Swift::RosterItemPayload::Remove); + + p->addItem(item); + + Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(p, barejid, m_component->getIQRouter()); + request->send(); + } + } + + // Remove user from database + m_storageBackend->removeUser(userInfo.id); + + // Disconnect the user + User *user = m_userManager->getUser(barejid); + if (user) { + m_userManager->removeUser(user); + } + + if (remoteRosterNotSupported || !payload) { + Swift::Presence::ref response; + response = Swift::Presence::create(); + response->setTo(Swift::JID(barejid)); + response->setFrom(m_component->getJID()); + response->setType(Swift::Presence::Unsubscribe); + m_component->getStanzaChannel()->sendPresence(response); + + response = Swift::Presence::create(); + response->setTo(Swift::JID(barejid)); + response->setFrom(m_component->getJID()); + response->setType(Swift::Presence::Unsubscribed); + m_component->getStanzaChannel()->sendPresence(response); + } + else { + Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload()); + Swift::RosterItemPayload item; + item.setJID(m_component->getJID()); + item.setSubscription(Swift::RosterItemPayload::Remove); + payload->addItem(item); + + Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, barejid, m_component->getIQRouter()); + request->send(); + } + + std::vector const &x = CONFIG_VECTOR(m_component->getConfig(),"registration.notify_jid"); + BOOST_FOREACH(const std::string ¬ify_jid, x) { + boost::shared_ptr msg(new Swift::Message()); + msg->setBody(std::string("unregistered: ") + barejid); + msg->setTo(notify_jid); + msg->setFrom(m_component->getJID()); + m_component->getStanzaChannel()->sendMessage(msg); + } +} + +bool UserRegistration::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload) { + // TODO: backend should say itself if registration is needed or not... + if (CONFIG_STRING(m_config, "service.protocol") == "irc") { + sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify); + return true; + } + + std::string barejid = from.toBare().toString(); + + if (!CONFIG_BOOL(m_config,"registration.enable_public_registration")) { + std::vector const &x = CONFIG_VECTOR(m_config,"service.allowed_servers"); + if (std::find(x.begin(), x.end(), from.getDomain()) == x.end()) { + LOG4CXX_INFO(logger, barejid << ": This user has no permissions to register an account") + sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify); + return true; + } + } + + boost::shared_ptr reg(new InBandRegistrationPayload()); + + UserInfo res; + bool registered = m_storageBackend->getUser(barejid, res); + + std::string instructions = CONFIG_STRING(m_config, "registration.instructions"); + std::string usernameField = CONFIG_STRING(m_config, "registration.username_label"); + + // normal jabber:iq:register + reg->setInstructions(instructions); + reg->setRegistered(registered); + reg->setUsername(res.uin); + if (CONFIG_BOOL_DEFAULTED(m_config, "registration.needPassword", true)) { + reg->setPassword(""); + } + + + // form + Form::ref form(new Form(Form::FormType)); + form->setTitle((("Registration"))); + form->setInstructions((instructions)); +#if HAVE_SWIFTEN_3 + FormField::ref type = boost::make_shared(FormField::HiddenType, "jabber:iq:register"); +#else + HiddenFormField::ref type = HiddenFormField::create(); + type->setValue("jabber:iq:register"); +#endif + type->setName("FORM_TYPE"); + form->addField(type); +#if HAVE_SWIFTEN_3 + FormField::ref username = boost::make_shared(FormField::TextSingleType, res.uin); +#else + TextSingleFormField::ref username = TextSingleFormField::create(); + username->setValue(res.uin); +#endif + username->setName("username"); + username->setLabel((usernameField)); + username->setRequired(true); + form->addField(username); + + if (CONFIG_BOOL_DEFAULTED(m_config, "registration.needPassword", true)) { +#if HAVE_SWIFTEN_3 + FormField::ref password = boost::make_shared(FormField::TextPrivateType); +#else + TextPrivateFormField::ref password = TextPrivateFormField::create(); +#endif + password->setName("password"); + password->setLabel((("Password"))); + password->setRequired(true); + form->addField(password); + } +#if HAVE_SWIFTEN_3 + FormField::ref language = boost::make_shared(FormField::ListSingleType); +#else + ListSingleFormField::ref language = ListSingleFormField::create(); +#endif + language->setName("language"); + language->setLabel((("Language"))); + language->addOption(Swift::FormField::Option(CONFIG_STRING(m_config, "registration.language"), CONFIG_STRING(m_config, "registration.language"))); + if (registered) +#if HAVE_SWIFTEN_3 + language->addValue(res.language); +#else + language->setValue(res.language); +#endif + else +#if HAVE_SWIFTEN_3 + language->addValue(CONFIG_STRING(m_config, "registration.language")); +#else + language->setValue(CONFIG_STRING(m_config, "registration.language")); +#endif + form->addField(language); + +// TextSingleFormField::ref encoding = TextSingleFormField::create(); +// encoding->setName("encoding"); +// encoding->setLabel((("Encoding"))); +// if (registered) +// encoding->setValue(res.encoding); +// else +// encoding->setValue(CONFIG_STRING(m_config, "registration.encoding")); +// form->addField(encoding); + + if (registered) { +#if HAVE_SWIFTEN_3 + FormField::ref boolean = boost::make_shared(FormField::BooleanType, "0"); +#else + BooleanFormField::ref boolean = BooleanFormField::create(); + boolean->setValue(0); +#endif + boolean->setName("unregister"); + boolean->setLabel((("Remove your registration"))); + form->addField(boolean); + } else { + if (CONFIG_BOOL(m_config,"registration.require_local_account")) { + std::string localUsernameField = CONFIG_STRING(m_config, "registration.local_username_label"); +#if HAVE_SWIFTEN_3 + FormField::ref local_username = boost::make_shared(FormField::TextSingleType); +#else + TextSingleFormField::ref local_username = TextSingleFormField::create(); +#endif + local_username->setName("local_username"); + local_username->setLabel((localUsernameField)); + local_username->setRequired(true); + form->addField(local_username); +#if HAVE_SWIFTEN_3 + FormField::ref local_password = boost::make_shared(FormField::TextPrivateType); +#else + TextPrivateFormField::ref local_password = TextPrivateFormField::create(); +#endif + local_password->setName("local_password"); + local_password->setLabel((("Local Password"))); + local_password->setRequired(true); + form->addField(local_password); + } + } + + reg->setForm(form); + + sendResponse(from, id, reg); + + return true; +} + +bool UserRegistration::handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload) { + // TODO: backend should say itself if registration is needed or not... + if (CONFIG_STRING(m_config, "service.protocol") == "irc") { + sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify); + return true; + } + + std::string barejid = from.toBare().toString(); + + if (!CONFIG_BOOL(m_config,"registration.enable_public_registration")) { + std::vector const &x = CONFIG_VECTOR(m_config,"service.allowed_servers"); + if (std::find(x.begin(), x.end(), from.getDomain()) == x.end()) { + LOG4CXX_INFO(logger, barejid << ": This user has no permissions to register an account") + sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify); + return true; + } + } + + UserInfo res; + bool registered = m_storageBackend->getUser(barejid, res); + + std::string encoding; + std::string language; + std::string local_username(""); + std::string local_password(""); + + 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++) { +#if HAVE_SWIFTEN_3 + FormField::ref textSingle = *it; + if (textSingle->getType() == FormField::TextSingleType) { +#else + TextSingleFormField::ref textSingle = boost::dynamic_pointer_cast(*it); + if (textSingle) { +#endif + if (textSingle->getName() == "username") { +#if HAVE_SWIFTEN_3 + payload->setUsername(textSingle->getTextSingleValue()); +#else + payload->setUsername(textSingle->getValue()); +#endif + } + else if (textSingle->getName() == "encoding") { +#if HAVE_SWIFTEN_3 + encoding = textSingle->getTextSingleValue(); +#else + encoding = textSingle->getValue(); +#endif + } + // Pidgin sends it as textSingle, not sure why... + else if (textSingle->getName() == "password") { +#if HAVE_SWIFTEN_3 + payload->setPassword(textSingle->getTextSingleValue()); +#else + payload->setPassword(textSingle->getValue()); +#endif + } + else if (textSingle->getName() == "local_username") { +#if HAVE_SWIFTEN_3 + local_username = textSingle->getTextSingleValue(); +#else + local_username = textSingle->getValue(); +#endif + } + // Pidgin sends it as textSingle, not sure why... + else if (textSingle->getName() == "local_password") { +#if HAVE_SWIFTEN_3 + local_password = textSingle->getTextSingleValue(); +#else + local_password = textSingle->getValue(); +#endif + } + // Pidgin sends it as textSingle, not sure why... + else if (textSingle->getName() == "unregister") { +#if HAVE_SWIFTEN_3 + if (textSingle->getTextSingleValue() == "1" || textSingle->getTextSingleValue() == "true") { +#else + if (textSingle->getValue() == "1" || textSingle->getValue() == "true") { +#endif + payload->setRemove(true); + } + } + continue; + } +#if HAVE_SWIFTEN_3 + FormField::ref textPrivate = *it; + if (textPrivate->getType() == FormField::TextPrivateType) { +#else + TextPrivateFormField::ref textPrivate = boost::dynamic_pointer_cast(*it); + if (textPrivate) { +#endif + if (textPrivate->getName() == "password") { +#if HAVE_SWIFTEN_3 + payload->setPassword(textPrivate->getTextPrivateValue()); +#else + payload->setPassword(textPrivate->getValue()); +#endif + } + else if (textPrivate->getName() == "local_password") { +#if HAVE_SWIFTEN_3 + local_password = textPrivate->getTextPrivateValue(); +#else + local_password = textPrivate->getValue(); +#endif + } + continue; + } +#if HAVE_SWIFTEN_3 + FormField::ref listSingle = *it; + if (listSingle->getType() == FormField::ListSingleType) { +#else + ListSingleFormField::ref listSingle = boost::dynamic_pointer_cast(*it); + if (listSingle) { +#endif + if (listSingle->getName() == "language") { +#if HAVE_SWIFTEN_3 + language = listSingle->getValues()[0]; +#else + language = listSingle->getValue(); +#endif + } + continue; + } +#if HAVE_SWIFTEN_3 + FormField::ref boolean = *it; + if (boolean->getType() == FormField::BooleanType) { +#else + BooleanFormField::ref boolean = boost::dynamic_pointer_cast(*it); + if (boolean) { +#endif + if (boolean->getName() == "unregister") { +#if HAVE_SWIFTEN_3 + if (boolean->getBoolValue()) { +#else + if (boolean->getValue()) { +#endif + payload->setRemove(true); + } + } + continue; + } + } + } + + if (payload->isRemove()) { + unregisterUser(barejid); + sendResponse(from, id, InBandRegistrationPayload::ref()); + return true; + } + + if (CONFIG_BOOL(m_config,"registration.require_local_account")) { + /* if (!local_username || !local_password) { + sendResponse(from, id, InBandRegistrationPayload::ref()); + return true + } else */ if (local_username == "" || local_password == "") { + sendResponse(from, id, InBandRegistrationPayload::ref()); + return true; + } +// Swift::logging = true; + bool validLocal = false; + std::string localLookupServer = CONFIG_STRING(m_config, "registration.local_account_server"); + std::string localLookupJID = local_username + std::string("@") + localLookupServer; + SimpleEventLoop localLookupEventLoop; + BoostNetworkFactories localLookupNetworkFactories(&localLookupEventLoop); + Client localLookupClient(localLookupJID, local_password, &localLookupNetworkFactories); + + // TODO: this is neccessary on my server ... but should maybe omitted + localLookupClient.setAlwaysTrustCertificates(); + localLookupClient.connect(); + + class SimpleLoopRunner { + public: + SimpleLoopRunner() {}; + + static void run(SimpleEventLoop * loop) { + loop->run(); + }; + }; + + // TODO: Really ugly and hacky solution, any other ideas more than welcome! + boost::thread thread(boost::bind(&(SimpleLoopRunner::run), &localLookupEventLoop)); + thread.timed_join(boost::posix_time::millisec(CONFIG_INT(m_config, "registration.local_account_server_timeout"))); + localLookupEventLoop.stop(); + thread.join(); + validLocal = localLookupClient.isAvailable(); + localLookupClient.disconnect(); + if (!validLocal) { + sendError(from, id, ErrorPayload::NotAuthorized, ErrorPayload::Modify); + return true; + } + } + + if (!payload->getUsername() || (!payload->getPassword() && CONFIG_BOOL_DEFAULTED(m_config, "registration.needPassword", true))) { + sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); + return true; + } + + if (!payload->getPassword()) { + payload->setPassword(""); + } + + // Register or change password + if (payload->getUsername()->empty()) { + sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); + return true; + } + + // TODO: Move this check to backend somehow + if (CONFIG_STRING(m_config, "service.protocol") == "prpl-jabber") { + // User tries to register himself. + if ((Swift::JID(*payload->getUsername()).toBare() == from.toBare())) { + 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(), user_row); + if (registered) { + sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); + return true; + } + } + + std::string username = *payload->getUsername(); + + std::string newUsername(username); + if (!CONFIG_STRING(m_config, "registration.username_mask").empty()) { + newUsername = CONFIG_STRING(m_config, "registration.username_mask"); + boost::replace_all(newUsername, "$username", username); + } + +//TODO: Part of spectrum1 registration stuff, this should be potentially rewritten for S2 too +// if (!m_component->protocol()->isValidUsername(newUsername)) { +// Log("UserRegistration", "This is not valid username: "<< newUsername); +// sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); +// return true; +// } + + if (!CONFIG_STRING(m_config, "registration.allowed_usernames").empty()) { + boost::regex expression(CONFIG_STRING(m_config, "registration.allowed_usernames")); + if (!regex_match(newUsername, expression)) { + LOG4CXX_INFO(logger, "This is not valid username: " << newUsername); + sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); + return true; + } + } + + if (!registered) { + res.jid = barejid; + res.uin = newUsername; + res.password = *payload->getPassword(); + res.language = language; + res.encoding = encoding; + res.vip = 0; + res.id = 0; + registerUser(res); + } + else { + res.jid = barejid; + res.uin = newUsername; + res.password = *payload->getPassword(); + res.language = language; + res.encoding = encoding; + m_storageBackend->setUser(res); + onUserUpdated(res); + } + + sendResponse(from, id, InBandRegistrationPayload::ref()); + return true; +} + +}