From 51533ef87b89da9ee1a8ee3c8441d22074c1f592 2012-11-30 16:47:18 From: Daniel Henninger Date: 2012-11-30 16:47:18 Subject: [PATCH] Merge remote branch 'upstream/master' --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e1c2792bda779dd60043d87d740b65222462e76..2f14df81dc3fcf623c5ba63acef3d8e707613b87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,23 @@ 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}) @@ -30,69 +47,114 @@ endif() set(CMAKE_MODULE_PATH "cmake_modules") -# FIND CPPUNIT -set(cppunit_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -find_package(cppunit) +###### Prerequisites ###### -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}") -else() +# 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_LIBRARIES}, ${Boost_INCLUDE_DIR}") + +# FIND POPT +if (NOT WIN32) + set(popt_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") + find_package(popt REQUIRED) endif() +###### Database ###### + # FIND SQLITE3 -if (NOT CMAKE_COMPILER_IS_GNUCXX) -ADD_SUBDIRECTORY(msvc-deps) -else() -if (WIN32) -ADD_SUBDIRECTORY(msvc-deps/sqlite3) -else() -set(sqlite3_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -find_package(sqlite3) -endif() +if (ENABLE_SQLITE3) + if (NOT CMAKE_COMPILER_IS_GNUCXX) + ADD_SUBDIRECTORY(msvc-deps) + else() + if (WIN32) + ADD_SUBDIRECTORY(msvc-deps/sqlite3) + else() + set(sqlite3_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") + find_package(sqlite3) + endif() + endif() endif() # FIND MYSQL -set(mysql_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -find_package(mysql) +if(ENABLE_MYSQL) + set(mysql_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") + find_package(mysql) +endif() -# FIND LIBPURPLE -set(purple_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -find_package(purple) +# FIND PQXX +if(ENABLE_PQXX) + set(pqxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") + find_package(pqxx) +endif() -if (WIN32) - if (PURPLE_NOT_RUNTIME) +###### 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) - 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 (GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES) -# set(GLIB2_FOUND TRUE) -# else() - set(glib_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") - find_package(glib) -# endif() +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 POPT -if (NOT WIN32) - set(popt_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") - find_package(popt) -endif() +# FIND PROTOBUF +set(Protobuf_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") +find_package(Protobuf REQUIRED) -# FIND LIBEVENT -set(event_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -find_package(event) +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() # FIND SWIFTEN @@ -132,78 +194,58 @@ set(openssl_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") find_package(openssl) 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) +if(ENABLE_IRC) + set(Communi_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") + find_package(Communi) -message( STATUS "Found Boost: ${Boost_LIBRARIES}, ${Boost_INCLUDE_DIR}") - -set(Protobuf_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -find_package(Protobuf) - -# FIND PROTOBUF -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() - - -set(Communi_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -find_package(Communi) - -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) + 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) -set(pqxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -find_package(pqxx) - -if (NOT WIN32) +if (NOT WIN32 AND ENABLE_SKYPE) set(dbus_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") find_package(dbus) endif() -set(yahoo2_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -find_package(yahoo2) +if(ENABLE_YAHOO2) + set(yahoo2_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") + find_package(yahoo2) +endif() -find_package(Doxygen) +####### Miscallanous ###### -INCLUDE(FindQt4) -FIND_PACKAGE(Qt4 COMPONENTS QtCore QtNetwork) +if(ENABLE_DOCS) + find_package(Doxygen) +endif() -# ADD_DEFINITIONS(${SWIFTEN_CFLAGS}) -ADD_DEFINITIONS(-DSUPPORT_LEGACY_CAPS) -# ADD_DEFINITIONS(-DBOOST_FILESYSTEM_VERSION=2) +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() message(" Supported features") message("-----------------------") @@ -234,14 +276,18 @@ if (SQLITE3_FOUND) include_directories(${SQLITE3_INCLUDE_DIR}) message("SQLite3 : yes") else (SQLITE3_FOUND) -if (WIN32) - ADD_DEFINITIONS(-DWITH_SQLITE) - include_directories(msvc-deps/sqlite3) - message("SQLite3 : bundled") -else() - set(SQLITE3_LIBRARIES "") - message("SQLite3 : no") -endif() + if (WIN32) + ADD_DEFINITIONS(-DWITH_SQLITE) + include_directories(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) @@ -250,7 +296,11 @@ if (MYSQL_FOUND) message("MySQL : yes") else (MYSQL_FOUND) set(MYSQL_LIBRARIES "") - message("MySQL : no (install mysql-devel)") + if(ENABLE_MYSQL) + message("MySQL : no (install mysql-devel)") + else(ENABLE_MYSQL) + message("MySQL : no (user disabled)") + endif() endif (MYSQL_FOUND) if (PQXX_FOUND) @@ -260,7 +310,11 @@ if (PQXX_FOUND) else (PQXX_FOUND) set(PQXX_LIBRARY "") set(PQ_LIBRARY "") - message("PostgreSQL : no (install libpqxx-devel)") + if(ENABLE_PQXX) + message("PostgreSQL : no (install libpqxx-devel)") + else(ENABLE_PQXX) + message("PostgreSQL : no (user disabled)") + endif() endif (PQXX_FOUND) if (PROTOBUF_FOUND) @@ -273,7 +327,11 @@ if (PROTOBUF_FOUND) include_directories(${PURPLE_INCLUDE_DIR}) include_directories(${GLIB2_INCLUDE_DIR}) else() - message("Libpurple plugin : no (install libpurple)") + if(ENABLE_PURPLE) + message("Libpurple plugin : no (install libpurple)") + else(ENABLE_PURPLE) + message("Libpurple plugin : no (user disabled)") + endif() endif() if (HAVE_EVENT) @@ -281,7 +339,9 @@ if (PROTOBUF_FOUND) include_directories(${EVENT_INCLUDE_DIRS}) message(" libev eventloop : yes") else() - message(" libev eventloop : no (install libev-devel)") + if(ENABLE_PURPLE) + message(" libev eventloop : no (install libev-devel)") + endif() endif() if(IRC_FOUND) @@ -291,35 +351,62 @@ if (PROTOBUF_FOUND) include_directories(${IRC_INCLUDE_DIR}) include(${QT_USE_FILE}) else() - message("IRC plugin : no (install libCommuni and libprotobuf-dev)") + if(ENABLE_IRC) + message("IRC plugin : no (install libCommuni and libprotobuf-dev)") + else(ENABLE_IRC) + message("IRC plugin : no (user disabled)") + endif() endif() -if (NOT WIN32) - message("Frotz plugin : yes") - message("SMSTools3 plugin : yes") - if(${LIBDBUSGLIB_FOUND}) - message("Skype plugin : yes") - include_directories(${LIBDBUSGLIB_INCLUDE_DIRS}) + 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() + if(ENABLE_TWITTER) + message("Twitter plugin : yes") + else(ENABLE_TWITTER) + message("Twitter plugin : no (user disabled)") + endif() else() - message("Skype plugin : no (install dbus-glib-devel)") + 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)") + message("Twitter plugin : no (does not run on Win32)") endif() -else() - message("Frotz plugin : no") - message("SMSTools3 plugin : no") - message("Skype plugin : no") -endif() -# We have our own copy now... -# if(YAHOO2_FOUND) + if(YAHOO2_FOUND) message("Libyahoo2 plugin : yes") -# include_directories(${YAHOO2_INCLUDE_DIR}) -# else() -# message("Libyahoo2 plugin : no (install libyahoo2-devel)") -# endif() - - message("Swiften plugin : yes") - message("Twitter plugin : yes") + include_directories(${YAHOO2_INCLUDE_DIR}) + else() + if(ENABLE_YAHOO2) + message("Libyahoo2 plugin : no (install libyahoo2-devel)") + 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)") @@ -336,22 +423,26 @@ if (LOG4CXX_FOUND) ADD_DEFINITIONS(-DWITH_LOG4CXX) else() set(LOG4CXX_LIBRARIES "") - message("Log4cxx : no (install log4cxx-devel)") + if(ENABLE_LOG) + message("Log4cxx : no (install log4cxx-devel)") + else(ENABLE_LOG) + message("Log4cxx : no (user disabled)") + 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) + 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() + if (CMAKE_COMPILER_IS_GNUCXX) + ADD_DEFINITIONS(-O0) + ADD_DEFINITIONS(-ggdb) + endif() ADD_DEFINITIONS(-DDEBUG) message("Debug : yes") else(CMAKE_BUILD_TYPE MATCHES Debug) @@ -386,14 +477,31 @@ if (CPPUNIT_FOUND) message("tests : yes") include_directories(${CPPUNIT_INCLUDE_DIR}) else() - message("tests : no (install CPPUnit)") + 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) - message("Docs : no (install doxygen)") + 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/CMakeLists.txt b/backends/CMakeLists.txt index ac9b6f0b592109b3ba66564eb8b41473860354cf..70918f9409fef3faf019de22128c6157d0f641b3 100644 --- a/backends/CMakeLists.txt +++ b/backends/CMakeLists.txt @@ -7,21 +7,27 @@ if (PROTOBUF_FOUND) ADD_SUBDIRECTORY(libcommuni) endif() - ADD_SUBDIRECTORY(swiften) + if (ENABLE_SWIFTEN) + ADD_SUBDIRECTORY(swiften) + endif() ADD_SUBDIRECTORY(template) - -if (NOT WIN32) - ADD_SUBDIRECTORY(smstools3) - ADD_SUBDIRECTORY(frotz) -# if(YAHOO2_FOUND) - ADD_SUBDIRECTORY(libyahoo2) -# endif() - ADD_SUBDIRECTORY(twitter) - if (${LIBDBUSGLIB_FOUND}) - ADD_SUBDIRECTORY(skype) + if (NOT WIN32) + if(ENABLE_SMSTOOLS3) + ADD_SUBDIRECTORY(smstools3) + endif() + if(ENABLE_FROTZ) + ADD_SUBDIRECTORY(frotz) + endif() + if(YAHOO2_FOUND) + ADD_SUBDIRECTORY(libyahoo2) + endif() + if(ENABLE_TWITTER) + ADD_SUBDIRECTORY(twitter) + endif() + if (${LIBDBUSGLIB_FOUND}) + ADD_SUBDIRECTORY(skype) + endif() endif() endif() - -endif() diff --git a/backends/libcommuni/CMakeLists.txt b/backends/libcommuni/CMakeLists.txt index 38e7507b1b8a07640daa16565c29ec9e6cf8b256..699b02bd2c94fe736bc138403e302d546c2a40f7 100644 --- a/backends/libcommuni/CMakeLists.txt +++ b/backends/libcommuni/CMakeLists.txt @@ -1,10 +1,14 @@ cmake_minimum_required(VERSION 2.6) FILE(GLOB SRC *.cpp) FILE(GLOB HEADERS *.h) -QT4_WRAP_CPP(SRC ${HEADERS}) +QT4_WRAP_CPP(SRC ${HEADERS} OPTIONS -DBOOST_TT_HAS_OPERATOR_HPP_INCLUDED) ADD_EXECUTABLE(spectrum2_libcommuni_backend ${SRC}) -target_link_libraries(spectrum2_libcommuni_backend ${IRC_LIBRARY} ${QT_LIBRARIES} transport pthread) +if (NOT WIN32) + target_link_libraries(spectrum2_libcommuni_backend ${IRC_LIBRARY} ${QT_LIBRARIES} transport pthread) +else () + target_link_libraries(spectrum2_libcommuni_backend ${IRC_LIBRARY} ${QT_LIBRARIES} transport) +endif() INSTALL(TARGETS spectrum2_libcommuni_backend RUNTIME DESTINATION bin) diff --git a/backends/libcommuni/ircnetworkplugin.cpp b/backends/libcommuni/ircnetworkplugin.cpp index 90e4d663460a9bbd0c332c0f4bccce225e96b2b9..bda4cc4d94b54cf18fa798169061c50cd6948184 100644 --- a/backends/libcommuni/ircnetworkplugin.cpp +++ b/backends/libcommuni/ircnetworkplugin.cpp @@ -11,6 +11,7 @@ DEFINE_LOGGER(logger, "IRCNetworkPlugin"); IRCNetworkPlugin::IRCNetworkPlugin(Config *config, Swift::QtEventLoop *loop, const std::string &host, int port) { this->config = config; m_currentServer = 0; + m_firstPing = true; m_socket = new QTcpSocket(); m_socket->connectToHost(FROM_UTF8(host), port); connect(m_socket, SIGNAL(readyRead()), this, SLOT(readData())); @@ -48,6 +49,17 @@ void IRCNetworkPlugin::readData() { if (availableBytes == 0) return; + if (m_firstPing) { + m_firstPing = false; + // Users can join the network without registering if we allow + // one user to connect multiple IRC networks. + if (m_servers.empty()) { + NetworkPlugin::PluginConfig cfg; + cfg.setNeedRegistration(false); + sendConfig(cfg); + } + } + std::string d = std::string(m_socket->readAll().data(), availableBytes); handleDataRead(d); } @@ -142,7 +154,13 @@ void IRCNetworkPlugin::handleMessageSendRequest(const std::string &user, const s std::string target = getTargetName(legacyName); LOG4CXX_INFO(logger, user << ": Session name: " << session << ", message to " << target); - m_sessions[session]->sendCommand(IrcCommand::createMessage(FROM_UTF8(target), FROM_UTF8(message))); + + if (message.find("/me") == 0) { + m_sessions[session]->sendCommand(IrcCommand::createCtcpAction(FROM_UTF8(target), FROM_UTF8(message.substr(4)))); + } + else { + m_sessions[session]->sendCommand(IrcCommand::createMessage(FROM_UTF8(target), FROM_UTF8(message))); + } if (target.find("#") == 0) { handleMessage(user, legacyName, message, TO_UTF8(m_sessions[session]->nickName())); diff --git a/backends/libcommuni/ircnetworkplugin.h b/backends/libcommuni/ircnetworkplugin.h index 043089cd807f494b451c35d78106f9e999343658..d4a84d730906c96b65d4da5b51d366fdd5e96ae3 100644 --- a/backends/libcommuni/ircnetworkplugin.h +++ b/backends/libcommuni/ircnetworkplugin.h @@ -46,4 +46,5 @@ class IRCNetworkPlugin : public QObject, public NetworkPlugin { std::vector m_servers; int m_currentServer; std::string m_identify; + bool m_firstPing; }; \ No newline at end of file diff --git a/backends/libcommuni/session.cpp b/backends/libcommuni/session.cpp index 2eabfe96040b267ff346b7839166220866c61eb2..025de37a889f1fd62287a2e76e0af2909a24e8e4 100644 --- a/backends/libcommuni/session.cpp +++ b/backends/libcommuni/session.cpp @@ -23,6 +23,8 @@ DEFINE_LOGGER(logger, "IRCSession"); +static bool sentList; + MyIrcSession::MyIrcSession(const std::string &user, IRCNetworkPlugin *np, const std::string &suffix, QObject* parent) : IrcSession(parent) { this->np = np; @@ -40,8 +42,14 @@ void MyIrcSession::on_connected() { m_connected = true; if (suffix.empty()) { np->handleConnected(user); +// if (!sentList) { +// sendCommand(IrcCommand::createList("", "")); +// sentList = true; +// } } + sendCommand(IrcCommand::createCapability("REQ", QStringList("away-notify"))); + for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) { sendCommand(IrcCommand::createJoin(FROM_UTF8(it->second->getChannel()), FROM_UTF8(it->second->getPassword()))); } @@ -80,8 +88,8 @@ void MyIrcSession::on_joined(IrcMessage *message) { bool flags = 0; std::string nickname = TO_UTF8(m->sender().name()); flags = correctNickname(nickname); - np->handleParticipantChanged(user, nickname, TO_UTF8(m->channel()), (int) flags, pbnetwork::STATUS_ONLINE); - LOG4CXX_INFO(logger, user << ": Joined " << TO_UTF8(m->parameters()[0])); + np->handleParticipantChanged(user, nickname, TO_UTF8(m->channel()) + suffix, (int) flags, pbnetwork::STATUS_ONLINE); + LOG4CXX_INFO(logger, user << ": " << nickname << " joined " << TO_UTF8(m->channel()) + suffix); } @@ -158,34 +166,56 @@ void MyIrcSession::on_messageReceived(IrcMessage *message) { } } + QString msg = m->message(); + if (m->isAction()) { + msg = QString("/me ") + msg; + } + std::string target = TO_UTF8(m->target()); LOG4CXX_INFO(logger, user << ": Message from " << target); if (target.find("#") == 0) { bool flags = 0; std::string nickname = TO_UTF8(m->sender().name()); flags = correctNickname(nickname); - np->handleMessage(user, target + suffix, TO_UTF8(m->message()), nickname); + np->handleMessage(user, target + suffix, TO_UTF8(msg), nickname); } else { bool flags = 0; std::string nickname = TO_UTF8(m->sender().name()); flags = correctNickname(nickname); LOG4CXX_INFO(logger, nickname + suffix); - np->handleMessage(user, nickname + suffix, TO_UTF8(m->message())); + np->handleMessage(user, nickname + suffix, TO_UTF8(msg)); } } void MyIrcSession::on_numericMessageReceived(IrcMessage *message) { QString channel; QStringList members; + std::string nick; IrcNumericMessage *m = (IrcNumericMessage *) message; switch (m->code()) { + case 301: + break; case 332: m_topicData = TO_UTF8(m->parameters().value(2)); break; case 333: - np->handleSubject(user, TO_UTF8(m->parameters().value(1)) + suffix, m_topicData, TO_UTF8(m->parameters().value(2))); + nick = TO_UTF8(m->parameters().value(2)); + if (nick.find("!") != std::string::npos) { + nick = nick.substr(0, nick.find("!")); + } + if (nick.find("/") != std::string::npos) { + nick = nick.substr(0, nick.find("/")); + } + np->handleSubject(user, TO_UTF8(m->parameters().value(1)) + suffix, m_topicData, nick); + break; + case 352: + channel = m->parameters().value(1); + nick = TO_UTF8(m->parameters().value(5)); + if (m->parameters().value(6).toUpper().startsWith("G")) { + np->handleParticipantChanged(user, nick, TO_UTF8(channel) + suffix, m_modes[TO_UTF8(channel) + nick], pbnetwork::STATUS_AWAY); + } break; case 353: channel = m->parameters().value(2); @@ -199,10 +229,24 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) { m_modes[TO_UTF8(channel) + nickname] = flags; np->handleParticipantChanged(user, nickname, TO_UTF8(channel) + suffix,(int) flags, pbnetwork::STATUS_ONLINE); } + + // ask /who to get away states + sendCommand(IrcCommand::createWho(channel)); break; case 432: np->handleDisconnected(user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Erroneous Nickname"); break; + case 321: + m_rooms.clear(); + m_names.clear(); + break; + case 322: + m_rooms.push_back(TO_UTF8(m->parameters().value(1))); + m_names.push_back(TO_UTF8(m->parameters().value(1))); + break; + case 323: + np->handleRoomList("", m_rooms, m_names); + break; default: break; } diff --git a/backends/libcommuni/session.h b/backends/libcommuni/session.h index 507feb93a6e136d0e9eb3424a99b89bc138d8603..d398dd256e04da61fc63b349d0c5f4616ca2163c 100644 --- a/backends/libcommuni/session.h +++ b/backends/libcommuni/session.h @@ -41,6 +41,7 @@ public: MyIrcSession(const std::string &user, IRCNetworkPlugin *np, const std::string &suffix = "", QObject* parent = 0); std::map m_modes; + std::map m_away; std::string suffix; int rooms; @@ -85,39 +86,8 @@ protected: AutoJoinMap m_autoJoin; std::string m_topicData; bool m_connected; + std::list m_rooms; + std::list m_names; }; -//class MyIrcBuffer : public Irc::Buffer -//{ -// Q_OBJECT - -//public: -// MyIrcBuffer(const QString& receiver, const std::string &user, NetworkPlugin *np, const std::string &suffix, Irc::Session* parent); -// NetworkPlugin *np; -// std::string user; -// MyIrcSession *p; -// std::string m_topicData; -// std::string suffix; - -//protected Q_SLOTS: -// void on_receiverChanged(const QString& receiver); -// void on_joined(const QString& origin); -// void on_parted(const QString& origin, const QString& message); -// void on_quit(const QString& origin, const QString& message); -// void on_nickChanged(const QString& origin, const QString& nick); -// void on_modeChanged(const QString& origin, const QString& mode, const QString& args); -// void on_topicChanged(const QString& origin, const QString& topic); -// void on_invited(const QString& origin, const QString& receiver, const QString& channel); -// void on_kicked(const QString& origin, const QString& nick, const QString& message); -// void on_messageReceived(const QString& origin, const QString& message, Irc::Buffer::MessageFlags flags); -// void on_noticeReceived(const QString& origin, const QString& notice, Irc::Buffer::MessageFlags flags); -// void on_ctcpRequestReceived(const QString& origin, const QString& request, Irc::Buffer::MessageFlags flags); -// void on_ctcpReplyReceived(const QString& origin, const QString& reply, Irc::Buffer::MessageFlags flags); -// void on_ctcpActionReceived(const QString& origin, const QString& action, Irc::Buffer::MessageFlags flags); -// void on_numericMessageReceived(const QString& origin, uint code, const QStringList& params); -// void on_unknownMessageReceived(const QString& origin, const QStringList& params); - -// bool correctNickname(std::string &nickname); -//}; - #endif // SESSION_H diff --git a/backends/libpurple/main.cpp b/backends/libpurple/main.cpp index 1c3f8c4850fb089284e030f4b345b405861f22e4..06df61eee007e8070bd65b3440437a67ea3cb511 100644 --- a/backends/libpurple/main.cpp +++ b/backends/libpurple/main.cpp @@ -933,14 +933,21 @@ static void conv_write_im(PurpleConversation *conv, const char *who, const char xhtml_ = ""; } + std::string timestamp; + if (mtime && (unsigned long) time(NULL)-10 > (unsigned long) mtime/* && (unsigned long) time(NULL) - 31536000 < (unsigned long) mtime*/) { + char buf[80]; + strftime(buf, sizeof(buf), "%Y%m%dT%H%M%S", gmtime(&mtime)); + timestamp = buf; + } + // LOG4CXX_INFO(logger, "Received message body='" << message_ << "' xhtml='" << xhtml_ << "'"); if (purple_conversation_get_type_wrapped(conv) == PURPLE_CONV_TYPE_IM) { - np->handleMessage(np->m_accounts[account], w, message_, "", xhtml_); + np->handleMessage(np->m_accounts[account], w, message_, "", xhtml_, timestamp); } else { LOG4CXX_INFO(logger, "Received message body='" << message_ << "' name='" << purple_conversation_get_name_wrapped(conv) << "' " << w); - np->handleMessage(np->m_accounts[account], purple_conversation_get_name_wrapped(conv), message_, w, xhtml_); + np->handleMessage(np->m_accounts[account], purple_conversation_get_name_wrapped(conv), message_, w, xhtml_, timestamp); } } diff --git a/backends/libpurple/utils.cpp b/backends/libpurple/utils.cpp index 9ae0d37a4a45281f439322c8b8b1740e67e713c1..c375421ed1c1d7c014cb88cfd55023d2de51331a 100644 --- a/backends/libpurple/utils.cpp +++ b/backends/libpurple/utils.cpp @@ -36,14 +36,14 @@ #ifndef WIN32 #include "sys/wait.h" #include "sys/signal.h" -#include -#include -#include -#include -#include -#include -#include "sys/socket.h" +#include +#include +#include #include +#include +#include +#include +#include #include #include #else @@ -137,30 +137,32 @@ void spectrum_sigchld_handler(int sig) #endif int create_socket(const char *host, int portno) { - struct sockaddr_in serv_addr; - - int main_socket = socket(AF_INET, SOCK_STREAM, 0); - memset((char *) &serv_addr, 0, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - serv_addr.sin_port = htons(portno); - - hostent *hos; // Resolve name + struct sockaddr_in stSockAddr; + int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (-1 == SocketFD) { + return 0; + } + + hostent *hos; if ((hos = gethostbyname(host)) == NULL) { // strerror() will not work for gethostbyname() and hstrerror() // is supposedly obsolete - exit(1); + return 0; } - serv_addr.sin_addr.s_addr = *((unsigned long *) hos->h_addr_list[0]); - if (connect(main_socket, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { - close(main_socket); - main_socket = 0; + memset(&stSockAddr, 0, sizeof(stSockAddr)); + + stSockAddr.sin_family = AF_INET; + stSockAddr.sin_port = htons(portno); + memcpy(&(stSockAddr.sin_addr.s_addr), hos->h_addr, hos->h_length); + + if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) { + close(SocketFD); + return 0; } -// int flags = fcntl(main_socket, F_GETFL); -// flags |= O_NONBLOCK; -// fcntl(main_socket, F_SETFL, flags); - return main_socket; + return SocketFD; } #ifdef _WIN32 diff --git a/backends/skype/main.cpp b/backends/skype/main.cpp index 99d77bc271bb4afc6a0dd625e13a45d236839ae9..7cb7c59fa0fcf22881ed5eb22fa6d5edd133d76b 100644 --- a/backends/skype/main.cpp +++ b/backends/skype/main.cpp @@ -19,7 +19,9 @@ #include "sys/wait.h" #include "sys/signal.h" // #include "valgrind/memcheck.h" +#ifndef __FreeBSD__ #include "malloc.h" +#endif #include diff --git a/backends/swiften/main.cpp b/backends/swiften/main.cpp index d9199aeb3dbfba5abb8ac1ff3cedc42659c59e1d..6db0d86c3c88bacfc093e865c4a9af0fbf09b430 100644 --- a/backends/swiften/main.cpp +++ b/backends/swiften/main.cpp @@ -14,8 +14,10 @@ #include "sys/signal.h" #endif +#ifndef __FreeBSD__ // malloc_trim #include "malloc.h" +#endif // Boost #include @@ -61,14 +63,15 @@ class SwiftenPlugin : public NetworkPlugin { 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"); break; + 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"); break; - case Swift::ClientError::ConnectionWriteError: message = ("Error while sending data to the server"); break; - case Swift::ClientError::XMLError: message = ("Error parsing server data"); 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; @@ -95,7 +98,7 @@ class SwiftenPlugin : public NetworkPlugin { } } LOG4CXX_INFO(logger, user << ": Disconnected " << message); - handleDisconnected(user, 3, message); + handleDisconnected(user, reconnect ? 0 : 3, message); boost::shared_ptr client = m_users[user]; if (client) { @@ -106,8 +109,10 @@ class SwiftenPlugin : public NetworkPlugin { } #ifndef WIN32 +#ifndef __FreeBSD__ // force returning of memory chunks allocated by libxml2 to kernel malloc_trim(0); +#endif #endif } diff --git a/backends/twitter/TwitterPlugin.cpp b/backends/twitter/TwitterPlugin.cpp index e6a0d52eac3fdaaecec7a2e4fdba6cce6efab146..ef84bbf3f2017c3a00f111bbed5c36c85ca63451 100644 --- a/backends/twitter/TwitterPlugin.cpp +++ b/backends/twitter/TwitterPlugin.cpp @@ -322,7 +322,7 @@ void TwitterPlugin::pollForTweets() std::set::iterator it = onlineUsers.begin(); while(it != onlineUsers.end()) { std::string user = *it; - tp->runAsThread(new TimelineRequest(userdb[user].sessions, user, "", userdb[user].mostRecentTweetID, + tp->runAsThread(new TimelineRequest(userdb[user].sessions, user, "", getMostRecentTweetIDUnsafe(user), boost::bind(&TwitterPlugin::displayTweets, this, _1, _2, _3, _4))); it++; } @@ -517,16 +517,41 @@ void TwitterPlugin::updateLastTweetID(const std::string user, const std::string { 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::getMostRecentTweetID(const std::string user) -{ - boost::mutex::scoped_lock lock(userlock); - std::string ID = "-1"; - if(onlineUsers.count(user)) ID = userdb[user].mostRecentTweetID; +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); @@ -622,16 +647,17 @@ void TwitterPlugin::displayTweets(std::string &user, std::string &userRequested, std::map lastTweet; std::map::iterator it; - for(int i=0 ; i= 0 ; i--) { if(userdb[user].twitterMode != CHATROOM) { - timeline += " - " + tweets[i].getUserData().getScreenName() + ": " + tweets[i].getTweet() + " (MsgId: " + tweets[i].getID() + ")\n"; + std::string m = " - " + tweets[i].getUserData().getScreenName() + ": " + tweets[i].getTweet() + " (MsgId: " + tweets[i].getID() + ")\n"; + handleMessage(user, adminLegacyName, m, "", "", tweets[i].getCreationTime()); 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].getID() + ")", tweets[i].getUserData().getScreenName()); + tweets[i].getTweet() + " (MsgId: " + tweets[i].getID() + ")", tweets[i].getUserData().getScreenName(), "", tweets[i].getCreationTime()); } } diff --git a/backends/twitter/TwitterPlugin.h b/backends/twitter/TwitterPlugin.h index b49ea86670e475ee038e228c41d56c903807374f..ab4aca3b689c9f6e5edea67817b9a68522c0fdd5 100644 --- a/backends/twitter/TwitterPlugin.h +++ b/backends/twitter/TwitterPlugin.h @@ -134,6 +134,8 @@ class TwitterPlugin : public NetworkPlugin { /***********************************************************************************/ private: + std::string getMostRecentTweetIDUnsafe(const std::string user); + enum status {NEW, WAITING_FOR_PIN, CONNECTED, DISCONNECTED}; enum mode {SINGLECONTACT, MULTIPLECONTACT, CHATROOM}; diff --git a/backends/twitter/TwitterResponseParser.cpp b/backends/twitter/TwitterResponseParser.cpp index 4ccc5e8507763bb88578dd6ba32a09fff7898d99..b908b770e445ea074eac569159a94729af28d3e9 100644 --- a/backends/twitter/TwitterResponseParser.cpp +++ b/backends/twitter/TwitterResponseParser.cpp @@ -1,5 +1,6 @@ #include "TwitterResponseParser.h" #include "transport/logging.h" +#include "boost/algorithm/string.hpp" #include DEFINE_LOGGER(logger, "TwitterResponseParser") @@ -11,6 +12,25 @@ static std::string tolowercase(std::string inp) return out; } +static std::string unescape(std::string data) { + using boost::algorithm::replace_all; + replace_all(data, "&", "&"); + replace_all(data, """, "\""); + replace_all(data, "'", "\'"); + replace_all(data, "<", "<"); + replace_all(data, ">", ">"); + return data; +} + +static std::string toIsoTime(std::string in) { + time_t now = time(0); + struct tm *mtime = gmtime(&now); + strptime(in.c_str(), "%a %b %d %H:%M:%S %z %Y", mtime); + char buf[80]; + strftime(buf, sizeof(buf), "%Y%m%dT%H%M%S", mtime); + return buf; +} + EmbeddedStatus getEmbeddedStatus(const Swift::ParserElement::ref &element, const std::string xmlns) { EmbeddedStatus status; @@ -19,9 +39,10 @@ EmbeddedStatus getEmbeddedStatus(const Swift::ParserElement::ref &element, const return status; } - status.setCreationTime( std::string( element->getChild(TwitterReponseTypes::created_at, xmlns)->getText() ) ); + status.setCreationTime( toIsoTime(std::string( element->getChild(TwitterReponseTypes::created_at, xmlns)->getText() ) ) ); + status.setID( std::string( element->getChild(TwitterReponseTypes::id, xmlns)->getText() ) ); - status.setTweet( std::string( element->getChild(TwitterReponseTypes::text, xmlns)->getText() ) ); + status.setTweet( unescape (std::string( element->getChild(TwitterReponseTypes::text, xmlns)->getText() ) ) ); status.setTruncated( std::string( element->getChild(TwitterReponseTypes::truncated, xmlns)->getText() )=="true" ); status.setReplyToStatusID( std::string( element->getChild(TwitterReponseTypes::in_reply_to_status_id, xmlns)->getText() ) ); status.setReplyToUserID( std::string( element->getChild(TwitterReponseTypes::in_reply_to_user_id, xmlns)->getText() ) ); @@ -60,9 +81,9 @@ Status getStatus(const Swift::ParserElement::ref &element, const std::string xml return status; } - status.setCreationTime( std::string( element->getChild(TwitterReponseTypes::created_at, xmlns)->getText() ) ); + status.setCreationTime( toIsoTime ( std::string( element->getChild(TwitterReponseTypes::created_at, xmlns)->getText() ) ) ); status.setID( std::string( element->getChild(TwitterReponseTypes::id, xmlns)->getText() ) ); - status.setTweet( std::string( element->getChild(TwitterReponseTypes::text, xmlns)->getText() ) ); + status.setTweet( unescape ( std::string( element->getChild(TwitterReponseTypes::text, xmlns)->getText() ) ) ); status.setTruncated( std::string( element->getChild(TwitterReponseTypes::truncated, xmlns)->getText() )=="true" ); status.setReplyToStatusID( std::string( element->getChild(TwitterReponseTypes::in_reply_to_status_id, xmlns)->getText() ) ); status.setReplyToUserID( std::string( element->getChild(TwitterReponseTypes::in_reply_to_user_id, xmlns)->getText() ) ); @@ -82,9 +103,9 @@ DirectMessage getDirectMessage(const Swift::ParserElement::ref &element, const s return DM; } - DM.setCreationTime( std::string( element->getChild(TwitterReponseTypes::created_at, xmlns)->getText() ) ); + DM.setCreationTime( toIsoTime ( std::string( element->getChild(TwitterReponseTypes::created_at, xmlns)->getText() ) ) ); DM.setID( std::string( element->getChild(TwitterReponseTypes::id, xmlns)->getText() ) ); - DM.setMessage( std::string( element->getChild(TwitterReponseTypes::text, xmlns)->getText() ) ); + DM.setMessage( unescape ( std::string( element->getChild(TwitterReponseTypes::text, xmlns)->getText() ) ) ); DM.setSenderID( std::string( element->getChild(TwitterReponseTypes::sender_id, xmlns)->getText() ) ); DM.setRecipientID( std::string( element->getChild(TwitterReponseTypes::recipient_id, xmlns)->getText() ) ); DM.setSenderScreenName( std::string( element->getChild(TwitterReponseTypes::sender_screen_name, xmlns)->getText() ) ); diff --git a/backends/twitter/main.cpp b/backends/twitter/main.cpp index b3404d82a161510ff2c7be5666ee2bcc9df29a96..bb28d601e3703ee830625d4ce8f43794f36b0061 100644 --- a/backends/twitter/main.cpp +++ b/backends/twitter/main.cpp @@ -27,61 +27,21 @@ int main (int argc, char* argv[]) { return -1; } - std::string configFile; - boost::program_options::variables_map vm; - boost::program_options::options_description desc("Usage: spectrum \nAllowed options"); - desc.add_options() - ("help", "help") - ("host,h", boost::program_options::value(&host)->default_value(""), "Host to connect to") - ("port,p", boost::program_options::value(&port)->default_value(10000), "Port to connect to") - ("config", boost::program_options::value(&configFile)->default_value(""), "Config file") - ; - - try - { - boost::program_options::positional_options_description p; - p.add("config", -1); - boost::program_options::store(boost::program_options::command_line_parser(argc, argv). - options(desc).positional(p).allow_unregistered().run(), vm); - boost::program_options::notify(vm); - - if(vm.count("help")) - { - std::cout << desc << "\n"; - return 1; - } - - if(vm.count("config") == 0) { - std::cout << desc << "\n"; - return 1; - } - } - catch (std::runtime_error& e) - { - std::cout << desc << "\n"; - return 1; - } - catch (...) - { - std::cout << desc << "\n"; + std::string error; + Config *cfg = Config::createFromArgs(argc, argv, error, host, port); + if (cfg == NULL) { + std::cerr << error; return 1; } - Config config(argc, argv); - if (!config.load(configFile)) { - std::cerr << "Can't open " << argv[1] << " configuration file.\n"; - return 1; - } + Logging::initBackendLogging(cfg); - Logging::initBackendLogging(&config); - - std::string error; StorageBackend *storagebackend; - - storagebackend = StorageBackend::createBackend(&config, error); + storagebackend = StorageBackend::createBackend(cfg, error); if (storagebackend == NULL) { - LOG4CXX_ERROR(logger, "Error creating StorageBackend! " << error) - return -2; + LOG4CXX_ERROR(logger, "Error creating StorageBackend! " << error); + LOG4CXX_ERROR(logger, "Twitter backend needs storage backend configured to work! " << error); + return NetworkPlugin::StorageBackendNeeded; } else if (!storagebackend->connect()) { @@ -91,7 +51,7 @@ int main (int argc, char* argv[]) { Swift::SimpleEventLoop eventLoop; loop_ = &eventLoop; - np = new TwitterPlugin(&config, &eventLoop, storagebackend, host, port); + np = new TwitterPlugin(cfg, &eventLoop, storagebackend, host, port); loop_->run(); return 0; diff --git a/docs/guide/debian_ubuntu.textile b/docs/guide/debian_ubuntu.textile index eff14e2cee9d3c86978069ce423af21ae763140f..d034dec0ae2fff00836aee8bc5eae4a422881b26 100644 --- a/docs/guide/debian_ubuntu.textile +++ b/docs/guide/debian_ubuntu.textile @@ -33,7 +33,7 @@ h2. Install spectrum2 - development version After you have done that, simply do:
-apt-get install spectrum2-git spectrum2-backend-libpurple-git
+apt-get install spectrum2 spectrum2-backend-libpurple
 
Note that these repositories pull in quite a few dependencies, depending on the distribution you use. diff --git a/docs/guide/libcommuni.textile b/docs/guide/libcommuni.textile index 4a0b6b127d2dcefb4190ea57d8d7051ef98b3ab0..395aca5f05cd05703244d4209b675d776b7bc853 100644 --- a/docs/guide/libcommuni.textile +++ b/docs/guide/libcommuni.textile @@ -32,7 +32,7 @@ irc_server=irc.freenode.org h3. 2.2 One transport for more IRC networks -In this mode users can connect more IRC networks, but they can't connect the network without being in the room. Currently this mode is not finished yet in Spectrum 2. +In this mode users can connect more IRC networks, but they can't connect the network without being in the room. To connect the network, user has to join the room in following format: #room%irc.freenode.org@irc.domain.tld. The nickname used in the first join request is used as a nickname for the IRC connection. h2. 3. All configuration variables diff --git a/docs/guide/logging.textile b/docs/guide/logging.textile index fa1c964096617c8355eb1ed8e51472d9dc15b3fc..490528114bfc652080a315f5b2c38199ff5168cc 100644 --- a/docs/guide/logging.textile +++ b/docs/guide/logging.textile @@ -95,7 +95,7 @@ log4j.additivity.Component.XML=false # Create new RollingFileAppender logger and set the file name log4j.appender.XML=org.apache.log4j.RollingFileAppender -log4j.appender.XML.File=/var/log/spectrum2/${jid}/spectrum2.log +log4j.appender.XML.File=/var/log/spectrum2/${jid}/spectrum2_xml.log # Set MaxFileSize. Log will be rotated automatically when this limit is reached log4j.appender.XML.MaxFileSize=100000KB @@ -107,6 +107,25 @@ log4j.appender.XML.layout=org.apache.log4j.PatternLayout log4j.appender.XML.layout.ConversionPattern=%d %-5p %c: %m%n +h3. Disable XML logging + +
+# We create two rootLoggers:
+#   - "debug" is internal logger used by log4cxx
+#   - "stdout" is name of our ConsoleAppender logger
+log4j.rootLogger=debug, stdout
+
+# Create new ConsoleAppender logger with custom PatternLayout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+
+# Define the output pattern. Characters are mentioned here: http://logging.apache.org/log4cxx/apidocs/classlog4cxx_1_1_pattern_layout.html
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d %-5p %c: %m%n
+
+# Disable XML category
+log4j.category.Component.XML = OFF
+
+ h3. Disable logging To disable logging, you still *must have* one logger created (probably the ConsoleAppender), but you can set log4j.threshold = OFF to not log everything later: diff --git a/include/transport/conversation.h b/include/transport/conversation.h index e137b5666b721fed90c30fbe0ec35c0d4887bf3e..ff109aa24bf2c2877c725e15f95c011907fe2eaa 100644 --- a/include/transport/conversation.h +++ b/include/transport/conversation.h @@ -148,6 +148,8 @@ class Conversation { Swift::JID m_jid; std::list m_jids; std::map m_participants; + boost::shared_ptr m_subject; + bool m_sentInitialPresence; }; } diff --git a/include/transport/conversationmanager.h b/include/transport/conversationmanager.h index 016914c848385cd26e6975b4ab1dd9d311b21681..865736b205ca11ffee46630cfd13a3df9325ab4e 100644 --- a/include/transport/conversationmanager.h +++ b/include/transport/conversationmanager.h @@ -69,6 +69,8 @@ class ConversationManager { /// \param conv Conversation. void removeConversation(Conversation *conv); + void deleteAllConversations(); + void resetResources(); void removeJID(const Swift::JID &jid); diff --git a/include/transport/discoitemsresponder.h b/include/transport/discoitemsresponder.h index 44bdc364ee98f6ab07c9068d88fccae65f274f16..9b17e8324af104c43e7fa7eaebacb021a8cedc92 100644 --- a/include/transport/discoitemsresponder.h +++ b/include/transport/discoitemsresponder.h @@ -28,21 +28,30 @@ namespace Transport { class Component; +class DiscoInfoResponder; class DiscoItemsResponder : public Swift::GetResponder { public: DiscoItemsResponder(Component *component); ~DiscoItemsResponder(); + Swift::CapsInfo &getBuddyCapsInfo(); + void addAdHocCommand(const std::string &node, const std::string &name); // void removeAdHocCommand(const std::string &node); + void addRoom(const std::string &node, const std::string &name); + void clearRooms(); + private: virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload); + private: Component *m_component; boost::shared_ptr m_commands; + boost::shared_ptr m_rooms; + DiscoInfoResponder *m_discoInfoResponder; }; } \ No newline at end of file diff --git a/include/transport/factory.h b/include/transport/factory.h index 85b7f37a11d010098fd7bd512619eb25cc4ae37e..0c4595d734d29ac6a2e86771ff50fb30711bd410 100644 --- a/include/transport/factory.h +++ b/include/transport/factory.h @@ -40,7 +40,7 @@ class RosterManager; class Factory { public: - virtual Conversation *createConversation(ConversationManager *conversationManager, const std::string &legacyName) = 0; + virtual Conversation *createConversation(ConversationManager *conversationManager, const std::string &legacyName, bool isMuc = false) = 0; virtual Buddy *createBuddy(RosterManager *rosterManager, const BuddyInfo &buddyInfo) = 0; }; diff --git a/include/transport/networkplugin.h b/include/transport/networkplugin.h index ea59422541bfa2f968cce3820bbde1709c3aee1d..a5e123f79b733f018c8f4a6d3fe4e2d59904d185 100644 --- a/include/transport/networkplugin.h +++ b/include/transport/networkplugin.h @@ -24,6 +24,7 @@ #include "transport/protocol.pb.h" // #include "conversation.h" #include +#include namespace Transport { @@ -34,6 +35,7 @@ namespace Transport { /// development. class NetworkPlugin { public: + enum ExitCode { StorageBackendNeeded = -2 }; class PluginConfig { public: @@ -109,7 +111,7 @@ class NetworkPlugin { /// \param message Plain text message. /// \param nickname Nickname of buddy in room. Empty if it's normal chat message. /// \param xhtml XHTML message. - void handleMessage(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &nickname = "", const std::string &xhtml = ""); + void handleMessage(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &nickname = "", const std::string &xhtml = "", const std::string ×tamp = ""); /// Call this function when subject in room changed. /// \param user XMPP JID of user for which this event occurs. You can get it from NetworkPlugin::handleLoginRequest(). (eg. "user%gmail.com@xmpp.domain.tld") @@ -164,6 +166,8 @@ class NetworkPlugin { void handleFTData(unsigned long ftID, const std::string &data); + void handleRoomList(const std::string &user, const std::list &rooms, const std::list &names); + /// Called when XMPP user wants to connect legacy network. /// You should connect him to legacy network and call handleConnected or handleDisconnected function later. /// \param user XMPP JID of user for which this event occurs. diff --git a/include/transport/networkpluginserver.h b/include/transport/networkpluginserver.h index 62fc5b08a7865a21f82fbed8e267994e9d6339e2..b0d742b817cfec243b9029b738a9a707e9982638 100644 --- a/include/transport/networkpluginserver.h +++ b/include/transport/networkpluginserver.h @@ -43,6 +43,7 @@ class RosterResponder; class BlockResponder; class DummyReadBytestream; class AdminInterface; +class DiscoItemsResponder; class NetworkPluginServer { public: @@ -60,10 +61,12 @@ class NetworkPluginServer { std::string id; }; - NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager); + NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager, DiscoItemsResponder *discoItemsResponder); virtual ~NetworkPluginServer(); + void start(); + void setAdminInterface(AdminInterface *adminInterface) { m_adminInterface = adminInterface; } @@ -86,7 +89,7 @@ class NetworkPluginServer { void handleMessageReceived(NetworkConversation *conv, boost::shared_ptr &message); - private: + public: void handleNewClientConnection(boost::shared_ptr c); void handleSessionFinished(Backend *c); void handlePongReceived(Backend *c); @@ -109,6 +112,7 @@ class NetworkPluginServer { 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 handleUserCreated(User *user); void handleRoomJoined(User *user, const Swift::JID &who, const std::string &room, const std::string &nickname, const std::string &password); @@ -131,6 +135,7 @@ class NetworkPluginServer { void handleFTRejected(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size); void handleFTDataNeeded(Backend *b, unsigned long ftid); + private: void send(boost::shared_ptr &, const std::string &data); void pingTimeout(); @@ -154,6 +159,7 @@ class NetworkPluginServer { std::vector m_crashedBackends; AdminInterface *m_adminInterface; bool m_startingBackend; + DiscoItemsResponder *m_discoItemsResponder; }; } diff --git a/include/transport/protocol.proto b/include/transport/protocol.proto index 929c02b79056edac6765ec74f2d5d78975c4b25a..4475b70764d336804eac13580ef9918afe359ac1 100644 --- a/include/transport/protocol.proto +++ b/include/transport/protocol.proto @@ -69,6 +69,7 @@ message ConversationMessage { required string message = 3; optional string nickname = 4; optional string xhtml = 5; + optional string timestamp = 6; } message Room { @@ -78,6 +79,11 @@ message Room { optional string password = 4; } +message RoomList { + repeated string room = 1; + repeated string name = 2; +} + message Participant { required string userName = 1; required string room = 2; @@ -159,6 +165,7 @@ message WrapperMessage { TYPE_EXIT = 29; TYPE_BACKEND_CONFIG = 30; TYPE_QUERY = 31; + TYPE_ROOM_LIST = 32; } required Type type = 1; optional bytes payload = 2; diff --git a/include/transport/storagebackend.h b/include/transport/storagebackend.h index 5289010447dea048ab47ae4fb6b00abc1f934bc6..a5d101be206572146936bdf8415b4a9690e1b76a 100644 --- a/include/transport/storagebackend.h +++ b/include/transport/storagebackend.h @@ -86,6 +86,14 @@ class Config; class StorageBackend { public: + static std::string encryptPassword(const std::string &password, const std::string &key); + + static std::string decryptPassword(std::string &encrypted, const std::string &key); + + static std::string serializeGroups(const std::vector &groups); + + static std::vector deserializeGroups(std::string &groups); + /// Virtual desctructor. virtual ~StorageBackend() {} diff --git a/include/transport/transport.h b/include/transport/transport.h index e6f0610d3dd0fc7660fe7db00009efcd8b8bb2a7..c69ca39697c2599b2e1e2b5f6983c3d61c47de08 100644 --- a/include/transport/transport.h +++ b/include/transport/transport.h @@ -47,7 +47,6 @@ namespace Transport { // } SpectrumImportantFeatures; // class StorageBackend; - class DiscoInfoResponder; class Factory; class UserRegistry; @@ -81,8 +80,6 @@ namespace Transport { /// \return Swift::StanzaChannel associated with this Transport::Component. Swift::StanzaChannel *getStanzaChannel(); - Swift::CapsInfo &getBuddyCapsInfo(); - /// Returns Swift::IQRouter associated with this Component. /// \return Swift::IQRouter associated with this Component. @@ -104,18 +101,6 @@ namespace Transport { void start(); void stop(); - /// Sets disco#info features which are sent as answer to disco#info IQ-get. - - /// This sets features of transport contact (For example "j2j.domain.tld"). - /// \param features list of features as sent in disco#info response - void setTransportFeatures(std::list &features); - - /// Sets disco#info features which are sent as answer to disco#info IQ-get. - - /// This sets features of legacy network buddies (For example "me\40gmail.com@j2j.domain.tld"). - /// \param features list of features as sent in disco#info response - void setBuddyFeatures(std::list &features); - /// Returns Jabber ID of this transport. /// \return Jabber ID of this transport @@ -186,7 +171,6 @@ namespace Transport { Transport::UserRegistry *m_userRegistry; StorageBackend *m_storageBackend; - DiscoInfoResponder *m_discoInfoResponder; int m_reconnectCount; Config* m_config; std::string m_protocol; diff --git a/include/transport/user.h b/include/transport/user.h index 11c253b046c33c60b2e06a5f996eb782aab34212..1719b13db155e5f2552ae935b23be15a6bc30784 100644 --- a/include/transport/user.h +++ b/include/transport/user.h @@ -71,12 +71,14 @@ class User : public Swift::EntityCapsProvider { Component *getComponent() { return m_component; } + UserManager *getUserManager() { return m_userManager; } + void setData(void *data) { m_data = data; } void *getData() { return m_data; } /// Handles presence from XMPP JID associated with this user. /// \param presence Swift::Presence. - void handlePresence(Swift::Presence::ref presence); + void handlePresence(Swift::Presence::ref presence, bool forceJoin = false); void handleSubscription(Swift::Presence::ref presence); @@ -142,6 +144,7 @@ class User : public Swift::EntityCapsProvider { std::vector > m_filetransfers; int m_resources; int m_reconnectCounter; + std::list m_joinedRooms; }; } diff --git a/include/transport/usermanager.h b/include/transport/usermanager.h index 2601be2173c15ae9276cdd093eb5b1dc921e75d7..38a5c7bcb0cdab778de40ea4821111a4af71ec75 100644 --- a/include/transport/usermanager.h +++ b/include/transport/usermanager.h @@ -32,6 +32,7 @@ class Component; class StorageBackend; class StorageResponder; class RosterResponder; +class DiscoItemsResponder; /// Manages online XMPP Users. @@ -55,7 +56,7 @@ class UserManager : public Swift::EntityCapsProvider { /// Creates new UserManager. /// \param component Component which's presence will be handled /// \param storageBackend Storage backend used to fetch UserInfos - UserManager(Component *component, UserRegistry *userRegistry, StorageBackend *storageBackend = NULL); + UserManager(Component *component, UserRegistry *userRegistry, DiscoItemsResponder *discoItemsResponder, StorageBackend *storageBackend = NULL); /// Destroys UserManager. ~UserManager(); @@ -84,6 +85,8 @@ class UserManager : public Swift::EntityCapsProvider { Swift::DiscoInfo::ref getCaps(const Swift::JID&) const; + DiscoItemsResponder *getDiscoResponder() { return m_discoItemsResponder; } + /// Called when new User class is created. /// \param user newly created User class boost::signal onUserCreated; @@ -128,6 +131,7 @@ class UserManager : public Swift::EntityCapsProvider { void handleMessageReceived(Swift::Message::ref message); void handleGeneralPresenceReceived(Swift::Presence::ref presence); void handleProbePresence(Swift::Presence::ref presence); + void handleErrorPresence(Swift::Presence::ref presence); void handleSubscription(Swift::Presence::ref presence); void handleRemoveTimeout(const std::string jid, User *user, bool reconnect); void handleDiscoInfo(const Swift::JID& jid, boost::shared_ptr info); @@ -143,6 +147,7 @@ class UserManager : public Swift::EntityCapsProvider { Swift::Timer::ref m_removeTimer; unsigned long m_sentToXMPP; unsigned long m_sentToBackend; + DiscoItemsResponder *m_discoItemsResponder; friend class RosterResponder; }; diff --git a/include/transport/util.h b/include/transport/util.h index 3301ad90d6e3178021509343c6dd98dbdf1e3e82..490724bf37620b5d4567ef4386e2b6ecef1cf43f 100644 --- a/include/transport/util.h +++ b/include/transport/util.h @@ -26,19 +26,16 @@ #include #include "Swiften/StringCodecs/Base64.h" +#include +#include "transport/config.h" + namespace Transport { namespace Util { -void removeEverythingOlderThan(const std::vector &dirs, time_t t); - -std::string encryptPassword(const std::string &password, const std::string &key); +void createDirectories(Transport::Config *config, const boost::filesystem::path& ph); -std::string decryptPassword(std::string &encrypted, const std::string &key); - -std::string serializeGroups(const std::vector &groups); - -std::vector deserializeGroups(std::string &groups); +void removeEverythingOlderThan(const std::vector &dirs, time_t t); int getRandomPort(const std::string &s); diff --git a/plugin/cpp/CMakeLists.txt b/plugin/cpp/CMakeLists.txt index d80b8f599d094d8d2c4614299dfe7eb45a10a44b..75ea2aa156b5523670fc8c59824471e30a13e58c 100644 --- a/plugin/cpp/CMakeLists.txt +++ b/plugin/cpp/CMakeLists.txt @@ -5,6 +5,7 @@ FILE(GLOB HEADERS ../include/transport/*.h) set(EXTRA_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/../../src/memoryusage.cpp) set(EXTRA_SOURCES ${EXTRA_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/../../src/logging.cpp) set(EXTRA_SOURCES ${EXTRA_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/../../src/config.cpp) +set(EXTRA_SOURCES ${EXTRA_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/../../src/util.cpp) set(EXTRA_SOURCES ${EXTRA_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/../../include/transport/protocol.pb.cc) if (NOT WIN32) diff --git a/plugin/cpp/networkplugin.cpp b/plugin/cpp/networkplugin.cpp index eba6dcffab6f5a23a305614f5f9e2d88643c1690..3dde9c53d4045e727e335c2e2ceb03d0ea7ae594 100644 --- a/plugin/cpp/networkplugin.cpp +++ b/plugin/cpp/networkplugin.cpp @@ -63,7 +63,7 @@ NetworkPlugin::~NetworkPlugin() { } void NetworkPlugin::sendConfig(const PluginConfig &cfg) { - std::string data = "[registration]"; + std::string data = "[registration]\n"; data += std::string("needPassword=") + (cfg.m_needPassword ? "1" : "0") + "\n"; data += std::string("needRegistration=") + (cfg.m_needRegistration ? "1" : "0") + "\n"; @@ -82,13 +82,14 @@ void NetworkPlugin::sendConfig(const PluginConfig &cfg) { send(message); } -void NetworkPlugin::handleMessage(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &nickname, const std::string &xhtml) { +void NetworkPlugin::handleMessage(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &nickname, const std::string &xhtml, const std::string ×tamp) { pbnetwork::ConversationMessage m; m.set_username(user); m.set_buddyname(legacyName); m.set_message(msg); m.set_nickname(nickname); m.set_xhtml(xhtml); + m.set_timestamp(timestamp); std::string message; m.SerializeToString(&message); @@ -336,6 +337,24 @@ void NetworkPlugin::handleFTData(unsigned long ftID, const std::string &data) { send(message); } +void NetworkPlugin::handleRoomList(const std::string &user, const std::list &rooms, const std::list &names) { + pbnetwork::RoomList d; + for (std::list::const_iterator it = rooms.begin(); it != rooms.end(); it++) { + d.add_room(*it); + } + + for (std::list::const_iterator it = names.begin(); it != names.end(); it++) { + d.add_name(*it); + } + + std::string message; + d.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_ROOM_LIST); + + send(message); +} + void NetworkPlugin::handleLoginPayload(const std::string &data) { pbnetwork::Login payload; if (payload.ParseFromString(data) == false) { diff --git a/spectrum/src/CMakeLists.txt b/spectrum/src/CMakeLists.txt index 2dd5b0da4293013aafc654631bc34e75c37ecf64..fb38f5f085255a3146169488fdeed4da74a8f075 100644 --- a/spectrum/src/CMakeLists.txt +++ b/spectrum/src/CMakeLists.txt @@ -1,13 +1,13 @@ cmake_minimum_required(VERSION 2.6) FILE(GLOB SRC *.cpp) -# if (WIN32) -# FILE(GLOB WIN_SRC win32/*.cpp) -# include_directories(win32) -# ADD_EXECUTABLE(spectrum2 ${SRC} ${WIN_SRC}) -# else() +if (WIN32) +FILE(GLOB WIN_SRC win32/*.cpp) +include_directories(win32) +ADD_EXECUTABLE(spectrum2 ${SRC} ${WIN_SRC}) +else() ADD_EXECUTABLE(spectrum2 ${SRC}) -# endif() +endif() diff --git a/spectrum/src/main.cpp b/spectrum/src/main.cpp index 75169b1a548d76cb3746196d14bc9b453d434460..21f6a1c44626006eaea158207ac9aeda34f9abc2 100644 --- a/spectrum/src/main.cpp +++ b/spectrum/src/main.cpp @@ -22,15 +22,18 @@ #include #ifndef WIN32 #include "sys/signal.h" +#include "sys/stat.h" #include #include #include #include "libgen.h" +#ifndef __FreeBSD__ #include +#endif #else #include #define getpid _getpid -// #include "win32/SpectrumService.h" +#include "win32/ServiceWrapper.h" #endif #include @@ -42,6 +45,13 @@ DEFINE_LOGGER(logger, "Spectrum"); Swift::SimpleEventLoop *eventLoop_ = NULL; Component *component_ = NULL; UserManager *userManager_ = NULL; +Config *config_ = NULL; + +void stop() { + userManager_->removeAllUsers(false); + component_->stop(); + eventLoop_->stop(); +} static void stop_spectrum() { userManager_->removeAllUsers(false); @@ -119,20 +129,157 @@ static void daemonize(const char *cwd, const char *lock_file) { #endif +int mainloop() { + +#ifndef WIN32 + mode_t old_cmask = umask(0007); +#endif + + Logging::initMainLogging(config_); + +#ifndef WIN32 + if (!CONFIG_STRING(config_, "service.group").empty() ||!CONFIG_STRING(config_, "service.user").empty() ) { + struct rlimit limit; + getrlimit(RLIMIT_CORE, &limit); + + if (!CONFIG_STRING(config_, "service.group").empty()) { + struct group *gr; + if ((gr = getgrnam(CONFIG_STRING(config_, "service.group").c_str())) == NULL) { + std::cerr << "Invalid service.group name " << CONFIG_STRING(config_, "service.group") << "\n"; + return 1; + } + + if (((setgid(gr->gr_gid)) != 0) || (initgroups(CONFIG_STRING(config_, "service.user").c_str(), gr->gr_gid) != 0)) { + std::cerr << "Failed to set service.group name " << CONFIG_STRING(config_, "service.group") << " - " << gr->gr_gid << ":" << strerror(errno) << "\n"; + return 1; + } + } + + if (!CONFIG_STRING(config_, "service.user").empty()) { + struct passwd *pw; + if ((pw = getpwnam(CONFIG_STRING(config_, "service.user").c_str())) == NULL) { + std::cerr << "Invalid service.user name " << CONFIG_STRING(config_, "service.user") << "\n"; + return 1; + } + + if ((setuid(pw->pw_uid)) != 0) { + std::cerr << "Failed to set service.user name " << CONFIG_STRING(config_, "service.user") << " - " << pw->pw_uid << ":" << strerror(errno) << "\n"; + return 1; + } + } + setrlimit(RLIMIT_CORE, &limit); + } + + struct rlimit limit; + limit.rlim_max = RLIM_INFINITY; + limit.rlim_cur = RLIM_INFINITY; + setrlimit(RLIMIT_CORE, &limit); +#endif + + Swift::SimpleEventLoop eventLoop; + + Swift::BoostNetworkFactories *factories = new Swift::BoostNetworkFactories(&eventLoop); + UserRegistry userRegistry(config_, factories); + + Component transport(&eventLoop, factories, config_, NULL, &userRegistry); + component_ = &transport; +// Logger logger(&transport); + + std::string error; + StorageBackend *storageBackend = StorageBackend::createBackend(config_, error); + if (storageBackend == NULL) { + if (!error.empty()) { + std::cerr << error << "\n"; + return -2; + } + } + else if (!storageBackend->connect()) { + std::cerr << "Can't connect to database. Check the log to find out the reason.\n"; + return -1; + } + + Logging::redirect_stderr(); + + DiscoItemsResponder discoItemsResponder(&transport); + discoItemsResponder.start(); + + UserManager userManager(&transport, &userRegistry, &discoItemsResponder, storageBackend); + userManager_ = &userManager; + + UserRegistration *userRegistration = NULL; + UsersReconnecter *usersReconnecter = NULL; + if (storageBackend) { + userRegistration = new UserRegistration(&transport, &userManager, storageBackend); + userRegistration->start(); + + usersReconnecter = new UsersReconnecter(&transport, storageBackend); + } + + FileTransferManager ftManager(&transport, &userManager); + + NetworkPluginServer plugin(&transport, config_, &userManager, &ftManager, &discoItemsResponder); + plugin.start(); + + AdminInterface adminInterface(&transport, &userManager, &plugin, storageBackend, userRegistration); + plugin.setAdminInterface(&adminInterface); + + StatsResponder statsResponder(&transport, &userManager, &plugin, storageBackend); + statsResponder.start(); + + GatewayResponder gatewayResponder(transport.getIQRouter(), &userManager); + gatewayResponder.start(); + + AdHocManager adhocmanager(&transport, &discoItemsResponder, &userManager, storageBackend); + adhocmanager.start(); + + SettingsAdHocCommandFactory settings; + adhocmanager.addAdHocCommand(&settings); + + eventLoop_ = &eventLoop; + + eventLoop.run(); + +#ifndef WIN32 + umask(old_cmask); +#endif + + if (userRegistration) { + userRegistration->stop(); + delete userRegistration; + } + + if (usersReconnecter) { + delete usersReconnecter; + } + + delete storageBackend; + delete factories; + return 0; +} + + int main(int argc, char **argv) { Config config(argc, argv); - + config_ = &config; boost::program_options::variables_map vm; bool no_daemon = false; std::string config_file; std::string jid; - +#ifdef WIN32 + std::string install_service_name, uninstall_service_name, run_service_name; + // determine the name of the currently executing file + char szFilePath[MAX_PATH]; + GetModuleFileNameA(NULL, szFilePath, sizeof(szFilePath)); + std::string exe_file(szFilePath); +#endif setlocale(LC_ALL, ""); #ifndef WIN32 +#ifndef __FreeBSD__ mallopt(M_CHECK_ACTION, 2); mallopt(M_PERTURB, 0xb); #endif +#endif #ifndef WIN32 if (signal(SIGINT, spectrum_sigint_handler) == SIG_ERR) { @@ -155,9 +302,10 @@ int main(int argc, char **argv) ("version,v", "Shows Spectrum version") ; #ifdef WIN32 -// desc.add_options() -// ("install-service,i", "Install spectrum as Windows service") -// ("uninstall-service,u", "Uninstall Windows service"); + desc.add_options() + ("install-service,i", boost::program_options::value(&install_service_name)->default_value(""), "Install spectrum as Windows service") + ("uninstall-service,u", boost::program_options::value(&uninstall_service_name)->default_value(""), "Uninstall Windows service") + ("run-as-service,r", boost::program_options::value(&run_service_name)->default_value(""), "stub for Windows Service Manager"); #endif try { @@ -186,46 +334,41 @@ int main(int argc, char **argv) if(vm.count("no-daemonize")) { no_daemon = true; } -#ifdef WIN32 -#if 0 - if (vm.count("install-service")) { - SpectrumService ntservice; +#ifdef WIN32 + if (!install_service_name.empty()) { + // build command line for Service Manager + std::string service_path = exe_file + std::string(" --config ") + vm["config"].as() + + std::string(" --run-as-service ") + install_service_name; + + ServiceWrapper ntservice((char *)install_service_name.c_str()); if (!ntservice.IsInstalled()) { - // determine the name of the currently executing file - char szFilePath[MAX_PATH]; - GetModuleFileName(NULL, szFilePath, sizeof(szFilePath)); - std::string exe_file(szFilePath); - std::string config_file = exe_file.replace(exe_file.end() - 4, exe_file.end(), ".cfg"); - std::string service_path = std::string(szFilePath) + std::string(" --config ") + config_file; - - if (ntservice.Install(service_path.c_str())) { - std::cout << "Successfully installed" << std::endl; + if (ntservice.Install((char *)service_path.c_str())) { + std::cout << "Successfully installed " << install_service_name << std::endl; return 0; } else { std::cout << "Error installing service, are you an Administrator?" << std::endl; return 1; } } else { - std::cout << "Already installed" << std::endl; + std::cout << "Already installed " << install_service_name << std::endl; return 1; } } - if (vm.count("uninstall-service")) { - SpectrumService ntservice; + if (!uninstall_service_name.empty()) { + ServiceWrapper ntservice((char *)uninstall_service_name.c_str()); if (ntservice.IsInstalled()) { - if (ntservice.Remove()) { - std::cout << "Successfully removed" << std::endl; + if (ntservice.UnInstall()) { + std::cout << "Successfully removed " << uninstall_service_name << std::endl; return 0; } else { std::cout << "Error removing service, are you an Administrator?" << std::endl; return 1; } } else { - std::cout << "Service not installed" << std::endl; + std::cout << "Service not installed: " << uninstall_service_name << std::endl; return 1; } - } -#endif + } #endif } catch (std::runtime_error& e) @@ -246,7 +389,8 @@ int main(int argc, char **argv) // create directories try { - boost::filesystem::create_directories(CONFIG_STRING(&config, "service.working_dir")); + + Transport::Util::createDirectories(&config, CONFIG_STRING(&config, "service.working_dir")); } catch (...) { std::cerr << "Can't create service.working_dir directory " << CONFIG_STRING(&config, "service.working_dir") << ".\n"; @@ -280,20 +424,6 @@ int main(int argc, char **argv) #endif #ifndef WIN32 - if (!CONFIG_STRING(&config, "service.group").empty() ||!CONFIG_STRING(&config, "service.user").empty() ) { - struct group *gr; - if ((gr = getgrnam(CONFIG_STRING(&config, "service.group").c_str())) == NULL) { - std::cerr << "Invalid service.group name " << CONFIG_STRING(&config, "service.group") << "\n"; - return 1; - } - struct passwd *pw; - if ((pw = getpwnam(CONFIG_STRING(&config, "service.user").c_str())) == NULL) { - std::cerr << "Invalid service.user name " << CONFIG_STRING(&config, "service.user") << "\n"; - return 1; - } - chown(CONFIG_STRING(&config, "service.working_dir").c_str(), pw->pw_uid, gr->gr_gid); - } - char backendport[20]; FILE* port_file_f; port_file_f = fopen(CONFIG_STRING(&config, "service.portfile").c_str(), "w+"); @@ -314,120 +444,19 @@ int main(int argc, char **argv) // removeOldIcons(CONFIG_STRING(&config, "service.working_dir") + "/icons"); } #endif - - Logging::initMainLogging(&config); - -#ifndef WIN32 - if (!CONFIG_STRING(&config, "service.group").empty() ||!CONFIG_STRING(&config, "service.user").empty() ) { - struct rlimit limit; - getrlimit(RLIMIT_CORE, &limit); - - if (!CONFIG_STRING(&config, "service.group").empty()) { - struct group *gr; - if ((gr = getgrnam(CONFIG_STRING(&config, "service.group").c_str())) == NULL) { - std::cerr << "Invalid service.group name " << CONFIG_STRING(&config, "service.group") << "\n"; - return 1; - } - - if (((setgid(gr->gr_gid)) != 0) || (initgroups(CONFIG_STRING(&config, "service.user").c_str(), gr->gr_gid) != 0)) { - std::cerr << "Failed to set service.group name " << CONFIG_STRING(&config, "service.group") << " - " << gr->gr_gid << ":" << strerror(errno) << "\n"; - return 1; - } - } - - if (!CONFIG_STRING(&config, "service.user").empty()) { - struct passwd *pw; - if ((pw = getpwnam(CONFIG_STRING(&config, "service.user").c_str())) == NULL) { - std::cerr << "Invalid service.user name " << CONFIG_STRING(&config, "service.user") << "\n"; - return 1; - } - - if ((setuid(pw->pw_uid)) != 0) { - std::cerr << "Failed to set service.user name " << CONFIG_STRING(&config, "service.user") << " - " << pw->pw_uid << ":" << strerror(errno) << "\n"; - return 1; - } +#ifdef WIN32 + if (!run_service_name.empty()) { + ServiceWrapper ntservice((char *)run_service_name.c_str()); + if (ntservice.IsInstalled()) { + ntservice.RunService(); + } else { + std::cerr << "Service not installed: " << run_service_name << std::endl; + return 1; } - setrlimit(RLIMIT_CORE, &limit); + } else { + mainloop(); } - - struct rlimit limit; - limit.rlim_max = RLIM_INFINITY; - limit.rlim_cur = RLIM_INFINITY; - setrlimit(RLIMIT_CORE, &limit); +#else + mainloop(); #endif - - Swift::SimpleEventLoop eventLoop; - - Swift::BoostNetworkFactories *factories = new Swift::BoostNetworkFactories(&eventLoop); - UserRegistry userRegistry(&config, factories); - - Component transport(&eventLoop, factories, &config, NULL, &userRegistry); - component_ = &transport; -// Logger logger(&transport); - - std::string error; - StorageBackend *storageBackend = StorageBackend::createBackend(&config, error); - if (storageBackend == NULL) { - if (!error.empty()) { - std::cerr << error << "\n"; - return -2; - } - } - else if (!storageBackend->connect()) { - std::cerr << "Can't connect to database. Check the log to find out the reason.\n"; - return -1; - } - - Logging::redirect_stderr(); - - UserManager userManager(&transport, &userRegistry, storageBackend); - userManager_ = &userManager; - - UserRegistration *userRegistration = NULL; - UsersReconnecter *usersReconnecter = NULL; - if (storageBackend) { - userRegistration = new UserRegistration(&transport, &userManager, storageBackend); - userRegistration->start(); - - usersReconnecter = new UsersReconnecter(&transport, storageBackend); - } - - FileTransferManager ftManager(&transport, &userManager); - - NetworkPluginServer plugin(&transport, &config, &userManager, &ftManager); - - AdminInterface adminInterface(&transport, &userManager, &plugin, storageBackend, userRegistration); - plugin.setAdminInterface(&adminInterface); - - StatsResponder statsResponder(&transport, &userManager, &plugin, storageBackend); - statsResponder.start(); - - GatewayResponder gatewayResponder(transport.getIQRouter(), &userManager); - gatewayResponder.start(); - - DiscoItemsResponder discoItemsResponder(&transport); - discoItemsResponder.start(); - - AdHocManager adhocmanager(&transport, &discoItemsResponder, &userManager, storageBackend); - adhocmanager.start(); - - SettingsAdHocCommandFactory settings; - adhocmanager.addAdHocCommand(&settings); - - eventLoop_ = &eventLoop; - - eventLoop.run(); - - if (userRegistration) { - userRegistration->stop(); - delete userRegistration; - } - - if (usersReconnecter) { - delete usersReconnecter; - } - - delete storageBackend; - delete factories; - return 0; } diff --git a/spectrum/src/sample.cfg b/spectrum/src/sample.cfg index 389c4872977de6f81aeae41059168a7d5e996ce3..d795b60e69d1ffd07570e9354c1e586881670eb1 100644 --- a/spectrum/src/sample.cfg +++ b/spectrum/src/sample.cfg @@ -13,15 +13,16 @@ admin_password=test #cert=server.pfx #patch to PKCS#12 certificate #cert_password=test #password to that certificate if any users_per_backend=10 -backend=../..//backends/libpurple/spectrum2_libpurple_backend +backend=../..//backends/swiften/spectrum2_swiften_backend #backend=../../backends/twitter/spectrum2_twitter_backend -#backend=/home/hanzz/code/libtransport/backends/libpurple/spectrum2_communi_backend -protocol=prpl-icq +#backend=/home/hanzz/code/libtransport/backends/libcommuni/spectrum2_libcommuni_backend +protocol=prpl-jabber #protocol=prpl-msn #protocol=any #protocol=prpl-icq working_dir=./ portfile=$jid.port +irc_server=irc.freenode.org [backend] #default_avatar=catmelonhead.jpg diff --git a/spectrum/src/sample2.cfg b/spectrum/src/sample2.cfg index 3181a6a1ee29f4170d4f9d7182a983cf43350314..0da3b1ecbbc380ee5d940a93155993c0a79d379b 100644 --- a/spectrum/src/sample2.cfg +++ b/spectrum/src/sample2.cfg @@ -39,7 +39,7 @@ users_per_backend=10 # Full path to backend binary. backend=/usr/bin/spectrum2_libpurple_backend -#backend=/usr/bin/spectrum2_libircclient-qt_backend +#backend=/usr/bin/spectrum2_libcommuni_backend # For skype: #backend=/usr/bin/xvfb-run -n BACKEND_ID -s "-screen 0 10x10x8" -f /tmp/x-skype-gw /usr/bin/spectrum2_skype_backend diff --git a/spectrum/src/win32/ServiceWrapper.cpp b/spectrum/src/win32/ServiceWrapper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..adb988d2f3741c9533a77907e86ea3b154ac73ab --- /dev/null +++ b/spectrum/src/win32/ServiceWrapper.cpp @@ -0,0 +1,120 @@ +#include "ServiceWrapper.h" + + +LPSTR ServiceName; +SERVICE_STATUS ServiceStatus; +SERVICE_STATUS_HANDLE ServiceStatusHandle; + +bool doAction (int action, int desiredAccess, LPSTR path = NULL); + +enum actions { + DO_INSTALL, DO_DELETE, DO_CHECK +}; + + +ServiceWrapper::ServiceWrapper(LPSTR serviceName) +{ + ServiceName = serviceName; + ServiceStatusHandle = 0; +} + +ServiceWrapper::~ServiceWrapper(void) +{ +} + +bool ServiceWrapper::Install(LPSTR commandLine) { + return doAction(DO_INSTALL, SC_MANAGER_ALL_ACCESS, commandLine); +} + +bool ServiceWrapper::UnInstall() { + return doAction(DO_DELETE, SC_MANAGER_ALL_ACCESS); +} + +bool ServiceWrapper::IsInstalled() { + return doAction(DO_CHECK, SC_MANAGER_CONNECT); +} + +bool doAction(int action, int desiredAccess, LPSTR path) { + SC_HANDLE scm = OpenSCManager(NULL, NULL, desiredAccess); + SC_HANDLE service = NULL; + if (!scm) return FALSE; + + switch(action) { + case DO_INSTALL: + service = CreateServiceA( + scm, + ServiceName, + ServiceName, + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_DEMAND_START, + SERVICE_ERROR_NORMAL, + path, + NULL, + NULL, + NULL, + NULL, + NULL + ); + return (service != NULL); + break; + case DO_DELETE: + service = OpenServiceA(scm, ServiceName, DELETE); + if (service == NULL) + return FALSE; + if (DeleteService(service)) + return TRUE; + break; + case DO_CHECK: + service = OpenServiceA(scm, ServiceName, SERVICE_QUERY_STATUS); + return (service != NULL); + default: + return FALSE; + } + CloseServiceHandle(service); + CloseServiceHandle(scm); + return FALSE; +} + +void WINAPI ServiceControlHandler(DWORD controlCode) { + switch (controlCode) { + case SERVICE_CONTROL_INTERROGATE: + break; + case SERVICE_CONTROL_STOP: + ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; + break; + default: + break; + } + SetServiceStatus(ServiceStatusHandle, &ServiceStatus); + stop(); +} + +void WINAPI ServiceMain(DWORD argc, LPSTR *argv) { + ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + ServiceStatus.dwServiceSpecificExitCode = NO_ERROR; + + ServiceStatusHandle = RegisterServiceCtrlHandlerA(ServiceName, ServiceControlHandler); + if (ServiceStatusHandle) { + ServiceStatus.dwCurrentState = SERVICE_START_PENDING; + SetServiceStatus(ServiceStatusHandle, &ServiceStatus); + ServiceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); + ServiceStatus.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus(ServiceStatusHandle, &ServiceStatus); + mainloop(); + ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; + SetServiceStatus(ServiceStatusHandle, &ServiceStatus); + ServiceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); + ServiceStatus.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(ServiceStatusHandle, &ServiceStatus); + } +} + +void ServiceWrapper::RunService() { + SERVICE_TABLE_ENTRYA serviceTable[] = { + { ServiceName, ServiceMain }, + { NULL, NULL} + }; + + StartServiceCtrlDispatcherA(serviceTable); +} \ No newline at end of file diff --git a/spectrum/src/win32/ServiceWrapper.h b/spectrum/src/win32/ServiceWrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..9ee2fb0def5d8e0284934cda18387f6b832dad05 --- /dev/null +++ b/spectrum/src/win32/ServiceWrapper.h @@ -0,0 +1,19 @@ +#pragma once +#include "transport/config.h" +#include +#include + +class ServiceWrapper +{ +public: + ServiceWrapper(LPSTR serviceName); + ~ServiceWrapper(void); + bool Install(LPSTR commandLine); + bool UnInstall(); + bool IsInstalled(); + void RunService(); +}; + +int mainloop(); +void stop(); + diff --git a/spectrum/src/win32/SpectrumService.cpp b/spectrum/src/win32/SpectrumService.cpp deleted file mode 100644 index 1fbcacd3a9b759dd64f98d6425803c256fd87793..0000000000000000000000000000000000000000 --- a/spectrum/src/win32/SpectrumService.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "SpectrumService.h" - -SpectrumService::SpectrumService(void) { - serviceName = "Spectrum2"; - displayName = "Spectrum2 XMPP Transport"; - username = NULL; - password = NULL; -} - -SpectrumService::~SpectrumService(void) {} - -void SpectrumService::Stop() { - ReportStatus((DWORD)SERVICE_STOP_PENDING); -} - -void SpectrumService::Run(DWORD argc, LPTSTR *argv) { - ReportStatus((DWORD)SERVICE_RUNNING); - main(argc, argv); -} \ No newline at end of file diff --git a/spectrum/src/win32/SpectrumService.h b/spectrum/src/win32/SpectrumService.h deleted file mode 100644 index 404b105743e32996099756cef7d3a81230b8482d..0000000000000000000000000000000000000000 --- a/spectrum/src/win32/SpectrumService.h +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include "WindowsService.h" - -class SpectrumService : public WindowsService { - -public: - SpectrumService(void); - ~SpectrumService(void); -protected: - void Stop(); - void Run(DWORD argc, LPTSTR *argv); -}; - -int main(int argc, char **argv); \ No newline at end of file diff --git a/spectrum/src/win32/WindowsService.cpp b/spectrum/src/win32/WindowsService.cpp deleted file mode 100644 index 0a7493785b0fa1f479984a2cc14222bee08ff2de..0000000000000000000000000000000000000000 --- a/spectrum/src/win32/WindowsService.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* Copyright (C) 2005 MySQL AB - - 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; version 2 of the License. - - 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 St, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include -#include -#include ".\windowsservice.h" - -static WindowsService *gService; - -WindowsService::WindowsService(void) : - statusCheckpoint(0), - serviceName(NULL), - inited(false), - dwAcceptedControls(SERVICE_ACCEPT_STOP), - debugging(false) -{ - gService= this; - status.dwServiceType= SERVICE_WIN32_OWN_PROCESS; - status.dwServiceSpecificExitCode= 0; -} - -WindowsService::~WindowsService(void) -{ -} - -BOOL WindowsService::Install(const char *szFilePath) -{ - bool ret_val= false; - SC_HANDLE newService; - SC_HANDLE scm; - - if (IsInstalled()) return true; - - // open a connection to the SCM - if (!(scm= OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE))) - return false; - - newService= CreateService(scm, serviceName, displayName, - SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, - SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, - szFilePath, NULL, NULL, NULL, username, - password); - - if (newService) - { - CloseServiceHandle(newService); - ret_val= true; - } - - CloseServiceHandle(scm); - return ret_val; -} - -BOOL WindowsService::Init() -{ - assert(serviceName != NULL); - - if (inited) return true; - - SERVICE_TABLE_ENTRY stb[] = - { - { (LPSTR)serviceName, (LPSERVICE_MAIN_FUNCTION) ServiceMain}, - { NULL, NULL } - }; - inited= true; - return StartServiceCtrlDispatcher(stb); //register with the Service Manager -} - -BOOL WindowsService::Remove() -{ - bool ret_val= false; - - if (! IsInstalled()) - return true; - - // open a connection to the SCM - SC_HANDLE scm= OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE); - if (! scm) - return false; - - SC_HANDLE service= OpenService(scm, serviceName, DELETE); - if (service) - { - if (DeleteService(service)) - ret_val= true; - DWORD dw= ::GetLastError(); - CloseServiceHandle(service); - } - - CloseServiceHandle(scm); - return ret_val; -} - -BOOL WindowsService::IsInstalled() -{ - BOOL ret_val= FALSE; - - SC_HANDLE scm= ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); - SC_HANDLE serv_handle= ::OpenService(scm, serviceName, SERVICE_QUERY_STATUS); - - ret_val= serv_handle != NULL; - - ::CloseServiceHandle(serv_handle); - ::CloseServiceHandle(scm); - - return ret_val; -} - -void WindowsService::SetAcceptedControls(DWORD acceptedControls) -{ - dwAcceptedControls= acceptedControls; -} - - -BOOL WindowsService::ReportStatus(DWORD currentState, DWORD waitHint, - DWORD dwError) -{ - if(debugging) return TRUE; - - if(currentState == SERVICE_START_PENDING) - status.dwControlsAccepted= 0; - else - status.dwControlsAccepted= dwAcceptedControls; - - status.dwCurrentState= currentState; - status.dwWin32ExitCode= dwError != 0 ? - ERROR_SERVICE_SPECIFIC_ERROR : NO_ERROR; - status.dwWaitHint= waitHint; - status.dwServiceSpecificExitCode= dwError; - - if(currentState == SERVICE_RUNNING || currentState == SERVICE_STOPPED) - { - status.dwCheckPoint= 0; - statusCheckpoint= 0; - } - else - status.dwCheckPoint= ++statusCheckpoint; - - // Report the status of the service to the service control manager. - BOOL result= SetServiceStatus(statusHandle, &status); - if (!result) - Log("ReportStatus failed"); - - return result; -} - -void WindowsService::RegisterAndRun(DWORD argc, LPTSTR *argv) -{ - statusHandle= ::RegisterServiceCtrlHandler(serviceName, ControlHandler); - if (statusHandle && ReportStatus(SERVICE_START_PENDING)) - Run(argc, argv); - ReportStatus(SERVICE_STOPPED); -} - -void WindowsService::HandleControlCode(DWORD opcode) -{ - // Handle the requested control code. - switch(opcode) { - case SERVICE_CONTROL_STOP: - // Stop the service. - status.dwCurrentState= SERVICE_STOP_PENDING; - Stop(); - break; - - case SERVICE_CONTROL_PAUSE: - status.dwCurrentState= SERVICE_PAUSE_PENDING; - Pause(); - break; - - case SERVICE_CONTROL_CONTINUE: - status.dwCurrentState= SERVICE_CONTINUE_PENDING; - Continue(); - break; - - case SERVICE_CONTROL_SHUTDOWN: - Shutdown(); - break; - - case SERVICE_CONTROL_INTERROGATE: - ReportStatus(status.dwCurrentState); - break; - - default: - // invalid control code - break; - } -} - -void WINAPI WindowsService::ServiceMain(DWORD argc, LPTSTR *argv) -{ - assert(gService != NULL); - - // register our service control handler: - gService->RegisterAndRun(argc, argv); -} - -void WINAPI WindowsService::ControlHandler(DWORD opcode) -{ - assert(gService != NULL); - - return gService->HandleControlCode(opcode); -} diff --git a/spectrum/src/win32/WindowsService.h b/spectrum/src/win32/WindowsService.h deleted file mode 100644 index 55e91d8b2ae9cc03d6001f5b02a270177c6ec0c3..0000000000000000000000000000000000000000 --- a/spectrum/src/win32/WindowsService.h +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright (C) 2005 MySQL AB - - 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; version 2 of the License. - - 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 St, Fifth Floor, Boston, MA 02110-1301 USA */ - -#pragma once - -class WindowsService -{ -protected: - bool inited; - const char *serviceName; - const char *displayName; - const char *username; - const char *password; - SERVICE_STATUS_HANDLE statusHandle; - DWORD statusCheckpoint; - SERVICE_STATUS status; - DWORD dwAcceptedControls; - bool debugging; - -public: - WindowsService(void); - ~WindowsService(void); - - BOOL Install(const char *szFilePath); - BOOL Remove(); - BOOL Init(); - BOOL IsInstalled(); - void SetAcceptedControls(DWORD acceptedControls); - void Debug(bool debugFlag) { debugging= debugFlag; } - -public: - static void WINAPI ServiceMain(DWORD argc, LPTSTR *argv); - static void WINAPI ControlHandler(DWORD CtrlType); - -protected: - virtual void Run(DWORD argc, LPTSTR *argv)= 0; - virtual void Stop() {} - virtual void Shutdown() {} - virtual void Pause() {} - virtual void Continue() {} - virtual void Log(const char *msg) {} - - BOOL ReportStatus(DWORD currentStatus, DWORD waitHint= 3000, DWORD dwError=0); - void HandleControlCode(DWORD opcode); - void RegisterAndRun(DWORD argc, LPTSTR *argv); -}; diff --git a/spectrum_manager/src/main.cpp b/spectrum_manager/src/main.cpp index 636172a613a5f5c1d69619a2659b21f17b0f8cfa..94d2934551d02578c8480e0e8f1df7ce0d3c6e8f 100644 --- a/spectrum_manager/src/main.cpp +++ b/spectrum_manager/src/main.cpp @@ -139,7 +139,10 @@ int main(int argc, char **argv) } else if (command[0] == "server") { Server server(&config); - server.start(); + if (server.start() == false) { + std::cerr << "Can't set up server handler.\n"; + return 1; + } while (1) { sleep(10); } } else { diff --git a/spectrum_manager/src/mongoose.c b/spectrum_manager/src/mongoose.c index 976870d3a8671628440744e1fad3ddcf89912111..8d52417b0b4b30db737f5ec70cfafef4706672e0 100644 --- a/spectrum_manager/src/mongoose.c +++ b/spectrum_manager/src/mongoose.c @@ -18,6 +18,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include + #if defined(_WIN32) #define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005 #else diff --git a/spectrum_manager/src/server.cpp b/spectrum_manager/src/server.cpp index cc86e9277cbb21977dfd0d91c4e0f5bf96d8e756..510fb3a4650252ace5fbe05ee4332e5ee166c466 100644 --- a/spectrum_manager/src/server.cpp +++ b/spectrum_manager/src/server.cpp @@ -149,7 +149,9 @@ Server::Server(ManagerConfig *config) { } Server::~Server() { - mg_stop(ctx); + if (ctx) { + mg_stop(ctx); + } } @@ -446,7 +448,12 @@ void *Server::event_handler(enum mg_event event, struct mg_connection *conn) { // try to serve the request. processed = NULL; } - } else { + } + else if (event == MG_EVENT_LOG) { + // Called by Mongoose's cry() + std::cerr << "Mongoose error: " << request_info->log_message << "\n"; + } + else { processed = NULL; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6c8155647f09fde0f2b86b1fd4b937aed9187a03..27fbfae786892775799ff4ab8f2d0700ae255f2b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,6 +15,7 @@ if (CPPUNIT_FOUND) FILE(GLOB SRC_TEST tests/*.cpp) ADD_EXECUTABLE(libtransport_test ${SRC_TEST}) + set_target_properties(libtransport_test PROPERTIES COMPILE_DEFINITIONS LIBTRANSPORT_TEST=1) target_link_libraries(libtransport_test transport ${CPPUNIT_LIBRARY} ${Boost_LIBRARIES}) endif() diff --git a/src/buddy.cpp b/src/buddy.cpp index 3a32c5580720063ca9e7702f3fc3b2f1b43276c1..07d1a76cd38aae9aec3531ce63131253e37cfef9 100644 --- a/src/buddy.cpp +++ b/src/buddy.cpp @@ -23,6 +23,8 @@ #include "transport/user.h" #include "transport/transport.h" #include "transport/BlockPayload.h" +#include "transport/usermanager.h" +#include "transport/discoitemsresponder.h" namespace Transport { @@ -106,7 +108,7 @@ Swift::Presence::ref Buddy::generatePresenceStanza(int features, bool only_new) if (presence->getType() != Swift::Presence::Unavailable) { // caps - presence->addPayload(boost::shared_ptr(new Swift::CapsInfo(m_rosterManager->getUser()->getComponent()->getBuddyCapsInfo()))); + presence->addPayload(boost::shared_ptr(new Swift::CapsInfo(m_rosterManager->getUser()->getUserManager()->getDiscoResponder()->getBuddyCapsInfo()))); // if (features & 0/*TRANSPORT_FEATURE_AVATARS*/) { presence->addPayload(boost::shared_ptr(new Swift::VCardUpdate (getIconHash()))); diff --git a/src/config.cpp b/src/config.cpp index ce49f6d6ef808d03a3bb1137ca9db719874d4ebc..6bf3a4b2919e4ce8b6730e6a403effc4d79956cd 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -97,6 +97,10 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description ("service.more_resources", value()->default_value(false), "Allow more resources to be connected in server mode at the same time.") ("service.enable_privacy_lists", value()->default_value(true), "") ("service.enable_xhtml", value()->default_value(true), "") + ("service.max_room_list_size", value()->default_value(100), "") + ("service.jid_escaping", value()->default_value(true), "") + ("service.vip_only", value()->default_value(false), "") + ("service.vip_message", value()->default_value(""), "") ("vhosts.vhost", value >()->multitoken(), "") ("identity.name", value()->default_value("Spectrum 2 Transport"), "Name showed in service discovery.") ("identity.category", value()->default_value("gateway"), "Disco#info identity category. 'gateway' by default.") @@ -123,6 +127,7 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description ("database.port", value()->default_value(0), "Database port.") ("database.prefix", value()->default_value(""), "Prefix of tables in database") ("database.encryption_key", value()->default_value(""), "Encryption key.") + ("database.vip_statement", value()->default_value(""), "Encryption key.") ("logging.config", value()->default_value(""), "Path to log4cxx config file which is used for Spectrum 2 instance") ("logging.backend_config", value()->default_value(""), "Path to log4cxx config file which is used for backends") ("backend.default_avatar", value()->default_value(""), "Full path to default avatar") diff --git a/src/conversation.cpp b/src/conversation.cpp index f610546131b596b6d51b683c113b6f58cb5ea086..9cd8f7391a936c1ef87e9fe3e8cb86464317e71e 100644 --- a/src/conversation.cpp +++ b/src/conversation.cpp @@ -33,6 +33,7 @@ Conversation::Conversation(ConversationManager *conversationManager, const std:: // m_conversationManager->addConversation(this); m_muc = isMUC; m_jid = m_conversationManager->getUser()->getJID().toBare(); + m_sentInitialPresence = false; } Conversation::~Conversation() { @@ -91,7 +92,17 @@ void Conversation::handleMessage(boost::shared_ptr &message, con message->setFrom(buddy->getJID()); } else { - message->setFrom(Swift::JID(Swift::JID::getEscapedNode(m_legacyName), m_conversationManager->getComponent()->getJID().toBare())); + std::string name = m_legacyName; + if (CONFIG_BOOL_DEFAULTED(m_conversationManager->getComponent()->getConfig(), "service.jid_escaping", true)) { + name = Swift::JID::getEscapedNode(m_legacyName); + } + else { + if (name.find_last_of("@") != std::string::npos) { + name.replace(name.find_last_of("@"), 1, "%"); + } + } + + message->setFrom(Swift::JID(name, m_conversationManager->getComponent()->getJID().toBare(), "bot")); } } // PM message @@ -118,6 +129,11 @@ void Conversation::handleMessage(boost::shared_ptr &message, con BOOST_FOREACH(const Swift::JID &jid, m_jids) { message->setTo(jid); message->setFrom(Swift::JID(legacyName, m_conversationManager->getComponent()->getJID().toBare(), n)); + // Subject has to be sent after our own presence (the one with code 110) + if (!message->getSubject().empty() && m_sentInitialPresence == false) { + m_subject = message; + return; + } m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(message); } } @@ -159,6 +175,7 @@ Swift::Presence::ref Conversation::generatePresence(const std::string &nick, int Swift::MUCUserPayload::StatusCode c; c.code = 110; p->addStatusCode(c); + m_sentInitialPresence = true; } @@ -207,6 +224,11 @@ void Conversation::handleParticipantChanged(const std::string &nick, int flag, i if (!newname.empty()) { handleParticipantChanged(newname, flag, status, statusMessage); } + + if (m_sentInitialPresence && m_subject) { + m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(m_subject); + m_subject.reset(); + } } } diff --git a/src/conversationmanager.cpp b/src/conversationmanager.cpp index 4fc23afc4a8b534612ec4f90fe4e89a4aaf6fe41..066a4603bb18a097af8469dc8e7d0d3dc4581c73 100644 --- a/src/conversationmanager.cpp +++ b/src/conversationmanager.cpp @@ -47,6 +47,15 @@ ConversationManager::~ConversationManager() { } } +void ConversationManager::deleteAllConversations() { + while(!m_convs.empty()) { + LOG4CXX_INFO(logger, m_user->getJID().toString() << ": Removing conversation " << (*m_convs.begin()).first); + (*m_convs.begin()).second->destroyRoom(); + delete (*m_convs.begin()).second; + m_convs.erase(m_convs.begin()); + } +} + Conversation *ConversationManager::getConversation(const std::string &name) { if (m_convs.find(name) != m_convs.end()) return m_convs[name]; diff --git a/src/discoinforesponder.cpp b/src/discoinforesponder.cpp index 6d701088ede48dba537e5ea61322247938a8876e..2ca5746c50befbfeddc0dfbea311dc0909a7a001 100644 --- a/src/discoinforesponder.cpp +++ b/src/discoinforesponder.cpp @@ -22,15 +22,19 @@ #include #include +#include #include "Swiften/Disco/DiscoInfoResponder.h" #include "Swiften/Queries/IQRouter.h" #include "Swiften/Elements/DiscoInfo.h" #include "Swiften/Swiften.h" #include "transport/config.h" +#include "transport/logging.h" using namespace Swift; using namespace boost; +DEFINE_LOGGER(logger, "DiscoInfoResponder"); + namespace Transport { DiscoInfoResponder::DiscoInfoResponder(Swift::IQRouter *router, Config *config) : Swift::GetResponder(router) { @@ -80,19 +84,51 @@ void DiscoInfoResponder::setBuddyFeatures(std::list &f) { onBuddyCapsInfoChanged(m_capsInfo); } -bool DiscoInfoResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr info) { - if (!info->getNode().empty()) { - sendError(from, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel); - return true; - } +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; +} - // presence for transport +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()) { - boost::shared_ptr res(new DiscoInfo(m_transportInfo)); + // 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().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, id, res); + sendResponse(from, to, id, res); } - // presence for buddy + // disco#info for buddy else { boost::shared_ptr res(new DiscoInfo(m_buddyInfo)); res->setNode(info->getNode()); diff --git a/src/discoinforesponder.h b/src/discoinforesponder.h index 7e3747025456904c260e7ca4c8b165f030710b84..a3109222d2c9d38b5aa7bdd13131358c73a77616 100644 --- a/src/discoinforesponder.h +++ b/src/discoinforesponder.h @@ -38,6 +38,11 @@ class DiscoInfoResponder : public Swift::GetResponder { 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() { @@ -51,6 +56,8 @@ class DiscoInfoResponder : public Swift::GetResponder { Swift::DiscoInfo m_buddyInfo; Config *m_config; Swift::CapsInfo m_capsInfo; + std::map m_rooms; + std::map m_commands; }; } \ No newline at end of file diff --git a/src/discoitemsresponder.cpp b/src/discoitemsresponder.cpp index 2365b27e7e0b7e47437c369ebe1c100eb272b5a8..72ac83906dd1bcfb6d1b88f0a318b54a00488719 100644 --- a/src/discoitemsresponder.cpp +++ b/src/discoitemsresponder.cpp @@ -26,6 +26,7 @@ #include "Swiften/Swiften.h" #include "transport/transport.h" #include "transport/logging.h" +#include "discoinforesponder.h" using namespace Swift; using namespace boost; @@ -38,14 +39,36 @@ DiscoItemsResponder::DiscoItemsResponder(Component *component) : Swift::GetRespo m_component = component; m_commands = boost::shared_ptr(new DiscoItems()); m_commands->setNode("http://jabber.org/protocol/commands"); + + m_rooms = boost::shared_ptr(new DiscoItems()); + m_discoInfoResponder = new DiscoInfoResponder(component->getIQRouter(), component->getConfig()); + m_discoInfoResponder->start(); } DiscoItemsResponder::~DiscoItemsResponder() { - + delete m_discoInfoResponder; } void DiscoItemsResponder::addAdHocCommand(const std::string &node, const std::string &name) { m_commands->addItem(DiscoItems::Item(name, m_component->getJID(), node)); + m_discoInfoResponder->addAdHocCommand(node, name); +} + +void DiscoItemsResponder::addRoom(const std::string &node, const std::string &name) { + if (m_rooms->getItems().size() > CONFIG_INT(m_component->getConfig(), "service.max_room_list_size")) { + return; + } + m_rooms->addItem(DiscoItems::Item(name, node)); + m_discoInfoResponder->addRoom(node, name); +} + +void DiscoItemsResponder::clearRooms() { + m_rooms = boost::shared_ptr(new DiscoItems()); + m_discoInfoResponder->clearRooms(); +} + +Swift::CapsInfo &DiscoItemsResponder::getBuddyCapsInfo() { + return m_discoInfoResponder->getBuddyCapsInfo(); } @@ -55,7 +78,7 @@ bool DiscoItemsResponder::handleGetRequest(const Swift::JID& from, const Swift:: sendResponse(from, id, m_commands); } else if (to.getNode().empty()) { - sendResponse(from, id, boost::shared_ptr(new DiscoItems())); + sendResponse(from, id, m_rooms); } else { sendResponse(from, id, boost::shared_ptr(new DiscoItems())); diff --git a/src/logging.cpp b/src/logging.cpp index f724993ef91b34f51756ffe97573e1147bd6be39..a7f8478b090172d28f076dfeb8dd0de89c02f510 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -20,6 +20,7 @@ #include "transport/logging.h" #include "transport/config.h" +#include "transport/util.h" #include #include #include @@ -31,6 +32,7 @@ #ifndef WIN32 #include "sys/signal.h" +#include #include #include #include @@ -136,35 +138,42 @@ static void initLogging(Config *config, std::string key) { break; } } - + mode_t old_cmask; if (!dir.empty()) { // create directories +#ifndef WIN32 + old_cmask = umask(0007); +#endif try { - boost::filesystem::create_directories( - boost::filesystem::path(dir).parent_path().string() - ); + Transport::Util::createDirectories(config, boost::filesystem::path(dir).parent_path()); } - catch (...) { - std::cerr << "Can't create logging directory directory " << boost::filesystem::path(dir).parent_path().string() << ".\n"; + catch (const boost::filesystem::filesystem_error &e) { + std::cerr << "Can't create logging directory directory " << boost::filesystem::path(dir).parent_path().string() << ": " << e.what() << ".\n"; } + } -#ifndef WIN32 - if (!CONFIG_STRING(config, "service.group").empty() && !CONFIG_STRING(config, "service.user").empty()) { - struct group *gr; - if ((gr = getgrnam(CONFIG_STRING(config, "service.group").c_str())) == NULL) { - std::cerr << "Invalid service.group name " << CONFIG_STRING(config, "service.group") << "\n"; - } - struct passwd *pw; - if ((pw = getpwnam(CONFIG_STRING(config, "service.user").c_str())) == NULL) { - std::cerr << "Invalid service.user name " << CONFIG_STRING(config, "service.user") << "\n"; - } - chown(dir.c_str(), pw->pw_uid, gr->gr_gid); - } + log4cxx::PropertyConfigurator::configure(p); -#endif + // Change owner of main log file +#ifndef WIN32 + if (!CONFIG_STRING(config, "service.group").empty() && !CONFIG_STRING(config, "service.user").empty()) { + struct group *gr; + if ((gr = getgrnam(CONFIG_STRING(config, "service.group").c_str())) == NULL) { + std::cerr << "Invalid service.group name " << CONFIG_STRING(config, "service.group") << "\n"; } + struct passwd *pw; + if ((pw = getpwnam(CONFIG_STRING(config, "service.user").c_str())) == NULL) { + std::cerr << "Invalid service.user name " << CONFIG_STRING(config, "service.user") << "\n"; + } + chown(dir.c_str(), pw->pw_uid, gr->gr_gid); + } +#endif - log4cxx::PropertyConfigurator::configure(p); +#ifndef WIN32 + if (!dir.empty()) { + umask(old_cmask); + } +#endif } } diff --git a/src/mysqlbackend.cpp b/src/mysqlbackend.cpp index 67aa6508e3b904045ee3f726aeb97543cff025b4..7d28d50622208164b5ed230d7d421d7a25fc7585 100644 --- a/src/mysqlbackend.cpp +++ b/src/mysqlbackend.cpp @@ -421,7 +421,7 @@ bool MySQLBackend::exec(const std::string &query) { void MySQLBackend::setUser(const UserInfo &user) { std::string encrypted = user.password; if (!CONFIG_STRING(m_config, "database.encryption_key").empty()) { - encrypted = Util::encryptPassword(encrypted, CONFIG_STRING(m_config, "database.encryption_key")); + encrypted = StorageBackend::encryptPassword(encrypted, CONFIG_STRING(m_config, "database.encryption_key")); } *m_setUser << user.jid << user.uin << encrypted << user.language << user.encoding << user.vip << user.uin << encrypted; EXEC(m_setUser, setUser(user)); @@ -439,7 +439,24 @@ bool MySQLBackend::getUser(const std::string &barejid, UserInfo &user) { *m_getUser >> user.id >> user.jid >> user.uin >> user.password >> user.encoding >> user.language >> user.vip; if (!CONFIG_STRING(m_config, "database.encryption_key").empty()) { - user.password = Util::decryptPassword(user.password, CONFIG_STRING(m_config, "database.encryption_key")); + user.password = StorageBackend::decryptPassword(user.password, CONFIG_STRING(m_config, "database.encryption_key")); + } + } + + if (!CONFIG_STRING(m_config, "database.vip_statement").empty()) { + std::string query = CONFIG_STRING(m_config, "database.vip_statement"); + boost::replace_all(query, "$barejid", barejid); + LOG4CXX_INFO(logger, "Executing '" << query << "' to find out if user " << barejid << " is VIP"); + if (exec(query)) { + MYSQL_RES *result = mysql_store_result(&m_conn); + if (result && mysql_num_rows(result) > 0) { + LOG4CXX_INFO(logger, "User " << barejid << " is VIP"); + user.vip = 1; + } + else { + LOG4CXX_INFO(logger, "User " << barejid << " is not VIP"); + user.vip = 0; + } } } @@ -467,7 +484,7 @@ bool MySQLBackend::getOnlineUsers(std::vector &users) { long MySQLBackend::addBuddy(long userId, const BuddyInfo &buddyInfo) { // "INSERT INTO " + m_prefix + "buddies (user_id, uin, subscription, groups, nickname, flags) VALUES (?, ?, ?, ?, ?, ?)" - std::string groups = Util::serializeGroups(buddyInfo.groups); + std::string groups = StorageBackend::serializeGroups(buddyInfo.groups); *m_addBuddy << userId << buddyInfo.legacyName << buddyInfo.subscription; *m_addBuddy << groups; *m_addBuddy << buddyInfo.alias << buddyInfo.flags; @@ -517,7 +534,7 @@ void MySQLBackend::removeBuddy(long id) { void MySQLBackend::updateBuddy(long userId, const BuddyInfo &buddyInfo) { // "UPDATE " + m_prefix + "buddies SET groups=?, nickname=?, flags=?, subscription=? WHERE user_id=? AND uin=?" - std::string groups = Util::serializeGroups(buddyInfo.groups); + std::string groups = StorageBackend::serializeGroups(buddyInfo.groups); *m_updateBuddy << groups; *m_updateBuddy << buddyInfo.alias << buddyInfo.flags << buddyInfo.subscription; *m_updateBuddy << userId << buddyInfo.legacyName; @@ -547,7 +564,7 @@ bool MySQLBackend::getBuddies(long id, std::list &roster) { *m_getBuddies >> b.id >> b.legacyName >> b.subscription >> b.alias >> group >> b.flags; if (!group.empty()) { - b.groups = Util::deserializeGroups(group); + b.groups = StorageBackend::deserializeGroups(group); } roster.push_back(b); diff --git a/src/networkpluginserver.cpp b/src/networkpluginserver.cpp index a7efd8fb011db300fe3851b34296bd6d153a113a..5061d1ce9d5ed620a665f8fb09f1a8a194ff7788 100644 --- a/src/networkpluginserver.cpp +++ b/src/networkpluginserver.cpp @@ -44,6 +44,7 @@ #include "Swiften/Elements/SpectrumErrorPayload.h" #include "transport/protocol.pb.h" #include "transport/util.h" +#include "transport/discoitemsresponder.h" #include "utf8.h" @@ -92,8 +93,8 @@ class NetworkFactory : public Factory { virtual ~NetworkFactory() {} // Creates new conversation (NetworkConversation in this case) - Conversation *createConversation(ConversationManager *conversationManager, const std::string &legacyName) { - NetworkConversation *nc = new NetworkConversation(conversationManager, legacyName); + 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; } @@ -184,6 +185,11 @@ static unsigned long exec_(const std::string& exePath, const char *host, const c pid_t pid = fork(); if ( pid == 0 ) { setsid(); + // close all files + int maxfd=sysconf(_SC_OPEN_MAX); + for(int fd=3; fd groups; for (int i = 0; i < payload.group_size(); i++) { - groups.push_back(payload.group(i)); + std::string group = payload.group(i); + utf8::replace_invalid(payload.group(i).begin(), payload.group(i).end(), group.begin(), '_'); + groups.push_back(group); } if (!groups.empty()) { buddy->setGroups(groups); @@ -242,7 +250,7 @@ static void handleBuddyPayload(LocalBuddy *buddy, const pbnetwork::Buddy &payloa buddy->setBlocked(payload.blocked()); } -NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager) { +NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager, DiscoItemsResponder *discoItemsResponder) { m_ftManager = ftManager; m_userManager = userManager; m_config = config; @@ -250,6 +258,7 @@ NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, U m_isNextLongRun = false; m_adminInterface = NULL; m_startingBackend = false; + 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)); @@ -281,6 +290,30 @@ NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, U 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")); @@ -299,7 +332,13 @@ NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, U if (result != 0) { if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) { - LOG4CXX_ERROR(logger, "Backend can not be started, exit_code=" << WEXITSTATUS(status) << ", possible error: " << strerror(WEXITSTATUS(status))); + 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))); + } + LOG4CXX_ERROR(logger, "Check backend log for more details"); continue; } } @@ -314,28 +353,6 @@ NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, U // quit the while loop break; } - -} - -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::handleNewClientConnection(boost::shared_ptr c) { @@ -475,11 +492,14 @@ void NetworkPluginServer::handleAuthorizationPayload(const std::string &data) { response->setTo(user->getJID()); std::string name = payload.buddyname(); - name = Swift::JID::getEscapedNode(name); - -// if (name.find_last_of("@") != std::string::npos) { // OK when commented -// name.replace(name.find_last_of("@"), 1, "%"); // OK when commented -// } + 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); @@ -528,15 +548,25 @@ void NetworkPluginServer::handleBuddyChangedPayload(const std::string &data) { buddy->handleBuddyChanged(); } 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)); } - buddy = new LocalBuddy(user->getRosterManager(), -1, payload.buddyname(), payload.alias(), groups, BUDDY_JID_ESCAPING); + 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->setStatus(Swift::StatusShow((Swift::StatusShow::Type) payload.status()), payload.statusmessage()); buddy->setIconHash(payload.iconhash()); buddy->setBlocked(payload.blocked()); @@ -624,8 +654,21 @@ void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool msg->addPayload(boost::make_shared(payload.xhtml())); } - // Create new Conversation if it does not exist + if (!payload.timestamp().empty()) { + boost::posix_time::ptime timestamp = boost::posix_time::from_iso_string(payload.timestamp()); + msg->addPayload(boost::make_shared(timestamp)); + } + + 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; + } + + // Create new Conversation if it does not exist if (!conv) { conv = new NetworkConversation(user->getConversationManager(), payload.buddyname()); user->getConversationManager()->addConversation(conv); @@ -746,6 +789,11 @@ void NetworkPluginServer::handleFTDataPayload(Backend *b, const std::string &dat // 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(); @@ -848,6 +896,19 @@ void NetworkPluginServer::handleBackendConfigPayload(const std::string &data) { 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::handleDataRead(Backend *c, boost::shared_ptr data) { // Append data to buffer c->data.insert(c->data.end(), data->begin(), data->end()); @@ -945,6 +1006,9 @@ void NetworkPluginServer::handleDataRead(Backend *c, boost::shared_ptrconnection, message); - - NetworkConversation *conv = new NetworkConversation(user->getConversationManager(), r, true); - user->getConversationManager()->addConversation(conv); - conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2)); - conv->setNickname(nickname); - conv->addJID(who); } void NetworkPluginServer::handleRoomLeft(User *user, const std::string &r) { diff --git a/src/pqxxbackend.cpp b/src/pqxxbackend.cpp index c2ae45804d0d5f8f0f76dac9492fec2f4c13e430..90fffb87b69b48d92bf63bf12d0854a7251a42ef 100644 --- a/src/pqxxbackend.cpp +++ b/src/pqxxbackend.cpp @@ -157,7 +157,7 @@ bool PQXXBackend::exec(pqxx::nontransaction &txn, const std::string &query, bool void PQXXBackend::setUser(const UserInfo &user) { std::string encrypted = user.password; if (!CONFIG_STRING(m_config, "database.encryption_key").empty()) { - encrypted = Util::encryptPassword(encrypted, CONFIG_STRING(m_config, "database.encryption_key")); + encrypted = StorageBackend::encryptPassword(encrypted, CONFIG_STRING(m_config, "database.encryption_key")); } try { pqxx::nontransaction txn(*m_conn); @@ -236,7 +236,7 @@ long PQXXBackend::addBuddy(long userId, const BuddyInfo &buddyInfo) { + "(" + pqxx::to_string(userId) + "," + quote(txn, buddyInfo.legacyName) + "," + quote(txn, buddyInfo.subscription) + "," - + quote(txn, Util::serializeGroups(buddyInfo.groups)) + "," + + quote(txn, StorageBackend::serializeGroups(buddyInfo.groups)) + "," + quote(txn, buddyInfo.alias) + "," + pqxx::to_string(buddyInfo.flags) + ") RETURNING id"); @@ -263,7 +263,7 @@ long PQXXBackend::addBuddy(long userId, const BuddyInfo &buddyInfo) { void PQXXBackend::updateBuddy(long userId, const BuddyInfo &buddyInfo) { try { pqxx::nontransaction txn(*m_conn); - txn.exec("UPDATE " + m_prefix + "buddies SET groups=" + quote(txn, Util::serializeGroups(buddyInfo.groups)) + ", nickname=" + quote(txn, buddyInfo.alias) + ", flags=" + pqxx::to_string(buddyInfo.flags) + ", subscription=" + quote(txn, buddyInfo.subscription) + " WHERE user_id=" + pqxx::to_string(userId) + " AND uin=" + quote(txn, buddyInfo.legacyName)); + txn.exec("UPDATE " + m_prefix + "buddies SET groups=" + quote(txn, StorageBackend::serializeGroups(buddyInfo.groups)) + ", nickname=" + quote(txn, buddyInfo.alias) + ", flags=" + pqxx::to_string(buddyInfo.flags) + ", subscription=" + quote(txn, buddyInfo.subscription) + " WHERE user_id=" + pqxx::to_string(userId) + " AND uin=" + quote(txn, buddyInfo.legacyName)); } catch (std::exception& e) { LOG4CXX_ERROR(logger, e.what()); @@ -287,7 +287,7 @@ bool PQXXBackend::getBuddies(long id, std::list &roster) { b.flags = r[0][5].as(); if (!group.empty()) { - b.groups = Util::deserializeGroups(group); + b.groups = StorageBackend::deserializeGroups(group); } roster.push_back(b); diff --git a/src/rostermanager.cpp b/src/rostermanager.cpp index 77ed09a3bf30eae1680905995ed955814767235b..0abec9f69ea42c441a8707aba3c8b84716b64eb7 100644 --- a/src/rostermanager.cpp +++ b/src/rostermanager.cpp @@ -181,7 +181,7 @@ void RosterManager::sendBuddyRosterPush(Buddy *buddy) { if (buddy->getSubscription() != Buddy::Both) { buddy->setSubscription(Buddy::Both); - handleBuddyChanged(buddy); + storeBuddy(buddy); } } @@ -446,13 +446,15 @@ void RosterManager::handleSubscription(Swift::Presence::ref presence) { } if (buddy->getSubscription() != Buddy::Both) { buddy->setSubscription(Buddy::Both); - handleBuddyChanged(buddy); + storeBuddy(buddy); } break; // remove buddy case Swift::Presence::Unsubscribe: response->setType(Swift::Presence::Unsubscribed); onBuddyRemoved(buddy); + removeBuddy(buddy->getName()); + buddy = NULL; break; // just send response case Swift::Presence::Unsubscribed: @@ -462,13 +464,13 @@ void RosterManager::handleSubscription(Swift::Presence::ref presence) { // to be send later again if (buddy->getSubscription() != Buddy::Both) { buddy->setSubscription(Buddy::Both); - handleBuddyChanged(buddy); + storeBuddy(buddy); } break; case Swift::Presence::Subscribed: if (buddy->getSubscription() != Buddy::Both) { buddy->setSubscription(Buddy::Both); - handleBuddyChanged(buddy); + storeBuddy(buddy); } return; default: @@ -491,10 +493,19 @@ void RosterManager::handleSubscription(Swift::Presence::ref presence) { onBuddyAdded(buddy); response->setType(Swift::Presence::Subscribed); break; - // buddy is already there, so nothing to do, just answer case Swift::Presence::Unsubscribe: + buddyInfo.id = -1; + buddyInfo.alias = ""; + buddyInfo.legacyName = Buddy::JIDToLegacyName(presence->getTo()); + buddyInfo.subscription = "both"; + buddyInfo.flags = Buddy::buddyFlagsFromJID(presence->getTo()); + response->setType(Swift::Presence::Unsubscribed); -// onBuddyRemoved(buddy); + + buddy = m_component->getFactory()->createBuddy(this, buddyInfo); + onBuddyRemoved(buddy); + delete buddy; + buddy = NULL; break; // just send response case Swift::Presence::Unsubscribed: diff --git a/src/rosterresponder.cpp b/src/rosterresponder.cpp index 9c0be47d3be66a033f55f09c158f6e0d8dbcbd8f..6bc496c1933703140c0d8ce5fce41a3967211c65 100644 --- a/src/rosterresponder.cpp +++ b/src/rosterresponder.cpp @@ -74,6 +74,10 @@ bool RosterResponder::handleSetRequest(const Swift::JID& from, const Swift::JID& Swift::RosterItemPayload item = payload->getItems()[0]; + if (item.getJID().getNode().empty()) { + return true; + } + Buddy *buddy = user->getRosterManager()->getBuddy(Buddy::JIDToLegacyName(item.getJID())); if (buddy) { if (item.getSubscription() == Swift::RosterItemPayload::Remove) { diff --git a/src/sqlite3backend.cpp b/src/sqlite3backend.cpp index 3de7371b0bddf798f3d93a765af1adecdd86ed23..b95deb3fe85ae96d41dc92d320bd8cb30f90c198 100644 --- a/src/sqlite3backend.cpp +++ b/src/sqlite3backend.cpp @@ -287,7 +287,7 @@ long SQLite3Backend::addBuddy(long userId, const BuddyInfo &buddyInfo) { BIND_INT(m_addBuddy, userId); BIND_STR(m_addBuddy, buddyInfo.legacyName); BIND_STR(m_addBuddy, buddyInfo.subscription); - BIND_STR(m_addBuddy, Util::serializeGroups(buddyInfo.groups)); + BIND_STR(m_addBuddy, StorageBackend::serializeGroups(buddyInfo.groups)); BIND_STR(m_addBuddy, buddyInfo.alias); BIND_INT(m_addBuddy, buddyInfo.flags); @@ -313,7 +313,7 @@ long SQLite3Backend::addBuddy(long userId, const BuddyInfo &buddyInfo) { void SQLite3Backend::updateBuddy(long userId, const BuddyInfo &buddyInfo) { // UPDATE " + m_prefix + "buddies SET groups=?, nickname=?, flags=?, subscription=? WHERE user_id=? AND uin=? BEGIN(m_updateBuddy); - BIND_STR(m_updateBuddy, Util::serializeGroups(buddyInfo.groups)); + BIND_STR(m_updateBuddy, StorageBackend::serializeGroups(buddyInfo.groups)); BIND_STR(m_updateBuddy, buddyInfo.alias); BIND_INT(m_updateBuddy, buddyInfo.flags); BIND_STR(m_updateBuddy, buddyInfo.subscription); @@ -355,7 +355,7 @@ bool SQLite3Backend::getBuddies(long id, std::list &roster) { b.subscription = GET_STR(m_getBuddies); b.alias = GET_STR(m_getBuddies); std::string groups = GET_STR(m_getBuddies); - b.groups = Util::deserializeGroups(groups); + b.groups = StorageBackend::deserializeGroups(groups); b.flags = GET_INT(m_getBuddies); if (buddy_id == b.id) { diff --git a/src/storagebackend.cpp b/src/storagebackend.cpp index 2247dee9a1b5b62c8507e7920a6e22070995b533..ce27dfae0e46cb7914b86e68977b3befb73e2f27 100644 --- a/src/storagebackend.cpp +++ b/src/storagebackend.cpp @@ -5,13 +5,14 @@ #include "transport/mysqlbackend.h" #include "transport/pqxxbackend.h" +#include "Swiften/Swiften.h" + namespace Transport { StorageBackend *StorageBackend::createBackend(Config *config, std::string &error) { StorageBackend *storageBackend = NULL; #ifdef WITH_SQLITE - if (CONFIG_STRING(config, "database.type") == "sqlite3" || - (CONFIG_STRING(config, "database.type") == "none" && !CONFIG_BOOL(config, "service.server_mode"))) { + if (CONFIG_STRING(config, "database.type") == "sqlite3") { storageBackend = new SQLite3Backend(config); } #else @@ -48,4 +49,56 @@ StorageBackend *StorageBackend::createBackend(Config *config, std::string &error return storageBackend; } +std::string StorageBackend::encryptPassword(const std::string &password, const std::string &key) { + std::string encrypted; + encrypted.resize(password.size()); + for (int i = 0; i < password.size(); i++) { + char c = password[i]; + char keychar = key[i % key.size()]; + c += keychar; + encrypted[i] = c; + } + + encrypted = Swift::Base64::encode(Swift::createByteArray(encrypted)); + return encrypted; +} + +std::string StorageBackend::decryptPassword(std::string &encrypted, const std::string &key) { + encrypted = Swift::byteArrayToString(Swift::Base64::decode(encrypted)); + std::string password; + password.resize(encrypted.size()); + for (int i = 0; i < encrypted.size(); i++) { + char c = encrypted[i]; + char keychar = key[i % key.size()]; + c -= keychar; + password[i] = c; + } + + return password; +} + +std::string StorageBackend::serializeGroups(const std::vector &groups) { + std::string ret; + BOOST_FOREACH(const std::string &group, groups) { + ret += group + "\n"; + } + if (!ret.empty()) { + ret.erase(ret.end() - 1); + } + return ret; +} + +std::vector StorageBackend::deserializeGroups(std::string &groups) { + std::vector ret; + if (groups.empty()) { + return ret; + } + + boost::split(ret, groups, boost::is_any_of("\n")); + if (ret.back().empty()) { + ret.erase(ret.end() - 1); + } + return ret; +} + } diff --git a/src/tests/basictest.cpp b/src/tests/basictest.cpp index 9a893e7d7cfdef81198b9703e9e325a7f18b17f2..1c0b2eeda350c342a142e4e71df0eda8cb77d6f7 100644 --- a/src/tests/basictest.cpp +++ b/src/tests/basictest.cpp @@ -57,14 +57,14 @@ void BasicTest::setMeUp (void) { component = new Component(loop, factories, cfg, factory, userRegistry); component->start(); - userManager = new UserManager(component, userRegistry, storage); + itemsResponder = new DiscoItemsResponder(component); + itemsResponder->start(); + + userManager = new UserManager(component, userRegistry, itemsResponder, storage); userRegistration = new UserRegistration(component, userManager, storage); userRegistration->start(); - itemsResponder = new DiscoItemsResponder(component); - itemsResponder->start(); - payloadSerializers = new Swift::FullPayloadSerializerCollection(); payloadParserFactories = new Swift::FullPayloadParserFactoryCollection(); @@ -202,6 +202,7 @@ void BasicTest::connectUser() { CPPUNIT_ASSERT_EQUAL(2, (int) received.size()); CPPUNIT_ASSERT(getStanza(received[0])->getPayload()); + received.clear(); receivedData.clear(); } @@ -236,6 +237,7 @@ void BasicTest::connectSecondResource() { } void BasicTest::disconnectUser() { + received.clear(); userManager->disconnectUser("user@localhost"); dynamic_cast(factories->getTimerFactory())->setTime(10); loop->processEvents(); diff --git a/src/tests/basictest.h b/src/tests/basictest.h index 554ab5b6662ef5e20876b72b1b98446fbd352c00..a1d9a0f14b4998aebdc483962bfaff6b3a1ab582 100644 --- a/src/tests/basictest.h +++ b/src/tests/basictest.h @@ -67,8 +67,8 @@ class TestingFactory : public Factory { } // Creates new conversation (NetworkConversation in this case) - Conversation *createConversation(ConversationManager *conversationManager, const std::string &legacyName) { - TestingConversation *nc = new TestingConversation(conversationManager, legacyName); + Conversation *createConversation(ConversationManager *conversationManager, const std::string &legacyName, bool isMuc = false) { + TestingConversation *nc = new TestingConversation(conversationManager, legacyName, isMuc); nc->onMessageToSend.connect(boost::bind(&TestingFactory::handleMessageToSend, this, _1, _2)); return nc; } @@ -225,6 +225,7 @@ class BasicTest : public Swift::XMPPParserClient { user.jid = "user@localhost"; user.uin = "legacyname"; user.password = "password"; + user.vip = 0; storage->setUser(user); } diff --git a/src/tests/conversationmanager.cpp b/src/tests/conversationmanager.cpp index 9aa739fa2151e56612750680589009fc8286937e..0869ec8ac86dd606f2151ff064bc431b066fad15 100644 --- a/src/tests/conversationmanager.cpp +++ b/src/tests/conversationmanager.cpp @@ -94,26 +94,35 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe void handleSubjectMessages() { User *user = userManager->getUser("user@localhost"); - - TestingConversation *conv = new TestingConversation(user->getConversationManager(), "buddy1"); - user->getConversationManager()->addConversation(conv); + TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true); + conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2)); + conv->setNickname("nickname"); + conv->addJID("user@localhost/resource"); boost::shared_ptr msg(new Swift::Message()); msg->setSubject("subject"); + msg->setType(Swift::Message::Groupchat); - // Forward it conv->handleMessage(msg); loop->processEvents(); + + // No response, because presence with code 110 has not been sent yet and we must not send + // subject before this one. + CPPUNIT_ASSERT_EQUAL(0, (int) received.size()); + + // this user presence - status code 110 + conv->handleParticipantChanged("nickname", 1, Swift::StatusShow::Away, "my status message"); + loop->processEvents(); - CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); - CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); - CPPUNIT_ASSERT_EQUAL(std::string("subject"), dynamic_cast(getStanza(received[0]))->getSubject()); + CPPUNIT_ASSERT_EQUAL(2, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[1]))); + CPPUNIT_ASSERT_EQUAL(std::string("subject"), dynamic_cast(getStanza(received[1]))->getSubject()); received.clear(); // send response msg->setFrom("user@localhost/resource"); - msg->setTo("buddy1@localhost/bot"); + msg->setTo("#room@localhost"); injectMessage(msg); loop->processEvents(); @@ -127,12 +136,12 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe void handleNormalMessages() { User *user = userManager->getUser("user@localhost"); - TestingConversation *conv = new TestingConversation(user->getConversationManager(), "buddy1"); + TestingConversation *conv = new TestingConversation(user->getConversationManager(), "buddy1@test"); user->getConversationManager()->addConversation(conv); conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2)); boost::shared_ptr msg(new Swift::Message()); - msg->setBody("hi there!"); + msg->setBody("hi there<>!"); // Forward it conv->handleMessage(msg); @@ -140,22 +149,22 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); - CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast(getStanza(received[0]))->getBody()); + CPPUNIT_ASSERT_EQUAL(std::string("hi there<>!"), dynamic_cast(getStanza(received[0]))->getBody()); CPPUNIT_ASSERT_EQUAL(std::string("user@localhost"), dynamic_cast(getStanza(received[0]))->getTo().toString()); - CPPUNIT_ASSERT_EQUAL(std::string("buddy1@localhost/bot"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40test@localhost/bot"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); received.clear(); // send response msg->setFrom("user@localhost/resource"); - msg->setTo("buddy1@localhost/bot"); - msg->setBody("response!"); + msg->setTo("buddy1\\40test@localhost/bot"); + msg->setBody("response<>!"); injectMessage(msg); loop->processEvents(); CPPUNIT_ASSERT_EQUAL(0, (int) received.size()); CPPUNIT_ASSERT(m_msg); - CPPUNIT_ASSERT_EQUAL(std::string("response!"), m_msg->getBody()); + CPPUNIT_ASSERT_EQUAL(std::string("response<>!"), m_msg->getBody()); // send another message from legacy network, should be sent to user@localhost/resource now boost::shared_ptr msg2(new Swift::Message()); @@ -169,20 +178,28 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast(getStanza(received[0]))->getBody()); CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast(getStanza(received[0]))->getTo().toString()); - CPPUNIT_ASSERT_EQUAL(std::string("buddy1@localhost/bot"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40test@localhost/bot"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); received.clear(); + // disable jid_escaping + std::istringstream ifs("service.server_mode = 1\nservice.jid_escaping=0\nservice.jid=localhost\nservice.more_resources=1\n"); + cfg->load(ifs); + // and now to bare JID again... user->getConversationManager()->resetResources(); conv->handleMessage(msg2); loop->processEvents(); + + // enable jid_escaping again + std::istringstream ifs2("service.server_mode = 1\nservice.jid_escaping=1\nservice.jid=localhost\nservice.more_resources=1\n"); + cfg->load(ifs2); CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast(getStanza(received[0]))->getBody()); CPPUNIT_ASSERT_EQUAL(std::string("user@localhost"), dynamic_cast(getStanza(received[0]))->getTo().toString()); - CPPUNIT_ASSERT_EQUAL(std::string("buddy1@localhost/bot"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("buddy1%test@localhost/bot"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); received.clear(); } diff --git a/src/tests/discoitemsresponder.cpp b/src/tests/discoitemsresponder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dfdf3619efa7470c8164ecbdc5714c5d5dc1f60e --- /dev/null +++ b/src/tests/discoitemsresponder.cpp @@ -0,0 +1,99 @@ +#include "transport/userregistry.h" +#include "transport/userregistration.h" +#include "transport/config.h" +#include "transport/storagebackend.h" +#include "transport/user.h" +#include "transport/transport.h" +#include "transport/conversation.h" +#include "transport/usermanager.h" +#include "transport/localbuddy.h" +#include "transport/settingsadhoccommand.h" +#include "transport/adhocmanager.h" +#include +#include +#include +#include +#include +#include +#include +#include "Swiften/Server/ServerStanzaChannel.h" +#include "Swiften/Server/ServerFromClientSession.h" +#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" +#include "basictest.h" + +using namespace Transport; + +class DiscoItemsResponderTest : public CPPUNIT_NS :: TestFixture, public BasicTest { + CPPUNIT_TEST_SUITE(DiscoItemsResponderTest); + CPPUNIT_TEST(roomList); + CPPUNIT_TEST(roomInfo); + CPPUNIT_TEST(clearRooms); + CPPUNIT_TEST_SUITE_END(); + + public: + + void setUp (void) { + setMeUp(); + } + + void tearDown (void) { + received.clear(); + tearMeDown(); + } + + void roomList() { + itemsResponder->addRoom("#room@localhost", "#room"); + + boost::shared_ptr payload(new Swift::DiscoItems()); + boost::shared_ptr iq = Swift::IQ::createRequest(Swift::IQ::Get, Swift::JID("localhost"), "id", payload); + iq->setFrom("user@localhost"); + injectIQ(iq); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(Swift::IQ::Result, dynamic_cast(getStanza(received[0]))->getType()); + CPPUNIT_ASSERT(getStanza(received[0])->getPayload()); + CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost"), getStanza(received[0])->getPayload()->getItems()[0].getJID().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("#room"), getStanza(received[0])->getPayload()->getItems()[0].getName()); + } + + void roomInfo() { + itemsResponder->addRoom("#room@localhost", "#room"); + + boost::shared_ptr payload(new Swift::DiscoInfo()); + boost::shared_ptr iq = Swift::IQ::createRequest(Swift::IQ::Get, Swift::JID("localhost"), "id", payload); + iq->setFrom("user@localhost"); + iq->setTo("#room@localhost"); + injectIQ(iq); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(Swift::IQ::Result, dynamic_cast(getStanza(received[0]))->getType()); + CPPUNIT_ASSERT(getStanza(received[0])->getPayload()); + CPPUNIT_ASSERT_EQUAL(std::string("#room"), getStanza(received[0])->getPayload()->getIdentities()[0].getName()); + CPPUNIT_ASSERT_EQUAL(std::string("conference"), getStanza(received[0])->getPayload()->getIdentities()[0].getCategory()); + CPPUNIT_ASSERT_EQUAL(std::string("text"), getStanza(received[0])->getPayload()->getIdentities()[0].getType()); + } + + void clearRooms() { + itemsResponder->addRoom("#room@localhost", "#room"); + itemsResponder->clearRooms(); + + boost::shared_ptr payload(new Swift::DiscoItems()); + boost::shared_ptr iq = Swift::IQ::createRequest(Swift::IQ::Get, Swift::JID("localhost"), "id", payload); + iq->setFrom("user@localhost"); + injectIQ(iq); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(Swift::IQ::Result, dynamic_cast(getStanza(received[0]))->getType()); + CPPUNIT_ASSERT(getStanza(received[0])->getPayload()); + CPPUNIT_ASSERT(getStanza(received[0])->getPayload()->getItems().empty()); + } + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION (DiscoItemsResponderTest); diff --git a/src/tests/localbuddy.cpp b/src/tests/localbuddy.cpp index fdc6fb87378a67f0e9456acd9c66c84698e38565..3a0ee7951b1db940ae0af601b4659cb4437f0504 100644 --- a/src/tests/localbuddy.cpp +++ b/src/tests/localbuddy.cpp @@ -25,6 +25,7 @@ class LocalBuddyTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_TEST(createWithInvalidName); CPPUNIT_TEST(buddyFlagsFromJID); CPPUNIT_TEST(JIDToLegacyName); + CPPUNIT_TEST(getSafeName); CPPUNIT_TEST(handleBuddyChanged); CPPUNIT_TEST(setAlias); CPPUNIT_TEST_SUITE_END(); @@ -70,6 +71,20 @@ class LocalBuddyTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_ASSERT_EQUAL(std::string("hanzz@test"), Buddy::JIDToLegacyName("hanzz%test@localhost/bot")); } + void getSafeName() { + User *user = userManager->getUser("user@localhost"); + CPPUNIT_ASSERT(user); + + std::vector grp; + grp.push_back("group1"); + LocalBuddy *buddy = new LocalBuddy(user->getRosterManager(), -1, "buddy1@test", "Buddy 1", grp, BUDDY_JID_ESCAPING); + + CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40test"), buddy->getSafeName()); + + buddy->setFlags(BUDDY_NO_FLAG); + CPPUNIT_ASSERT_EQUAL(std::string("buddy1%test"), buddy->getSafeName()); + } + void buddyFlagsFromJID() { CPPUNIT_ASSERT_EQUAL(BUDDY_JID_ESCAPING, Buddy::buddyFlagsFromJID("hanzz\\40test@localhost/bot")); CPPUNIT_ASSERT_EQUAL(BUDDY_NO_FLAG, Buddy::buddyFlagsFromJID("hanzz%test@localhost/bot")); diff --git a/src/tests/main.cpp b/src/tests/main.cpp index aea740f0279c6ee91d57c32f3e289eb77d2a95f6..218d66fb3d06e820a1faca24f2c34d0f03087316 100644 --- a/src/tests/main.cpp +++ b/src/tests/main.cpp @@ -22,6 +22,16 @@ int main (int argc, char* argv[]) root->addAppender(new FileAppender(new PatternLayout("%d %-5p %c: %m%n"), "libtransport_test.log", false)); #endif + std::vector testsToRun; + for (int i = 1; i < argc; ++i) { + std::string param(argv[i]); + testsToRun.push_back(param); + } + + if (testsToRun.empty()) { + testsToRun.push_back(""); + } + // informs test-listener about testresults CPPUNIT_NS :: TestResult testresult; @@ -36,7 +46,15 @@ int main (int argc, char* argv[]) // insert test-suite at test-runner by registry CPPUNIT_NS :: TestRunner testrunner; testrunner.addTest (CPPUNIT_NS :: TestFactoryRegistry :: getRegistry ().makeTest ()); - testrunner.run (testresult); + for (std::vector::const_iterator i = testsToRun.begin(); i != testsToRun.end(); ++i) { + try { + testrunner.run(testresult, *i); + } + catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + return -1; + } + } // output results in compiler-format CPPUNIT_NS :: CompilerOutputter compileroutputter (&collectedresults, std::cerr); diff --git a/src/tests/networkpluginserver.cpp b/src/tests/networkpluginserver.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d9e1655d9a00a1409a4d540bcfd2b74c156de647 --- /dev/null +++ b/src/tests/networkpluginserver.cpp @@ -0,0 +1,109 @@ +#include "transport/userregistry.h" +#include "transport/userregistration.h" +#include "transport/config.h" +#include "transport/storagebackend.h" +#include "transport/user.h" +#include "transport/transport.h" +#include "transport/conversation.h" +#include "transport/usermanager.h" +#include "transport/localbuddy.h" +#include "transport/settingsadhoccommand.h" +#include "transport/adhocmanager.h" +#include "transport/protocol.pb.h" +#include "transport/networkpluginserver.h" +#include +#include +#include +#include +#include +#include +#include +#include "Swiften/Server/ServerStanzaChannel.h" +#include "Swiften/Server/ServerFromClientSession.h" +#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" +#include "basictest.h" + +using namespace Transport; + +class NetworkPluginServerTest : public CPPUNIT_NS :: TestFixture, public BasicTest { + CPPUNIT_TEST_SUITE(NetworkPluginServerTest); + CPPUNIT_TEST(handleBuddyChangedPayload); + CPPUNIT_TEST(handleBuddyChangedPayloadNoEscaping); + CPPUNIT_TEST(handleBuddyChangedPayloadUserContactInRoster); + CPPUNIT_TEST_SUITE_END(); + + public: + NetworkPluginServer *serv; + + void setUp (void) { + setMeUp(); + + serv = new NetworkPluginServer(component, cfg, userManager, NULL, NULL); + connectUser(); + received.clear(); + } + + void tearDown (void) { + received.clear(); + disconnectUser(); + delete serv; + tearMeDown(); + } + + void handleBuddyChangedPayload() { + User *user = userManager->getUser("user@localhost"); + + pbnetwork::Buddy buddy; + buddy.set_username("user@localhost"); + buddy.set_buddyname("buddy1@test"); + + std::string message; + buddy.SerializeToString(&message); + + serv->handleBuddyChangedPayload(message); + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + Swift::RosterPayload::ref payload1 = getStanza(received[0])->getPayload(); + CPPUNIT_ASSERT_EQUAL(1, (int) payload1->getItems().size()); + Swift::RosterItemPayload item = payload1->getItems()[0]; + CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40test@localhost"), item.getJID().toString()); + } + + void handleBuddyChangedPayloadNoEscaping() { + std::istringstream ifs("service.server_mode = 1\nservice.jid_escaping=0\nservice.jid=localhost\nservice.more_resources=1\n"); + cfg->load(ifs); + User *user = userManager->getUser("user@localhost"); + + pbnetwork::Buddy buddy; + buddy.set_username("user@localhost"); + buddy.set_buddyname("buddy1@test"); + + std::string message; + buddy.SerializeToString(&message); + + serv->handleBuddyChangedPayload(message); + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + Swift::RosterPayload::ref payload1 = getStanza(received[0])->getPayload(); + CPPUNIT_ASSERT_EQUAL(1, (int) payload1->getItems().size()); + Swift::RosterItemPayload item = payload1->getItems()[0]; + CPPUNIT_ASSERT_EQUAL(std::string("buddy1%test@localhost"), item.getJID().toString()); + + std::istringstream ifs2("service.server_mode = 1\nservice.jid_escaping=1\nservice.jid=localhost\nservice.more_resources=1\n"); + cfg->load(ifs2); + } + + void handleBuddyChangedPayloadUserContactInRoster() { + User *user = userManager->getUser("user@localhost"); + + pbnetwork::Buddy buddy; + buddy.set_username("user@localhost"); + buddy.set_buddyname("user"); + + std::string message; + buddy.SerializeToString(&message); + + serv->handleBuddyChangedPayload(message); + CPPUNIT_ASSERT_EQUAL(0, (int) received.size()); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION (NetworkPluginServerTest); diff --git a/src/tests/rosterresponder.cpp b/src/tests/rosterresponder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2c6cf86a008b1f3ac623248d17f6e3716ffd792a --- /dev/null +++ b/src/tests/rosterresponder.cpp @@ -0,0 +1,86 @@ +#include "transport/userregistry.h" +#include "transport/config.h" +#include "transport/storagebackend.h" +#include "transport/user.h" +#include "transport/transport.h" +#include "transport/conversation.h" +#include "transport/rosterresponder.h" +#include "transport/usermanager.h" +#include "transport/localbuddy.h" +#include +#include +#include +#include +#include +#include +#include +#include "Swiften/Server/ServerStanzaChannel.h" +#include "Swiften/Server/ServerFromClientSession.h" +#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" +#include "basictest.h" + +using namespace Transport; + +class RosterResponderTest : public CPPUNIT_NS :: TestFixture, public BasicTest { + CPPUNIT_TEST_SUITE(RosterResponderTest); + CPPUNIT_TEST(addEmptyBuddy); + CPPUNIT_TEST_SUITE_END(); + + public: + RosterResponder *m_rosterResponder; + std::string m_buddy; + + void setUp (void) { + m_buddy = "none"; + setMeUp(); + connectUser(); + + m_rosterResponder = new RosterResponder(component->getIQRouter(), userManager); + m_rosterResponder->onBuddyAdded.connect(boost::bind(&RosterResponderTest::handleBuddyAdded, this, _1, _2)); + m_rosterResponder->onBuddyRemoved.connect(boost::bind(&RosterResponderTest::handleBuddyRemoved, this, _1)); + m_rosterResponder->onBuddyUpdated.connect(boost::bind(&RosterResponderTest::handleBuddyUpdated, this, _1, _2)); + m_rosterResponder->start(); + + received.clear(); + } + + void tearDown (void) { + received.clear(); + disconnectUser(); + tearMeDown(); + } + + void handleBuddyAdded(Buddy *buddy, const Swift::RosterItemPayload &item) { + m_buddy = buddy->getName(); + } + + void handleBuddyRemoved(Buddy *buddy) { + m_buddy = buddy->getName(); + } + + void handleBuddyUpdated(Buddy *buddy, const Swift::RosterItemPayload &item) { + m_buddy = buddy->getName(); + } + + void addEmptyBuddy() { + Swift::RosterPayload::ref p = Swift::RosterPayload::ref(new Swift::RosterPayload()); + Swift::RosterItemPayload item; + item.setJID("icq.localhost"); + item.setSubscription(Swift::RosterItemPayload::Both); + + p->addItem(item); + Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(p, "user@localhost", component->getIQRouter()); + + boost::shared_ptr iq(new Swift::IQ(Swift::IQ::Set)); + iq->setTo("icq.localhost"); + iq->setFrom("user@localhost"); + iq->addPayload(p); + iq->setID("123"); + injectIQ(iq); + + CPPUNIT_ASSERT_EQUAL(std::string("none"), m_buddy); + } + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION (RosterResponderTest); diff --git a/src/tests/settingsadhoccommand.cpp b/src/tests/settingsadhoccommand.cpp index e3e14d9cd61fb9ce964beb456a40ce0cce7ed1ae..3e9602be2e2b266e854d3ad70e9a81d8ceb9eb9a 100644 --- a/src/tests/settingsadhoccommand.cpp +++ b/src/tests/settingsadhoccommand.cpp @@ -26,6 +26,7 @@ using namespace Transport; class SettingsAdHocCommandTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_TEST_SUITE(SettingsAdHocCommandTest); CPPUNIT_TEST(getItems); + CPPUNIT_TEST(getInfo); CPPUNIT_TEST(execute); CPPUNIT_TEST(executeBadSessionID); CPPUNIT_TEST(executeNotRegistered); @@ -70,6 +71,22 @@ class SettingsAdHocCommandTest : public CPPUNIT_NS :: TestFixture, public BasicT CPPUNIT_ASSERT_EQUAL(std::string("settings"), getStanza(received[0])->getPayload()->getItems()[0].getNode()); } + void getInfo() { + boost::shared_ptr payload(new Swift::DiscoInfo()); + payload->setNode("settings"); + boost::shared_ptr iq = Swift::IQ::createRequest(Swift::IQ::Get, Swift::JID("localhost"), "id", payload); + iq->setFrom("user@localhost"); + injectIQ(iq); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(Swift::IQ::Result, dynamic_cast(getStanza(received[0]))->getType()); + CPPUNIT_ASSERT(getStanza(received[0])->getPayload()); + CPPUNIT_ASSERT_EQUAL(std::string("automation"), getStanza(received[0])->getPayload()->getIdentities()[0].getCategory()); + CPPUNIT_ASSERT_EQUAL(std::string("command-node"), getStanza(received[0])->getPayload()->getIdentities()[0].getType()); + } + void executeNotRegistered() { boost::shared_ptr payload(new Swift::Command("settings")); boost::shared_ptr iq = Swift::IQ::createRequest(Swift::IQ::Set, Swift::JID("localhost"), "id", payload); diff --git a/src/tests/stringtreeparser.cpp b/src/tests/stringtreeparser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e25b24d8fdf1c53ee962ee2cbf5feb2d279d1478 --- /dev/null +++ b/src/tests/stringtreeparser.cpp @@ -0,0 +1,47 @@ +#include "transport/userregistry.h" +#include "transport/config.h" +#include "transport/storagebackend.h" +#include "transport/user.h" +#include "transport/transport.h" +#include "transport/storagebackend.h" +#include "transport/conversation.h" +#include "transport/usermanager.h" +#include "transport/localbuddy.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "Swiften/Server/ServerStanzaChannel.h" +#include "Swiften/Server/ServerFromClientSession.h" +#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" +#include "basictest.h" + +#include "transport/util.h" + +using namespace Transport; + +class StringTreeParserTest : public CPPUNIT_NS :: TestFixture{ + CPPUNIT_TEST_SUITE(StringTreeParserTest); + CPPUNIT_TEST(parseEscapedCharacters); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp (void) { + } + + void tearDown (void) { + + } + + void parseEscapedCharacters() { + Swift::ParserElement::ref root = Swift::StringTreeParser::parse("<test>"); + CPPUNIT_ASSERT_EQUAL(std::string(""), root->getText()); + } + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION (StringTreeParserTest); diff --git a/src/tests/user.cpp b/src/tests/user.cpp index bbafedbb1d90e15f00b630ba55c83e70e1026777..054cc100984c219f01fde0b65b3205cfec84a004 100644 --- a/src/tests/user.cpp +++ b/src/tests/user.cpp @@ -30,8 +30,10 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_TEST(handlePresenceLeaveRoomTwoResources); CPPUNIT_TEST(handlePresenceLeaveRoomTwoResourcesOneDisconnects); CPPUNIT_TEST(leaveJoinedRoom); + CPPUNIT_TEST(joinRoomBeforeConnected); CPPUNIT_TEST(handleDisconnected); CPPUNIT_TEST(handleDisconnectedReconnect); + CPPUNIT_TEST(joinRoomHandleDisconnectedRejoin); CPPUNIT_TEST_SUITE_END(); public: @@ -318,6 +320,56 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest { user = userManager->getUser("user@localhost"); CPPUNIT_ASSERT(user); CPPUNIT_ASSERT(readyToConnect); + + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo("localhost"); + response->setFrom("user@localhost/resource"); + injectPresence(response); + loop->processEvents(); + } + + void joinRoomBeforeConnected() { + User *user = userManager->getUser("user@localhost"); + user->setConnected(false); + + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo("#room@localhost/hanzz"); + response->setFrom("user@localhost/resource"); + + Swift::MUCPayload *payload = new Swift::MUCPayload(); + payload->setPassword("password"); + response->addPayload(boost::shared_ptr(payload)); + injectPresence(response); + loop->processEvents(); + + // no presence received in server mode, just disco#info + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT(getStanza(received[0])->getPayload()); + + CPPUNIT_ASSERT_EQUAL(std::string(""), room); + CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname); + CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword); + + user->setConnected(true); + CPPUNIT_ASSERT_EQUAL(std::string("#room"), room); + CPPUNIT_ASSERT_EQUAL(std::string("hanzz"), roomNickname); + CPPUNIT_ASSERT_EQUAL(std::string("password"), roomPassword); + } + + void joinRoomHandleDisconnectedRejoin() { + User *user = userManager->getUser("user@localhost"); + handlePresenceJoinRoom(); + handleDisconnectedReconnect(); + room = ""; + roomNickname = ""; + roomPassword = ""; + received.clear(); + user->setConnected(true); + + CPPUNIT_ASSERT_EQUAL(std::string("#room"), room); + CPPUNIT_ASSERT_EQUAL(std::string("hanzz"), roomNickname); + CPPUNIT_ASSERT_EQUAL(std::string("password"), roomPassword); + CPPUNIT_ASSERT_EQUAL(0, (int) received.size()); } }; diff --git a/src/tests/usermanager.cpp b/src/tests/usermanager.cpp index 1f03575daffc06c7c9f59833d533f0ecfa256821..8f59ab8fd8edd2a4da3a866b3bb2eb232499e3eb 100644 --- a/src/tests/usermanager.cpp +++ b/src/tests/usermanager.cpp @@ -27,6 +27,7 @@ class UserManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_TEST(connectUserTransportDisabled); CPPUNIT_TEST(connectUserRegistrationNeeded); CPPUNIT_TEST(connectUserRegistrationNeededRegistered); + CPPUNIT_TEST(connectUserVipOnlyNonVip); CPPUNIT_TEST(handleProbePresence); CPPUNIT_TEST(disconnectUser); CPPUNIT_TEST_SUITE_END(); @@ -71,7 +72,32 @@ class UserManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_ASSERT(!streamEnded); } + void connectUserVipOnlyNonVip() { + addUser(); + std::istringstream ifs("service.server_mode = 1\nservice.jid_escaping=0\nservice.jid=localhost\nservice.vip_only=1\nservice.vip_message=Ahoj\n"); + cfg->load(ifs); + CPPUNIT_ASSERT_EQUAL(0, userManager->getUserCount()); + userRegistry->isValidUserPassword(Swift::JID("user@localhost/resource"), serverFromClientSession.get(), Swift::createSafeByteArray("password")); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(3, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[1]))); + CPPUNIT_ASSERT_EQUAL(std::string("Ahoj"), dynamic_cast(getStanza(received[1]))->getBody()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast(getStanza(received[1]))->getTo().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("localhost"), dynamic_cast(getStanza(received[1]))->getFrom().toString()); + + CPPUNIT_ASSERT_EQUAL(0, userManager->getUserCount()); + CPPUNIT_ASSERT(streamEnded); + std::istringstream ifs2("service.server_mode = 1\nservice.jid_escaping=1\nservice.jid=localhost\nservice.more_resources=1\n"); + cfg->load(ifs2); + } + void handleProbePresence() { + UserInfo info; + info.id = 1; + info.jid = "user@localhost"; + storage->setUser(info); + Swift::Presence::ref response = Swift::Presence::create(); response->setTo("localhost"); response->setFrom("user@localhost/resource"); @@ -79,12 +105,63 @@ class UserManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTest { dynamic_cast(component->getStanzaChannel())->onPresenceReceived(response); loop->processEvents(); - CPPUNIT_ASSERT_EQUAL(2, (int) received.size()); + CPPUNIT_ASSERT_EQUAL(3, (int) received.size()); CPPUNIT_ASSERT(getStanza(received[0])->getPayload()); Swift::Presence *presence = dynamic_cast(getStanza(received[1])); CPPUNIT_ASSERT(presence); CPPUNIT_ASSERT_EQUAL(Swift::Presence::Unavailable, presence->getType()); + + presence = dynamic_cast(getStanza(received[2])); + CPPUNIT_ASSERT(presence); + CPPUNIT_ASSERT_EQUAL(Swift::Presence::Probe, presence->getType()); + + received.clear(); + response = Swift::Presence::create(); + response->setTo("localhost"); + response->setFrom("user@localhost"); + response->setType(Swift::Presence::Unsubscribed); + dynamic_cast(component->getStanzaChannel())->onPresenceReceived(response); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(2, (int) received.size()); + presence = dynamic_cast(getStanza(received[1])); + CPPUNIT_ASSERT(presence); + CPPUNIT_ASSERT_EQUAL(Swift::Presence::Subscribe, presence->getType()); + + received.clear(); + response = Swift::Presence::create(); + response->setTo("localhost"); + response->setFrom("user@localhost"); + response->setType(Swift::Presence::Error); + dynamic_cast(component->getStanzaChannel())->onPresenceReceived(response); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(0, (int) received.size()); + + response = Swift::Presence::create(); + response->setTo("localhost"); + response->setFrom("user@localhost"); + response->setType(Swift::Presence::Error); + response->addPayload(boost::shared_ptr(new Swift::ErrorPayload(Swift::ErrorPayload::SubscriptionRequired))); + dynamic_cast(component->getStanzaChannel())->onPresenceReceived(response); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + presence = dynamic_cast(getStanza(received[0])); + CPPUNIT_ASSERT(presence); + CPPUNIT_ASSERT_EQUAL(Swift::Presence::Subscribe, presence->getType()); + + storage->removeUser(1); + received.clear(); + response = Swift::Presence::create(); + response->setTo("localhost"); + response->setFrom("user@localhost"); + response->setType(Swift::Presence::Unsubscribed); + dynamic_cast(component->getStanzaChannel())->onPresenceReceived(response); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); } void connectTwoResources() { diff --git a/src/tests/util.cpp b/src/tests/util.cpp index f5c66819974148e45579dbfd2b4bb0f9b81f76d6..c091f2ece0832c8ce7c4f59230a9aa1523f36731 100644 --- a/src/tests/util.cpp +++ b/src/tests/util.cpp @@ -3,6 +3,7 @@ #include "transport/storagebackend.h" #include "transport/user.h" #include "transport/transport.h" +#include "transport/storagebackend.h" #include "transport/conversation.h" #include "transport/usermanager.h" #include "transport/localbuddy.h" @@ -36,8 +37,8 @@ class UtilTest : public CPPUNIT_NS :: TestFixture{ } void encryptDecryptPassword() { - std::string encrypted = Util::encryptPassword("password", "key"); - CPPUNIT_ASSERT_EQUAL(std::string("password"), Util::decryptPassword(encrypted, "key")); + std::string encrypted = StorageBackend::encryptPassword("password", "key"); + CPPUNIT_ASSERT_EQUAL(std::string("password"), StorageBackend::decryptPassword(encrypted, "key")); } }; diff --git a/src/transport.cpp b/src/transport.cpp index 99ef21d93bad98fddd108002f4978eb3f6063288..f4e0480b7bfee9436f632fd46d7c69ea123822b0 100644 --- a/src/transport.cpp +++ b/src/transport.cpp @@ -26,7 +26,6 @@ #include "transport/factory.h" #include "transport/userregistry.h" #include "transport/logging.h" -#include "discoinforesponder.h" #include "storageparser.h" #ifdef _WIN32 #include @@ -158,8 +157,7 @@ Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, m_presenceOracle = new Transport::PresenceOracle(m_stanzaChannel); m_presenceOracle->onPresenceChange.connect(bind(&Component::handlePresence, this, _1)); - m_discoInfoResponder = new DiscoInfoResponder(m_iqRouter, m_config); - m_discoInfoResponder->start(); + // // m_registerHandler = new SpectrumRegisterHandler(m_component); @@ -171,7 +169,6 @@ Component::~Component() { delete m_entityCapsManager; delete m_capsManager; delete m_capsMemoryStorage; - delete m_discoInfoResponder; if (m_component) delete m_component; if (m_server) { @@ -188,19 +185,6 @@ Transport::PresenceOracle *Component::getPresenceOracle() { return m_presenceOracle; } -void Component::setTransportFeatures(std::list &features) { - m_discoInfoResponder->setTransportFeatures(features); -} - -Swift::CapsInfo &Component::getBuddyCapsInfo() { - return m_discoInfoResponder->getBuddyCapsInfo(); -} - -void Component::setBuddyFeatures(std::list &features) { - // TODO: handle caps change - m_discoInfoResponder->setBuddyFeatures(features); -} - 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")); @@ -297,9 +281,16 @@ void Component::handlePresence(Swift::Presence::ref presence) { return; } - if (presence->getType() == Presence::Error) { - 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) { diff --git a/src/user.cpp b/src/user.cpp index f791b992d69704d6fa5ec761aae3705b5aae1c5a..558138a24ad5232ece6461324aa9a03defc2352f 100644 --- a/src/user.cpp +++ b/src/user.cpp @@ -176,14 +176,21 @@ void User::setConnected(bool connected) { updateLastActivity(); sendCurrentPresence(); + + if (m_connected) { + BOOST_FOREACH(Swift::Presence::ref &presence, m_joinedRooms) { + handlePresence(presence, true); + } + } } -void User::handlePresence(Swift::Presence::ref presence) { +void User::handlePresence(Swift::Presence::ref presence, bool forceJoin) { int currentResourcesCount = m_presenceOracle->getAllPresence(m_jid).size(); m_conversationManager->resetResources(); + 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) { @@ -229,6 +236,13 @@ void User::handlePresence(Swift::Presence::ref presence) { LOG4CXX_INFO(logger, m_jid.toString() << ": Going to left room " << room); 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; @@ -241,7 +255,13 @@ void User::handlePresence(Swift::Presence::ref presence) { 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()) { @@ -251,14 +271,35 @@ void User::handlePresence(Swift::Presence::ref presence) { conv->addJID(presence->getFrom()); conv->sendParticipants(presence->getFrom()); } + + if (forceJoin) { + onRoomJoined(presence->getFrom(), room, presence->getTo().getResource(), password); + } return; } - LOG4CXX_INFO(logger, m_jid.toString() << ": Going to join room " << room << " as " << presence->getTo().getResource()); - std::string password = ""; - if (presence->getPayload() != NULL) { - password = presence->getPayload()->getPassword() ? *presence->getPayload()->getPassword() : ""; + 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()); + onRoomJoined(presence->getFrom(), room, presence->getTo().getResource(), password); } return; diff --git a/src/usermanager.cpp b/src/usermanager.cpp index 98c75dcf07865128589287264f6d5c055d2cbf40..fd4305ce7eae16075ebd7030ef7a2052e9e18da3 100644 --- a/src/usermanager.cpp +++ b/src/usermanager.cpp @@ -26,6 +26,7 @@ #include "transport/rostermanager.h" #include "transport/userregistry.h" #include "transport/logging.h" +#include "transport/discoitemsresponder.h" #include "storageresponder.h" #include "Swiften/Swiften.h" @@ -40,7 +41,7 @@ namespace Transport { DEFINE_LOGGER(logger, "UserManager"); -UserManager::UserManager(Component *component, UserRegistry *userRegistry, StorageBackend *storageBackend) { +UserManager::UserManager(Component *component, UserRegistry *userRegistry, DiscoItemsResponder *discoItemsResponder, StorageBackend *storageBackend) { m_cachedUser = NULL; m_onlineBuddies = 0; m_sentToXMPP = 0; @@ -49,6 +50,7 @@ UserManager::UserManager(Component *component, UserRegistry *userRegistry, Stora m_storageBackend = storageBackend; m_storageResponder = NULL; m_userRegistry = userRegistry; + m_discoItemsResponder = discoItemsResponder; if (m_storageBackend) { m_storageResponder = new StorageResponder(component->getIQRouter(), m_storageBackend, this); @@ -182,14 +184,27 @@ void UserManager::handlePresence(Swift::Presence::ref presence) { } } + 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) { + 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; @@ -201,9 +216,6 @@ void UserManager::handlePresence(Swift::Presence::ref presence) { return; } - UserInfo res; - bool registered = m_storageBackend ? m_storageBackend->getUser(userkey, res) : false; - // 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. @@ -235,7 +247,8 @@ void UserManager::handlePresence(Swift::Presence::ref presence) { // 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")) { + if (!registered && (CONFIG_BOOL(m_component->getConfig(), "registration.auto_register") || + !CONFIG_BOOL_DEFAULTED(m_component->getConfig(), "registration.needRegistration", true))) { res.password = ""; res.jid = userkey; @@ -254,7 +267,7 @@ void UserManager::handlePresence(Swift::Presence::ref presence) { registered = m_storageBackend->getUser(userkey, res); } else { - registered = false; + registered = true; } } } @@ -265,6 +278,22 @@ void UserManager::handlePresence(Swift::Presence::ref presence) { 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"; @@ -370,12 +399,20 @@ void UserManager::handleGeneralPresenceReceived(Swift::Presence::ref presence) { case Swift::Presence::Probe: handleProbePresence(presence); break; + case Swift::Presence::Error: + handleErrorPresence(presence); + break; default: break; }; } 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) { @@ -390,7 +427,34 @@ void UserManager::handleProbePresence(Swift::Presence::ref presence) { } } +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(); @@ -406,6 +470,19 @@ void UserManager::handleSubscription(Swift::Presence::ref presence) { // 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()) { diff --git a/src/userregistration.cpp b/src/userregistration.cpp index a13006ca8ad43e865dd2aa83e3b49f5cd06f1016..4160f9e078d97323eaa8050612b00cae7a693196 100644 --- a/src/userregistration.cpp +++ b/src/userregistration.cpp @@ -94,7 +94,15 @@ void UserRegistration::handleUnregisterRemoteRosterResponse(boost::shared_ptr roster; m_storageBackend->getBuddies(userInfo.id, roster); for(std::list::iterator u = roster.begin(); u != roster.end() ; u++){ - std::string name = Swift::JID::getEscapedNode((*u).legacyName); + 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(); diff --git a/src/util.cpp b/src/util.cpp index 7589f21314dd71dfe2e1c3918bd5f49a5da95782..b8c3adc590bfad40393e57da69843a0f7fc8ef96 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -19,6 +19,7 @@ */ #include "transport/util.h" +#include "transport/config.h" #include #include #include @@ -27,6 +28,19 @@ #include #include +#ifndef WIN32 +#include "sys/signal.h" +#include +#include +#include +#include +#include "libgen.h" +#else +#include +#include +#define getpid _getpid +#endif + using namespace boost::filesystem; using namespace boost; @@ -35,6 +49,32 @@ namespace Transport { namespace Util { +void createDirectories(Transport::Config *config, const boost::filesystem::path& ph) { + if (ph.empty() || exists(ph)) { + return; + } + + // First create branch, by calling ourself recursively + createDirectories(config, ph.branch_path()); + + // Now that parent's path exists, create the directory + create_directory(ph); + +#ifndef WIN32 + if (!CONFIG_STRING(config, "service.group").empty() && !CONFIG_STRING(config, "service.user").empty()) { + struct group *gr; + if ((gr = getgrnam(CONFIG_STRING(config, "service.group").c_str())) == NULL) { + std::cerr << "Invalid service.group name " << CONFIG_STRING(config, "service.group") << "\n"; + } + struct passwd *pw; + if ((pw = getpwnam(CONFIG_STRING(config, "service.user").c_str())) == NULL) { + std::cerr << "Invalid service.user name " << CONFIG_STRING(config, "service.user") << "\n"; + } + chown(ph.string().c_str(), pw->pw_uid, gr->gr_gid); + } +#endif +} + void removeEverythingOlderThan(const std::vector &dirs, time_t t) { BOOST_FOREACH(const std::string &dir, dirs) { path p(dir); @@ -58,7 +98,7 @@ void removeEverythingOlderThan(const std::vector &dirs, time_t t) { std::vector nextDirs; nextDirs.push_back(itr->path().string()); removeEverythingOlderThan(nextDirs, t); - if (is_empty(itr->path())) { + if (boost::filesystem::is_empty(itr->path())) { remove_all(itr->path()); } } @@ -77,58 +117,6 @@ void removeEverythingOlderThan(const std::vector &dirs, time_t t) { } } -std::string encryptPassword(const std::string &password, const std::string &key) { - std::string encrypted; - encrypted.resize(password.size()); - for (int i = 0; i < password.size(); i++) { - char c = password[i]; - char keychar = key[i % key.size()]; - c += keychar; - encrypted[i] = c; - } - - encrypted = Swift::Base64::encode(Swift::createByteArray(encrypted)); - return encrypted; -} - -std::string decryptPassword(std::string &encrypted, const std::string &key) { - encrypted = Swift::byteArrayToString(Swift::Base64::decode(encrypted)); - std::string password; - password.resize(encrypted.size()); - for (int i = 0; i < encrypted.size(); i++) { - char c = encrypted[i]; - char keychar = key[i % key.size()]; - c -= keychar; - password[i] = c; - } - - return password; -} - -std::string serializeGroups(const std::vector &groups) { - std::string ret; - BOOST_FOREACH(const std::string &group, groups) { - ret += group + "\n"; - } - if (!ret.empty()) { - ret.erase(ret.end() - 1); - } - return ret; -} - -std::vector deserializeGroups(std::string &groups) { - std::vector ret; - if (groups.empty()) { - return ret; - } - - boost::split(ret, groups, boost::is_any_of("\n")); - if (ret.back().empty()) { - ret.erase(ret.end() - 1); - } - return ret; -} - int getRandomPort(const std::string &s) { unsigned long r = 0; BOOST_FOREACH(char c, s) {