Merge remote branch 'upstream/master'

This commit is contained in:
Daniel Henninger 2012-11-30 10:47:18 -05:00
commit 51533ef87b
79 changed files with 2050 additions and 1029 deletions

View file

@ -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 PQXX
if(ENABLE_PQXX)
set(pqxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
find_package(pqxx)
endif()
###### Plugins ######
# FIND LIBPURPLE
set(purple_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
find_package(purple)
if(ENABLE_PURPLE)
set(purple_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
find_package(purple)
if (WIN32)
if (PURPLE_NOT_RUNTIME)
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()

View file

@ -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()

View file

@ -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)

View file

@ -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()));

View file

@ -46,4 +46,5 @@ class IRCNetworkPlugin : public QObject, public NetworkPlugin {
std::vector<std::string> m_servers;
int m_currentServer;
std::string m_identify;
bool m_firstPing;
};

View file

@ -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;
}

View file

@ -41,6 +41,7 @@ public:
MyIrcSession(const std::string &user, IRCNetworkPlugin *np, const std::string &suffix = "", QObject* parent = 0);
std::map<std::string, bool> m_modes;
std::map<std::string, bool> m_away;
std::string suffix;
int rooms;
@ -85,39 +86,8 @@ protected:
AutoJoinMap m_autoJoin;
std::string m_topicData;
bool m_connected;
std::list<std::string> m_rooms;
std::list<std::string> 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

View file

@ -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);
}
}

View file

@ -36,14 +36,14 @@
#ifndef WIN32
#include "sys/wait.h"
#include "sys/signal.h"
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <netinet/ether.h>
#include "sys/socket.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#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);
struct sockaddr_in stSockAddr;
int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
hostent *hos; // Resolve name
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);
}
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;
return 0;
}
// int flags = fcntl(main_socket, F_GETFL);
// flags |= O_NONBLOCK;
// fcntl(main_socket, F_SETFL, flags);
return main_socket;
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;
}
return SocketFD;
}
#ifdef _WIN32

View file

@ -19,7 +19,9 @@
#include "sys/wait.h"
#include "sys/signal.h"
// #include "valgrind/memcheck.h"
#ifndef __FreeBSD__
#include "malloc.h"
#endif
#include <dbus-1.0/dbus/dbus-glib-lowlevel.h>

View file

@ -14,8 +14,10 @@
#include "sys/signal.h"
#endif
#ifndef __FreeBSD__
// malloc_trim
#include "malloc.h"
#endif
// Boost
#include <boost/algorithm/string.hpp>
@ -61,14 +63,15 @@ class SwiftenPlugin : public NetworkPlugin {
void handleSwiftDisconnected(const std::string &user, const boost::optional<Swift::ClientError> &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<Swift::Client> 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
}

View file

@ -322,7 +322,7 @@ void TwitterPlugin::pollForTweets()
std::set<std::string>::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,14 +517,39 @@ 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::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);
std::string ID = "-1";
if(onlineUsers.count(user)) ID = userdb[user].mostRecentTweetID;
return ID;
{
boost::mutex::scoped_lock lock(userlock);
return getMostRecentTweetIDUnsafe(user);
}
void TwitterPlugin::updateLastDMID(const std::string user, const std::string ID)
@ -622,16 +647,17 @@ void TwitterPlugin::displayTweets(std::string &user, std::string &userRequested,
std::map<std::string, int> lastTweet;
std::map<std::string, int>::iterator it;
for(int i=0 ; i<tweets.size() ; i++) {
for(int i = tweets.size() - 1 ; 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());
}
}

View file

@ -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};

View file

@ -1,5 +1,6 @@
#include "TwitterResponseParser.h"
#include "transport/logging.h"
#include "boost/algorithm/string.hpp"
#include <cctype>
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, "&amp;", "&");
replace_all(data, "&quot;", "\"");
replace_all(data, "&apos;", "\'");
replace_all(data, "&lt;", "<");
replace_all(data, "&gt;", ">");
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() ) );

View file

@ -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 <config_file.cfg>\nAllowed options");
desc.add_options()
("help", "help")
("host,h", boost::program_options::value<std::string>(&host)->default_value(""), "Host to connect to")
("port,p", boost::program_options::value<int>(&port)->default_value(10000), "Port to connect to")
("config", boost::program_options::value<std::string>(&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";
return 1;
}
Config config(argc, argv);
if (!config.load(configFile)) {
std::cerr << "Can't open " << argv[1] << " configuration file.\n";
return 1;
}
Logging::initBackendLogging(&config);
std::string error;
Config *cfg = Config::createFromArgs(argc, argv, error, host, port);
if (cfg == NULL) {
std::cerr << error;
return 1;
}
Logging::initBackendLogging(cfg);
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;

View file

@ -33,7 +33,7 @@ h2. Install spectrum2 - development version
After you have done that, simply do:
<pre>
apt-get install spectrum2-git spectrum2-backend-libpurple-git
apt-get install spectrum2 spectrum2-backend-libpurple
</pre>
Note that these repositories pull in quite a few dependencies, depending on the distribution you use.

View file

@ -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

View file

@ -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
</pre>
h3. Disable XML logging
<pre>
# 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
</pre>
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:

View file

@ -148,6 +148,8 @@ class Conversation {
Swift::JID m_jid;
std::list<Swift::JID> m_jids;
std::map<std::string, Participant> m_participants;
boost::shared_ptr<Swift::Message> m_subject;
bool m_sentInitialPresence;
};
}

View file

@ -69,6 +69,8 @@ class ConversationManager {
/// \param conv Conversation.
void removeConversation(Conversation *conv);
void deleteAllConversations();
void resetResources();
void removeJID(const Swift::JID &jid);

View file

@ -28,21 +28,30 @@
namespace Transport {
class Component;
class DiscoInfoResponder;
class DiscoItemsResponder : public Swift::GetResponder<Swift::DiscoItems> {
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<Swift::DiscoItems> payload);
private:
Component *m_component;
boost::shared_ptr<Swift::DiscoItems> m_commands;
boost::shared_ptr<Swift::DiscoItems> m_rooms;
DiscoInfoResponder *m_discoInfoResponder;
};
}

View file

@ -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;
};

View file

@ -24,6 +24,7 @@
#include "transport/protocol.pb.h"
// #include "conversation.h"
#include <iostream>
#include <list>
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 &timestamp = "");
/// 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<std::string> &rooms, const std::list<std::string> &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.

View file

@ -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<Swift::Message> &message);
private:
public:
void handleNewClientConnection(boost::shared_ptr<Swift::Connection> 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<Swift::Connection> &, const std::string &data);
void pingTimeout();
@ -154,6 +159,7 @@ class NetworkPluginServer {
std::vector<std::string> m_crashedBackends;
AdminInterface *m_adminInterface;
bool m_startingBackend;
DiscoItemsResponder *m_discoItemsResponder;
};
}

View file

@ -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;

View file

@ -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<std::string> &groups);
static std::vector<std::string> deserializeGroups(std::string &groups);
/// Virtual desctructor.
virtual ~StorageBackend() {}

View file

@ -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<std::string> &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<std::string> &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;

View file

@ -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<boost::shared_ptr<Swift::OutgoingFileTransfer> > m_filetransfers;
int m_resources;
int m_reconnectCounter;
std::list<Swift::Presence::ref> m_joinedRooms;
};
}

View file

@ -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<void (User *user)> 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<Swift::DiscoInfo> 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;
};

View file

@ -26,20 +26,17 @@
#include <string>
#include "Swiften/StringCodecs/Base64.h"
#include <boost/filesystem.hpp>
#include "transport/config.h"
namespace Transport {
namespace Util {
void createDirectories(Transport::Config *config, const boost::filesystem::path& ph);
void removeEverythingOlderThan(const std::vector<std::string> &dirs, time_t t);
std::string encryptPassword(const std::string &password, const std::string &key);
std::string decryptPassword(std::string &encrypted, const std::string &key);
std::string serializeGroups(const std::vector<std::string> &groups);
std::vector<std::string> deserializeGroups(std::string &groups);
int getRandomPort(const std::string &s);
#ifdef _WIN32

View file

@ -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)

View file

@ -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 &timestamp) {
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<std::string> &rooms, const std::list<std::string> &names) {
pbnetwork::RoomList d;
for (std::list<std::string>::const_iterator it = rooms.begin(); it != rooms.end(); it++) {
d.add_room(*it);
}
for (std::list<std::string>::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) {

View file

@ -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()

View file

@ -22,15 +22,18 @@
#include <boost/algorithm/string.hpp>
#ifndef WIN32
#include "sys/signal.h"
#include "sys/stat.h"
#include <pwd.h>
#include <grp.h>
#include <sys/resource.h>
#include "libgen.h"
#ifndef __FreeBSD__
#include <malloc.h>
#endif
#else
#include <process.h>
#define getpid _getpid
// #include "win32/SpectrumService.h"
#include "win32/ServiceWrapper.h"
#endif
#include <sys/stat.h>
@ -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<std::string>(&install_service_name)->default_value(""), "Install spectrum as Windows service")
("uninstall-service,u", boost::program_options::value<std::string>(&uninstall_service_name)->default_value(""), "Uninstall Windows service")
("run-as-service,r", boost::program_options::value<std::string>(&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>()
+ 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;
}
#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;
}
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);
} 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;
}

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -0,0 +1,19 @@
#pragma once
#include "transport/config.h"
#include <windows.h>
#include <tchar.h>
class ServiceWrapper
{
public:
ServiceWrapper(LPSTR serviceName);
~ServiceWrapper(void);
bool Install(LPSTR commandLine);
bool UnInstall();
bool IsInstalled();
void RunService();
};
int mainloop();
void stop();

View file

@ -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);
}

View file

@ -1,14 +0,0 @@
#include <windows.h>
#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);

View file

@ -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 <windows.h>
#include <assert.h>
#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);
}

View file

@ -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);
};

View file

@ -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 {

View file

@ -18,6 +18,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <netinet/in.h>
#if defined(_WIN32)
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
#else

View file

@ -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;
}

View file

@ -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()

View file

@ -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<Swift::Payload>(new Swift::CapsInfo(m_rosterManager->getUser()->getComponent()->getBuddyCapsInfo())));
presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::CapsInfo(m_rosterManager->getUser()->getUserManager()->getDiscoResponder()->getBuddyCapsInfo())));
// if (features & 0/*TRANSPORT_FEATURE_AVATARS*/) {
presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::VCardUpdate (getIconHash())));

View file

@ -97,6 +97,10 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description
("service.more_resources", value<bool>()->default_value(false), "Allow more resources to be connected in server mode at the same time.")
("service.enable_privacy_lists", value<bool>()->default_value(true), "")
("service.enable_xhtml", value<bool>()->default_value(true), "")
("service.max_room_list_size", value<int>()->default_value(100), "")
("service.jid_escaping", value<bool>()->default_value(true), "")
("service.vip_only", value<bool>()->default_value(false), "")
("service.vip_message", value<std::string>()->default_value(""), "")
("vhosts.vhost", value<std::vector<std::string> >()->multitoken(), "")
("identity.name", value<std::string>()->default_value("Spectrum 2 Transport"), "Name showed in service discovery.")
("identity.category", value<std::string>()->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<int>()->default_value(0), "Database port.")
("database.prefix", value<std::string>()->default_value(""), "Prefix of tables in database")
("database.encryption_key", value<std::string>()->default_value(""), "Encryption key.")
("database.vip_statement", value<std::string>()->default_value(""), "Encryption key.")
("logging.config", value<std::string>()->default_value(""), "Path to log4cxx config file which is used for Spectrum 2 instance")
("logging.backend_config", value<std::string>()->default_value(""), "Path to log4cxx config file which is used for backends")
("backend.default_avatar", value<std::string>()->default_value(""), "Full path to default avatar")

View file

@ -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<Swift::Message> &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<Swift::Message> &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();
}
}
}

View file

@ -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];

View file

@ -22,15 +22,19 @@
#include <iostream>
#include <boost/bind.hpp>
#include <boost/algorithm/string.hpp>
#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<DiscoInfo>(router) {
@ -80,19 +84,51 @@ void DiscoInfoResponder::setBuddyFeatures(std::list<std::string> &f) {
onBuddyCapsInfoChanged(m_capsInfo);
}
bool DiscoInfoResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::DiscoInfo> 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;
}
// presence for transport
void DiscoInfoResponder::clearRooms() {
m_rooms.clear();
}
void DiscoInfoResponder::addAdHocCommand(const std::string &node, const std::string &name) {
m_commands[node] = node;
}
bool DiscoInfoResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::DiscoInfo> info) {
// disco#info for transport
if (to.getNode().empty()) {
boost::shared_ptr<DiscoInfo> res(new DiscoInfo(m_transportInfo));
res->setNode(info->getNode());
sendResponse(from, id, res);
// Adhoc command
if (m_commands.find(info->getNode()) != m_commands.end()) {
boost::shared_ptr<DiscoInfo> 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<DiscoInfo> res(new DiscoInfo(m_transportInfo));
res->setNode(info->getNode());
sendResponse(from, id, res);
}
}
// presence for buddy
// disco#info for room
else if (m_rooms.find(to.toBare().toString()) != m_rooms.end()) {
boost::shared_ptr<DiscoInfo> res(new DiscoInfo());
res->addIdentity(DiscoInfo::Identity(m_rooms[to.toBare().toString()], "conference", "text"));
res->setNode(info->getNode());
sendResponse(from, to, id, res);
}
// disco#info for buddy
else {
boost::shared_ptr<DiscoInfo> res(new DiscoInfo(m_buddyInfo));
res->setNode(info->getNode());

View file

@ -38,6 +38,11 @@ class DiscoInfoResponder : public Swift::GetResponder<Swift::DiscoInfo> {
void setTransportFeatures(std::list<std::string> &features);
void setBuddyFeatures(std::list<std::string> &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<void (const Swift::CapsInfo &capsInfo)> onBuddyCapsInfoChanged;
Swift::CapsInfo &getBuddyCapsInfo() {
@ -51,6 +56,8 @@ class DiscoInfoResponder : public Swift::GetResponder<Swift::DiscoInfo> {
Swift::DiscoInfo m_buddyInfo;
Config *m_config;
Swift::CapsInfo m_capsInfo;
std::map<std::string, std::string> m_rooms;
std::map<std::string, std::string> m_commands;
};
}

View file

@ -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<DiscoItems>(new DiscoItems());
m_commands->setNode("http://jabber.org/protocol/commands");
m_rooms = boost::shared_ptr<DiscoItems>(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<DiscoItems>(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<DiscoItems>(new DiscoItems()));
sendResponse(from, id, m_rooms);
}
else {
sendResponse(from, id, boost::shared_ptr<DiscoItems>(new DiscoItems()));

View file

@ -20,6 +20,7 @@
#include "transport/logging.h"
#include "transport/config.h"
#include "transport/util.h"
#include <boost/foreach.hpp>
#include <iostream>
#include <iterator>
@ -31,6 +32,7 @@
#ifndef WIN32
#include "sys/signal.h"
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <sys/resource.h>
@ -136,35 +138,42 @@ static void initLogging(Config *config, std::string key) {
break;
}
}
mode_t old_cmask;
if (!dir.empty()) {
// create directories
try {
boost::filesystem::create_directories(
boost::filesystem::path(dir).parent_path().string()
);
}
catch (...) {
std::cerr << "Can't create logging directory directory " << boost::filesystem::path(dir).parent_path().string() << ".\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);
}
old_cmask = umask(0007);
#endif
try {
Transport::Util::createDirectories(config, boost::filesystem::path(dir).parent_path());
}
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";
}
}
log4cxx::PropertyConfigurator::configure(p);
// 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
#ifndef WIN32
if (!dir.empty()) {
umask(old_cmask);
}
#endif
}
}

View file

@ -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<std::string> &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<BuddyInfo> &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);

View file

@ -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<maxfd; fd++) {
close(fd);
}
// child process
errno = 0;
int ret = execv(argv[0], argv);
@ -231,7 +237,9 @@ static void handleBuddyPayload(LocalBuddy *buddy, const pbnetwork::Buddy &payloa
// Change groups if it's not empty. The same as above...
std::vector<std::string> 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,40 +290,6 @@ 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<int>(CONFIG_STRING(m_config, "service.backend_port")));
m_server->onNewConnection.connect(boost::bind(&NetworkPluginServer::handleNewClientConnection, this, _1));
m_server->start();
LOG4CXX_INFO(logger, "Listening on host " << CONFIG_STRING(m_config, "service.backend_host") << " port " << CONFIG_STRING(m_config, "service.backend_port"));
while (true) {
unsigned long pid = exec_(CONFIG_STRING(m_config, "service.backend"), CONFIG_STRING(m_config, "service.backend_host").c_str(), CONFIG_STRING(m_config, "service.backend_port").c_str(), m_config->getCommandLineArgs().c_str());
LOG4CXX_INFO(logger, "Tried to spawn first backend with pid " << pid);
LOG4CXX_INFO(logger, "Backend should now connect to Spectrum2 instance. Spectrum2 won't accept any connection before backend connects");
#ifndef _WIN32
// wait if the backend process will still be alive after 1 second
sleep(1);
pid_t result;
int status;
result = waitpid(-1, &status, WNOHANG);
if (result != 0) {
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) != 0) {
LOG4CXX_ERROR(logger, "Backend can not be started, exit_code=" << WEXITSTATUS(status) << ", possible error: " << strerror(WEXITSTATUS(status)));
continue;
}
}
else {
LOG4CXX_ERROR(logger, "Backend can not be started");
continue;
}
}
signal(SIGCHLD, SigCatcher);
#endif
// quit the while loop
break;
}
}
NetworkPluginServer::~NetworkPluginServer() {
@ -338,6 +313,48 @@ NetworkPluginServer::~NetworkPluginServer() {
delete m_blockResponder;
}
void NetworkPluginServer::start() {
m_server->start();
LOG4CXX_INFO(logger, "Listening on host " << CONFIG_STRING(m_config, "service.backend_host") << " port " << CONFIG_STRING(m_config, "service.backend_port"));
while (true) {
unsigned long pid = exec_(CONFIG_STRING(m_config, "service.backend"), CONFIG_STRING(m_config, "service.backend_host").c_str(), CONFIG_STRING(m_config, "service.backend_port").c_str(), m_config->getCommandLineArgs().c_str());
LOG4CXX_INFO(logger, "Tried to spawn first backend with pid " << pid);
LOG4CXX_INFO(logger, "Backend should now connect to Spectrum2 instance. Spectrum2 won't accept any connection before backend connects");
#ifndef _WIN32
// wait if the backend process will still be alive after 1 second
sleep(1);
pid_t result;
int status;
result = waitpid(-1, &status, WNOHANG);
if (result != 0) {
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) != 0) {
if (status == 254) {
LOG4CXX_ERROR(logger, "Backend can not be started, because it needs database to store data, but the database backend is not configured.");
}
else {
LOG4CXX_ERROR(logger, "Backend can not be started, exit_code=" << WEXITSTATUS(status) << ", possible error: " << strerror(WEXITSTATUS(status)));
}
LOG4CXX_ERROR(logger, "Check backend log for more details");
continue;
}
}
else {
LOG4CXX_ERROR(logger, "Backend can not be started");
continue;
}
}
signal(SIGCHLD, SigCatcher);
#endif
// quit the while loop
break;
}
}
void NetworkPluginServer::handleNewClientConnection(boost::shared_ptr<Swift::Connection> c) {
// Create new Backend instance
Backend *client = new Backend;
@ -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<std::string> 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<Swift::XHTMLIMPayload>(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<Swift::Delay>(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<Swift::SafeByteArray> 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_ptr<Swift::Sa
case pbnetwork::WrapperMessage_Type_TYPE_BACKEND_CONFIG:
handleBackendConfigPayload(wrapper.payload());
break;
case pbnetwork::WrapperMessage_Type_TYPE_ROOM_LIST:
handleRoomListPayload(wrapper.payload());
break;
default:
return;
}
@ -1189,12 +1253,6 @@ void NetworkPluginServer::handleRoomJoined(User *user, const Swift::JID &who, co
return;
}
send(c->connection, 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) {

View file

@ -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<BuddyInfo> &roster) {
b.flags = r[0][5].as<long>();
if (!group.empty()) {
b.groups = Util::deserializeGroups(group);
b.groups = StorageBackend::deserializeGroups(group);
}
roster.push_back(b);

View file

@ -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:

View file

@ -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) {

View file

@ -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<BuddyInfo> &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) {

View file

@ -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<std::string> &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<std::string> StorageBackend::deserializeGroups(std::string &groups) {
std::vector<std::string> 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;
}
}

View file

@ -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<Swift::DiscoInfo>());
received.clear();
receivedData.clear();
}
@ -236,6 +237,7 @@ void BasicTest::connectSecondResource() {
}
void BasicTest::disconnectUser() {
received.clear();
userManager->disconnectUser("user@localhost");
dynamic_cast<Swift::DummyTimerFactory *>(factories->getTimerFactory())->setTime(10);
loop->processEvents();

View file

@ -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);
}

View file

@ -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<Swift::Message> 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<Swift::Message *>(getStanza(received[0])));
CPPUNIT_ASSERT_EQUAL(std::string("subject"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getSubject());
CPPUNIT_ASSERT_EQUAL(2, (int) received.size());
CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[1])));
CPPUNIT_ASSERT_EQUAL(std::string("subject"), dynamic_cast<Swift::Message *>(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<Swift::Message> 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<Swift::Message *>(getStanza(received[0])));
CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getBody());
CPPUNIT_ASSERT_EQUAL(std::string("hi there<>!"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getBody());
CPPUNIT_ASSERT_EQUAL(std::string("user@localhost"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getTo().toString());
CPPUNIT_ASSERT_EQUAL(std::string("buddy1@localhost/bot"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getFrom().toString());
CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40test@localhost/bot"), dynamic_cast<Swift::Message *>(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<Swift::Message> msg2(new Swift::Message());
@ -169,20 +178,28 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe
CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[0])));
CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getBody());
CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getTo().toString());
CPPUNIT_ASSERT_EQUAL(std::string("buddy1@localhost/bot"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getFrom().toString());
CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40test@localhost/bot"), dynamic_cast<Swift::Message *>(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<Swift::Message *>(getStanza(received[0])));
CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getBody());
CPPUNIT_ASSERT_EQUAL(std::string("user@localhost"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getTo().toString());
CPPUNIT_ASSERT_EQUAL(std::string("buddy1@localhost/bot"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getFrom().toString());
CPPUNIT_ASSERT_EQUAL(std::string("buddy1%test@localhost/bot"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getFrom().toString());
received.clear();
}

View file

@ -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 <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <Swiften/Swiften.h>
#include <Swiften/EventLoop/DummyEventLoop.h>
#include <Swiften/Server/Server.h>
#include <Swiften/Network/DummyNetworkFactories.h>
#include <Swiften/Network/DummyConnectionServer.h>
#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<Swift::DiscoItems> payload(new Swift::DiscoItems());
boost::shared_ptr<Swift::IQ> 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<Swift::IQ *>(getStanza(received[0])));
CPPUNIT_ASSERT_EQUAL(Swift::IQ::Result, dynamic_cast<Swift::IQ *>(getStanza(received[0]))->getType());
CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::DiscoItems>());
CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost"), getStanza(received[0])->getPayload<Swift::DiscoItems>()->getItems()[0].getJID().toString());
CPPUNIT_ASSERT_EQUAL(std::string("#room"), getStanza(received[0])->getPayload<Swift::DiscoItems>()->getItems()[0].getName());
}
void roomInfo() {
itemsResponder->addRoom("#room@localhost", "#room");
boost::shared_ptr<Swift::DiscoInfo> payload(new Swift::DiscoInfo());
boost::shared_ptr<Swift::IQ> 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<Swift::IQ *>(getStanza(received[0])));
CPPUNIT_ASSERT_EQUAL(Swift::IQ::Result, dynamic_cast<Swift::IQ *>(getStanza(received[0]))->getType());
CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::DiscoInfo>());
CPPUNIT_ASSERT_EQUAL(std::string("#room"), getStanza(received[0])->getPayload<Swift::DiscoInfo>()->getIdentities()[0].getName());
CPPUNIT_ASSERT_EQUAL(std::string("conference"), getStanza(received[0])->getPayload<Swift::DiscoInfo>()->getIdentities()[0].getCategory());
CPPUNIT_ASSERT_EQUAL(std::string("text"), getStanza(received[0])->getPayload<Swift::DiscoInfo>()->getIdentities()[0].getType());
}
void clearRooms() {
itemsResponder->addRoom("#room@localhost", "#room");
itemsResponder->clearRooms();
boost::shared_ptr<Swift::DiscoItems> payload(new Swift::DiscoItems());
boost::shared_ptr<Swift::IQ> 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<Swift::IQ *>(getStanza(received[0])));
CPPUNIT_ASSERT_EQUAL(Swift::IQ::Result, dynamic_cast<Swift::IQ *>(getStanza(received[0]))->getType());
CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::DiscoItems>());
CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::DiscoItems>()->getItems().empty());
}
};
CPPUNIT_TEST_SUITE_REGISTRATION (DiscoItemsResponderTest);

View file

@ -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<std::string> 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"));

View file

@ -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<std::string> 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<std::string>::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);

View file

@ -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 <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <Swiften/Swiften.h>
#include <Swiften/EventLoop/DummyEventLoop.h>
#include <Swiften/Server/Server.h>
#include <Swiften/Network/DummyNetworkFactories.h>
#include <Swiften/Network/DummyConnectionServer.h>
#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<Swift::RosterPayload>();
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<Swift::RosterPayload>();
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);

View file

@ -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 <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <Swiften/Swiften.h>
#include <Swiften/EventLoop/DummyEventLoop.h>
#include <Swiften/Server/Server.h>
#include <Swiften/Network/DummyNetworkFactories.h>
#include <Swiften/Network/DummyConnectionServer.h>
#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<Swift::IQ> 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);

View file

@ -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<Swift::DiscoItems>()->getItems()[0].getNode());
}
void getInfo() {
boost::shared_ptr<Swift::DiscoInfo> payload(new Swift::DiscoInfo());
payload->setNode("settings");
boost::shared_ptr<Swift::IQ> 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<Swift::IQ *>(getStanza(received[0])));
CPPUNIT_ASSERT_EQUAL(Swift::IQ::Result, dynamic_cast<Swift::IQ *>(getStanza(received[0]))->getType());
CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::DiscoInfo>());
CPPUNIT_ASSERT_EQUAL(std::string("automation"), getStanza(received[0])->getPayload<Swift::DiscoInfo>()->getIdentities()[0].getCategory());
CPPUNIT_ASSERT_EQUAL(std::string("command-node"), getStanza(received[0])->getPayload<Swift::DiscoInfo>()->getIdentities()[0].getType());
}
void executeNotRegistered() {
boost::shared_ptr<Swift::Command> payload(new Swift::Command("settings"));
boost::shared_ptr<Swift::IQ> iq = Swift::IQ::createRequest(Swift::IQ::Set, Swift::JID("localhost"), "id", payload);

View file

@ -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 <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <Swiften/Swiften.h>
#include <Swiften/EventLoop/DummyEventLoop.h>
#include <Swiften/Server/Server.h>
#include <Swiften/Parser/StringTreeParser.h>
#include <Swiften/Network/DummyNetworkFactories.h>
#include <Swiften/Network/DummyConnectionServer.h>
#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("<body>&lt;test&gt;</body>");
CPPUNIT_ASSERT_EQUAL(std::string("<test>"), root->getText());
}
};
CPPUNIT_TEST_SUITE_REGISTRATION (StringTreeParserTest);

View file

@ -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<Swift::Payload>(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<Swift::DiscoInfo>());
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());
}
};

View file

@ -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<Swift::Message *>(getStanza(received[1])));
CPPUNIT_ASSERT_EQUAL(std::string("Ahoj"), dynamic_cast<Swift::Message *>(getStanza(received[1]))->getBody());
CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Message *>(getStanza(received[1]))->getTo().toString());
CPPUNIT_ASSERT_EQUAL(std::string("localhost"), dynamic_cast<Swift::Message *>(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<Swift::ServerStanzaChannel *>(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::DiscoInfo>());
Swift::Presence *presence = dynamic_cast<Swift::Presence *>(getStanza(received[1]));
CPPUNIT_ASSERT(presence);
CPPUNIT_ASSERT_EQUAL(Swift::Presence::Unavailable, presence->getType());
presence = dynamic_cast<Swift::Presence *>(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<Swift::ServerStanzaChannel *>(component->getStanzaChannel())->onPresenceReceived(response);
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(2, (int) received.size());
presence = dynamic_cast<Swift::Presence *>(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<Swift::ServerStanzaChannel *>(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<Swift::ErrorPayload>(new Swift::ErrorPayload(Swift::ErrorPayload::SubscriptionRequired)));
dynamic_cast<Swift::ServerStanzaChannel *>(component->getStanzaChannel())->onPresenceReceived(response);
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
presence = dynamic_cast<Swift::Presence *>(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<Swift::ServerStanzaChannel *>(component->getStanzaChannel())->onPresenceReceived(response);
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
}
void connectTwoResources() {

View file

@ -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"));
}
};

View file

@ -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 <Swiften/TLS/CAPICertificate.h>
@ -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<std::string> &features) {
m_discoInfoResponder->setTransportFeatures(features);
}
Swift::CapsInfo &Component::getBuddyCapsInfo() {
return m_discoInfoResponder->getBuddyCapsInfo();
}
void Component::setBuddyFeatures(std::list<std::string> &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) {

View file

@ -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<Swift::MUCPayload>() != NULL) {
password = presence->getPayload<Swift::MUCPayload>()->getPassword() ? *presence->getPayload<Swift::MUCPayload>()->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;
}
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());
std::string password = "";
if (presence->getPayload<Swift::MUCPayload>() != NULL) {
password = presence->getPayload<Swift::MUCPayload>()->getPassword() ? *presence->getPayload<Swift::MUCPayload>()->getPassword() : "";
}
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;

View file

@ -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<Swift::Message> 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<Swift::ErrorPayload>()) {
return;
}
if (presence->getPayload<Swift::ErrorPayload>()->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()) {

View file

@ -94,7 +94,15 @@ void UserRegistration::handleUnregisterRemoteRosterResponse(boost::shared_ptr<Sw
std::list <BuddyInfo> roster;
m_storageBackend->getBuddies(userInfo.id, roster);
for(std::list<BuddyInfo>::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();

View file

@ -19,6 +19,7 @@
*/
#include "transport/util.h"
#include "transport/config.h"
#include <boost/foreach.hpp>
#include <iostream>
#include <iterator>
@ -27,6 +28,19 @@
#include <boost/algorithm/string.hpp>
#include <boost/numeric/conversion/cast.hpp>
#ifndef WIN32
#include "sys/signal.h"
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <sys/resource.h>
#include "libgen.h"
#else
#include <windows.h>
#include <process.h>
#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<std::string> &dirs, time_t t) {
BOOST_FOREACH(const std::string &dir, dirs) {
path p(dir);
@ -58,7 +98,7 @@ void removeEverythingOlderThan(const std::vector<std::string> &dirs, time_t t) {
std::vector<std::string> 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<std::string> &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<std::string> &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<std::string> deserializeGroups(std::string &groups) {
std::vector<std::string> 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) {