Changeset - 317b82998a01
[Not reviewed]
Merge
0 74 0
Vitaly Takmazov - 13 years ago 2013-01-29 08:22:01
vitalyster@gmail.com
Merge branch 'master' of https://github.com/hanzz/libtransport
73 files changed with 669 insertions and 229 deletions:
0 comments (0 inline, 0 general)
CMakeLists.txt
Show inline comments
 
cmake_minimum_required(VERSION 2.6)
 
project(libtransport)
 

	
 
message(STATUS "Variables to override default places where to find libraries:")
 
message(STATUS "|- cppunit : -DCPPUNIT_INCLUDE_DIR,  -DCPPUNIT_LIBRARY")
 
message(STATUS "|- swiften : -DSWIFTEN_INCLUDE_DIR,  -DSWIFTEN_LIBRARY")
 
message(STATUS "  |- zlib  :                         -DZLIB_LIBRARY")
 
message(STATUS "  |- expat :                         -DEXPAT_LIBRARY")
 
message(STATUS "  |-libidn :                         -DLIBIDN_LIBRARY")
 
message(STATUS "  |-libxml :                         -DLIBXML_LIBRARY")
 
message(STATUS "|- boost   : -DBOOST_INCLUDEDIR,     -DBOOST_LIBRARYDIR")
 
message(STATUS "|- protobuf: -DPROTOBUF_INCLUDE_DIR, -DPROTOBUF_LIBRARY")
 
message(STATUS "           : -DPROTOBUF_PROTOC_EXECUTABLE")
 
message(STATUS "|- log4cxx : -DLOG4CXX_INCLUDE_DIR,  -DLOG4CXX_LIBRARY")
 
message(STATUS "|- purple  : -DPURPLE_INCLUDE_DIR,   -DPURPLE_LIBRARY")
 
message(STATUS "           : -DPURPLE_NOT_RUNTIME - enables compilation with libpurple.lib")
 

	
 
option(ENABLE_SQLITE3 "Build with SQLite3 support" ON)
 
option(ENABLE_MYSQL "Build with MySQL support" ON)
 
option(ENABLE_PQXX "Build with Postgres supoort" ON)
 

	
 
option(ENABLE_FROTZ "Build Frotz plugin" ON)
 
option(ENABLE_IRC "Build IRC plugin" ON)
 
option(ENABLE_PURPLE "Build Libpurple plugin" ON)
 
option(ENABLE_SMSTOOLS3 "Build SMSTools3 plugin" ON)
 
option(ENABLE_SKYPE "Build Skype plugin" ON)
 
option(ENABLE_SWIFTEN "Build Swiften plugin" ON)
 
option(ENABLE_TWITTER "Build Twitter plugin" ON)
 
option(ENABLE_YAHOO2 "Build Libyahoo2 plugin" ON)
 

	
 
option(ENABLE_DOCS "Build Docs" ON)
 
option(ENABLE_LOG "Build with logging using Log4cxx" ON)
 
# option(ENABLE_LOG "Build with logging using Log4cxx" ON)
 
option(ENABLE_TESTS "Build Tests using CppUnit" OFF)
 

	
 
MACRO(LIST_CONTAINS var value)
 
	SET(${var})
 
	FOREACH (value2 ${ARGN})
 
		IF (${value} STREQUAL ${value2})
 
		SET(${var} TRUE)
 
		ENDIF (${value} STREQUAL ${value2})
 
	ENDFOREACH (value2)
 
ENDMACRO(LIST_CONTAINS)
 

	
 
if(NOT LIB_INSTALL_DIR)
 
	set(LIB_INSTALL_DIR "lib")
 
endif()
 

	
 
set(CMAKE_MODULE_PATH "cmake_modules")
 

	
 
###### Prerequisites ######
 

	
 

	
 
# FIND SWIFTEN
 

	
 
set(Swiften_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(Swiften)
 

	
 
if(NOT SWIFTEN_FOUND)
 
	if (ZLIB_LIBRARY)
 
		set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${ZLIB_LIBRARY})
 
	endif()
 
	if (EXPAT_LIBRARY)
 
		set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${EXPAT_LIBRARY})
 
	endif()
 
	if (LIBIDN_LIBRARY)
 
		set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${LIBIDN_LIBRARY})
 
	endif()
 
	if (LIBXML_LIBRARY)
 
		set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${LIBXML_LIBRARY})
 
	endif()
 
	set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Dnsapi")
 
	set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Crypt32")
 
	set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Secur32")
 
	set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Iphlpapi")
 
	set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Winscard")
 
	message(STATUS "Using swiften: ${SWIFTEN_INCLUDE_DIR} ${SWIFTEN_LIBRARY}")
 
endif()
 

	
 
# FIND BOOST
 
set(Boost_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
if (WIN32)
 
	set(Boost_USE_STATIC_LIBS      ON)
 
	set(Boost_USE_MULTITHREADED      ON)
 
	set(Boost_USE_STATIC_RUNTIME    OFF)
 
	find_package(Boost COMPONENTS program_options date_time system filesystem regex thread signals REQUIRED)
 
else(WIN32)
 
	LIST_CONTAINS(contains -lboost_program_options ${SWIFTEN_LIBRARY})
 
	if(contains)
 
		message(STATUS "Using non-multithreaded boost")
 
		set(Boost_USE_MULTITHREADED 0)
 
	endif(contains)
 
	set(Boost_FIND_QUIETLY ON)
 
	find_package(Boost COMPONENTS program_options date_time system filesystem regex thread-mt signals)
 
	if (NOT Boost_FOUND)
 
		set(Boost_FIND_QUIETLY OFF)
 
		find_package(Boost COMPONENTS program_options date_time system filesystem regex thread signals REQUIRED)
 
	endif()
 
endif(WIN32)
 

	
 
message( STATUS "Found Boost: ${Boost_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 (ENABLE_SQLITE3)
 
	if (NOT CMAKE_COMPILER_IS_GNUCXX)
 
		ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/msvc-deps)
 
	else()
 
		if (WIN32)
 
			ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/msvc-deps/sqlite3)
 
		else()
 
			set(sqlite3_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
			find_package(sqlite3)
 
		endif()
 
	endif()
 
endif()
 

	
 
# FIND MYSQL
 
if(ENABLE_MYSQL)
 
	set(mysql_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(mysql)
 
endif()
 
@@ -132,376 +132,376 @@ if(ENABLE_PQXX)
 
	set(pqxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(pqxx)
 
endif()
 

	
 
###### Plugins ######
 

	
 
# FIND LIBPURPLE
 
if(ENABLE_PURPLE)
 
	set(purple_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(purple)
 

	
 
	if (WIN32)
 
		if (PURPLE_NOT_RUNTIME)
 
			add_definitions(-DPURPLE_RUNTIME=0)
 
		else(PURPLE_NOT_RUNTIME)
 
			add_definitions(-DPURPLE_RUNTIME=1)
 
		endif(PURPLE_NOT_RUNTIME)
 
	else()
 
		add_definitions(-DPURPLE_RUNTIME=0)
 
	endif()
 

	
 
	# FIND LIBEVENT
 
	set(event_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(event)
 
endif()
 

	
 
# FIND GLIB
 
if(ENABLE_SKYPE OR ENABLE_PURPLE)
 
#	if (GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES)
 
#		set(GLIB2_FOUND TRUE)
 
#	else()
 
		set(glib_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
		find_package(glib)
 
#	endif()
 
endif()
 

	
 
# FIND LIBXML2
 
# set(libxml2_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
# find_package(libxml2)
 

	
 
# FIND PROTOBUF
 
set(Protobuf_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(Protobuf REQUIRED)
 

	
 
if (NOT PROTOBUF_FOUND AND PROTOBUF_INCLUDE_DIR AND PROTOBUF_LIBRARY)
 
	set(PROTOBUF_FOUND 1)
 
	set(PROTOBUF_INCLUDE_DIRS ${PROTOBUF_INCLUDE_DIR})
 
	if (PROTOBUF_PROTOC_EXECUTABLE)
 
	else()
 
		set(PROTOBUF_PROTOC_EXECUTABLE protoc)
 
	endif()
 
	message(STATUS "Using protobuf: ${PROTOBUF_INCLUDE_DIRS} ${PROTOBUF_LIBRARY}")
 
endif()
 

	
 
if (WIN32)
 
	add_definitions(-DSWIFTEN_STATIC=1)
 
	ADD_DEFINITIONS(-D_UNICODE)
 
	ADD_DEFINITIONS(-DUNICODE)
 
endif()
 

	
 

	
 
if (CMAKE_COMPILER_IS_GNUCXX)
 
set(openssl_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(openssl)
 
endif()
 

	
 
if(ENABLE_IRC)
 
	set(Communi_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(Communi)
 

	
 
	INCLUDE(FindQt4)
 
	FIND_PACKAGE(Qt4 COMPONENTS QtCore QtNetwork)
 
	# ADD_DEFINITIONS(${SWIFTEN_CFLAGS})
 
	ADD_DEFINITIONS(-DSUPPORT_LEGACY_CAPS)
 
	# ADD_DEFINITIONS(-DBOOST_FILESYSTEM_VERSION=2)
 
endif()
 

	
 
set(event_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(event)
 

	
 
if (NOT WIN32 AND ENABLE_SKYPE)
 
set(dbus_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(dbus)
 
endif()
 

	
 
# if(ENABLE_YAHOO2)
 
# 	set(yahoo2_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
# 	find_package(yahoo2)
 
# endif()
 

	
 
####### Miscallanous ######
 

	
 
if(ENABLE_DOCS)
 
	find_package(Doxygen)
 
endif()
 

	
 
if(ENABLE_LOG)
 
# if(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()
 
# 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("-----------------------")
 

	
 
if (SPECTRUM_VERSION)
 
	ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}")
 
else (SPECTRUM_VERSION)
 
	if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
 
		if (NOT GIT_EXECUTABLE)
 
		set (GIT_EXECUTABLE git)
 
		endif()
 
		execute_process(COMMAND ${GIT_EXECUTABLE} "--git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git" rev-parse --short HEAD
 
						OUTPUT_VARIABLE GIT_REVISION
 
						OUTPUT_STRIP_TRAILING_WHITESPACE
 
		)
 
		set(SPECTRUM_VERSION 2.0.0-beta-git-${GIT_REVISION})
 
		ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}")
 
	else (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
 
		set(SPECTRUM_VERSION 2.0.0-alpha)
 
		ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}")
 
	endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
 
endif (SPECTRUM_VERSION)
 

	
 
message("Version           : " ${SPECTRUM_VERSION})
 

	
 
if (SQLITE3_FOUND)
 
	ADD_DEFINITIONS(-DWITH_SQLITE)
 
	include_directories(${SQLITE3_INCLUDE_DIR})
 
	message("SQLite3           : yes")
 
else (SQLITE3_FOUND)
 
	if (WIN32)
 
		ADD_DEFINITIONS(-DWITH_SQLITE)
 
		include_directories("${CMAKE_SOURCE_DIR}/msvc-deps/sqlite3")
 
		message("SQLite3           : bundled")
 
	else()
 
		set(SQLITE3_LIBRARIES "")
 
		if(ENABLE_SQLITE3)
 
			message("SQLite3           : no (install sqlite3)")
 
		else(ENABLE_SQLITE3)
 
			message("SQLite3           : no (user disabled)")
 
		endif()
 
	endif()
 
endif (SQLITE3_FOUND)
 

	
 
if (MYSQL_FOUND)
 
	ADD_DEFINITIONS(-DWITH_MYSQL)
 
	include_directories(${MYSQL_INCLUDE_DIR})
 
	message("MySQL             : yes")
 
else (MYSQL_FOUND)
 
	set(MYSQL_LIBRARIES "")
 
	if(ENABLE_MYSQL)
 
		message("MySQL             : no (install mysql-devel)")
 
	else(ENABLE_MYSQL)
 
		message("MySQL             : no (user disabled)")
 
	endif()
 
endif (MYSQL_FOUND)
 

	
 
if (PQXX_FOUND)
 
	ADD_DEFINITIONS(-DWITH_PQXX)
 
	include_directories(${PQXX_INCLUDE_DIR})
 
	message("PostgreSQL        : yes")
 
else (PQXX_FOUND)
 
	set(PQXX_LIBRARY "")
 
	set(PQ_LIBRARY "")
 
	if(ENABLE_PQXX)
 
		message("PostgreSQL        : no (install libpqxx-devel)")
 
	else(ENABLE_PQXX)
 
		message("PostgreSQL        : no (user disabled)")
 
	endif()
 
endif (PQXX_FOUND)
 

	
 
if (PROTOBUF_FOUND)
 
	ADD_DEFINITIONS(-DWITH_PROTOBUF)
 
	include_directories(${PROTOBUF_INCLUDE_DIRS})
 
	message("Network plugins   : yes")
 

	
 
	if(PURPLE_FOUND)
 
		message("Libpurple plugin  : yes")
 
		include_directories(${PURPLE_INCLUDE_DIR})
 
		include_directories(${GLIB2_INCLUDE_DIR})
 
	else()
 
		if(ENABLE_PURPLE)
 
			message("Libpurple plugin  : no (install libpurple)")
 
		else(ENABLE_PURPLE)
 
			message("Libpurple plugin  : no (user disabled)")
 
		endif()
 
	endif()
 

	
 
	if (HAVE_EVENT)
 
		ADD_DEFINITIONS(-DWITH_LIBEVENT)
 
		include_directories(${EVENT_INCLUDE_DIRS})
 
		message("  libev eventloop : yes")
 
	else()
 
		if(ENABLE_PURPLE)
 
			message("  libev eventloop : no (install libev-devel)")
 
		endif()
 
	endif()
 

	
 
	if(IRC_FOUND)
 
		ADD_DEFINITIONS(-DCOMMUNI_SHARED)
 
		message("IRC plugin        : yes")
 
		include_directories(${QT_QTNETWORK_INCLUDE_DIR})
 
		include_directories(${IRC_INCLUDE_DIR})
 
		include(${QT_USE_FILE})
 
	else()
 
		if(ENABLE_IRC)
 
			message("IRC plugin        : no (install libCommuni and libprotobuf-dev)")
 
		else(ENABLE_IRC)
 
			message("IRC plugin        : no (user disabled)")
 
		endif()
 
	endif()
 
	if(ENABLE_TWITTER)
 
		message("Twitter plugin    : yes")
 
	else(ENABLE_TWITTER)
 
		message("Twitter plugin    : no (user disabled)")
 
	endif()
 
	if (NOT WIN32)
 
		if(ENABLE_FROTZ)
 
			message("Frotz plugin      : yes")
 
		else()
 
			message("Frotz plugin      : no (user disabled)")
 
		endif()
 
		if(ENABLE_SMSTOOLS3)
 
			message("SMSTools3 plugin  : yes")
 
		else()
 
			message("SMSTools3 plugin  : no (user disabled)")
 
		endif()
 
		if(${LIBDBUSGLIB_FOUND})
 
			message("Skype plugin      : yes")
 
			include_directories(${LIBDBUSGLIB_INCLUDE_DIRS})
 
		else()
 
			if(ENABLE_SKYPE)
 
				message("Skype plugin      : no (install dbus-glib-devel)")
 
			else(ENABLE_SKYPE)
 
				message("Skype plugin      : no (user disabled)")
 
			endif()
 
		endif()
 
	else()
 
		message("Frotz plugin      : no (does not run on Win32)")
 
		message("SMSTools3 plugin  : no (does not run on Win32)")
 
		message("Skype plugin      : no (does not run on Win32)")
 
	endif()
 

	
 
#  	if(YAHOO2_FOUND)
 
# 		message("Libyahoo2 plugin  : yes")
 
#  		include_directories(${YAHOO2_INCLUDE_DIR})
 
# 	else()
 
		if(ENABLE_YAHOO2)
 
			set(YAHOO2_FOUND 1)
 
			message("Libyahoo2 plugin  : yes")
 
		else(ENABLE_YAHOO2)
 
			message("Libyahoo2 plugin  : no (user disabled)")
 
		endif()
 
#  	endif()
 

	
 
	if(ENABLE_SWIFTEN)
 
		message("Swiften plugin    : yes")
 
	else()
 
		message("Swiften plugin    : no (user disabled)")
 
	endif()
 
else()
 
	message("Network plugins   : no (install libprotobuf-dev)")
 
	message("Libpurple plugin  : no (install libpurple and libprotobuf-dev)")
 
	message("IRC plugin        : no (install libircclient-qt and libprotobuf-dev)")
 
	message("Frotz plugin      : no (install libprotobuf-dev)")
 
	message("SMSTools3 plugin  : no (install libprotobuf-dev)")
 
	message("Swiften plugin    : no (install libprotobuf-dev)")
 
    message("Twitter plugin    : no (install libprotobuf-dev)")
 
endif()
 

	
 
if (LOG4CXX_FOUND)
 
	message("Log4cxx           : yes")
 
	include_directories(${LOG4CXX_INCLUDE_DIR})
 
	ADD_DEFINITIONS(-DWITH_LOG4CXX)
 
else()
 
	set(LOG4CXX_LIBRARIES "")
 
	if(ENABLE_LOG)
 
	if (WIN32)
 
		message("Log4cxx           : no (install log4cxx-devel)")
 
	else(ENABLE_LOG)
 
		message("Log4cxx           : no (user disabled)")
 
	else()
 
		message(FATAL_ERROR "Log4cxx           : no (install log4cxx-devel)")
 
	endif()
 
endif()
 

	
 
if (WIN32)
 
	ADD_DEFINITIONS(-DLOG4CXX_STATIC)
 
	ADD_DEFINITIONS(-D_WIN32_WINNT=0x501)
 
	ADD_DEFINITIONS(-DWIN32_LEAN_AND_MEAN)
 
	ADD_DEFINITIONS(-DBOOST_USE_WINDOWS_H)
 
	ADD_DEFINITIONS(-DBOOST_THREAD_USE_LIB)
 
endif()
 

	
 
if(CMAKE_BUILD_TYPE MATCHES Debug)
 
	if (CMAKE_COMPILER_IS_GNUCXX)
 
		ADD_DEFINITIONS(-O0)
 
		ADD_DEFINITIONS(-ggdb)
 
	endif()
 
	ADD_DEFINITIONS(-DDEBUG)
 
	message("Debug             : yes")
 
else(CMAKE_BUILD_TYPE MATCHES Debug)
 
	message("Debug             : no (run \"cmake . -DCMAKE_BUILD_TYPE=Debug\")")
 
endif(CMAKE_BUILD_TYPE MATCHES Debug)
 

	
 

	
 
SET(TRANSPORT_VERSION 2.0)
 
SET(PROJECT_VERSION 2.0)
 
include_directories(include)
 

	
 

	
 
include_directories(${EVENT_INCLUDE_DIRS})
 
include_directories(${SWIFTEN_INCLUDE_DIR})
 
include_directories(${Boost_INCLUDE_DIRS})
 

	
 
if (CMAKE_COMPILER_IS_GNUCXX)
 
include_directories(${OPENSSL_INCLUDE_DIR})
 
endif()
 

	
 
ADD_SUBDIRECTORY(src)
 
ADD_SUBDIRECTORY(plugin)
 
ADD_SUBDIRECTORY(include)
 
ADD_SUBDIRECTORY(spectrum)
 
ADD_SUBDIRECTORY(backends)
 
if (NOT WIN32)
 
	ADD_SUBDIRECTORY(spectrum_manager)
 
#	ADD_SUBDIRECTORY(spectrum2_send_message)
 
endif()
 

	
 
if (CPPUNIT_FOUND)
 
	message("tests             : yes")
 
	include_directories(${CPPUNIT_INCLUDE_DIR})
 
else()
 
	if(ENABLE_TESTS)
 
		message("tests             : no (install CPPUnit)")
 
	else(ENABLE_TESTS)
 
		message("tests             : no (user disabled)")
 
	endif()
 
endif()
 

	
 
if(DOXYGEN_FOUND)
 
	message("Docs              : yes")
 
	ADD_SUBDIRECTORY(docs)
 
else(DOXYGEN_FOUND)
 
	if(ENABLE_DOCS)
 
		message("Docs              : no (install doxygen)")
 
	else(ENABLE_DOCS)
 
		message("Docs              : no (user disabled)")
 
	endif()
 
endif(DOXYGEN_FOUND)
 

	
 
message("----------------------")
 

	
 
if(NOT SQLITE3_FOUND AND NOT MYSQL_FOUND AND NOT PQXX_FOUND)
 
	if(ENABLE_SQLITE3 OR ENABLE_MYSQL OR ENABLE_PQXX)
 
		message("Could not find any database - Please install at least one of sqlite3-devel, mysql-devel or pqxx-devel if you want to use transport mode.")
 
	else(ENABLE_SQLITE3 OR ENABLE_MYSQL OR ENABLE_PQXX)
 
		message("Please enable at least one of SQLITE3, MYSQL, PQXX databases to use transport mode.")
 
	endif()
 
endif()
 

	
backends/libcommuni/ircnetworkplugin.cpp
Show inline comments
 
#include "ircnetworkplugin.h"
 
#include <IrcCommand>
 
#include <IrcMessage>
 
#include "transport/logging.h"
 

	
 
DEFINE_LOGGER(logger, "IRCNetworkPlugin");
 

	
 
#define FROM_UTF8(WHAT) QString::fromUtf8((WHAT).c_str(), (WHAT).size())
 
#define TO_UTF8(WHAT) std::string((WHAT).toUtf8().data(), (WHAT).toUtf8().size())
 

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

	
 
	std::string server = CONFIG_STRING_DEFAULTED(config, "service.irc_server", "");
 
	if (!server.empty()) {
 
		m_servers.push_back(server);
 
	}
 
	else {
 
		
 
		std::list<std::string> list;
 
		list = CONFIG_LIST_DEFAULTED(config, "service.irc_server", list);
 
		
 
		m_servers.insert(m_servers.begin(), list.begin(), list.end());
 
	}
 

	
 
	if (CONFIG_HAS_KEY(config, "service.irc_identify")) {
 
		m_identify = CONFIG_STRING(config, "service.irc_identify");
 
	}
 
	else {
 
		m_identify = "NickServ identify $name $password";
 
	}
 
}
 

	
 
void IRCNetworkPlugin::tryNextServer() {
 
	if (!m_servers.empty()) {
 
		int nextServer = (m_currentServer + 1) % m_servers.size();
 
		LOG4CXX_INFO(logger, "Server " << m_servers[m_currentServer] << " disconnected user. Next server to try will be " << m_servers[nextServer]);
 
		m_currentServer = nextServer;
 
	}
 
}
 

	
 
void IRCNetworkPlugin::readData() {
 
	size_t availableBytes = m_socket->bytesAvailable();
 
	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);
 
			cfg.setSupportMUC(true);
 
			sendConfig(cfg);
 
		}
 
	}
 

	
 
	std::string d = std::string(m_socket->readAll().data(), availableBytes);
 
	handleDataRead(d);
 
}
 

	
 
void IRCNetworkPlugin::sendData(const std::string &string) {
 
	m_socket->write(string.c_str(), string.size());
 
}
 

	
 
MyIrcSession *IRCNetworkPlugin::createSession(const std::string &user, const std::string &hostname, const std::string &nickname, const std::string &password, const std::string &suffix) {
 
	MyIrcSession *session = new MyIrcSession(user, this, suffix);
 
	session->setUserName(FROM_UTF8(nickname));
 
	session->setNickName(FROM_UTF8(nickname));
 
	session->setRealName(FROM_UTF8(nickname));
 
	session->setHost(FROM_UTF8(hostname));
 
	session->setPort(6667);
 
// 	session->setEncoding("UTF8");
 

	
 
	if (!password.empty()) {
 
		std::string identify = m_identify;
 
		boost::replace_all(identify, "$password", password);
 
		boost::replace_all(identify, "$name", nickname);
 
		session->setIdentify(identify);
 
	}
 

	
 
	LOG4CXX_INFO(logger, user << ": Connecting " << hostname << " as " << nickname << ", suffix=" << suffix);
 

	
 
	session->open();
 

	
 
	return session;
 
}
 

	
 
void IRCNetworkPlugin::handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) {
 
	if (!m_servers.empty()) {
 
		// legacy name is users nickname
 
		if (m_sessions[user] != NULL) {
 
			LOG4CXX_WARN(logger, user << ": Already logged in.");
 
			return;
 
		}
 

	
 
		m_sessions[user] = createSession(user, m_servers[m_currentServer], legacyName, password, "");
 
	}
 
	else {
 
		// We are waiting for first room join to connect user to IRC network, because we don't know which
 
		// network he choose...
 
		LOG4CXX_INFO(logger, user << ": Ready for connections");
 
		handleConnected(user);
 
	}
 
}
 

	
 
void IRCNetworkPlugin::handleLogoutRequest(const std::string &user, const std::string &legacyName) {
 
	if (m_sessions[user] == NULL) {
 
		LOG4CXX_WARN(logger, user << ": Already disconnected.");
 
		return;
 
	}
 
	LOG4CXX_INFO(logger, user << ": Disconnecting.");
 
	m_sessions[user]->close();
 
	m_sessions[user]->deleteLater();
 
	m_sessions.erase(user);
 
}
 

	
 
std::string IRCNetworkPlugin::getSessionName(const std::string &user, const std::string &legacyName) {
 
	std::string u = user;
 
	if (!CONFIG_BOOL(config, "service.server_mode") && m_servers.empty()) {
 
		u = user + legacyName.substr(legacyName.find("@") + 1);
 
		if (u.find("/") != std::string::npos) {
 
			u = u.substr(0, u.find("/"));
 
		}
 
	}
 
	return u;
 
}
 

	
 
std::string IRCNetworkPlugin::getTargetName(const std::string &legacyName) {
 
	std::string r = legacyName;
 
// 	if (!CONFIG_BOOL(config, "service.server_mode")) {
 
		if (legacyName.find("/") == std::string::npos) {
 
			r = legacyName.substr(0, r.find("@"));
 
		}
 
		else {
 
			r = legacyName.substr(legacyName.find("/") + 1);
 
		}
 
// 	}
 
	return r;
 
}
 

	
 
void IRCNetworkPlugin::handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &/*xhtml*/, const std::string &/*id*/) {
 
	std::string session = getSessionName(user, legacyName);
 
	if (m_sessions[session] == NULL) {
 
		LOG4CXX_WARN(logger, user << ": Session name: " << session << ", No session for user");
 
		return;
 
	}
 

	
 
	std::string target = getTargetName(legacyName);
backends/libcommuni/main.cpp
Show inline comments
 
/*
 
 * Copyright (C) 2008-2009 J-P Nurmi jpnurmi@gmail.com
 
 *
 
 * This example is free, and not covered by LGPL license. There is no
 
 * restriction applied to their modification, redistribution, using and so on.
 
 * You can study them, modify them, use them in your own program - either
 
 * completely or partially. By using it you may give me some credits in your
 
 * program, but you don't have to.
 
 */
 

	
 
#include "transport/config.h"
 
#include "transport/networkplugin.h"
 
#include "transport/logging.h"
 
#include "session.h"
 
#include <QtCore>
 
#include <QtNetwork>
 
#include "Swiften/EventLoop/Qt/QtEventLoop.h"
 
#include "ircnetworkplugin.h"
 

	
 
using namespace boost::program_options;
 
using namespace Transport;
 

	
 
NetworkPlugin * np = NULL;
 

	
 
int main (int argc, char* argv[]) {
 
	std::string host;
 
	int port;
 

	
 
	std::string error;
 
	Config *cfg = Config::createFromArgs(argc, argv, error, host, port);
 
	if (cfg == NULL) {
 
		std::cerr << error;
 
		return 1;
 
	}
 

	
 
	QCoreApplication app(argc, argv);
 

	
 
	Logging::initBackendLogging(cfg);
 

	
 
	QCoreApplication app(argc, argv);
 

	
 
	Swift::QtEventLoop eventLoop;
 

	
 

	
 
	np = new IRCNetworkPlugin(cfg, &eventLoop, host, port);
 
	return app.exec();
 
}
backends/libcommuni/session.cpp
Show inline comments
 
/*
 
 * Copyright (C) 2008-2009 J-P Nurmi jpnurmi@gmail.com
 
 *
 
 * This example is free, and not covered by LGPL license. There is no
 
 * restriction applied to their modification, redistribution, using and so on.
 
 * You can study them, modify them, use them in your own program - either
 
 * completely or partially. By using it you may give me some credits in your
 
 * program, but you don't have to.
 
 */
 

	
 
#include "session.h"
 
#include <QtCore>
 
#include <iostream>
 
#include <IrcCommand>
 
#include <IrcMessage>
 

	
 
#include "ircnetworkplugin.h"
 

	
 
#define FROM_UTF8(WHAT) QString::fromUtf8((WHAT).c_str(), (WHAT).size())
 
#define TO_UTF8(WHAT) std::string((WHAT).toUtf8().data(), (WHAT).toUtf8().size())
 

	
 
#include "transport/logging.h"
 

	
 
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;
 
	this->user = user;
 
	this->suffix = suffix;
 
	m_connected = false;
 
	rooms = 0;
 
	connect(this, SIGNAL(disconnected()), SLOT(on_disconnected()));
 
	connect(this, SIGNAL(socketError(QAbstractSocket::SocketError)), SLOT(on_socketError(QAbstractSocket::SocketError)));
 
	connect(this, SIGNAL(connected()), SLOT(on_connected()));
 
	connect(this, SIGNAL(messageReceived(IrcMessage*)), this, SLOT(onMessageReceived(IrcMessage*)));
 

	
 
	m_awayTimer = new QTimer(this);
 
	connect(m_awayTimer, SIGNAL(timeout()), this, SLOT(awayTimeout()));
 
	m_awayTimer->start(5*1000);
 
}
 

	
 
MyIrcSession::~MyIrcSession() {
 
	delete m_awayTimer;
 
}
 

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

	
 
	if (getIdentify().find(" ") != std::string::npos) {
 
		std::string to = getIdentify().substr(0, getIdentify().find(" "));
 
		std::string what = getIdentify().substr(getIdentify().find(" ") + 1);
 
		sendCommand(IrcCommand::createMessage(FROM_UTF8(to), FROM_UTF8(what)));
 
	}
 
}
 

	
 
void MyIrcSession::on_socketError(QAbstractSocket::SocketError error) {
 
	on_disconnected();
 
}
 

	
 
void MyIrcSession::on_disconnected() {
 
	if (suffix.empty()) {
 
		np->handleDisconnected(user, 0, "");
 
		np->tryNextServer();
 
	}
 
	m_connected = false;
 
}
 

	
 
bool MyIrcSession::correctNickname(std::string &nickname) {
 
	bool flags = 0;
 
	switch(nickname.at(0)) {
 
		case '@': nickname = nickname.substr(1); flags = 1; break;
 
		case '+': nickname = nickname.substr(1); break;
 
		case '~': nickname = nickname.substr(1); break;
 
		case '&': nickname = nickname.substr(1); break;
 
		case '%': nickname = nickname.substr(1); break;
 
		default: break;
 
	}
 
	return flags;
 
}
 

	
 
void MyIrcSession::on_joined(IrcMessage *message) {
 
	IrcJoinMessage *m = (IrcJoinMessage *) message;
 
	bool op = 0;
 
	std::string nickname = TO_UTF8(m->sender().name());
 
	op = correctNickname(nickname);
 
	getIRCBuddy(TO_UTF8(m->channel()), nickname).setOp(op);
 
	np->handleParticipantChanged(user, nickname, TO_UTF8(m->channel()) + suffix, op, pbnetwork::STATUS_ONLINE);
 
	LOG4CXX_INFO(logger, user << ": " << nickname << " joined " << TO_UTF8(m->channel()) + suffix);
 
	getIRCBuddy(TO_UTF8(m->channel().toLower()), nickname).setOp(op);
 
	np->handleParticipantChanged(user, nickname, TO_UTF8(m->channel().toLower()) + suffix, op, pbnetwork::STATUS_ONLINE);
 
	LOG4CXX_INFO(logger, user << ": " << nickname << " joined " << TO_UTF8(m->channel().toLower()) + suffix);
 
}
 

	
 

	
 
void MyIrcSession::on_parted(IrcMessage *message) {
 
	IrcPartMessage *m = (IrcPartMessage *) message;
 
	bool op = 0;
 
	std::string nickname = TO_UTF8(m->sender().name());
 
	op = correctNickname(nickname);
 
	removeIRCBuddy(TO_UTF8(m->channel()), nickname);
 
	LOG4CXX_INFO(logger, user << ": " << nickname << " parted " << TO_UTF8(m->channel()) + suffix);
 
	np->handleParticipantChanged(user, nickname, TO_UTF8(m->channel()) + suffix, op, pbnetwork::STATUS_NONE, TO_UTF8(m->reason()));
 
	removeIRCBuddy(TO_UTF8(m->channel().toLower()), nickname);
 
	LOG4CXX_INFO(logger, user << ": " << nickname << " parted " << TO_UTF8(m->channel().toLower()) + suffix);
 
	np->handleParticipantChanged(user, nickname, TO_UTF8(m->channel().toLower()) + suffix, op, pbnetwork::STATUS_NONE, TO_UTF8(m->reason()));
 
}
 

	
 
void MyIrcSession::on_quit(IrcMessage *message) {
 
	IrcQuitMessage *m = (IrcQuitMessage *) message;
 
	for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) {
 
		bool op = 0;
 
		std::string nickname = TO_UTF8(m->sender().name());
 
		if (!hasIRCBuddy(it->second->getChannel(), nickname)) {
 
			continue;
 
		}
 
		op = correctNickname(nickname);
 
		removeIRCBuddy(it->second->getChannel(), nickname);
 
		LOG4CXX_INFO(logger, user << ": " << nickname << " quit " << it->second->getChannel() + suffix);
 
		np->handleParticipantChanged(user, nickname, it->second->getChannel() + suffix, op, pbnetwork::STATUS_NONE, TO_UTF8(m->reason()));
 
	}
 
}
 

	
 
void MyIrcSession::on_nickChanged(IrcMessage *message) {
 
	IrcNickMessage *m = (IrcNickMessage *) message;
 

	
 
	for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) {
 
		std::string nickname = TO_UTF8(m->sender().name());
 
		if (!hasIRCBuddy(it->second->getChannel(), nickname)) {
 
			continue;
 
		}
 
		IRCBuddy &buddy = getIRCBuddy(it->second->getChannel(), nickname);
 
		LOG4CXX_INFO(logger, user << ": " << nickname << " changed nickname to " << TO_UTF8(m->nick()));
 
		np->handleParticipantChanged(user, nickname, it->second->getChannel() + suffix,(int) buddy.isOp(), pbnetwork::STATUS_ONLINE, "", TO_UTF8(m->nick()));
 
	}
 
}
 

	
 
void MyIrcSession::on_modeChanged(IrcMessage *message) {
 
	IrcModeMessage *m = (IrcModeMessage *) message;
 

	
 
	// mode changed: "#testik" "HanzZ" "+o" "hanzz_k"
 
	std::string nickname = TO_UTF8(m->argument());
 
	std::string mode = TO_UTF8(m->mode());
 
	if (nickname.empty())
 
		return;
 
	LOG4CXX_INFO(logger, user << ": " << nickname << " changed mode to " << mode);
 
	for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) {
 
		if (!hasIRCBuddy(it->second->getChannel(), nickname)) {
 
			continue;
 
		}
 
		IRCBuddy &buddy = getIRCBuddy(it->second->getChannel(), nickname);
 
		if (mode == "+o") {
 
			buddy.setOp(true);
 
		}
 
		else {
 
			buddy.setOp(false);
 
		}
 
		np->handleParticipantChanged(user, nickname, it->second->getChannel() + suffix,(int) buddy.isOp(), pbnetwork::STATUS_ONLINE, "");
 
	}
 
}
 

	
 
void MyIrcSession::on_topicChanged(IrcMessage *message) {
 
	IrcTopicMessage *m = (IrcTopicMessage *) message;
 

	
 
	std::string nickname = TO_UTF8(m->sender().name());
 
	correctNickname(nickname);
 

	
 
	LOG4CXX_INFO(logger, user << ": " << nickname << " topic changed to " << TO_UTF8(m->topic()));
 
	np->handleSubject(user, TO_UTF8(m->channel()) + suffix, TO_UTF8(m->topic()), nickname);
 
	np->handleSubject(user, TO_UTF8(m->channel().toLower()) + suffix, TO_UTF8(m->topic()), nickname);
 
}
 

	
 
void MyIrcSession::on_messageReceived(IrcMessage *message) {
 
	IrcPrivateMessage *m = (IrcPrivateMessage *) message;
 
	if (m->isRequest()) {
 
		QString request = m->message().split(" ", QString::SkipEmptyParts).value(0).toUpper();
 
		if (request == "PING" || request == "TIME" || request == "VERSION") {
 
			LOG4CXX_INFO(logger, user << ": " << TO_UTF8(request) << " received and has been answered");
 
			return;
 
		}
 
	}
 

	
 
	QString msg = m->message();
 
	if (m->isAction()) {
 
		msg = QString("/me ") + msg;
 
	}
 

	
 
	std::string target = TO_UTF8(m->target());
 
	std::string target = TO_UTF8(m->target().toLower());
 
	LOG4CXX_INFO(logger, user << ": Message from " << target);
 
	if (target.find("#") == 0) {
 
		std::string nickname = TO_UTF8(m->sender().name());
 
		correctNickname(nickname);
 
		np->handleMessage(user, target + suffix, TO_UTF8(msg), nickname);
 
	}
 
	else {
 
		std::string nickname = TO_UTF8(m->sender().name());
 
		correctNickname(nickname);
 
		LOG4CXX_INFO(logger, nickname + suffix);
 
		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;
 
	QStringList parameters = m->parameters();
 
	switch (m->code()) {
 
		case 301:
 
			break;
 
		case 315:
 
			LOG4CXX_INFO(logger, "End of /who request " << TO_UTF8(parameters[1]));
 
			break;
 
		case 332:
 
			m_topicData = TO_UTF8(parameters[2]);
 
			break;
 
		case 333:
 
			nick = TO_UTF8(parameters[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(parameters[1]) + suffix, m_topicData, nick);
 
			np->handleSubject(user, TO_UTF8(parameters[1].toLower()) + suffix, m_topicData, nick);
 
			break;
 
		case 352: {
 
			channel = parameters[1];
 
			channel = parameters[1].toLower();
 
			nick = TO_UTF8(parameters[5]);
 
			IRCBuddy &buddy = getIRCBuddy(TO_UTF8(channel), nick);
 

	
 
			if (parameters[6].toUpper().startsWith("G")) {
 
				if (!buddy.isAway()) {
 
					buddy.setAway(true);
 
					np->handleParticipantChanged(user, nick, TO_UTF8(channel) + suffix, buddy.isOp(), pbnetwork::STATUS_AWAY);
 
				}
 
			}
 
			else if (buddy.isAway()) {
 
				buddy.setAway(false);
 
				np->handleParticipantChanged(user, nick, TO_UTF8(channel) + suffix, buddy.isOp(), pbnetwork::STATUS_ONLINE);
 
			}
 
			break;
 
		}
 
		case 353:
 
			channel = parameters[2];
 
			channel = parameters[2].toLower();
 
			members = parameters[3].split(" ");
 

	
 
			LOG4CXX_INFO(logger, user << ": Received members for " << TO_UTF8(channel) << suffix);
 
			for (int i = 0; i < members.size(); i++) {
 
				bool op = 0;
 
				std::string nickname = TO_UTF8(members.at(i));
 
				op = correctNickname(nickname);
 
				IRCBuddy &buddy = getIRCBuddy(TO_UTF8(channel), nickname);
 
				buddy.setOp(op);
 
				np->handleParticipantChanged(user, nickname, TO_UTF8(channel) + suffix, buddy.isOp(), pbnetwork::STATUS_ONLINE);
 
			}
 

	
 
			break;
 
		case 366:
 
			// ask /who to get away states
 
			channel = parameters[1];
 
			channel = parameters[1].toLower();
 
			LOG4CXX_INFO(logger, user << "Asking /who for channel " << TO_UTF8(channel));
 
			sendCommand(IrcCommand::createWho(channel));
 
			break;
 
		case 432:
 
			np->handleDisconnected(user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Erroneous Nickname");
 
			break;
 
		case 433:
 
			for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) {
 
				np->handleParticipantChanged(user, TO_UTF8(nickName()), it->second->getChannel() + suffix, pbnetwork::PARTICIPANT_FLAG_CONFLICT);
 
			}
 
			if (suffix.empty()) {
 
				np->handleDisconnected(user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Nickname is already in use");
 
			}
 
			break;
 
		case 436:
 
			for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) {
 
				np->handleParticipantChanged(user, TO_UTF8(nickName()), it->second->getChannel() + suffix, pbnetwork::PARTICIPANT_FLAG_CONFLICT);
 
			}
 
			np->handleDisconnected(user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Nickname collision KILL");
 
		case 464:
 
			for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) {
 
				np->handleParticipantChanged(user, TO_UTF8(nickName()), it->second->getChannel() + suffix, pbnetwork::PARTICIPANT_FLAG_NOT_AUTHORIZED);
 
			}
 
			if (suffix.empty()) {
 
				np->handleDisconnected(user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Password incorrect");
 
			}
 
		case 321:
 
			m_rooms.clear();
 
			m_names.clear();
 
			break;
 
		case 322:
 
			m_rooms.push_back(TO_UTF8(parameters[1]));
 
			m_names.push_back(TO_UTF8(parameters[1]));
 
			break;
 
		case 323:
 
			np->handleRoomList("", m_rooms, m_names);
 
			break;
 
		default:
 
			break;
 
	}
 

	
 
	if (m->code() >= 400 && m->code() < 500) {
 
			LOG4CXX_INFO(logger, user << ": Error message received: " << message->toData().data());
 
	}
 

	
 
	//qDebug() << "numeric message received:" << receiver() << origin << code << params;
 
}
 

	
 
void MyIrcSession::awayTimeout() {
 
	for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) {
 
		if (it->second->shouldAskWho()) {
 
			LOG4CXX_INFO(logger, "The time has come. Asking /who " << it->second->getChannel() << " again to get current away states.");
 
			sendCommand(IrcCommand::createWho(FROM_UTF8(it->second->getChannel())));
 
		}
 
	}
 
}
 

	
 
void MyIrcSession::onMessageReceived(IrcMessage *message) {
 
// 	LOG4CXX_INFO(logger, user << ": " << TO_UTF8(message->toString()));
 
	switch (message->type()) {
 
		case IrcMessage::Join:
 
			on_joined(message);
 
			break;
 
		case IrcMessage::Part:
 
			on_parted(message);
 
			break;
 
		case IrcMessage::Quit:
 
			on_quit(message);
 
			break;
 
		case IrcMessage::Nick:
 
			on_nickChanged(message);
 
			break;
 
		case IrcMessage::Mode:
 
			on_modeChanged(message);
 
			break;
 
		case IrcMessage::Topic:
 
			on_topicChanged(message);
 
			break;
 
		case IrcMessage::Private:
 
			on_messageReceived(message);
 
			break;
 
		case IrcMessage::Numeric:
 
			on_numericMessageReceived(message);
 
			break;
 
		default:break;
 
	}
 
}
backends/libpurple/main.cpp
Show inline comments
 
#include "utils.h"
 

	
 
#include "glib.h"
 

	
 
// win32/libc_interface.h defines its own socket(), read() and so on.
 
// We don't want to use it here.
 
#define _LIBC_INTERFACE_H_ 1
 

	
 
#include "purple.h"
 
#include <algorithm>
 
#include <iostream>
 

	
 
#include "transport/networkplugin.h"
 
#include "transport/logging.h"
 
#include "transport/config.h"
 
#include "transport/logging.h"
 
#include "geventloop.h"
 

	
 
// #include "valgrind/memcheck.h"
 
#ifndef __FreeBSD__
 
#include "malloc.h"
 
#endif
 
#include <algorithm>
 
#include "errno.h"
 
#include <boost/make_shared.hpp>
 

	
 
#ifdef WITH_LIBEVENT
 
#include <event.h>
 
#endif
 

	
 
#ifdef WIN32
 
#include "win32/win32dep.h"
 
#define close closesocket
 
#define ssize_t SSIZE_T
 
#include <process.h>
 
#define getpid _getpid
 
#endif
 

	
 
#include "purple_defs.h"
 

	
 
DEFINE_LOGGER(logger_libpurple, "libpurple");
 
DEFINE_LOGGER(logger, "backend");
 

	
 
int main_socket;
 
static int writeInput;
 
bool firstPing = true;
 

	
 
using namespace Transport;
 

	
 
template <class T> T fromString(const std::string &str) {
 
	T i;
 
	std::istringstream os(str);
 
	os >> i;
 
	return i;
 
}
 

	
 
template <class T> std::string stringOf(T object) {
 
	std::ostringstream os;
 
	os << object;
 
	return (os.str());
 
}
 

	
 
static std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
 
    std::stringstream ss(s);
 
    std::string item;
 
    while(std::getline(ss, item, delim)) {
 
        elems.push_back(item);
 
    }
 
    return elems;
 
}
 

	
 

	
 
static std::vector<std::string> split(const std::string &s, char delim) {
 
    std::vector<std::string> elems;
 
    return split(s, delim, elems);
 
}
 

	
 
static void transportDataReceived(gpointer data, gint source, PurpleInputCondition cond);
 

	
 
class SpectrumNetworkPlugin;
 

	
 
boost::shared_ptr<Config> config;
 
SpectrumNetworkPlugin *np;
 

	
 
static std::string host;
 
static int port = 10000;
 

	
 
struct FTData {
 
	unsigned long id;
 
	unsigned long timer;
 
	bool paused;
 
};
 

	
 
static void *notify_user_info(PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info);
 

	
 
static gboolean ft_ui_ready(void *data) {
 
	PurpleXfer *xfer = (PurpleXfer *) data;
 
	FTData *ftdata = (FTData *) xfer->ui_data;
 
	ftdata->timer = 0;
 
	purple_xfer_ui_ready_wrapped((PurpleXfer *) data);
 
	return FALSE;
 
}
 

	
 
struct authRequest {
 
	PurpleAccountRequestAuthorizationCb authorize_cb;
 
	PurpleAccountRequestAuthorizationCb deny_cb;
 
	void *user_data;
 
	std::string who;
 
	PurpleAccount *account;
 
	std::string mainJID;	// JID of user connected with this request
 
};
 

	
 
static void * requestInput(const char *title, const char *primary,const char *secondary, const char *default_value, gboolean multiline, gboolean masked, gchar *hint,const char *ok_text, GCallback ok_cb,const char *cancel_text, GCallback cancel_cb, PurpleAccount *account, const char *who,PurpleConversation *conv, void *user_data) {
 
	if (primary) {
 
		std::string primaryString(primary);
 
		if (primaryString == "Authorization Request Message:") {
 
			LOG4CXX_INFO(logger, "Authorization Request Message: calling ok_cb(...)");
 
			((PurpleRequestInputCb) ok_cb)(user_data, "Please authorize me.");
 
			return NULL;
 
		}
 
		else if (primaryString == "Authorization Request Message:") {
 
			LOG4CXX_INFO(logger, "Authorization Request Message: calling ok_cb(...)");
 
			((PurpleRequestInputCb) ok_cb)(user_data, "Please authorize me.");
 
			return NULL;
 
		}
 
		else if (primaryString == "Authorization Denied Message:") {
 
			LOG4CXX_INFO(logger, "Authorization Deined Message: calling ok_cb(...)");
 
			((PurpleRequestInputCb) ok_cb)(user_data, "Authorization denied.");
 
			return NULL;
 
		}
 
		else {
 
			LOG4CXX_WARN(logger, "Unhandled request input. primary=" << primaryString);
 
		}
 
	}
 
	else if (title) {
 
		std::string titleString(title);
 
		if (titleString == "Xfire Invitation Message") {
 
			LOG4CXX_INFO(logger, "Authorization Request Message: calling ok_cb(...)");
 
			((PurpleRequestInputCb) ok_cb)(user_data, "Please authorize me.");
 
			return NULL;
 
		}
 
		else {
 
@@ -456,192 +457,193 @@ class SpectrumNetworkPlugin : public NetworkPlugin {
 
					}
 
					else if (purple_conversation_get_type_wrapped(conv) == PURPLE_CONV_TYPE_CHAT) {
 
						purple_conv_chat_send_wrapped(PURPLE_CONV_CHAT_WRAPPED(conv), xhtml.c_str());
 
					}
 
				}
 
			}
 
		}
 

	
 
		void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (account) {
 
				std::string name = legacyName;
 
				if (CONFIG_STRING(config, "service.protocol") == "any" && legacyName.find("prpl-") == 0) {
 
					name = name.substr(name.find(".") + 1);
 
				}
 
				m_vcards[user + name] = id;
 

	
 
				if (CONFIG_BOOL(config, "backend.no_vcard_fetch") && name != purple_account_get_username_wrapped(account)) {
 
					PurpleNotifyUserInfo *user_info = purple_notify_user_info_new_wrapped();
 
					notify_user_info(purple_account_get_connection_wrapped(account), name.c_str(), user_info);
 
					purple_notify_user_info_destroy_wrapped(user_info);
 
				}
 
				else {
 
					serv_get_info_wrapped(purple_account_get_connection_wrapped(account), name.c_str());
 
				}
 
				
 
			}
 
		}
 

	
 
		void handleVCardUpdatedRequest(const std::string &user, const std::string &image, const std::string &nickname) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (account) {
 
				purple_account_set_alias_wrapped(account, nickname.c_str());
 
#if PURPLE_MAJOR_VERSION >= 2 && PURPLE_MINOR_VERSION >= 7
 
				purple_account_set_public_alias_wrapped(account, nickname.c_str(), NULL, NULL);
 
#endif
 
				gssize size = image.size();
 
				// this will be freed by libpurple
 
				guchar *photo = (guchar *) g_malloc(size * sizeof(guchar));
 
				memcpy(photo, image.c_str(), size);
 

	
 
				if (!photo)
 
					return;
 
				purple_buddy_icons_set_account_icon_wrapped(account, photo, size);
 
			}
 
		}
 

	
 
		void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector<std::string> &groups) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (account) {
 
				if (m_authRequests.find(user + buddyName) != m_authRequests.end()) {
 
					m_authRequests[user + buddyName]->deny_cb(m_authRequests[user + buddyName]->user_data);
 
					m_authRequests.erase(user + buddyName);
 
				}
 
				PurpleBuddy *buddy = purple_find_buddy_wrapped(account, buddyName.c_str());
 
				if (buddy) {
 
					purple_account_remove_buddy_wrapped(account, buddy, purple_buddy_get_group_wrapped(buddy));
 
					purple_blist_remove_buddy_wrapped(buddy);
 
				}
 
			}
 
		}
 

	
 
		void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups_) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (account) {
 
				std::string groups = groups_.empty() ? "" : groups_[0];
 

	
 
				if (m_authRequests.find(user + buddyName) != m_authRequests.end()) {
 
					m_authRequests[user + buddyName]->authorize_cb(m_authRequests[user + buddyName]->user_data);
 
					m_authRequests.erase(user + buddyName);
 
				}
 

	
 
				PurpleBuddy *buddy = purple_find_buddy_wrapped(account, buddyName.c_str());
 
				if (buddy) {
 
					if (getAlias(buddy) != alias) {
 
						purple_blist_alias_buddy_wrapped(buddy, alias.c_str());
 
						purple_blist_server_alias_buddy_wrapped(buddy, alias.c_str());
 
						serv_alias_buddy_wrapped(buddy);
 
					}
 

	
 
					PurpleGroup *group = purple_find_group_wrapped(groups.c_str());
 
					if (!group) {
 
						group = purple_group_new_wrapped(groups.c_str());
 
					}
 
					purple_blist_add_contact_wrapped(purple_buddy_get_contact_wrapped(buddy), group ,NULL);
 
				}
 
				else {
 
					PurpleBuddy *buddy = purple_buddy_new_wrapped(account, buddyName.c_str(), alias.c_str());
 

	
 
					// Add newly created buddy to legacy network roster.
 
					PurpleGroup *group = purple_find_group_wrapped(groups.c_str());
 
					if (!group) {
 
						group = purple_group_new_wrapped(groups.c_str());
 
					}
 
					purple_blist_add_buddy_wrapped(buddy, NULL, group ,NULL);
 
					purple_account_add_buddy_wrapped(account, buddy);
 
					LOG4CXX_INFO(logger, "Adding new buddy " << buddyName.c_str() << " to legacy network roster");
 
				}
 
			}
 
		}
 

	
 
		void handleBuddyBlockToggled(const std::string &user, const std::string &buddyName, bool blocked) {
 
			if (CONFIG_BOOL(config, "service.enable_privacy_lists")) {
 
				PurpleAccount *account = m_sessions[user];
 
				if (account) {
 
					if (blocked) {
 
						purple_privacy_deny_wrapped(account, buddyName.c_str(), FALSE, FALSE);
 
					}
 
					else {
 
						purple_privacy_allow_wrapped(account, buddyName.c_str(), FALSE, FALSE);
 
					}
 
				}
 
			}
 
		}
 

	
 
		void handleTypingRequest(const std::string &user, const std::string &buddyName) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (account) {
 
				LOG4CXX_INFO(logger, user << ": sending typing notify to " << buddyName);
 
				serv_send_typing_wrapped(purple_account_get_connection_wrapped(account), buddyName.c_str(), PURPLE_TYPING);
 
			}
 
		}
 

	
 
		void handleTypedRequest(const std::string &user, const std::string &buddyName) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (account) {
 
				serv_send_typing_wrapped(purple_account_get_connection_wrapped(account), buddyName.c_str(), PURPLE_TYPED);
 
			}
 
		}
 

	
 
		void handleStoppedTypingRequest(const std::string &user, const std::string &buddyName) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (account) {
 
				serv_send_typing_wrapped(purple_account_get_connection_wrapped(account), buddyName.c_str(), PURPLE_NOT_TYPING);
 
			}
 
		}
 

	
 
		void handleAttentionRequest(const std::string &user, const std::string &buddyName, const std::string &message) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (account) {
 
				purple_prpl_send_attention_wrapped(purple_account_get_connection_wrapped(account), buddyName.c_str(), 0);
 
			}
 
		}
 

	
 
		void handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &pasword) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (!account) {
 
				return;
 
			}
 

	
 
			PurpleConnection *gc = purple_account_get_connection_wrapped(account);
 
			GHashTable *comps = NULL;
 

	
 
			// Check if the PurpleChat is not stored in buddy list
 
			PurpleChat *chat = purple_blist_find_chat_wrapped(account, room.c_str());
 
			if (chat) {
 
				comps = purple_chat_get_components_wrapped(chat);
 
			}
 
			else if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults != NULL) {
 
				comps = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults(gc, room.c_str());
 
			}
 

	
 
			LOG4CXX_INFO(logger, user << ": Joining the room " << room);
 
			if (comps) {
 
				serv_join_chat_wrapped(gc, comps);
 
				g_hash_table_destroy(comps);
 
			}
 
		}
 

	
 
		void handleLeaveRoomRequest(const std::string &user, const std::string &room) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (!account) {
 
				return;
 
			}
 

	
 
			PurpleConversation *conv = purple_find_conversation_with_account_wrapped(PURPLE_CONV_TYPE_CHAT, room.c_str(), account);
 
			purple_conversation_destroy_wrapped(conv);
 
		}
 

	
 
		void handleFTStartRequest(const std::string &user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID) {
 
			PurpleXfer *xfer = m_unhandledXfers[user + fileName + buddyName];
 
			if (xfer) {
 
				m_unhandledXfers.erase(user + fileName + buddyName);
 
				FTData *ftData = (FTData *) xfer->ui_data;
 
				
 
				ftData->id = ftID;
 
				m_xfers[ftID] = xfer;
 
				purple_xfer_request_accepted_wrapped(xfer, fileName.c_str());
 
				purple_xfer_ui_ready_wrapped(xfer);
 
			}
 
		}
 

	
 
		void handleFTFinishRequest(const std::string &user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID) {
 
@@ -815,237 +817,237 @@ static void buddyListNewNode(PurpleBlistNode *node) {
 

	
 
	// Status
 
	pbnetwork::StatusType status = pbnetwork::STATUS_NONE;
 
	std::string message;
 
	getStatus(buddy, status, message);
 

	
 
	// Tooltip
 
	PurplePlugin *prpl = purple_find_prpl_wrapped(purple_account_get_protocol_id_wrapped(account));
 
	PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 

	
 
	bool blocked = false;
 
	if (CONFIG_BOOL(config, "service.enable_privacy_lists")) {
 
		if (prpl_info && prpl_info->tooltip_text) {
 
			PurpleNotifyUserInfo *user_info = purple_notify_user_info_new_wrapped();
 
			prpl_info->tooltip_text(buddy, user_info, true);
 
			GList *entries = purple_notify_user_info_get_entries_wrapped(user_info);
 

	
 
			while (entries) {
 
				PurpleNotifyUserInfoEntry *entry = (PurpleNotifyUserInfoEntry *)(entries->data);
 
				if (purple_notify_user_info_entry_get_label_wrapped(entry) && purple_notify_user_info_entry_get_value_wrapped(entry)) {
 
					std::string label = purple_notify_user_info_entry_get_label_wrapped(entry);
 
					if (label == "Blocked" ) {
 
						if (std::string(purple_notify_user_info_entry_get_value_wrapped(entry)) == "Yes") {
 
							blocked = true;
 
							break;
 
						}
 
					}
 
				}
 
				entries = entries->next;
 
			}
 
			purple_notify_user_info_destroy_wrapped(user_info);
 
		}
 

	
 
		if (!blocked) {
 
			blocked = purple_privacy_check_wrapped(account, purple_buddy_get_name_wrapped(buddy)) == false;
 
		}
 
		else {
 
			bool purpleBlocked = purple_privacy_check_wrapped(account, purple_buddy_get_name_wrapped(buddy)) == false;
 
			if (blocked != purpleBlocked) {
 
				purple_privacy_deny_wrapped(account, purple_buddy_get_name_wrapped(buddy), FALSE, FALSE);
 
			}
 
		}
 
	}
 

	
 
	np->handleBuddyChanged(np->m_accounts[account], purple_buddy_get_name_wrapped(buddy), getAlias(buddy), getGroups(buddy), status, message, getIconHash(buddy),
 
		blocked
 
	);
 
}
 

	
 
static void buddyListUpdate(PurpleBuddyList *list, PurpleBlistNode *node) {
 
	if (!PURPLE_BLIST_NODE_IS_BUDDY_WRAPPED(node))
 
		return;
 
	buddyListNewNode(node);
 
}
 

	
 
static void buddyPrivacyChanged(PurpleBlistNode *node, void *data) {
 
	if (!PURPLE_BLIST_NODE_IS_BUDDY_WRAPPED(node))
 
		return;
 
	buddyListUpdate(NULL, node);
 
}
 

	
 
static void NodeRemoved(PurpleBlistNode *node, void *data) {
 
	if (!PURPLE_BLIST_NODE_IS_BUDDY_WRAPPED(node))
 
		return;
 
// 	PurpleBuddy *buddy = (PurpleBuddy *) node;
 
}
 

	
 
static PurpleBlistUiOps blistUiOps =
 
{
 
	NULL,
 
	buddyListNewNode,
 
	NULL,
 
	buddyListUpdate,
 
	NULL, //NodeRemoved,
 
	NULL,
 
	NULL,
 
	NULL, // buddyListAddBuddy,
 
	NULL,
 
	NULL,
 
	NULL, //buddyListSaveNode,
 
	NULL, //buddyListRemoveNode,
 
	NULL, //buddyListSaveAccount,
 
	NULL
 
};
 

	
 
static void conv_write_im(PurpleConversation *conv, const char *who, const char *msg, PurpleMessageFlags flags, time_t mtime) {
 
	// Don't forwards our own messages.
 
	if (purple_conversation_get_type_wrapped(conv) == PURPLE_CONV_TYPE_IM && (flags & PURPLE_MESSAGE_SEND || flags & PURPLE_MESSAGE_SYSTEM)) {
 
		return;
 
	}
 
	PurpleAccount *account = purple_conversation_get_account_wrapped(conv);
 

	
 
// 	char *striped = purple_markup_strip_html_wrapped(message);
 
// 	std::string msg = striped;
 
// 	g_free(striped);
 

	
 
	std::string w = purple_normalize_wrapped(account, who);
 
	size_t pos = w.find("/");
 
	if (pos != std::string::npos)
 
		w.erase((int) pos, w.length() - (int) pos);
 

	
 
	// Escape HTML characters.
 
	char *newline = purple_strdup_withhtml_wrapped(msg);
 
	char *strip, *xhtml;
 
	purple_markup_html_to_xhtml_wrapped(newline, &xhtml, &strip);
 
// 	xhtml_linkified = spectrum_markup_linkify(xhtml);
 
	std::string message_(strip);
 

	
 
	std::string xhtml_(xhtml);
 
	g_free(newline);
 
	g_free(xhtml);
 
// 	g_free(xhtml_linkified);
 
	g_free(strip);
 

	
 
	// AIM and XMPP adds <body>...</body> here...
 
	if (xhtml_.find("<body>") == 0) {
 
		xhtml_ = xhtml_.substr(6);
 
		if (xhtml_.find("</body>") != std::string::npos) {
 
			xhtml_ = xhtml_.substr(0, xhtml_.find("</body>"));
 
		}
 
	}
 

	
 
	if (xhtml_ == message_) {
 
		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) {
 
		std::string w = purple_normalize_wrapped(account, who);
 
		size_t pos = w.find("/");
 
		if (pos != std::string::npos)
 
			w.erase((int) pos, w.length() - (int) pos);
 
		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_, timestamp);
 
		LOG4CXX_INFO(logger, "Received message body='" << message_ << "' name='" << purple_conversation_get_name_wrapped(conv) << "' " << who);
 
		np->handleMessage(np->m_accounts[account], purple_conversation_get_name_wrapped(conv), message_, who, xhtml_, timestamp);
 
	}
 
}
 

	
 
static void conv_chat_add_users(PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals) {
 
	PurpleAccount *account = purple_conversation_get_account_wrapped(conv);
 

	
 
	GList *l = cbuddies;
 
	while (l != NULL) {
 
		PurpleConvChatBuddy *cb = (PurpleConvChatBuddy *)l->data;
 
		std::string name(cb->name);
 
		int flags = GPOINTER_TO_INT(cb->flags);
 
		if (flags & PURPLE_CBFLAGS_OP || flags & PURPLE_CBFLAGS_HALFOP) {
 
// 			item->addAttribute("affiliation", "admin");
 
// 			item->addAttribute("role", "moderator");
 
			flags = 1;
 
		}
 
		else if (flags & PURPLE_CBFLAGS_FOUNDER) {
 
// 			item->addAttribute("affiliation", "owner");
 
// 			item->addAttribute("role", "moderator");
 
			flags = 1;
 
		}
 
		else {
 
			flags = 0;
 
// 			item->addAttribute("affiliation", "member");
 
// 			item->addAttribute("role", "participant");
 
		}
 

	
 
		np->handleParticipantChanged(np->m_accounts[account], name, purple_conversation_get_name_wrapped(conv), (int) flags, pbnetwork::STATUS_ONLINE);
 

	
 
		l = l->next;
 
	}
 
}
 

	
 
static void conv_chat_remove_users(PurpleConversation *conv, GList *users) {
 
	PurpleAccount *account = purple_conversation_get_account_wrapped(conv);
 

	
 
	GList *l = users;
 
	while (l != NULL) {
 
		std::string name((char *) l->data);
 
		np->handleParticipantChanged(np->m_accounts[account], name, purple_conversation_get_name_wrapped(conv), 0, pbnetwork::STATUS_NONE);
 

	
 
		l = l->next;
 
	}
 
}
 

	
 
static PurpleConversationUiOps conversation_ui_ops =
 
{
 
	NULL,
 
	NULL,
 
	conv_write_im,//conv_write_chat,                              /* write_chat           */
 
	conv_write_im,             /* write_im             */
 
	NULL,//conv_write_conv,           /* write_conv           */
 
	conv_chat_add_users,       /* chat_add_users       */
 
	NULL,//conv_chat_rename_user,     /* chat_rename_user     */
 
	conv_chat_remove_users,    /* chat_remove_users    */
 
	NULL,//pidgin_conv_chat_update_user,     /* chat_update_user     */
 
	NULL,//pidgin_conv_present_conversation, /* present              */
 
	NULL,//pidgin_conv_has_focus,            /* has_focus            */
 
	NULL,//pidgin_conv_custom_smiley_add,    /* custom_smiley_add    */
 
	NULL,//pidgin_conv_custom_smiley_write,  /* custom_smiley_write  */
 
	NULL,//pidgin_conv_custom_smiley_close,  /* custom_smiley_close  */
 
	NULL,//pidgin_conv_send_confirm,         /* send_confirm         */
 
	NULL,
 
	NULL,
 
	NULL,
 
	NULL
 
};
 

	
 
struct Dis {
 
	std::string name;
 
	std::string protocol;
 
};
 

	
 
static gboolean disconnectMe(void *data) {
 
	Dis *d = (Dis *) data;
 
	PurpleAccount *account = purple_accounts_find_wrapped(d->name.c_str(), d->protocol.c_str());
 
	delete d;
 

	
 
	if (account) {
 
		np->handleLogoutRequest(np->m_accounts[account], purple_account_get_username_wrapped(account));
 
	}
 
	return FALSE;
 
}
 

	
 
static gboolean pingTimeout(void *data) {
 
	np->checkPing();
 
	return TRUE;
 
}
 

	
 
static void connection_report_disconnect(PurpleConnection *gc, PurpleConnectionError reason, const char *text){
 
	PurpleAccount *account = purple_connection_get_account_wrapped(gc);
 
	np->handleDisconnected(np->m_accounts[account], (int) reason, text ? text : "");
 
// 	Dis *d = new Dis;
 
// 	d->name = purple_account_get_username_wrapped(account);
 
// 	d->protocol = purple_account_get_protocol_id_wrapped(account);
 
// 	purple_timeout_add_seconds_wrapped(10, disconnectMe, d);
 
@@ -1562,162 +1564,170 @@ static bool initPurple() {
 

	
 
	std::string cacertsDir = CONFIG_STRING_DEFAULTED(config, "purple.cacerts_dir", "./ca-certs");
 
	LOG4CXX_INFO(logger, "Setting libpurple cacerts directory to: " << cacertsDir);
 
	purple_certificate_add_ca_search_path_wrapped(cacertsDir.c_str());
 
 
 
	std::string userDir = CONFIG_STRING_DEFAULTED(config, "service.working_dir", "./");
 
	LOG4CXX_INFO(logger, "Setting libpurple user directory to: " << userDir);
 

	
 
	purple_util_set_user_dir_wrapped(userDir.c_str());
 
	remove("./accounts.xml");
 
	remove("./blist.xml");
 

	
 
	purple_debug_set_ui_ops_wrapped(&debugUiOps);
 
// 	purple_debug_set_verbose_wrapped(true);
 

	
 
	purple_core_set_ui_ops_wrapped(&coreUiOps);
 
	if (CONFIG_STRING_DEFAULTED(config, "service.eventloop", "") == "libev") {
 
		LOG4CXX_INFO(logger, "Will use libev based event loop");
 
	}
 
	else {
 
		LOG4CXX_INFO(logger, "Will use glib based event loop");
 
	}
 
	purple_eventloop_set_ui_ops_wrapped(getEventLoopUiOps(CONFIG_STRING_DEFAULTED(config, "service.eventloop", "") == "libev"));
 

	
 
	ret = purple_core_init_wrapped("spectrum");
 
	if (ret) {
 
		static int blist_handle;
 
		static int conversation_handle;
 

	
 
		purple_set_blist_wrapped(purple_blist_new_wrapped());
 
		purple_blist_load_wrapped();
 

	
 
		purple_prefs_load_wrapped();
 

	
 
		/* Good default preferences */
 
		/* The combination of these two settings mean that libpurple will never
 
		 * (of its own accord) set all the user accounts idle.
 
		 */
 
		purple_prefs_set_bool_wrapped("/purple/away/away_when_idle", false);
 
		/*
 
		 * This must be set to something not "none" for idle reporting to work
 
		 * for, e.g., the OSCAR prpl. We don't implement the UI ops, so this is
 
		 * okay for now.
 
		 */
 
		purple_prefs_set_string_wrapped("/purple/away/idle_reporting", "system");
 

	
 
		/* Disable all logging */
 
		purple_prefs_set_bool_wrapped("/purple/logging/log_ims", false);
 
		purple_prefs_set_bool_wrapped("/purple/logging/log_chats", false);
 
		purple_prefs_set_bool_wrapped("/purple/logging/log_system", false);
 

	
 

	
 
// 		purple_signal_connect_wrapped(purple_conversations_get_handle_wrapped(), "received-im-msg", &conversation_handle, PURPLE_CALLBACK(newMessageReceived), NULL);
 
		purple_signal_connect_wrapped(purple_conversations_get_handle_wrapped(), "buddy-typing", &conversation_handle, PURPLE_CALLBACK(buddyTyping), NULL);
 
		purple_signal_connect_wrapped(purple_conversations_get_handle_wrapped(), "buddy-typed", &conversation_handle, PURPLE_CALLBACK(buddyTyped), NULL);
 
		purple_signal_connect_wrapped(purple_conversations_get_handle_wrapped(), "buddy-typing-stopped", &conversation_handle, PURPLE_CALLBACK(buddyTypingStopped), NULL);
 
		purple_signal_connect_wrapped(purple_blist_get_handle_wrapped(), "buddy-privacy-changed", &conversation_handle, PURPLE_CALLBACK(buddyPrivacyChanged), NULL);
 
		purple_signal_connect_wrapped(purple_conversations_get_handle_wrapped(), "got-attention", &conversation_handle, PURPLE_CALLBACK(gotAttention), NULL);
 
		purple_signal_connect_wrapped(purple_connections_get_handle_wrapped(), "signed-on", &blist_handle,PURPLE_CALLBACK(signed_on), NULL);
 
// 		purple_signal_connect_wrapped(purple_blist_get_handle_wrapped(), "buddy-removed", &blist_handle,PURPLE_CALLBACK(buddyRemoved), NULL);
 
// 		purple_signal_connect_wrapped(purple_blist_get_handle_wrapped(), "buddy-signed-on", &blist_handle,PURPLE_CALLBACK(buddySignedOn), NULL);
 
// 		purple_signal_connect_wrapped(purple_blist_get_handle_wrapped(), "buddy-signed-off", &blist_handle,PURPLE_CALLBACK(buddySignedOff), NULL);
 
// 		purple_signal_connect_wrapped(purple_blist_get_handle_wrapped(), "buddy-status-changed", &blist_handle,PURPLE_CALLBACK(buddyStatusChanged), NULL);
 
		purple_signal_connect_wrapped(purple_blist_get_handle_wrapped(), "blist-node-removed", &blist_handle,PURPLE_CALLBACK(NodeRemoved), NULL);
 
// 		purple_signal_connect_wrapped(purple_conversations_get_handle_wrapped(), "chat-topic-changed", &conversation_handle, PURPLE_CALLBACK(conv_chat_topic_changed), NULL);
 
		static int xfer_handle;
 
		purple_signal_connect_wrapped(purple_xfers_get_handle_wrapped(), "file-send-start", &xfer_handle, PURPLE_CALLBACK(fileSendStart), NULL);
 
		purple_signal_connect_wrapped(purple_xfers_get_handle_wrapped(), "file-recv-start", &xfer_handle, PURPLE_CALLBACK(fileRecvStart), NULL);
 
		purple_signal_connect_wrapped(purple_xfers_get_handle_wrapped(), "file-recv-request", &xfer_handle, PURPLE_CALLBACK(newXfer), NULL);
 
		purple_signal_connect_wrapped(purple_xfers_get_handle_wrapped(), "file-recv-complete", &xfer_handle, PURPLE_CALLBACK(XferReceiveComplete), NULL);
 
		purple_signal_connect_wrapped(purple_xfers_get_handle_wrapped(), "file-send-complete", &xfer_handle, PURPLE_CALLBACK(XferSendComplete), NULL);
 
// 
 
// 		purple_commands_init();
 

	
 
	}
 
	return ret;
 
}
 

	
 

	
 
static void transportDataReceived(gpointer data, gint source, PurpleInputCondition cond) {
 
	if (cond & PURPLE_INPUT_READ) {
 
		char buffer[65535];
 
		char *ptr = buffer;
 
#ifdef WIN32
 
		ssize_t n = recv(source, ptr, sizeof(buffer), 0);
 
#else
 
		ssize_t n = read(source, ptr, sizeof(buffer));
 
#endif
 
		if (n <= 0) {
 
			if (errno == EAGAIN) {
 
				return;
 
			}
 
			LOG4CXX_INFO(logger, "Diconnecting from spectrum2 server");
 
			exit(errno);
 
		}
 
		std::string d = std::string(buffer, n);
 

	
 
		if (firstPing) {
 
			firstPing = false;
 
			NetworkPlugin::PluginConfig cfg;
 
			cfg.setSupportMUC(true);
 
			np->sendConfig(cfg);
 
		}
 

	
 
		np->handleDataRead(d);
 
	}
 
	else {
 
		if (writeInput != 0) {
 
			purple_input_remove_wrapped(writeInput);
 
			writeInput = 0;
 
		}
 
		np->readyForData();
 
	}
 
}
 

	
 
int main(int argc, char **argv) {
 
#ifndef WIN32
 
#ifndef __FreeBSD__
 
		mallopt(M_CHECK_ACTION, 2);
 
		mallopt(M_PERTURB, 0xb);
 
#endif
 

	
 
		signal(SIGPIPE, SIG_IGN);
 

	
 
		if (signal(SIGCHLD, spectrum_sigchld_handler) == SIG_ERR) {
 
			std::cout << "SIGCHLD handler can't be set\n";
 
			return -1;
 
		}
 
#endif
 

	
 
	std::string error;
 
	Config *cfg = Config::createFromArgs(argc, argv, error, host, port);
 
	if (cfg == NULL) {
 
		std::cerr << error;
 
		return 1;
 
	}
 

	
 
	config = boost::shared_ptr<Config>(cfg);
 
 
 
	Logging::initBackendLogging(config.get());
 
	initPurple();
 
 
 
	main_socket = create_socket(host.c_str(), port);
 
	purple_input_add_wrapped(main_socket, PURPLE_INPUT_READ, &transportDataReceived, NULL);
 
	purple_timeout_add_seconds_wrapped(30, pingTimeout, NULL);
 
 
 
	np = new SpectrumNetworkPlugin();
 
	bool libev = CONFIG_STRING_DEFAULTED(config, "service.eventloop", "") == "libev";
 

	
 
	GMainLoop *m_loop;
 
#ifdef WITH_LIBEVENT
 
	if (!libev) {
 
		m_loop = g_main_loop_new(NULL, FALSE);
 
	}
 
	else {
 
		event_init();
 
	}
 
#endif
 
	m_loop = g_main_loop_new(NULL, FALSE);
 
	if (m_loop) {
 
		g_main_loop_run(m_loop);
 
	}
 
#ifdef WITH_LIBEVENT
 
	else {
 
		event_loop(0);
 
	}
 
#endif
 

	
 
	return 0;
 
}
backends/skype/main.cpp
Show inline comments
 
@@ -109,192 +109,193 @@ class SpectrumNetworkPlugin : public NetworkPlugin {
 
			LOG4CXX_INFO(logger, "Starting the backend.");
 
		}
 

	
 
		~SpectrumNetworkPlugin() {
 
			for (std::map<Skype *, std::string>::iterator it = m_accounts.begin(); it != m_accounts.end(); it++) {
 
				delete (*it).first;
 
			}
 
		}
 

	
 
		void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) {
 
			std::string name = legacyName;
 
			if (name.find("skype.") == 0 || name.find("prpl-skype.") == 0) {
 
				name = name.substr(name.find(".") + 1);
 
			}
 
			LOG4CXX_INFO(logger,  "Creating account with name '" << name << "'");
 

	
 
			Skype *skype = new Skype(user, name, password);
 
			m_sessions[user] = skype;
 
			m_accounts[skype] = user;
 

	
 
			skype->login();
 
		}
 

	
 
		void handleMemoryUsage(double &res, double &shared) {
 
			res = 0;
 
			shared = 0;
 
			for(std::map<std::string, Skype *>::const_iterator it = m_sessions.begin(); it != m_sessions.end(); it++) {
 
				Skype *skype = it->second;
 
				if (skype) {
 
					double r;
 
					double s;
 
					process_mem_usage(s, r, skype->getPid());
 
					res += r;
 
					shared += s;
 
				}
 
			}
 
		}
 

	
 
		void handleLogoutRequest(const std::string &user, const std::string &legacyName) {
 
			Skype *skype = m_sessions[user];
 
			if (skype) {
 
				LOG4CXX_INFO(logger, "User wants to logout, logging out");
 
				skype->logout();
 
				Logging::shutdownLogging();
 
				exit(1);
 
			}
 
		}
 

	
 
		void handleStatusChangeRequest(const std::string &user, int status, const std::string &statusMessage) {
 
			Skype *skype = m_sessions[user];
 
			if (!skype)
 
				return;
 

	
 
			std::string st;
 
			switch(status) {
 
				case Swift::StatusShow::Away: {
 
					st = "AWAY";
 
					break;
 
				}
 
				case Swift::StatusShow::DND: {
 
					st = "DND";
 
					break;
 
				}
 
				case Swift::StatusShow::XA: {
 
					st = "NA";
 
					break;
 
				}
 
				case Swift::StatusShow::None: {
 
					break;
 
				}
 
				case pbnetwork::STATUS_INVISIBLE:
 
					st = "INVISIBLE";
 
					break;
 
				default:
 
					st = "ONLINE";
 
					break;
 
			}
 
			skype->send_command("SET USERSTATUS " + st);
 

	
 
			if (!statusMessage.empty()) {
 
				skype->send_command("SET PROFILE MOOD_TEXT " + statusMessage);
 
			}
 
		}
 

	
 
		void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups) {
 
			Skype *skype = m_sessions[user];
 
			if (skype) {
 
				skype->send_command("SET USER " + buddyName + " BUDDYSTATUS 2 Please authorize me");
 
				skype->send_command("SET USER " + buddyName + " ISAUTHORIZED TRUE");
 
			}
 
		}
 

	
 
		void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector<std::string> &groups) {
 
			Skype *skype = m_sessions[user];
 
			if (skype) {
 
				skype->send_command("SET USER " + buddyName + " BUDDYSTATUS 1");
 
				skype->send_command("SET USER " + buddyName + " ISAUTHORIZED FALSE");
 
			}
 
		}
 

	
 
		void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml = "", const std::string &id = "") {
 
			Skype *skype = m_sessions[user];
 
			if (skype) {
 
				skype->send_command("MESSAGE " + legacyName + " " + message);
 
			}
 
			
 
		}
 

	
 
		void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) {
 
			Skype *skype = m_sessions[user];
 
			if (skype) {
 
				std::string name = legacyName;
 
				if (name.find("skype.") == 0) {
 
					name = name.substr(6);
 
				}
 
				std::string photo;
 
				gchar *filename = NULL;
 
				gchar *new_filename = NULL;
 
				gchar *image_data = NULL;
 
				gsize image_data_len = 0;
 
				gchar *ret;
 
				int fh;
 
				GError *error;
 
				const gchar *userfiles[] = {"user256", "user1024", "user4096", "user16384", "user32768", "user65536",
 
											"profile256", "profile1024", "profile4096", "profile16384", "profile32768", 
 
											NULL};
 
				char *username = g_strdup_printf("\x03\x10%s", name.c_str());
 
				for (fh = 0; userfiles[fh]; fh++) {
 
					filename = g_strconcat("/tmp/skype/", skype->getUsername().c_str(), "/", skype->getUsername().c_str(), "/", userfiles[fh], ".dbb", NULL);
 
					std::cout << "getting filename:" << filename << "\n";
 
					if (g_file_get_contents(filename, &image_data, &image_data_len, NULL))
 
					{
 
						std::cout << "got\n";
 
						char *start = (char *)memmem(image_data, image_data_len, username, strlen(username)+1);
 
						if (start != NULL)
 
						{
 
							char *next = image_data;
 
							char *last = next;
 
							//find last index of l33l
 
							while ((next = (char *)memmem(next+4, start-next-4, "l33l", 4)))
 
							{
 
								last = next;
 
							}
 
							start = last;
 
							if (start != NULL)
 
							{
 
								char *img_start;
 
								//find end of l33l block
 
								char *end = (char *)memmem(start+4, image_data+image_data_len-start-4, "l33l", 4);
 
								if (!end) end = image_data+image_data_len;
 
								
 
								//look for start of JPEG block
 
								img_start = (char *)memmem(start, end-start, "\xFF\xD8", 2);
 
								if (img_start)
 
								{
 
									//look for end of JPEG block
 
									char *img_end = (char *)memmem(img_start, end-img_start, "\xFF\xD9", 2);
 
									if (img_end)
 
									{
 
										image_data_len = img_end - img_start + 2;
 
										photo = std::string(img_start, image_data_len);
 
									}
 
								}
 
							}
 
						}
 
						g_free(image_data);
 
					}
 
					g_free(filename);
 
				}
 
				g_free(username);
 
				
 
				std::string alias = "";
 
				std::cout << skype->getUsername() << " " << name << "\n";
 
				if (skype->getUsername() == name) {
 
					alias = skype->send_command("GET PROFILE FULLNAME");
 
					alias = GET_RESPONSE_DATA(alias, "FULLNAME")
 
				}
 
				handleVCard(user, id, legacyName, "", alias, photo);
 
			}
 
		}
 

	
 
		void sendData(const std::string &string) {
 
			write(m_sock, string.c_str(), string.size());
 
// 			if (writeInput == 0)
 
// 				writeInput = purple_input_add(m_sock, PURPLE_INPUT_WRITE, &transportDataReceived, NULL);
 
		}
 

	
 
		void handleVCardUpdatedRequest(const std::string &user, const std::string &p, const std::string &nickname) {
 
		}
 

	
 
		void handleBuddyBlockToggled(const std::string &user, const std::string &buddyName, bool blocked) {
 

	
 
		}
 
@@ -512,296 +513,287 @@ bool Skype::loadSkypeBuddies() {
 

	
 
	close(fd_output);
 

	
 
	if (send_command("PROTOCOL 7") != "PROTOCOL 7") {
 
		LOG4CXX_ERROR(logger, "PROTOCOL 7 failed, logging out");
 
		np->handleDisconnected(m_user, pbnetwork::CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, "Skype is not ready. This issue have been logged and admins will check it and try to fix it soon.");
 
		logout();
 
		return FALSE;
 
	}
 

	
 
	np->handleConnected(m_user);
 

	
 
	std::map<std::string, std::string> group_map;
 
	std::string groups = send_command("SEARCH GROUPS CUSTOM");
 
	if (groups.find(' ') != std::string::npos) {
 
		groups = groups.substr(groups.find(' ') + 1);
 
		std::vector<std::string> grps;
 
		boost::split(grps, groups, boost::is_any_of(","));
 
		BOOST_FOREACH(std::string grp, grps) {
 
			std::vector<std::string> data;
 
			std::string name = send_command("GET GROUP " + grp + " DISPLAYNAME");
 

	
 
			if (name.find("ERROR") == 0) {
 
				continue;
 
			}
 

	
 
			boost::split(data, name, boost::is_any_of(" "));
 
			name = GET_RESPONSE_DATA(name, "DISPLAYNAME");
 

	
 
			std::string users = send_command("GET GROUP " + data[1] + " USERS");
 
			try {
 
				users = GET_RESPONSE_DATA(users, "USERS");
 
			}
 
			catch (std::out_of_range& oor) {
 
				continue;
 
			}
 
			boost::split(data, users, boost::is_any_of(","));
 
			BOOST_FOREACH(std::string u, data) {
 
				group_map[u] = grp;
 
			}
 
		}
 
	}
 

	
 
	std::string friends = send_command("GET AUTH_CONTACTS_PROFILES");
 

	
 
	char **full_friends_list = g_strsplit((strchr(friends.c_str(), ' ')+1), ";", 0);
 
	if (full_friends_list && full_friends_list[0])
 
	{
 
		//in the format of: username;full name;phone;office phone;mobile phone;
 
		//                  online status;friendly name;voicemail;mood
 
		// (comma-seperated lines, usernames can have comma's)
 

	
 
		for (int i=0; full_friends_list[i] && full_friends_list[i+1] && *full_friends_list[i] != '\0'; i+=8)
 
		{
 
			std::string buddy = full_friends_list[i];
 

	
 
			if (buddy[0] == ',') {
 
				buddy.erase(buddy.begin());
 
			}
 

	
 
			if (buddy.rfind(",") != std::string::npos) {
 
				buddy = buddy.substr(buddy.rfind(","));
 
			}
 

	
 
			if (buddy[0] == ',') {
 
				buddy.erase(buddy.begin());
 
			}
 

	
 
			LOG4CXX_INFO(logger, "Got buddy " << buddy);
 
			std::string st = full_friends_list[i + 5];
 

	
 
			pbnetwork::StatusType status = getStatus(st);
 

	
 
			std::string alias = full_friends_list[i + 6];
 

	
 
			std::string mood_text = "";
 
			if (full_friends_list[i + 8] && *full_friends_list[i + 8] != '\0' && *full_friends_list[i + 8] != ',') {
 
				mood_text = full_friends_list[i + 8];
 
			}
 

	
 
			std::vector<std::string> groups;
 
			if (group_map.find(buddy) != group_map.end()) {
 
				groups.push_back(group_map[buddy]);
 
			}
 
			np->handleBuddyChanged(m_user, buddy, alias, groups, status, mood_text);
 
		}
 
	}
 
	g_strfreev(full_friends_list);
 

	
 
	send_command("SET AUTOAWAY OFF");
 
	send_command("SET USERSTATUS ONLINE");
 
	return FALSE;
 
}
 

	
 
void Skype::logout() {
 
	if (m_pid != 0) {
 
		if (m_proxy) {
 
			send_command("SET USERSTATUS INVISIBLE");
 
			send_command("SET USERSTATUS OFFLINE");
 
			sleep(2);
 
			g_object_unref(m_proxy);
 
		}
 
		LOG4CXX_INFO(logger,  m_username << ": Terminating Skype instance (SIGTERM)");
 
		kill((int) m_pid, SIGTERM);
 
		// Give skype a chance
 
		sleep(2);
 
		LOG4CXX_INFO(logger,  m_username << ": Killing Skype instance (SIGKILL)");
 
		kill((int) m_pid, SIGKILL);
 
		m_pid = 0;
 
	}
 
}
 

	
 
std::string Skype::send_command(const std::string &message) {
 
	GError *error = NULL;
 
	gchar *str = NULL;
 
// 			int message_num;
 
// 			gchar error_return[30];
 

	
 
	LOG4CXX_INFO(logger, "Sending: '" << message << "'");
 
	if (!dbus_g_proxy_call (m_proxy, "Invoke", &error, G_TYPE_STRING, message.c_str(), G_TYPE_INVALID,
 
						G_TYPE_STRING, &str, G_TYPE_INVALID))
 
	{
 
			if (error && error->message)
 
			{
 
			LOG4CXX_INFO(logger,  m_username << ": DBUS Error: " << error->message);
 
			g_error_free(error);
 
			return "";
 
		} else {
 
			LOG4CXX_INFO(logger,  m_username << ": DBUS no response");
 
			return "";
 
		}
 

	
 
	}
 
	if (str != NULL)
 
	{
 
		LOG4CXX_INFO(logger,  m_username << ": DBUS:'" << str << "'");
 
	}
 
	return str ? std::string(str) : std::string();
 
}
 

	
 
static void handle_skype_message(std::string &message, Skype *sk) {
 
	std::vector<std::string> cmd;
 
	boost::split(cmd, message, boost::is_any_of(" "));
 

	
 
	if (cmd[0] == "USER") {
 
		if (cmd[1] == sk->getUsername()) {
 
			return;
 
		}
 

	
 
		if (cmd[2] == "ONLINESTATUS") {
 
			if (cmd[3] == "SKYPEOUT" || cmd[3] == "UNKNOWN") {
 
				return;
 
			}
 
			else {
 
				pbnetwork::StatusType status = getStatus(cmd[3]);
 
				GET_PROPERTY(mood_text, "USER", cmd[1], "MOOD_TEXT");
 
				GET_PROPERTY(alias, "USER", cmd[1], "FULLNAME");
 

	
 
				std::vector<std::string> groups;
 
				np->handleBuddyChanged(sk->getUser(), cmd[1], alias, groups, status, mood_text);
 
			}
 
		}
 
		else if (cmd[2] == "MOOD_TEXT") {
 
			GET_PROPERTY(st, "USER", cmd[1], "ONLINESTATUS");
 
			pbnetwork::StatusType status = getStatus(st);
 

	
 
			std::string mood_text = GET_RESPONSE_DATA(message, "MOOD_TEXT");
 

	
 
			std::vector<std::string> groups;
 
			np->handleBuddyChanged(sk->getUser(), cmd[1], "", groups, status, mood_text);
 
		}
 
		else if (cmd[2] == "BUDDYSTATUS" && cmd[3] == "3") {
 
			GET_PROPERTY(mood_text, "USER", cmd[1], "MOOD_TEXT");
 
			GET_PROPERTY(st, "USER", cmd[1], "ONLINESTATUS");
 
			pbnetwork::StatusType status = getStatus(st);
 

	
 
			std::vector<std::string> groups;
 
			np->handleBuddyChanged(sk->getUser(), cmd[1], "", groups, status, mood_text);
 
		}
 
		else if (cmd[2] == "FULLNAME") {
 
			GET_PROPERTY(alias, "USER", cmd[1], "FULLNAME");
 
			GET_PROPERTY(mood_text, "USER", cmd[1], "MOOD_TEXT");
 
			GET_PROPERTY(st, "USER", cmd[1], "ONLINESTATUS");
 
			pbnetwork::StatusType status = getStatus(st);
 

	
 
			std::vector<std::string> groups;
 
			np->handleBuddyChanged(sk->getUser(), cmd[1], alias, groups, status, mood_text);
 
		}
 
		//TODO: handle RECEIVEDAUTHREQUEST and reply it with:
 
// 				void
 
// 				skype_auth_allow(gpointer sender)
 
// 				{
 
// 					skype_send_message("SET USER %s ISAUTHORIZED TRUE", sender);
 
// 					g_free(sender);
 
// 				}
 
// 
 
// 				void
 
// 				skype_auth_deny(gpointer sender)
 
// 				{
 
// 					skype_send_message("SET USER %s ISAUTHORIZED FALSE", sender);
 
// 					g_free(sender);
 
// 				}
 
		else if(cmd[2] == "RECEIVEDAUTHREQUEST") {
 
			np->handleAuthorization(sk->getUser(), cmd[1]);
 
		}
 
	}
 
	else if (cmd[0] == "CHATMESSAGE") {
 
		if (cmd[3] == "RECEIVED") {
 
			GET_PROPERTY(body, "CHATMESSAGE", cmd[1], "BODY");
 
			GET_PROPERTY(from_handle, "CHATMESSAGE", cmd[1], "FROM_HANDLE");
 

	
 
			if (from_handle == sk->getUsername())
 
				return;
 

	
 
			np->handleMessage(sk->getUser(), from_handle, body);
 

	
 
			sk->send_command("SET CHATMESSAGE " + cmd[1] + " SEEN");
 
		}
 
	}
 
	else if (cmd[0] == "CALL") {
 
		// CALL 884 STATUS RINGING
 
		if (cmd[2] == "STATUS") {
 
			if (cmd[3] == "RINGING" || cmd[3] == "MISSED") {
 
				// handle only incoming calls
 
				GET_PROPERTY(type, "CALL", cmd[1], "TYPE");
 
				if (type.find("INCOMING") != 0) {
 
					return;
 
				}
 

	
 
				GET_PROPERTY(from, "CALL", cmd[1], "PARTNER_HANDLE");
 
				GET_PROPERTY(dispname, "CALL", cmd[1], "PARTNER_DISPNAME");
 

	
 
				if (cmd[3] == "RINGING") {
 
					np->handleMessage(sk->getUser(), from, "User " + dispname + " is calling you.");
 
				}
 
				else {
 
					np->handleMessage(sk->getUser(), from, "You have missed call from user " + dispname + ".");
 
				}
 
			}
 
		}
 
	}
 
}
 

	
 
DBusHandlerResult skype_notify_handler(DBusConnection *connection, DBusMessage *message, gpointer user_data) {
 
	DBusMessageIter iterator;
 
	gchar *message_temp;
 
	DBusMessage *temp_message;
 
	
 
	temp_message = dbus_message_ref(message);
 
	dbus_message_iter_init(temp_message, &iterator);
 
	if (dbus_message_iter_get_arg_type(&iterator) != DBUS_TYPE_STRING)
 
	{
 
		dbus_message_unref(message);
 
		return (DBusHandlerResult) FALSE;
 
	}
 
	
 
	do {
 
		dbus_message_iter_get_basic(&iterator, &message_temp);
 
		std::string m(message_temp);
 
		LOG4CXX_INFO(logger,"DBUS message: " << m);
 
		handle_skype_message(m, (Skype *) user_data);
 
	} while(dbus_message_iter_has_next(&iterator) && dbus_message_iter_next(&iterator));
 
	
 
	dbus_message_unref(message);
 
	
 
	return DBUS_HANDLER_RESULT_HANDLED;
 
}
 

	
 
static void spectrum_sigchld_handler(int sig)
 
{
 
	int status;
 
	pid_t pid;
 

	
 
	do {
 
		pid = waitpid(-1, &status, WNOHANG);
 
	} while (pid != 0 && pid != (pid_t)-1);
 

	
 
	if ((pid == (pid_t) - 1) && (errno != ECHILD)) {
 
		char errmsg[BUFSIZ];
 
		snprintf(errmsg, BUFSIZ, "Warning: waitpid() returned %d", pid);
 
		perror(errmsg);
 
	}
 
}
 

	
 
static int create_socket(const char *host, int portno) {
 
	struct sockaddr_in serv_addr;
 
	
 
	int m_sock = socket(AF_INET, SOCK_STREAM, 0);
 
	memset((char *) &serv_addr, 0, sizeof(serv_addr));
 
	serv_addr.sin_family = AF_INET;
 
	serv_addr.sin_port = htons(portno);
 

	
 
	hostent *hos;  // Resolve name
 
	if ((hos = gethostbyname(host)) == NULL) {
 
		// strerror() will not work for gethostbyname() and hstrerror() 
 
		// is supposedly obsolete
 
		Logging::shutdownLogging();
 
		exit(1);
 
	}
 
	serv_addr.sin_addr.s_addr = *((unsigned long *) hos->h_addr_list[0]);
 

	
backends/swiften/main.cpp
Show inline comments
 
// Transport includes
 
#include "transport/config.h"
 
#include "transport/networkplugin.h"
 
#include "transport/logging.h"
 

	
 
#include "boost/date_time/posix_time/posix_time.hpp"
 

	
 
// Swiften
 
#include "Swiften/Swiften.h"
 

	
 
#ifndef WIN32
 
// for signal handler
 
#include "unistd.h"
 
#include "signal.h"
 
#include "sys/wait.h"
 
#include "sys/signal.h"
 
#endif
 

	
 
#ifndef __FreeBSD__
 
// malloc_trim
 
#include "malloc.h"
 
#endif
 

	
 
// Boost
 
#include <boost/algorithm/string.hpp>
 
using namespace boost::filesystem;
 
using namespace boost::program_options;
 
using namespace Transport;
 

	
 
DEFINE_LOGGER(logger, "Swiften");
 

	
 
// eventloop
 
Swift::SimpleEventLoop *loop_;
 

	
 
// Plugins
 
class SwiftenPlugin;
 
SwiftenPlugin *np = NULL;
 
NetworkPlugin *np = NULL;
 

	
 
class MUCController {
 
	public:
 
		MUCController(const std::string &user, boost::shared_ptr<Swift::Client> client, const std::string &room, const std::string &nickname, const std::string &password) {
 
			m_user = user;
 
			m_room = room;
 
			muc = client->getMUCManager()->createMUC(room);
 
			if (!password.empty()) {
 
				muc->setPassword(password);
 
			}
 

	
 
			muc->onJoinComplete.connect(boost::bind(&MUCController::handleJoinComplete, this, _1));
 
			muc->onJoinFailed.connect(boost::bind(&MUCController::handleJoinFailed, this, _1));
 
			muc->onOccupantJoined.connect(boost::bind(&MUCController::handleOccupantJoined, this, _1));
 
			muc->onOccupantPresenceChange.connect(boost::bind(&MUCController::handleOccupantPresenceChange, this, _1));
 
			muc->onOccupantLeft.connect(boost::bind(&MUCController::handleOccupantLeft, this, _1, _2, _3));
 
			muc->onOccupantRoleChanged.connect(boost::bind(&MUCController::handleOccupantRoleChanged, this, _1, _2, _3));
 
			muc->onOccupantAffiliationChanged.connect(boost::bind(&MUCController::handleOccupantAffiliationChanged, this, _1, _2, _3));
 

	
 
			muc->joinAs(nickname);
 
		}
 

	
 
		virtual ~MUCController() {
 
			muc->onJoinComplete.disconnect(boost::bind(&MUCController::handleJoinComplete, this, _1));
 
			muc->onJoinFailed.disconnect(boost::bind(&MUCController::handleJoinFailed, this, _1));
 
			muc->onOccupantJoined.disconnect(boost::bind(&MUCController::handleOccupantJoined, this, _1));
 
			muc->onOccupantPresenceChange.disconnect(boost::bind(&MUCController::handleOccupantPresenceChange, this, _1));
 
			muc->onOccupantLeft.disconnect(boost::bind(&MUCController::handleOccupantLeft, this, _1, _2, _3));
 
			muc->onOccupantRoleChanged.disconnect(boost::bind(&MUCController::handleOccupantRoleChanged, this, _1, _2, _3));
 
			muc->onOccupantAffiliationChanged.disconnect(boost::bind(&MUCController::handleOccupantAffiliationChanged, this, _1, _2, _3));
 
		}
 

	
 
		const std::string &getNickname() {
 
			//return muc->getCurrentNick();
 
			return m_nick;
 
		}
 

	
 
		void handleOccupantJoined(const Swift::MUCOccupant& occupant) {
 
			np->handleParticipantChanged(m_user, occupant.getNick(), m_room, occupant.getRole() == Swift::MUCOccupant::Moderator, pbnetwork::STATUS_ONLINE);
 
		}
 

	
 
		void handleOccupantLeft(const Swift::MUCOccupant& occupant, Swift::MUC::LeavingType type, const std::string& reason) {
 
			np->handleParticipantChanged(m_user, occupant.getNick(), m_room, occupant.getRole() == Swift::MUCOccupant::Moderator, pbnetwork::STATUS_NONE);
 
		}
 

	
 
		void handleOccupantPresenceChange(boost::shared_ptr<Swift::Presence> presence) {
 
			const Swift::MUCOccupant& occupant = muc->getOccupant(presence->getFrom().getResource());
 
			np->handleParticipantChanged(m_user, presence->getFrom().getResource(), m_room, (int) occupant.getRole() == Swift::MUCOccupant::Moderator, (pbnetwork::StatusType) presence->getShow(), presence->getStatus());
 
		}
 

	
 
		void handleOccupantRoleChanged(const std::string& nick, const Swift::MUCOccupant& occupant, const Swift::MUCOccupant::Role& oldRole) {
 

	
 
		}
 

	
 
		void handleOccupantAffiliationChanged(const std::string& nick, const Swift::MUCOccupant::Affiliation& affiliation, const Swift::MUCOccupant::Affiliation& oldAffiliation) {
 
// 			np->handleParticipantChanged(m_user, occupant->getNick(), m_room, (int) occupant.getRole() == Swift::MUCOccupant::Moderator, pbnetwork::STATUS_ONLINE);
 
		}
 

	
 
		void handleJoinComplete(const std::string& nick) {
 
			m_nick = nick;
 
		}
 

	
 
		void handleJoinFailed(boost::shared_ptr<Swift::ErrorPayload> error) {
 
			
 
		}
 

	
 
		void part() {
 
			muc->part();
 
		}
 

	
 
	private:
 
		Swift::MUC::ref muc;
 
		std::string m_user;
 
		std::string m_room;
 
		std::string m_nick;
 
};
 

	
 
class SwiftenPlugin : public NetworkPlugin {
 
	public:
 
		Swift::BoostNetworkFactories *m_factories;
 
		Swift::BoostIOServiceThread m_boostIOServiceThread;
 
		boost::shared_ptr<Swift::Connection> m_conn;
 

	
 
		SwiftenPlugin(Config *config, Swift::SimpleEventLoop *loop, const std::string &host, int port) : NetworkPlugin() {
 
			this->config = config;
 
			m_factories = new Swift::BoostNetworkFactories(loop);
 
			m_conn = m_factories->getConnectionFactory()->createConnection();
 
			m_conn->onDataRead.connect(boost::bind(&SwiftenPlugin::_handleDataRead, this, _1));
 
			m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(host), port));
 

	
 
			LOG4CXX_INFO(logger, "Starting the plugin.");
 
		}
 

	
 
		// NetworkPlugin uses this method to send the data to networkplugin server
 
		void sendData(const std::string &string) {
 
			m_conn->write(Swift::createSafeByteArray(string));
 
		}
 

	
 
		// This method has to call handleDataRead with all received data from network plugin server
 
		void _handleDataRead(boost::shared_ptr<Swift::SafeByteArray> data) {
 
			std::string d(data->begin(), data->end());
 
			handleDataRead(d);
 
		}
 

	
 
		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"); reconnect = true; break;
 
					case Swift::ClientError::DomainNameResolveError: message = ("Unable to find server"); break;
 
					case Swift::ClientError::ConnectionError: message = ("Error connecting to server"); break;
 
					case Swift::ClientError::ConnectionReadError: message = ("Error while receiving server data"); reconnect = true; break;
 
					case Swift::ClientError::ConnectionWriteError: message = ("Error while sending data to the server"); reconnect = true; break;
 
					case Swift::ClientError::XMLError: message = ("Error parsing server data"); reconnect = true; break;
 
					case Swift::ClientError::AuthenticationFailedError: message = ("Login/password invalid"); break;
 
					case Swift::ClientError::CompressionFailedError: message = ("Error while compressing stream"); break;
 
					case Swift::ClientError::ServerVerificationFailedError: message = ("Server verification failed"); break;
 
					case Swift::ClientError::NoSupportedAuthMechanismsError: message = ("Authentication mechanisms not supported"); break;
 
					case Swift::ClientError::UnexpectedElementError: message = ("Unexpected response"); break;
 
					case Swift::ClientError::ResourceBindError: message = ("Error binding resource"); break;
 
					case Swift::ClientError::SessionStartError: message = ("Error starting session"); break;
 
					case Swift::ClientError::StreamError: message = ("Stream error"); break;
 
					case Swift::ClientError::TLSError: message = ("Encryption error"); break;
 
					case Swift::ClientError::ClientCertificateLoadError: message = ("Error loading certificate (Invalid password?)"); break;
 
					case Swift::ClientError::ClientCertificateError: message = ("Certificate not authorized"); break;
 

	
 
					case Swift::ClientError::UnknownCertificateError: message = ("Unknown certificate"); break;
 
					case Swift::ClientError::CertificateExpiredError: message = ("Certificate has expired"); break;
 
					case Swift::ClientError::CertificateNotYetValidError: message = ("Certificate is not yet valid"); break;
 
					case Swift::ClientError::CertificateSelfSignedError: message = ("Certificate is self-signed"); break;
 
					case Swift::ClientError::CertificateRejectedError: message = ("Certificate has been rejected"); break;
 
					case Swift::ClientError::CertificateUntrustedError: message = ("Certificate is not trusted"); break;
 
					case Swift::ClientError::InvalidCertificatePurposeError: message = ("Certificate cannot be used for encrypting your connection"); break;
 
					case Swift::ClientError::CertificatePathLengthExceededError: message = ("Certificate path length constraint exceeded"); break;
 
					case Swift::ClientError::InvalidCertificateSignatureError: message = ("Invalid certificate signature"); break;
 
					case Swift::ClientError::InvalidCAError: message = ("Invalid Certificate Authority"); break;
 
					case Swift::ClientError::InvalidServerIdentityError: message = ("Certificate does not match the host identity"); break;
 
				}
 
			}
 
			LOG4CXX_INFO(logger, user << ": Disconnected " << message);
 
			handleDisconnected(user, reconnect ? 0 : 3, message);
 

	
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client) {
 
				client->onConnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user));
 
				client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1));
 
				client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1));
 
				m_users.erase(user);
 
				m_mucs.erase(user);
 
			}
 

	
 
#ifndef WIN32
 
#ifndef __FreeBSD__
 
			// force returning of memory chunks allocated by libxml2 to kernel
 
			malloc_trim(0);
 
#endif
 
#endif
 
		}
 

	
 
		void handleSwiftConnected(const std::string &user) {
 
			LOG4CXX_INFO(logger, user << ": Connected to XMPP server.");
 
			handleConnected(user);
 
			m_users[user]->requestRoster();
 
			Swift::Presence::ref response = Swift::Presence::create();
 
			response->setFrom(m_users[user]->getJID());
 
			m_users[user]->sendPresence(response);
 
		}
 

	
 
		void handleSwiftRosterReceived(const std::string &user) {
 
			Swift::PresenceOracle *oracle = m_users[user]->getPresenceOracle();
 
			BOOST_FOREACH(const Swift::XMPPRosterItem &item, m_users[user]->getRoster()->getItems()) {
 
				Swift::Presence::ref lastPresence = oracle->getLastPresence(item.getJID());
 
				pbnetwork::StatusType status = lastPresence ? ((pbnetwork::StatusType) lastPresence->getShow()) : pbnetwork::STATUS_NONE;
 
				handleBuddyChanged(user, item.getJID().toBare().toString(),
 
								   item.getName(), item.getGroups(), status);
 
			}
 
		}
 

	
 
		void handleSwiftPresenceChanged(const std::string &user, Swift::Presence::ref presence) {
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client->getMUCRegistry()->isMUC(presence->getFrom().toBare())) {
 
				return;
 
			}
 

	
 
			LOG4CXX_INFO(logger, user << ": " << presence->getFrom().toBare().toString() << " presence changed");
 

	
 
			std::string message = presence->getStatus();
 
			std::string photo = "";
 

	
 
			boost::shared_ptr<Swift::VCardUpdate> update = presence->getPayload<Swift::VCardUpdate>();
 
			if (update) {
 
				photo = update->getPhotoHash();
 
			}
 

	
 
			boost::optional<Swift::XMPPRosterItem> item = m_users[user]->getRoster()->getItem(presence->getFrom());
 
			if (item) {
 
				handleBuddyChanged(user, presence->getFrom().toBare().toString(), item->getName(), item->getGroups(), (pbnetwork::StatusType) presence->getShow(), message, photo);
 
			}
 
			else {
 
				std::vector<std::string> groups;
 
				handleBuddyChanged(user, presence->getFrom().toBare().toString(), presence->getFrom().toBare(), groups, (pbnetwork::StatusType) presence->getShow(), message, photo);
 
			}
 
		}
 

	
 
		void handleSwiftMessageReceived(const std::string &user, Swift::Message::ref message) {
 
			std::string body = message->getBody();
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client) {
 
				if (message->getType() == Swift::Message::Groupchat) {
 
					boost::shared_ptr<Swift::Delay> delay = message->getPayload<Swift::Delay>();
 
					std::string timestamp = "";
 
					if (delay) {
 
						timestamp = boost::posix_time::to_iso_string(delay->getStamp());
 
					}
 
					handleMessage(user, message->getFrom().toBare().toString(), body, message->getFrom().getResource(), "", timestamp);
 
				}
 
				else {
 
					if (client->getMUCRegistry()->isMUC(message->getFrom().toBare())) {
 
						handleMessage(user, message->getFrom().toBare().toString(), body, message->getFrom().getResource(), "", "", false, true);
 
					}
 
					else {
 
						handleMessage(user, message->getFrom().toBare().toString(), body, "", "");
 
					}
 
				}
 
			}
 
		}
 

	
 
		void handleSwiftVCardReceived(const std::string &user, unsigned int id, Swift::VCard::ref vcard, Swift::ErrorPayload::ref error) {
 
			if (error || !vcard) {
 
				LOG4CXX_INFO(logger, user << ": error fetching VCard with id=" << id);
 
				handleVCard(user, id, "", "", "", "");
 
				return;
 
			}
 
			LOG4CXX_INFO(logger, user << ": VCard fetched - id=" << id);
 
			std::string photo((const char *)&vcard->getPhoto()[0], vcard->getPhoto().size());
 
			handleVCard(user, id, vcard->getFullName(), vcard->getFullName(), vcard->getNickname(), photo);
 
		}
 

	
 
		void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) {
 
			LOG4CXX_INFO(logger, user << ": connecting as " << legacyName);
 
			boost::shared_ptr<Swift::Client> client = boost::make_shared<Swift::Client>(Swift::JID(legacyName), password, m_factories);
 
			m_users[user] = client;
 
			client->setAlwaysTrustCertificates();
 
			client->onConnected.connect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user));
 
			client->onDisconnected.connect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1));
 
			client->onMessageReceived.connect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1));
 
			client->getRoster()->onInitialRosterPopulated.connect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user));
 
			client->getPresenceOracle()->onPresenceChange.connect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1));
 
			Swift::ClientOptions opt;
 
			opt.allowPLAINWithoutTLS = true;
 
			client->connect(opt);
 
		}
 

	
 
		void handleLogoutRequest(const std::string &user, const std::string &legacyName) {
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client) {
 
				client->onConnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user));
 
// 				client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1));
 
				client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1));
 
				client->getRoster()->onInitialRosterPopulated.disconnect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user));
 
				client->getPresenceOracle()->onPresenceChange.disconnect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1));
 
				client->disconnect();
 
				m_mucs.erase(user);
 
				m_users.erase(user);
 
			}
 
		}
 

	
 
		void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &xhtml = "", const std::string &id = "") {
 
			LOG4CXX_INFO(logger, "Sending message from " << user << " to " << legacyName << ".");
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client) {
 
				boost::shared_ptr<Swift::Message> message(new Swift::Message());
 
				message->setTo(Swift::JID(legacyName));
 
				message->setFrom(client->getJID());
 
				message->setBody(msg);
 
				if (client->getMUCRegistry()->isMUC(legacyName)) {
 
					message->setType(Swift::Message::Groupchat);
 
					boost::shared_ptr<MUCController> muc = m_mucs[user][legacyName];
 
					handleMessage(user, legacyName, msg, muc->getNickname(), xhtml);
 
				}
 

	
 
				client->sendMessage(message);
 
			}
 
		}
 

	
 
		void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) {
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client) {
 
				LOG4CXX_INFO(logger, user << ": fetching VCard of " << legacyName << " id=" << id);
 
				Swift::GetVCardRequest::ref request = Swift::GetVCardRequest::create(Swift::JID(legacyName), client->getIQRouter());
 
				request->onResponse.connect(boost::bind(&SwiftenPlugin::handleSwiftVCardReceived, this, user, id, _1, _2));
 
				request->send();
 
			}
 
		}
 

	
 
		void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups) {
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client) {
 
				LOG4CXX_INFO(logger, user << ": Added/Updated buddy " << buddyName << ".");
 
// 			handleBuddyChanged(user, buddyName, alias, groups, pbnetwork::STATUS_ONLINE);
 
				if (!client->getRoster()->containsJID(buddyName)) {
 
					Swift::RosterItemPayload item;
 
					item.setName(alias);
 
					item.setJID(buddyName);
 
					item.setGroups(groups);
 
					boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
 
					roster->addItem(item);
 
					Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
 
// 					request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
 
					request->send();
 
					client->getSubscriptionManager()->requestSubscription(buddyName);
 
				}
 
				else {
 
					Swift::JID contact(buddyName);
 
					Swift::RosterItemPayload item(contact, alias, client->getRoster()->getSubscriptionStateForJID(contact));
 
					item.setGroups(groups);
 
					boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
 
					roster->addItem(item);
 
					Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
 
// 					request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
 
					request->send();
 
				}
 

	
 
			}
 
		}
 

	
 
		void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector<std::string> &groups) {
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client) {
 
				Swift::RosterItemPayload item(buddyName, "", Swift::RosterItemPayload::Remove);
 
				boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
 
				roster->addItem(item);
 
				Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
 
// 				request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
 
				request->send();
 
			}
 
		}
 

	
 
		void handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password) {
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client) {
 
				if (client->getMUCRegistry()->isMUC(room)) {
 
					return;
 
				}
 

	
 
				boost::shared_ptr<MUCController> muc = boost::shared_ptr<MUCController>( new MUCController(user, client, room, nickname, password));
 
				m_mucs[user][room] = muc;
 
			}
 
		}
 

	
 
		void handleLeaveRoomRequest(const std::string &user, const std::string &room) {
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client) {
 
				if (!client->getMUCRegistry()->isMUC(room)) {
 
					return;
 
				}
 

	
 
				boost::shared_ptr<MUCController> muc = m_mucs[user][room];
 
				if (!muc) {
 
					m_mucs[user].erase(room);
 
					return;
 
				}
 

	
 
				muc->part();
 
				m_mucs[user].erase(room);
 
			}
 
		}
 

	
 
	private:
 
		Config *config;
 
		std::map<std::string, boost::shared_ptr<Swift::Client> > m_users;
 
		std::map<std::string, std::map<std::string, boost::shared_ptr<MUCController> > > m_mucs;
 
};
 

	
 
#ifndef WIN32
 
static void spectrum_sigchld_handler(int sig)
 
{
 
	int status;
 
	pid_t pid;
 

	
 
	do {
 
		pid = waitpid(-1, &status, WNOHANG);
 
	} while (pid != 0 && pid != (pid_t)-1);
 

	
 
	if ((pid == (pid_t) - 1) && (errno != ECHILD)) {
 
		char errmsg[BUFSIZ];
 
		snprintf(errmsg, BUFSIZ, "Warning: waitpid() returned %d", pid);
 
		perror(errmsg);
 
	}
 
}
 
#endif
 

	
 

	
 
int main (int argc, char* argv[]) {
 
	std::string host;
 
	int port;
 

	
 
#ifndef WIN32
 
	if (signal(SIGCHLD, spectrum_sigchld_handler) == SIG_ERR) {
 
		std::cout << "SIGCHLD handler can't be set\n";
 
		return -1;
 
	}
 
#endif
 

	
 
	std::string error;
 
	Config *cfg = Config::createFromArgs(argc, argv, error, host, port);
 
	if (cfg == NULL) {
 
		std::cerr << error;
 
		return 1;
 
	}
 

	
 
	Logging::initBackendLogging(cfg);
 

	
 
	Swift::SimpleEventLoop eventLoop;
 
	loop_ = &eventLoop;
 
	np = new SwiftenPlugin(cfg, &eventLoop, host, port);
 
	loop_->run();
 

	
 
	return 0;
 
}
include/transport/adhoccommand.h
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <string>
 
#include <algorithm>
 
#include <map>
 
#include "Swiften/Swiften.h"
 
#include <time.h>
 

	
 
#include "Swiften/Elements/FormField.h"
 
#include "Swiften/Elements/Command.h"
 

	
 
namespace Transport {
 

	
 
class Component;
 
class UserManager;
 
class StorageBackend;
 

	
 
class AdHocCommand {
 
	public:
 
		/// Creates new AdHocManager.
 

	
 
		/// \param component Transport instance associated with this AdHocManager.
 
		AdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to);
 

	
 
		/// Destructor.
 
		virtual ~AdHocCommand();
 

	
 
		virtual boost::shared_ptr<Swift::Command> handleRequest(boost::shared_ptr<Swift::Command> payload) = 0;
 

	
 
		void addFormField(Swift::FormField::ref field);
 

	
 
		const std::string &getId() {
 
			return m_id;
 
		}
 

	
 
		void refreshLastActivity() {
 
			m_lastActivity = time(NULL);
 
		}
 

	
 
		time_t getLastActivity() {
 
			return m_lastActivity;
 
		}
 

	
 
	protected:
 
		Component *m_component;
 
		UserManager *m_userManager;
 
		StorageBackend *m_storageBackend;
 
		Swift::JID m_initiator;
 
		Swift::JID m_to;
 
		std::vector<Swift::FormField::ref> m_fields;
 
		std::string m_id;
 

	
 
	private:
 
		// This is used to remove AdHocCommand after long inactivity to prevent memory leaks
 
		// caused by users which disconnect before they finish the command.
 
		// AdHocManager uses this to garbage collect old AdHocCommands.
 
		time_t m_lastActivity;
 
};
 

	
 
}
include/transport/adhoccommandfactory.h
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <string>
 
#include <algorithm>
 
#include <map>
 
#include "transport/adhoccommand.h"
 
#include "Swiften/Swiften.h"
 

	
 
namespace Transport {
 

	
 
class Component;
 
class UserManager;
 
class StorageBackend;
 

	
 
class AdHocCommandFactory {
 
	public:
 
		AdHocCommandFactory() {}
 

	
 
		/// Destructor.
 
		virtual ~AdHocCommandFactory() {}
 

	
 
		virtual AdHocCommand *createAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to) = 0;
 

	
 
		virtual std::string getNode() = 0;
 

	
 
		virtual std::string getName() = 0;
 

	
 
		virtual std::map<std::string, std::string> &getUserSettings() {
 
			return m_userSettings;
 
		}
 

	
 
	protected:
 
		std::map<std::string, std::string> m_userSettings;
 
};
 

	
 
}
include/transport/adhocmanager.h
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <string>
 
#include <algorithm>
 
#include <map>
 
#include "Swiften/Swiften.h"
 

	
 
#include "Swiften/Queries/Responder.h"
 
#include "Swiften/Elements/Command.h"
 
#include "Swiften/Network/Timer.h"
 

	
 
namespace Transport {
 

	
 
class Conversation;
 
class User;
 
class Component;
 
class DiscoItemsResponder;
 
class AdHocCommandFactory;
 
class AdHocCommand;
 
class UserManager;
 
class StorageBackend;
 

	
 
/// Listens for AdHoc commands and manages all AdHoc commands sessions
 
class AdHocManager : public Swift::Responder<Swift::Command> {
 
	public:
 
		typedef std::map<std::string, AdHocCommand *> CommandsMap;
 
		typedef std::map<Swift::JID, CommandsMap> SessionsMap;
 
		/// Creates new AdHocManager.
 

	
 
		/// \param component Transport instance associated with this AdHocManager.
 
		AdHocManager(Component *component, DiscoItemsResponder *discoItemsResponder, UserManager *userManager, StorageBackend *storageBackend = NULL);
 

	
 
		/// Destructor.
 
		virtual ~AdHocManager();
 

	
 
		/// Starts handling AdHoc commands payloads.
 
		void start();
 

	
 
		/// Stops handling AdHoc commands payloads and destroys all existing
 
		/// AdHoc commands sessions.
 
		void stop();
 

	
 
		/// Adds factory to create new AdHoc commands sessions of particular type.
 
		void addAdHocCommand(AdHocCommandFactory *factory);
 

	
 
		/// Remove sessions older than N seconds.
 
		void removeOldSessions();
 

	
 

	
 
	private:
 
		virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::Command> payload);
 
		virtual bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::Command> payload);
 
		
 
		void handleUserCreated(User *user);
 

	
 
		Component *m_component;
 
		DiscoItemsResponder *m_discoItemsResponder;
 
		std::map<std::string, AdHocCommandFactory *> m_factories;
 
		SessionsMap m_sessions;
 
		Swift::Timer::ref m_collectTimer;
 
		UserManager *m_userManager;
 
		StorageBackend *m_storageBackend;
 
};
 

	
 
}
include/transport/admininterface.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <string>
 
#include <map>
 
#include "Swiften/Swiften.h"
 

	
 
#include "Swiften/Elements/Message.h"
 

	
 
namespace Transport {
 

	
 
class Component;
 
class StorageBackend;
 
class UserManager;
 
class NetworkPluginServer;
 
class UserRegistration;
 

	
 
class AdminInterface {
 
	public:
 
		AdminInterface(Component *component, UserManager *userManager, NetworkPluginServer *server = NULL, StorageBackend *storageBackend = NULL, UserRegistration *userRegistration = NULL);
 

	
 
		~AdminInterface();
 

	
 
		void handleQuery(Swift::Message::ref message);
 

	
 
	private:
 
		void handleMessageReceived(Swift::Message::ref message);
 

	
 
		Component *m_component;
 
		StorageBackend *m_storageBackend;
 
		UserManager *m_userManager;
 
		NetworkPluginServer *m_server;
 
		UserRegistration *m_userRegistration;
 
		time_t m_start;
 
};
 

	
 
}
include/transport/buddy.h
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <string>
 
#include <algorithm>
 
#include "transport/transport.h"
 
#include "Swiften/Elements/VCard.h"
 

	
 
#include "Swiften/Swiften.h"
 

	
 
namespace Transport {
 

	
 
class RosterManager;
 

	
 
typedef enum { 	BUDDY_NO_FLAG = 0,
 
				BUDDY_JID_ESCAPING = 2,
 
				BUDDY_IGNORE = 4,
 
				BUDDY_BLOCKED = 8,
 
			} BuddyFlag;
 

	
 
/// Represents one legacy network Buddy.
 
class Buddy {
 
	public:
 
		typedef enum { 	Ask,
 
						Both,
 
					} Subscription;
 
		/// Constructor.
 

	
 
		/// \param rosterManager RosterManager associated with this buddy.
 
		/// \param id ID which identifies the buddy in database or -1 if it's new buddy which is
 
		/// not in database yet.
 
		Buddy(RosterManager *rosterManager, long id = -1, BuddyFlag flags = BUDDY_NO_FLAG);
 

	
 
		/// Destructor
 
		virtual ~Buddy();
 
		
 
		/// Sets unique ID used to identify this buddy by StorageBackend.
 
		
 
		/// This is set
 
		/// by RosterStorage class once the buddy is stored into database or when the
 
		/// buddy is loaded from database.
 
		/// You should not need to set this ID manually.
 
		/// \param id ID
 
		void setID(long id);
 

	
 
		/// Returns unique ID used to identify this buddy by StorageBackend.
 

	
 
		/// \return ID which identifies the buddy in database or -1 if it's new buddy which is
 
		/// not in database yet.
 
		long getID();
 

	
 
		/// Returns full JID of this buddy.
 

	
 
		/// \param hostname hostname used as domain in returned JID
 
		/// \return full JID of this buddy
 
		const Swift::JID &getJID();
 

	
 
		/// Generates whole Presennce stanza with current status/show for this buddy.
 

	
 
		/// Presence stanza does not containt "to" attribute, it has to be added manually.
 
		/// \param features features used in returned stanza
 
		/// \param only_new if True, this function returns Presence stanza only if it's different
 
		/// than the previously generated one.
 
		/// \return Presence stanza or NULL.
 
		Swift::Presence::ref generatePresenceStanza(int features, bool only_new = false);
 

	
 
		void setBlocked(bool block) {
 
			if (block)
 
				m_flags = (BuddyFlag) (m_flags | BUDDY_BLOCKED);
 
			else
 
				m_flags = (BuddyFlag) (m_flags & ~BUDDY_BLOCKED);
 
		}
 

	
 
		bool isBlocked() {
 
			return (m_flags & BUDDY_BLOCKED)  != 0;
 
		}
 

	
 
		/// Sets current subscription.
 

	
 
		/// \param subscription "to", "from", "both", "ask"
 
		void setSubscription(Subscription subscription);
 

	
 
		/// Returns current subscription
 

	
 
		/// \return subscription "to", "from", "both", "ask"
 
		Subscription getSubscription();
 

	
 
		/// Sets this buddy's flags.
 

	
 
		/// \param flags flags
 
		void setFlags(BuddyFlag flags);
 

	
 
		/// Returns this buddy's flags.
 

	
 
		/// \param flags flags
 
		BuddyFlag getFlags();
 

	
 
		/// Returns RosterManager associated with this buddy.
 

	
 
		/// \return RosterManager associated with this buddy.
 
		RosterManager *getRosterManager() { return m_rosterManager; }
 

	
 
		/// Returns legacy network username which does not contain unsafe characters,
 
		/// so it can be used in JIDs.
 
		std::string getSafeName();
include/transport/conversation.h
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <string>
 
#include <algorithm>
 
#include "transport/transport.h"
 

	
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Elements/Message.h"
 

	
 
namespace Transport {
 

	
 
class ConversationManager;
 

	
 
/// Represents one XMPP-Legacy network conversation.
 
class Conversation {
 
	public:
 
		typedef enum {
 
			PARTICIPANT_FLAG_NONE = 0,
 
			PARTICIPANT_FLAG_MODERATOR = 1,
 
			PARTICIPANT_FLAG_CONFLICT = 2,
 
			PARTICIPANT_FLAG_BANNED = 4,
 
			PARTICIPANT_FLAG_NOT_AUTHORIZED = 8,
 
			PARTICIPANT_FLAG_ME = 16,
 
			PARTICIPANT_FLAG_KICKED = 32
 
		} ParticipantFlag;
 

	
 
		typedef struct _Participant {
 
			int flag;
 
			ParticipantFlag flag;
 
			int status;
 
			std::string statusMessage;
 
		} Participant;
 

	
 
		/// Type of participants in MUC rooms.
 
		enum ParticipantFlag {None, Moderator};
 

	
 
		/// Creates new conversation.
 

	
 
		/// \param conversationManager ConversationManager associated with this Conversation.
 
		/// \param legacyName Legacy network name of recipient.
 
		/// \param muc True if this conversation is Multi-user chat.
 
		Conversation(ConversationManager *conversationManager, const std::string &legacyName, bool muc = false);
 

	
 
		/// Destructor.
 
		virtual ~Conversation();
 

	
 
		/// Returns legacy network name of this conversation.
 

	
 
		/// \return legacy network name of this conversation.
 
		const std::string &getLegacyName() { return m_legacyName; }
 

	
 
		/// Handles new message from Legacy network and forwards it to XMPP.
 

	
 
		/// \param message Message received from legacy network.
 
		/// \param nickname For MUC conversation this is nickname of room participant who sent this message.
 
		void handleMessage(boost::shared_ptr<Swift::Message> &message, const std::string &nickname = "");
 

	
 
		/// Handles participant change in MUC.
 

	
 
		/// \param nickname Nickname of participant which changed.
 
		/// \param flag ParticipantFlag.
 
		/// \param status Current status of this participant.
 
		/// \param statusMessage Current status message of this participant.
 
		/// \param newname If participant was renamed, this variable contains his new name.
 
		void handleParticipantChanged(const std::string &nickname, int flag, int status = Swift::StatusShow::None, const std::string &statusMessage = "", const std::string &newname = "");
 
		void handleParticipantChanged(const std::string &nickname, ParticipantFlag flag, int status = Swift::StatusShow::None, const std::string &statusMessage = "", const std::string &newname = "");
 

	
 
		/// Sets XMPP user nickname in MUC rooms.
 

	
 
		/// \param nickname XMPP user nickname in MUC rooms.
 
		void setNickname(const std::string &nickname) {
 
			m_nickname = nickname;
 
		}
 

	
 
		const std::string &getNickname() {
 
			return m_nickname;
 
		}
 

	
 
		void setJID(const Swift::JID &jid) {
 
			m_jid = jid;
 
		}
 

	
 
		void addJID(const Swift::JID &jid) {
 
			m_jids.push_back(jid);
 
		}
 

	
 
		void clearJIDs() {
 
			m_jids.clear();
 
		}
 

	
 
		void removeJID(const Swift::JID &jid) {
 
			m_jids.remove(jid);
 
		}
 

	
 
		const std::list<Swift::JID> &getJIDs() {
 
			return m_jids;
 
		}
 

	
 
		/// Sends message to Legacy network.
 

	
 
		/// \param message Message.
 
		virtual void sendMessage(boost::shared_ptr<Swift::Message> &message) = 0;
 

	
 
		/// Returns ConversationManager associated with this Conversation.
 

	
 
		/// \return  ConversationManager associated with this Conversation.
 
		ConversationManager *getConversationManager() {
 
			return m_conversationManager;
 
		}
 

	
 
		/// Returns True if this conversation is MUC room.
 

	
 
		/// \return  True if this conversation is MUC room.
 
		bool isMUC() {
 
			return m_muc;
 
		}
 

	
 
		/// Sets room name associated with this Conversation.
 

	
 
		/// This is used to detect Private messages associated with particular room.
 
		/// \param room room name associated with this Conversation.
 
		void setRoom(const std::string &room);
 

	
 
		/// Returns room name associated with this Conversation.
 

	
 
		/// \return room name associated with this Conversation.
 
		const std::string &getRoom() {
 
			return m_room;
 
		}
 

	
 
		void destroyRoom();
 

	
 
		void sendParticipants(const Swift::JID &to);
 

	
 
		void sendCachedMessages(const Swift::JID &to = Swift::JID());
 

	
 
	private:
 
		Swift::Presence::ref generatePresence(const std::string &nick, int flag, int status, const std::string &statusMessage, const std::string &newname = "");
 

	
 
	private:
 
		ConversationManager *m_conversationManager;
 
		std::string m_legacyName;
 
		std::string m_nickname;
 
		std::string m_room;
 
		bool m_muc;
 
		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;
 
		std::list<boost::shared_ptr<Swift::Message> > m_cachedMessages;
 
};
 

	
 
}
include/transport/conversationmanager.h
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <string>
 
#include <algorithm>
 
#include <map>
 
#include "Swiften/Swiften.h"
 

	
 
#include "Swiften/Elements/Message.h"
 

	
 
namespace Transport {
 

	
 
class Conversation;
 
class User;
 
class Component;
 

	
 
/// Manages all Conversations of particular User.
 
class ConversationManager {
 
	public:
 
		/// Creates new ConversationManager.
 

	
 
		/// \param user User associated with this ConversationManager.
 
		/// \param component Transport instance associated with this ConversationManager.
 
		ConversationManager(User *user, Component *component);
 

	
 
		/// Destructor.
 
		virtual ~ConversationManager();
 

	
 
		/// Returns user associated with this manager.
 

	
 
		/// \return User
 
		User *getUser() { return m_user; }
 

	
 
		/// Returns component associated with this ConversationManager.
 

	
 
		/// \return component associated with this ConversationManager.
 
		Component *getComponent() { return m_component; }
 

	
 
		/// Returns Conversation by its legacy network name (for example by UIN in case of ICQ).
 

	
 
		/// \param name legacy network name.
 
		/// \return Conversation or NULL.
 
		Conversation *getConversation(const std::string &name);
 

	
 
		void sendCachedChatMessages();
 

	
 
		/// Adds new Conversation to the manager.
 

	
 
		/// \param conv Conversation.
 
		void addConversation(Conversation *conv);
 

	
 
		/// Removes Conversation from the manager.
 

	
 
		/// \param conv Conversation.
 
		void removeConversation(Conversation *conv);
 

	
 
		void deleteAllConversations();
 

	
 
		void resetResources();
 
		void removeJID(const Swift::JID &jid);
 
		void clearJIDs();
 

	
 
	private:
 
		void handleMessageReceived(Swift::Message::ref message);
 

	
 
		Component *m_component;
 
		User *m_user;
 

	
 
		std::map<std::string, Conversation *> m_convs;
 
		friend class UserManager;
 
};
 

	
 
}
include/transport/discoitemsresponder.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <vector>
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Queries/GetResponder.h"
 
#include "Swiften/Elements/DiscoItems.h"
 
#include "Swiften/Elements/CapsInfo.h"
 

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

	
 
}
include/transport/factory.h
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <string>
 
#include <algorithm>
 
#include "transport/transport.h"
 

	
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Elements/Message.h"
 
#include "transport/conversation.h"
 
#include "transport/buddy.h"
 
#include "transport/storagebackend.h"
 

	
 
namespace Transport {
 

	
 
class Conversation;
 
class Buddy;
 
class ConversationManager;
 
class RosterManager;
 

	
 
class Factory {
 
	public:
 
		
 
		virtual Conversation *createConversation(ConversationManager *conversationManager, const std::string &legacyName, bool isMuc = false) = 0;
 

	
 
		virtual Buddy *createBuddy(RosterManager *rosterManager, const BuddyInfo &buddyInfo) = 0;
 
};
 

	
 
}
include/transport/gatewayresponder.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <vector>
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Queries/Responder.h"
 
#include "Swiften/Elements/GatewayPayload.h"
 

	
 
namespace Transport {
 

	
 
class UserManager;
 

	
 
class GatewayResponder : public Swift::Responder<Swift::GatewayPayload> {
 
	public:
 
		GatewayResponder(Swift::IQRouter *router, UserManager *userManager);
 
		~GatewayResponder();
 

	
 
	private:
 
		virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::GatewayPayload> payload);
 
		virtual bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::GatewayPayload> payload);
 
		UserManager *m_userManager;
 
};
 

	
 
}
include/transport/memoryreadbytestream.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <string>
 
#include <map>
 
#include "Swiften/Swiften.h"
 

	
 
#include "Swiften/FileTransfer/ReadBytestream.h"
 

	
 
namespace Transport {
 

	
 
class MemoryReadBytestream : public Swift::ReadBytestream {
 
	public:
 
		MemoryReadBytestream(unsigned long size);
 
		virtual ~MemoryReadBytestream();
 

	
 
		unsigned long appendData(const std::string &data);
 

	
 
		virtual boost::shared_ptr<std::vector<unsigned char> > read(size_t size);
 

	
 
		void setFinished() { m_finished = true; }
 
		bool isFinished() const;
 

	
 
		boost::signal<void ()> onDataNeeded;
 

	
 
	private:
 
		bool m_finished;
 
		std::string m_data;
 
		bool neededData;
 
		unsigned long m_sent;
 
		unsigned long m_size;
 
};
 

	
 
}
include/transport/mysqlbackend.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#ifdef WITH_MYSQL
 

	
 
#include <string>
 
#include <map>
 
#include "Swiften/Swiften.h"
 
#include "transport/storagebackend.h"
 
#include "transport/config.h"
 
#include "mysql.h"
 

	
 
namespace Transport {
 

	
 
/// Used to store transport data into SQLite3 database.
 
class MySQLBackend : public StorageBackend
 
{
 
	public:
 
		/// Creates new MySQLBackend instance.
 
		/// \param config cofiguration, this class uses following Config values:
 
		/// 	- database.database - path to SQLite3 database file, database file is created automatically
 
		/// 	- service.prefix - prefix for tables created by createDatabase method
 
		MySQLBackend(Config *config);
 

	
 
		/// Destructor.
 
		~MySQLBackend();
 

	
 
		/// Connects to the database and creates it if it's needed. This method call createDatabase() function
 
		/// automatically.
 
		/// \return true if database is opened successfully.
 
		bool connect();
 
		void disconnect();
 

	
 
		/// Creates database structure.
 
		/// \see connect()
 
		/// \return true if database structure has been created successfully. Note that it returns True also if database structure
 
		/// already exists.
 
		bool createDatabase();
 

	
 
		/// Stores user into database.
 
		/// \param user user struct containing all information about user which have to be stored
 
		void setUser(const UserInfo &user);
 

	
 
		/// Gets user data from database and stores them into user reference.
 
		/// \param barejid barejid of user
 
		/// \param user UserInfo object where user data will be stored
 
		/// \return true if user has been found in database
 
		bool getUser(const std::string &barejid, UserInfo &user);
 

	
 
		/// Changes users online state variable in database.
 
		/// \param id id of user - UserInfo.id
 
		/// \param online online state
 
		void setUserOnline(long id, bool online);
 

	
 
		/// Removes user and all connected data from database.
 
		/// \param id id of user - UserInfo.id
 
		/// \return true if user has been found in database and removed
 
		bool removeUser(long id);
 

	
 
		/// Returns JIDs of all buddies in user's roster.
 
		/// \param id id of user - UserInfo.id
 
		/// \param roster string list used to store user's roster
 
		/// \return true if user has been found in database and roster has been fetched
 
		bool getBuddies(long id, std::list<BuddyInfo> &roster);
 

	
 
		bool getOnlineUsers(std::vector<std::string> &users);
 

	
 
		long addBuddy(long userId, const BuddyInfo &buddyInfo);
 

	
 
		void updateBuddy(long userId, const BuddyInfo &buddyInfo);
 
		void removeBuddy(long id);
 

	
 
		void getBuddySetting(long userId, long buddyId, const std::string &variable, int &type, std::string &value);
 
		void updateBuddySetting(long userId, long buddyId, const std::string &variable, int type, const std::string &value);
 

	
 
		void getUserSetting(long userId, const std::string &variable, int &type, std::string &value);
 
		void updateUserSetting(long userId, const std::string &variable, const std::string &value);
 

	
 
		void beginTransaction();
 
		void commitTransaction();
 

	
 
	private:
 
		bool exec(const std::string &query);
 

	
 
		class Statement {
 
			public:
 
				Statement(MYSQL *conn, const std::string &format, const std::string &statement);
 
				~Statement();
 

	
 
				int execute();
 

	
 
				int fetch();
 

	
 
				// Pushes new data used as input for the statement.
 
				template <typename T>
 
				Statement& operator << (const T& t);
 

	
 
				Statement& operator << (const std::string& str);
 

	
 
				// Pulls fetched data by previous execute(); call.
 
				template <typename T>
 
				Statement& operator >> (T& t);
 
				
 
				Statement& operator >> (std::string& t);
include/transport/networkplugin.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <time.h>
 
#include "transport/protocol.pb.h"
 
// #include "conversation.h"
 
#include <iostream>
 
#include <list>
 

	
 
namespace Transport {
 

	
 
/// Represents Spectrum2 legacy network plugin.
 

	
 
/// This class is base class for all C++ legacy network plugins. It provides a way to connect 
 
/// Spectrum2 NetworkPluginServer and allows to use high-level API for legacy network plugins
 
/// development.
 
class NetworkPlugin {
 
	public:
 
		enum ExitCode { StorageBackendNeeded = -2 };
 

	
 
		class PluginConfig {
 
			public:
 
				PluginConfig() : m_needPassword(true), m_needRegistration(false) {}
 
				PluginConfig() : m_needPassword(true), m_needRegistration(false), m_supportMUC(false) {}
 
				virtual ~PluginConfig() {}
 

	
 
				void setNeedRegistration(bool needRegistration = false) { m_needRegistration = needRegistration; }
 
				void setNeedPassword(bool needPassword = true) { m_needPassword = needPassword; }
 
				void setSupportMUC(bool supportMUC = true) { m_supportMUC = supportMUC; }
 
				void setExtraFields(const std::vector<std::string> &fields) { m_extraFields = fields; }
 

	
 
			private:
 
				bool m_needPassword;
 
				bool m_needRegistration;
 
				bool m_supportMUC;
 
				std::vector<std::string> m_extraFields;
 

	
 
				friend class NetworkPlugin;
 
		};
 

	
 
		/// Creates new NetworkPlugin and connects the Spectrum2 NetworkPluginServer.
 
		/// \param loop Event loop.
 
		/// \param host Host where Spectrum2 NetworkPluginServer runs.
 
		/// \param port Port.
 
		NetworkPlugin();
 

	
 
		/// Destructor.
 
		virtual ~NetworkPlugin();
 

	
 
		void sendConfig(const PluginConfig &cfg);
 

	
 
		/// Call this function when legacy network buddy 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")
 
		/// \param buddyName Name of legacy network buddy. (eg. "user2@gmail.com")
 
		/// \param alias Alias of legacy network buddy. If empty, then it's not changed on XMPP side.
 
		/// \param groups Groups in which buddy currently is. If empty, then it's not changed on XMPP side.
 
		/// \param status Status of this buddy.
 
		/// \param statusMessage Status message of this buddy.
 
		/// \param iconHash MD5 hash of buddy icon. Empty if none buddy icon.
 
		/// \param blocked True if this buddy is blocked in privacy lists in legacy network.
 
		void handleBuddyChanged(const std::string &user, const std::string &buddyName, const std::string &alias,
 
			const std::vector<std::string> &groups, pbnetwork::StatusType status, const std::string &statusMessage = "", const std::string &iconHash = "",
 
			bool blocked = false
 
		);
 

	
 
		/// Call this method when buddy is removed from legacy network contact list.
 
		/// \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")
 
		/// \param buddyName Name of legacy network buddy. (eg. "user2@gmail.com")
 
		void handleBuddyRemoved(const std::string &user, const std::string &buddyName);
 

	
 
		/// Call this function when participant 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")
 
		/// \param nickname Nickname of participant. If participant renamed, this is old name of participant. (eg. "HanzZ")
 
		/// \param room Room in which participant changed. (eg. #spectrum)
 
		/// \param flags Participant flags.
 
		/// \param status Current status of participant. Swift::StatusShow::None if participant left the room.
 
		/// \param statusMessage Current status message of participant.
 
		/// \param newname New name of participant if he changed the nickname. Otherwise empty.
 
		void handleParticipantChanged(const std::string &user, const std::string &nickname, const std::string &room, int flags,
 
			pbnetwork::StatusType = pbnetwork::STATUS_NONE, const std::string &statusMessage = "", const std::string &newname = "");
 

	
 
		/// Call this function when user disconnected the legacy network because of some legacy network error.
 
		/// \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")
 
		/// \param error Reserved for future use, currently keep it on 0.
 
		/// \param message XMPP message which is sent to XMPP user.
 
		void handleDisconnected(const std::string &user, int error = 0, const std::string &message = "");
 

	
 
		/// Call this function when user connected the legacy network and is logged in.
 
		/// \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")
 
		void handleConnected(const std::string &user);
 

	
 
		/// Call this function when new message is received from legacy network for user.
 
		/// \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")
 
		/// \param legacyName Name of legacy network buddy or name of room. (eg. "user2@gmail.com")
 
		/// \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 = "", const std::string &timestamp = "", bool headline = false);
 
		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 = "", bool headline = false, bool pm = false);
 

	
 
		void handleMessageAck(const std::string &user, const std::string &legacyName, const std::string &id);
 

	
 
		/// 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")
 
		/// \param legacyName Name of room. (eg. "#spectrum")
 
		/// \param message Subject message.
 
		/// \param nickname Nickname of user who changed subject.
 
		void handleSubject(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &nickname = "");
 

	
 
		/// Call this function XMPP user's nickname 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")
 
		/// \param room Room in which participant changed. (eg. #spectrum)
 
		/// \param nickname New nickname.
 
		void handleRoomNicknameChanged(const std::string &user, const std::string &room, const std::string &nickname);
 

	
 
		/// Call this function when requested VCard arrived.
 
		/// \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")
 
		/// \param id VCard ID.
 
		/// \param legacyName Name of legacy network buddy. (eg. "user2@gmail.com")
 
		/// \param fullName Name of legacy network buddy. (eg. "Monty Python")
 
		/// \param nickname Nickname.
 
		/// \param photo Raw photo.
 
		void handleVCard(const std::string &user, unsigned int id, const std::string &legacyName, const std::string &fullName, const std::string &nickname, const std::string &photo);
 

	
 
		/// Call this function when buddy started typing.
 
		/// \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")
 
		/// \param buddyName Name of legacy network buddy. (eg. "user2@gmail.com")
 
		void handleBuddyTyping(const std::string &user, const std::string &buddyName);
 

	
 
		/// Call this function when buddy typed, but is not typing anymore.
 
		/// \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")
 
		/// \param buddyName Name of legacy network buddy. (eg. "user2@gmail.com")
 
		void handleBuddyTyped(const std::string &user, const std::string &buddyName);
 

	
 
		/// Call this function when buddy has been typing, but paused for a while.
 
		/// \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")
 
		/// \param buddyName Name of legacy network buddy. (eg. "user2@gmail.com")
 
		void handleBuddyStoppedTyping(const std::string &user, const std::string &buddyName);
 

	
 
		/// Call this function when new authorization request arrived form legacy network
 
		/// \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")
 
		/// \param buddyName Name of legacy network buddy. (eg. "user2@gmail.com")
 
		void handleAuthorization(const std::string &user, const std::string &buddyName);
 

	
 
		/// Call this function when attention request arrived from legacy network.
 
		/// \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")
 
		/// \param buddyName Name of legacy network buddy. (eg. "user2@gmail.com")
 
		/// \param message Message.
 
		void handleAttention(const std::string &user, const std::string &buddyName, const std::string &message);
 

	
 
		void handleFTStart(const std::string &user, const std::string &buddyName, const std::string fileName, unsigned long size);
 
		void handleFTFinish(const std::string &user, const std::string &buddyName, const std::string fileName, unsigned long size, unsigned long ftid);
 

	
 
		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.
 
		/// \param legacyName Legacy network name of this user used for login.
 
		/// \param password Legacy network password of this user.
 
		/**
 
			\msc
 
			NetworkPlugin,YourNetworkPlugin,LegacyNetwork;
 
			NetworkPlugin->YourNetworkPlugin [label="handleLoginRequest(...)", URL="\ref NetworkPlugin::handleLoginRequest()"];
 
			YourNetworkPlugin->LegacyNetwork [label="connect the legacy network"];
 
			--- [label="If password was valid and user is connected and logged in"];
 
			YourNetworkPlugin<-LegacyNetwork [label="connected"];
 
			YourNetworkPlugin->NetworkPlugin [label="handleConnected()", URL="\ref NetworkPlugin::handleConnected()"];
 
			--- [label="else"];
 
			YourNetworkPlugin<-LegacyNetwork [label="disconnected"];
 
			YourNetworkPlugin->NetworkPlugin [label="handleDisconnected()", URL="\ref NetworkPlugin::handleDisconnected()"];
 
			\endmsc
 
		*/
 
		virtual void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) = 0;
 

	
 
		/// Called when XMPP user wants to disconnect legacy network.
 
		/// You should disconnect him from legacy network.
 
		/// \param user XMPP JID of user for which this event occurs.
 
		/// \param legacyName Legacy network name of this user used for login.
 
		virtual void handleLogoutRequest(const std::string &user, const std::string &legacyName) = 0;
 

	
 
		/// Called when XMPP user sends message to legacy network.
 
		/// \param user XMPP JID of user for which this event occurs.
 
		/// \param legacyName Legacy network name of buddy or room.
 
		/// \param message Plain text message.
 
		/// \param xhtml XHTML message.
 
		virtual void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml = "", const std::string &id = "") = 0;
 

	
 
		/// Called when XMPP user requests VCard of buddy.
 
		/// \param user XMPP JID of user for which this event occurs.
 
		/// \param legacyName Legacy network name of buddy whose VCard is requested.
 
		/// \param id ID which is associated with this request. You have to pass it to handleVCard function when you receive VCard.
 
		/**
include/transport/networkpluginserver.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <time.h>
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Presence/PresenceOracle.h"
 
#include "Swiften/Disco/EntityCapsManager.h"
 
#include "Swiften/Network/BoostConnectionServer.h"
 
#include "Swiften/Network/Connection.h"
 
#include "Swiften/Elements/ChatState.h"
 
#include "Swiften/Elements/RosterItemPayload.h"
 
#include "Swiften/Elements/VCard.h"
 
#include "storagebackend.h"
 
#include "transport/filetransfermanager.h"
 

	
 
namespace Transport {
 

	
 
class UserManager;
 
class User;
 
class Component;
 
class Buddy;
 
class LocalBuddy;
 
class Config;
 
class NetworkConversation;
 
class VCardResponder;
 
class RosterResponder;
 
class BlockResponder;
 
class DummyReadBytestream;
 
class AdminInterface;
 
class DiscoItemsResponder;
 

	
 
class NetworkPluginServer {
 
	public:
 
		struct Backend {
 
			int pongReceived;
 
			std::list<User *> users;
 
			Swift::SafeByteArray data;
 
			boost::shared_ptr<Swift::Connection> connection;
 
			unsigned long res;
 
			unsigned long init_res;
 
			unsigned long shared;
 
			bool acceptUsers;
 
			bool longRun;
 
			bool willDie;
 
			std::string id;
 
		};
 

	
 
		NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager, DiscoItemsResponder *discoItemsResponder);
 

	
 
		virtual ~NetworkPluginServer();
 

	
 
		void start();
 

	
 
		void setAdminInterface(AdminInterface *adminInterface) {
 
			m_adminInterface = adminInterface;
 
		}
 

	
 
		int getBackendCount() {
 
			return m_clients.size();
 
		}
 

	
 
		const std::list<Backend *> &getBackends() {
 
			return m_clients;
 
		}
 

	
 
		const std::vector<std::string> &getCrashedBackends() {
 
			return m_crashedBackends;
 
		}
 

	
 
		void collectBackend();
 

	
 
		bool moveToLongRunBackend(User *user);
 

	
 
		void handleMessageReceived(NetworkConversation *conv, boost::shared_ptr<Swift::Message> &message);
 

	
 
	public:
 
		void handleNewClientConnection(boost::shared_ptr<Swift::Connection> c);
 
		void handleSessionFinished(Backend *c);
 
		void handlePongReceived(Backend *c);
 
		void handleDataRead(Backend *c, boost::shared_ptr<Swift::SafeByteArray> data);
 

	
 
		void handleConnectedPayload(const std::string &payload);
 
		void handleDisconnectedPayload(const std::string &payload);
 
		void handleBuddyChangedPayload(const std::string &payload);
 
		void handleBuddyRemovedPayload(const std::string &payload);
 
		void handleConvMessagePayload(const std::string &payload, bool subject = false);
 
		void handleConvMessageAckPayload(const std::string &payload);
 
		void handleParticipantChangedPayload(const std::string &payload);
 
		void handleRoomChangedPayload(const std::string &payload);
 
		void handleVCardPayload(const std::string &payload);
 
		void handleChatStatePayload(const std::string &payload, Swift::ChatState::ChatStateType type);
 
		void handleAuthorizationPayload(const std::string &payload);
 
		void handleAttentionPayload(const std::string &payload);
 
		void handleStatsPayload(Backend *c, const std::string &payload);
 
		void handleFTStartPayload(const std::string &payload);
 
		void handleFTFinishPayload(const std::string &payload);
 
		void handleFTDataPayload(Backend *b, const std::string &payload);
 
		void handleQueryPayload(Backend *b, const std::string &payload);
 
		void handleBackendConfigPayload(const std::string &payload);
 
		void handleRoomListPayload(const std::string &payload);
 

	
 
		void handleUserCreated(User *user);
 
		void handleRoomJoined(User *user, const Swift::JID &who, const std::string &room, const std::string &nickname, const std::string &password);
 
		void handleRoomLeft(User *user, const std::string &room);
 
		void handleUserReadyToConnect(User *user);
 
		void handleUserPresenceChanged(User *user, Swift::Presence::ref presence);
 
		void handleUserDestroyed(User *user);
 

	
 
		void handleBuddyUpdated(Buddy *buddy, const Swift::RosterItemPayload &item);
 
		void handleBuddyRemoved(Buddy *buddy);
 
		void handleBuddyAdded(Buddy *buddy, const Swift::RosterItemPayload &item);
 
		void handleUserBuddyAdded(User *user, Buddy *buddy);
 
		void handleUserBuddyRemoved(User *user, Buddy *buddy);
 

	
 
		void handleBlockToggled(Buddy *buddy);
 

	
 
		void handleVCardUpdated(User *user, boost::shared_ptr<Swift::VCard> vcard);
 
		void handleVCardRequired(User *user, const std::string &name, unsigned int id);
 

	
 
		void handleFTStateChanged(Swift::FileTransfer::State state, const std::string &userName, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long id);
 
		void handleFTAccepted(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID);
 
		void handleFTRejected(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size);
 
		void handleFTDataNeeded(Backend *b, unsigned long ftid);
 

	
 
		void handlePIDTerminated(unsigned long pid);
 
	private:
 
		void send(boost::shared_ptr<Swift::Connection> &, const std::string &data);
 

	
 
		void pingTimeout();
 
		void sendPing(Backend *c);
 
		Backend *getFreeClient(bool acceptUsers = true, bool longRun = false, bool check = false);
 
		void connectWaitingUsers();
 
		void loginDelayFinished();
 

	
 
		UserManager *m_userManager;
 
		VCardResponder *m_vcardResponder;
 
		RosterResponder *m_rosterResponder;
 
		BlockResponder *m_blockResponder;
 
		Config *m_config;
 
		boost::shared_ptr<Swift::ConnectionServer> m_server;
 
		std::list<Backend *>  m_clients;
 
		std::vector<unsigned long> m_pids;
 
		Swift::Timer::ref m_pingTimer;
 
		Swift::Timer::ref m_collectTimer;
 
		Swift::Timer::ref m_loginTimer;
 
		Component *m_component;
 
		std::list<User *> m_waitingUsers;
 
		bool m_isNextLongRun;
 
		std::map<unsigned long, FileTransferManager::Transfer> m_filetransfers;
 
		FileTransferManager *m_ftManager;
 
		std::vector<std::string> m_crashedBackends;
 
		AdminInterface *m_adminInterface;
 
		bool m_startingBackend;
 
		DiscoItemsResponder *m_discoItemsResponder;
 
		time_t m_lastLogin;
 
};
 

	
 
}
include/transport/pqxxbackend.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#ifdef WITH_PQXX
 

	
 
#include <string>
 
#include <map>
 
#include "Swiften/Swiften.h"
 
#include "transport/storagebackend.h"
 
#include "transport/config.h"
 
#include <pqxx/pqxx>
 

	
 
namespace Transport {
 

	
 
/// Used to store transport data into SQLite3 database.
 
class PQXXBackend : public StorageBackend
 
{
 
	public:
 
		/// Creates new PQXXBackend instance.
 
		/// \param config cofiguration, this class uses following Config values:
 
		/// 	- database.database - path to SQLite3 database file, database file is created automatically
 
		/// 	- service.prefix - prefix for tables created by createDatabase method
 
		PQXXBackend(Config *config);
 

	
 
		/// Destructor.
 
		~PQXXBackend();
 

	
 
		/// Connects to the database and creates it if it's needed. This method call createDatabase() function
 
		/// automatically.
 
		/// \return true if database is opened successfully.
 
		bool connect();
 
		void disconnect();
 

	
 
		/// Creates database structure.
 
		/// \see connect()
 
		/// \return true if database structure has been created successfully. Note that it returns True also if database structure
 
		/// already exists.
 
		bool createDatabase();
 

	
 
		/// Stores user into database.
 
		/// \param user user struct containing all information about user which have to be stored
 
		void setUser(const UserInfo &user);
 

	
 
		/// Gets user data from database and stores them into user reference.
 
		/// \param barejid barejid of user
 
		/// \param user UserInfo object where user data will be stored
 
		/// \return true if user has been found in database
 
		bool getUser(const std::string &barejid, UserInfo &user);
 

	
 
		/// Changes users online state variable in database.
 
		/// \param id id of user - UserInfo.id
 
		/// \param online online state
 
		void setUserOnline(long id, bool online);
 

	
 
		/// Removes user and all connected data from database.
 
		/// \param id id of user - UserInfo.id
 
		/// \return true if user has been found in database and removed
 
		bool removeUser(long id);
 

	
 
		/// Returns JIDs of all buddies in user's roster.
 
		/// \param id id of user - UserInfo.id
 
		/// \param roster string list used to store user's roster
 
		/// \return true if user has been found in database and roster has been fetched
 
		bool getBuddies(long id, std::list<BuddyInfo> &roster);
 

	
 
		bool getOnlineUsers(std::vector<std::string> &users);
 

	
 
		long addBuddy(long userId, const BuddyInfo &buddyInfo);
 

	
 
		void updateBuddy(long userId, const BuddyInfo &buddyInfo);
 
		void removeBuddy(long id) {}
 

	
 
		void getBuddySetting(long userId, long buddyId, const std::string &variable, int &type, std::string &value) {}
 
		void updateBuddySetting(long userId, long buddyId, const std::string &variable, int type, const std::string &value) {}
 

	
 
		void getUserSetting(long userId, const std::string &variable, int &type, std::string &value);
 
		void updateUserSetting(long userId, const std::string &variable, const std::string &value);
 

	
 
		void beginTransaction();
 
		void commitTransaction();
 

	
 
	private:
 
		bool exec(const std::string &query, bool show_error = true);
 
		bool exec(pqxx::nontransaction &txn, const std::string &query, bool show_error = true);
 
		template<typename T>
 
		std::string quote(pqxx::nontransaction &txn, const T &t);
 

	
 
		Config *m_config;
 
		std::string m_prefix;
 

	
 
		pqxx::connection *m_conn;
 
};
 

	
 
}
 

	
 
#endif
include/transport/protocol.proto
Show inline comments
 
package pbnetwork;
 

	
 
enum ConnectionError {
 
	CONNECTION_ERROR_NETWORK_ERROR = 0;
 
	CONNECTION_ERROR_INVALID_USERNAME = 1;
 
	CONNECTION_ERROR_AUTHENTICATION_FAILED = 2;
 
	CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE = 3;
 
	CONNECTION_ERROR_NO_SSL_SUPPORT = 4;
 
	CONNECTION_ERROR_ENCRYPTION_ERROR = 5;
 
	CONNECTION_ERROR_NAME_IN_USE = 6;
 
	CONNECTION_ERROR_INVALID_SETTINGS = 7;
 
	CONNECTION_ERROR_CERT_NOT_PROVIDED = 8;
 
	CONNECTION_ERROR_CERT_UNTRUSTED = 9;
 
	CONNECTION_ERROR_CERT_EXPIRED = 10;
 
	CONNECTION_ERROR_CERT_NOT_ACTIVATED = 11;
 
	CONNECTION_ERROR_CERT_HOSTNAME_MISMATCH = 12;
 
	CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH = 13;
 
	CONNECTION_ERROR_CERT_SELF_SIGNED = 14;
 
	CONNECTION_ERROR_CERT_OTHER_ERROR = 15;
 
	CONNECTION_ERROR_OTHER_ERROR = 16;
 
}
 

	
 
enum StatusType {
 
	STATUS_ONLINE		= 0;
 
	STATUS_AWAY			= 1;
 
	STATUS_FFC			= 2;
 
	STATUS_XA			= 3;
 
	STATUS_DND			= 4;
 
	STATUS_NONE			= 5;
 
	STATUS_INVISIBLE	= 6;
 
}
 

	
 
message Connected {
 
	required string user = 1;
 
}
 

	
 
message Disconnected {
 
	required string user = 1;
 
	required int32 error = 2;
 
	optional string message = 3;
 
}
 

	
 
message Login {
 
	required string user = 1;
 
	required string legacyName = 2;
 
	required string password = 3;
 
	repeated string extraFields = 4;
 
}
 

	
 
message Logout {
 
	required string user = 1;
 
	required string legacyName = 2;
 
}
 

	
 
message Buddy {
 
	required string userName = 1;
 
	required string buddyName = 2;
 
	optional string alias = 3;
 
	repeated string group = 4;
 
	optional StatusType status = 5;
 
	optional string statusMessage = 6;
 
	optional string iconHash = 7;
 
	optional bool blocked = 8;
 
}
 

	
 
message ConversationMessage {
 
	required string userName = 1;
 
	required string buddyName = 2;
 
	required string message = 3;
 
	optional string nickname = 4;
 
	optional string xhtml = 5;
 
	optional string timestamp = 6;
 
	optional bool headline = 7;
 
	optional string id = 8;
 
	optional bool pm = 9;
 
}
 

	
 
message Room {
 
	required string userName = 1;
 
	required string nickname = 2;
 
	required string room = 3;
 
	optional string password = 4;
 
}
 

	
 
message RoomList {
 
	repeated string room = 1;
 
	repeated string name = 2;
 
}
 

	
 
enum ParticipantFlag {
 
	PARTICIPANT_FLAG_NONE = 0;
 
	PARTICIPANT_FLAG_MODERATOR = 1;
 
	PARTICIPANT_FLAG_CONFLICT = 2;
 
	PARTICIPANT_FLAG_BANNED = 4;
 
	PARTICIPANT_FLAG_NOT_AUTHORIZED = 8;
 
	PARTICIPANT_FLAG_ME = 16;
 
	PARTICIPANT_FLAG_KICKED = 32;
 
}
 

	
 
message Participant {
 
	required string userName = 1;
 
	required string room = 2;
 
	required string nickname = 3;
 
	required int32 flag = 4;
 
	required StatusType status = 5;
 
	optional string statusMessage = 6;
 
	optional string newname = 7;
 
}
 

	
 
message VCard {
 
	required string userName = 1;
 
	required string buddyName = 2;
 
	required int32 id = 3;
 
	optional string fullname = 4;
 
	optional string nickname = 5;
 
	optional bytes photo = 6;
 
}
 

	
 
message Status {
 
	required string userName = 1;
 
	required StatusType status = 3;
 
	optional string statusMessage = 4;
 
}
 

	
 
message Stats {
 
	required int32 res = 1;
 
	required int32 init_res = 2;
 
	required int32 shared = 3;
 
	required string id = 4;
 
}
 

	
 
message File {
 
	required string userName = 1;
 
	required string buddyName = 2;
 
	required string fileName = 3;
 
	required int32 size = 4;
 
	optional int32 ftID = 5;
 
}
 

	
 
message FileTransferData {
 
	required int32 ftID = 1;
 
	required bytes data = 2;
 
}
 

	
 
message BackendConfig {
 
	required string config = 1;
 
}
 

	
 
message WrapperMessage {
 
	enum Type { 
 
		TYPE_CONNECTED 				= 1;
 
		TYPE_DISCONNECTED 			= 2;
 
		TYPE_LOGIN 					= 3;
 
		TYPE_LOGOUT 				= 4;
 
		TYPE_BUDDY_CHANGED			= 6;
 
		TYPE_BUDDY_REMOVED			= 7;
 
		TYPE_CONV_MESSAGE			= 8;
 
		TYPE_PING					= 9;
 
		TYPE_PONG					= 10;
 
		TYPE_JOIN_ROOM				= 11;
 
		TYPE_LEAVE_ROOM				= 12;
 
		TYPE_PARTICIPANT_CHANGED	= 13;
 
		TYPE_ROOM_NICKNAME_CHANGED	= 14;
 
		TYPE_ROOM_SUBJECT_CHANGED	= 15;
 
		TYPE_VCARD					= 16;
 
		TYPE_STATUS_CHANGED			= 17;
 
		TYPE_BUDDY_TYPING			= 18;
 
		TYPE_BUDDY_STOPPED_TYPING	= 19;
 
		TYPE_BUDDY_TYPED			= 20;
 
		TYPE_AUTH_REQUEST			= 21;
 
		TYPE_ATTENTION				= 22;
 
		TYPE_STATS					= 23;
 
		TYPE_FT_START				= 24;
 
		TYPE_FT_FINISH				= 25;
 
		TYPE_FT_DATA				= 26;
 
		TYPE_FT_PAUSE				= 27;
 
		TYPE_FT_CONTINUE			= 28;
 
		TYPE_EXIT					= 29;
 
		TYPE_BACKEND_CONFIG			= 30;
 
		TYPE_QUERY					= 31;
 
		TYPE_ROOM_LIST				= 32;
 
		TYPE_CONV_MESSAGE_ACK		= 33;
 
	}
 
	required Type type = 1;
 
	optional bytes payload = 2;
 
}
 
;
include/transport/rostermanager.h
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <string>
 
#include <algorithm>
 
#include <map>
 
#include <boost/pool/pool_alloc.hpp>
 
#include <boost/pool/object_pool.hpp>
 
#include "Swiften/Swiften.h"
 
// #include "rosterstorage.h"
 
#include "Swiften/Elements/RosterPayload.h"
 
#include "Swiften/Queries/GenericRequest.h"
 
#include "Swiften/Roster/SetRosterRequest.h"
 
#include "Swiften/Elements/Presence.h"
 
#include "Swiften/Network/Timer.h"
 

	
 
namespace Transport {
 

	
 
class Buddy;
 
class User;
 
class Component;
 
class StorageBackend;
 
class RosterStorage;
 

	
 
// TODO: Once Swiften GetRosterRequest will support setting to="", this can be removed
 
class AddressedRosterRequest : public Swift::GenericRequest<Swift::RosterPayload> {
 
	public:
 
		typedef boost::shared_ptr<AddressedRosterRequest> ref;
 

	
 
		AddressedRosterRequest(Swift::IQRouter* router, Swift::JID to) :
 
				Swift::GenericRequest<Swift::RosterPayload>(Swift::IQ::Get, to, boost::shared_ptr<Swift::Payload>(new Swift::RosterPayload()), router) {
 
		}
 
};
 

	
 
/// Manages roster of one XMPP user.
 
class RosterManager {
 
	public:
 
		typedef std::map<std::string, Buddy *, std::less<std::string>, boost::pool_allocator< std::pair<std::string, Buddy *> > > BuddiesMap;
 
		/// Creates new RosterManager.
 
		/// \param user User associated with this RosterManager.
 
		/// \param component Transport instance associated with this roster.
 
		RosterManager(User *user, Component *component);
 

	
 
		/// Destructor.
 
		virtual ~RosterManager();
 

	
 
		/// Associates the buddy with this roster,
 
		/// and if the buddy is not already in XMPP user's server-side roster, the proper requests
 
		/// are sent to XMPP user (subscribe presences, Roster Item Exchange stanza or
 
		/// the buddy is added to server-side roster using remote-roster protoXEP).
 
		/// \param buddy Buddy
 
		void setBuddy(Buddy *buddy);
 

	
 
		/// Deassociates the buddy with this roster.
 
		/// \param buddy Buddy.
 
		void unsetBuddy(Buddy *buddy);
 

	
 
		/// Removes buddy from this roster, sends proper XML to XMPP side and deletes it.
 
		/// \param name Buddy name.
 
		void removeBuddy(const std::string &name);
 

	
 
		Buddy *getBuddy(const std::string &name);
 

	
 
		void setStorageBackend(StorageBackend *storageBackend);
 

	
 
		void storeBuddy(Buddy *buddy);
 

	
 
		Swift::RosterPayload::ref generateRosterPayload();
 

	
 
		/// Returns user associated with this roster.
 
		/// \return User
 
		User *getUser() { return m_user; }
 

	
 
		const BuddiesMap &getBuddies() {
 
			return m_buddies;
 
		}
 

	
 
		bool isRemoteRosterSupported() {
 
			return m_supportRemoteRoster;
 
		}
 

	
 
		/// Called when new Buddy is added to this roster.
 
		/// \param buddy newly added Buddy
 
		boost::signal<void (Buddy *buddy)> onBuddySet;
 

	
 
		/// Called when Buddy has been removed from this roster.
 
		/// \param buddy removed Buddy
 
		boost::signal<void (Buddy *buddy)> onBuddyUnset;
 

	
 
		boost::signal<void (Buddy *buddy)> onBuddyAdded;
 
		
 
		boost::signal<void (Buddy *buddy)> onBuddyRemoved;
 

	
 
		void handleBuddyChanged(Buddy *buddy);
 

	
 
		void handleSubscription(Swift::Presence::ref presence);
 

	
 
		void sendBuddyRosterPush(Buddy *buddy);
 

	
 
		void sendBuddyRosterRemove(Buddy *buddy);
 

	
 
		void sendBuddySubscribePresence(Buddy *buddy);
 
		
 
		void sendBuddyUnsubscribePresence(Buddy *buddy);
 

	
 
		void sendCurrentPresences(const Swift::JID &to);
 

	
 
		void sendCurrentPresence(const Swift::JID &from, const Swift::JID &to);
 

	
 
		void sendUnavailablePresences(const Swift::JID &to);
 

	
include/transport/rosterresponder.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <vector>
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Queries/Responder.h"
 
#include "Swiften/Elements/RosterPayload.h"
 

	
 
#include <boost/signal.hpp>
 

	
 
namespace Transport {
 

	
 
class UserManager;
 
class Buddy;
 

	
 
class RosterResponder : public Swift::Responder<Swift::RosterPayload> {
 
	public:
 
		RosterResponder(Swift::IQRouter *router, UserManager *userManager);
 
		~RosterResponder();
 

	
 
		boost::signal<void (Buddy *, const Swift::RosterItemPayload &item)> onBuddyUpdated;
 

	
 
		boost::signal<void (Buddy *)> onBuddyRemoved;
 

	
 
		boost::signal<void (Buddy *, const Swift::RosterItemPayload &item)> onBuddyAdded;
 

	
 
	private:
 
		virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::RosterPayload> payload);
 
		virtual bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::RosterPayload> payload);
 
		UserManager *m_userManager;
 
};
 

	
 
}
include/transport/rosterstorage.h
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <string>
 
#include <algorithm>
 
#include "Swiften/Swiften.h"
 
#include <map>
 

	
 
#include "Swiften/Network/Timer.h"
 

	
 
namespace Transport {
 

	
 
class User;
 
class StorageBackend;
 
class Buddy;
 

	
 
// Stores buddies into DB Backend.
 
class RosterStorage {
 
	public:
 
		RosterStorage(User *user, StorageBackend *storageBackend);
 
		virtual ~RosterStorage();
 

	
 
		// Add buddy to store queue and store it in future. Nothing
 
		// will happen if buddy is already added.
 
		void storeBuddy(Buddy *buddy);
 

	
 
		void removeBuddy(Buddy *buddy);
 

	
 
		// Store all buddies from queue immediately. Returns true
 
		// if some buddies were stored.
 
		bool storeBuddies();
 

	
 
		// Remove buddy from storage queue.
 
		void removeBuddyFromQueue(Buddy *buddy);
 

	
 
	private:
 
		User *m_user;
 
		StorageBackend *m_storageBackend;
 
		std::map<std::string, Buddy *> m_buddies;
 
		Swift::Timer::ref m_storageTimer;
 
};
 

	
 
}
include/transport/settingsadhoccommand.h
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <string>
 
#include <algorithm>
 
#include <map>
 
#include "Swiften/Swiften.h"
 
#include "transport/adhoccommand.h"
 
#include "transport/adhoccommandfactory.h"
 

	
 

	
 
namespace Transport {
 

	
 
class Component;
 
class UserManager;
 
class StorageBackend;
 

	
 
class SettingsAdHocCommand : public AdHocCommand {
 
	public:
 
		typedef enum { Init, WaitingForResponse } State;
 

	
 
		SettingsAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to);
 

	
 
		/// Destructor.
 
		virtual ~SettingsAdHocCommand();
 

	
 
		virtual boost::shared_ptr<Swift::Command> handleRequest(boost::shared_ptr<Swift::Command> payload);
 

	
 
	private:
 
		boost::shared_ptr<Swift::Command> getForm();
 
		boost::shared_ptr<Swift::Command> handleResponse(boost::shared_ptr<Swift::Command> payload);
 
		State m_state;
 
};
 

	
 
class SettingsAdHocCommandFactory : public AdHocCommandFactory {
 
	public:
 
		SettingsAdHocCommandFactory() {
 
			m_userSettings["send_headlines"] = "0";
 
			m_userSettings["stay_connected"] = "0";
 
		}
 

	
 
		virtual ~SettingsAdHocCommandFactory() {}
 

	
 
		AdHocCommand *createAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to) {
 
			return new SettingsAdHocCommand(component, userManager, storageBackend, initiator, to);
 
		}
 

	
 
		std::string getNode() {
 
			return "settings";
 
		}
 

	
 
		std::string getName() {
 
			return "Transport settings";
 
		}
 
};
 

	
 
}
include/transport/sqlite3backend.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#ifdef WITH_SQLITE
 

	
 
#include <string>
 
#include <map>
 
#include "Swiften/Swiften.h"
 
#include "transport/storagebackend.h"
 
#include "transport/config.h"
 
#include "sqlite3.h"
 

	
 
namespace Transport {
 

	
 
/// Used to store transport data into SQLite3 database.
 
class SQLite3Backend : public StorageBackend
 
{
 
	public:
 
		/// Creates new SQLite3Backend instance.
 
		/// \param config cofiguration, this class uses following Config values:
 
		/// 	- database.database - path to SQLite3 database file, database file is created automatically
 
		/// 	- service.prefix - prefix for tables created by createDatabase method
 
		SQLite3Backend(Config *config);
 

	
 
		/// Destructor.
 
		~SQLite3Backend();
 

	
 
		/// Connects to the database and creates it if it's needed. This method call createDatabase() function
 
		/// automatically.
 
		/// \return true if database is opened successfully.
 
		bool connect();
 

	
 
		/// Creates database structure.
 
		/// \see connect()
 
		/// \return true if database structure has been created successfully. Note that it returns True also if database structure
 
		/// already exists.
 
		bool createDatabase();
 

	
 
		/// Stores user into database.
 
		/// \param user user struct containing all information about user which have to be stored
 
		void setUser(const UserInfo &user);
 

	
 
		/// Gets user data from database and stores them into user reference.
 
		/// \param barejid barejid of user
 
		/// \param user UserInfo object where user data will be stored
 
		/// \return true if user has been found in database
 
		bool getUser(const std::string &barejid, UserInfo &user);
 

	
 
		/// Changes users online state variable in database.
 
		/// \param id id of user - UserInfo.id
 
		/// \param online online state
 
		void setUserOnline(long id, bool online);
 

	
 
		bool getOnlineUsers(std::vector<std::string> &users);
 

	
 
		/// Removes user and all connected data from database.
 
		/// \param id id of user - UserInfo.id
 
		/// \return true if user has been found in database and removed
 
		bool removeUser(long id);
 

	
 
		/// Returns JIDs of all buddies in user's roster.
 
		/// \param id id of user - UserInfo.id
 
		/// \param roster string list used to store user's roster
 
		/// \return true if user has been found in database and roster has been fetched
 
		bool getBuddies(long id, std::list<BuddyInfo> &roster);
 

	
 
		long addBuddy(long userId, const BuddyInfo &buddyInfo);
 

	
 
		void updateBuddy(long userId, const BuddyInfo &buddyInfo);
 
		void removeBuddy(long id);
 

	
 
		void getBuddySetting(long userId, long buddyId, const std::string &variable, int &type, std::string &value);
 
		void updateBuddySetting(long userId, long buddyId, const std::string &variable, int type, const std::string &value);
 

	
 
		void getUserSetting(long userId, const std::string &variable, int &type, std::string &value);
 
		void updateUserSetting(long userId, const std::string &variable, const std::string &value);
 

	
 
		void beginTransaction();
 
		void commitTransaction();
 

	
 
	private:
 
		bool exec(const std::string &query);
 

	
 
		sqlite3 *m_db;
 
		Config *m_config;
 
		std::string m_prefix;
 

	
 
		// statements
 
		sqlite3_stmt *m_setUser;
 
		sqlite3_stmt *m_getUser;
 
		sqlite3_stmt *m_getUserSetting;
 
		sqlite3_stmt *m_setUserSetting;
 
		sqlite3_stmt *m_updateUserSetting;
 
		sqlite3_stmt *m_removeUser;
 
		sqlite3_stmt *m_removeUserBuddies;
 
		sqlite3_stmt *m_removeUserSettings;
 
		sqlite3_stmt *m_removeUserBuddiesSettings;
 
		sqlite3_stmt *m_removeBuddy;
 
		sqlite3_stmt *m_removeBuddySettings;
 
		sqlite3_stmt *m_addBuddy;
 
		sqlite3_stmt *m_updateBuddy;
 
		sqlite3_stmt *m_updateBuddySetting;
 
		sqlite3_stmt *m_getBuddySetting;
 
		sqlite3_stmt *m_getBuddies;
include/transport/statsresponder.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <vector>
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Queries/SetResponder.h"
 
#include "Swiften/Elements/StatsPayload.h"
 

	
 
namespace Transport {
 

	
 
class Component;
 
class UserManager;
 
class NetworkPluginServer;
 
class StorageBackend;
 

	
 
class StatsResponder : public Swift::Responder<Swift::StatsPayload> {
 
	public:
 
		StatsResponder(Component *component, UserManager *userManager, NetworkPluginServer *server, StorageBackend *storageBackend);
 
		~StatsResponder();
 

	
 
	private:
 
		virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::StatsPayload> payload);
 
		virtual bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::StatsPayload> payload);
 

	
 
		unsigned long usedMemory();
 

	
 
		Component *m_component;
 
		UserManager *m_userManager;
 
		NetworkPluginServer *m_server;
 
		StorageBackend *m_storageBackend;
 
		time_t m_start;
 
};
 

	
 
}
include/transport/threadpool.h
Show inline comments
 
#ifndef THREAD_POOL
 
#define THREAD_POOL
 

	
 
#include <boost/thread.hpp>
 
#include <boost/thread/mutex.hpp>
 
#include <boost/signals2/signal.hpp>
 
#include <queue>
 
#include <iostream>
 
#include "transport/logging.h"
 
#include "Swiften/Swiften.h"
 
#include "Swiften/EventLoop/EventLoop.h"
 

	
 

	
 
/*
 
 * Thread serves as a base class for any code that has to be excuted as a thread
 
 * by the ThreadPool class. The run method defines the code that has to be run
 
 * as a theard. For example, code in run could be sendinga request to a server
 
 * waiting for the response and storing the response. When the thread finishes
 
 * execution, the ThreadPool invokes finalize where one could have the code necessary
 
 * to collect all the responses and release any resources. 
 
 *
 
 * NOTE: The object of the Thread class must be valid (in scope) throughout the 
 
 * execution of the thread.
 
 */
 

	
 
class Thread
 
{
 
	int threadID;
 

	
 
	public:
 
	
 
	Thread() {}
 
	virtual ~Thread() {}
 
	virtual void run() = 0;
 
	virtual void finalize() {}
 
	int getThreadID() {return threadID;}
 
	void setThreadID(int tid) {threadID = tid;}
 
};
 

	
 
/*
 
 * ThreadPool provides the interface to manage a pool of threads. It schedules jobs
 
 * on free threads and when the thread completes it automatically deletes the object
 
 * corresponding to a Thread. If free threads are not available, the requests are 
 
 * added to a queue and scheduled later when threads become available.
 
 */
 

	
 
class ThreadPool
 
{
 
	const int MAX_THREADS;
 
	int activeThreads;
 
	std::queue<int> freeThreads;
 
	
 
	std::queue<Thread*> requestQueue;
 
	boost::thread **worker;
 

	
 
	boost::mutex count_lock;
 
	boost::mutex pool_lock;
 
	boost::mutex criticalregion;
 
	Swift::EventLoop *loop;
 

	
 
	boost::signals2::signal  < void () > onWorkerAvailable;
 

	
 
	public:
 
	ThreadPool(Swift::EventLoop *loop, int maxthreads);
 
	~ThreadPool();
 
	void runAsThread(Thread *t);
 
	int getActiveThreadCount(); 
 
	void updateActiveThreadCount(int k);
 
	void cleandUp(Thread *, int);
 
	void scheduleFromQueue();
 
	int getFreeThread();
 
	void releaseThread(int i);
 
};
 

	
 
#endif
include/transport/transport.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <vector>
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Server/Server.h"
 
#include "Swiften/Disco/GetDiscoInfoRequest.h"
 
#include "Swiften/Disco/EntityCapsManager.h"
 
#include "Swiften/Disco/CapsManager.h"
 
#include "Swiften/Disco/CapsMemoryStorage.h"
 
#include "Swiften/Network/BoostTimerFactory.h"
 
#include "Swiften/Network/BoostIOServiceThread.h"
 
#include "Swiften/Server/UserRegistry.h"
 
#include "Swiften/Base/SafeByteArray.h"
 
#include "Swiften/Jingle/JingleSessionManager.h"
 
#include "Swiften/Component/ComponentError.h"
 
#include "Swiften/Component/Component.h"
 

	
 
#include <boost/bind.hpp>
 
#include "transport/config.h"
 
#include "transport/factory.h"
 
#include "transport/presenceoracle.h"
 
#include <Swiften/Network/BoostConnectionServer.h>
 

	
 
namespace Transport {
 
	// typedef enum { 	CLIENT_FEATURE_ROSTERX = 2,
 
	// 				CLIENT_FEATURE_XHTML_IM = 4,
 
	// 				CLIENT_FEATURE_FILETRANSFER = 8,
 
	// 				CLIENT_FEATURE_CHATSTATES = 16
 
	// 				} SpectrumImportantFeatures;
 
	// 
 
	class StorageBackend;
 
	class Factory;
 
	class UserRegistry;
 

	
 
	/// Represents one transport instance.
 

	
 
	/// It's used to connect the Jabber server and provides transaction layer
 
	/// between Jabber server and other classes.
 
	///
 
	/// In server mode it represents Jabber server to which users can connect and use
 
	/// it as transport.
 
	class Component {
 
		public:
 
			/// Creates new Component instance.
 

	
 
			/// \param loop Main event loop.
 
			/// \param config Cofiguration; this class uses following Config values:
 
			/// 	- service.jid
 
			/// 	- service.password
 
			/// 	- service.server
 
			/// 	- service.port
 
			/// 	- service.server_mode
 
			/// \param factories Swift::NetworkFactories.
 
			/// \param factory Transport Abstract factory used to create basic transport structures.
 
			/// \param userRegistery UserRegistry class instance. It's needed only when running transport in server-mode.
 
			Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, Config *config, Factory *factory, Transport::UserRegistry *userRegistry = NULL);
 

	
 
			/// Component destructor.
 
			~Component();
 

	
 
			/// Returns Swift::StanzaChannel associated with this Transport::Component.
 

	
 
			/// It can be used to send presences and other stanzas.
 
			/// \return Swift::StanzaChannel associated with this Transport::Component.
 
			Swift::StanzaChannel *getStanzaChannel();
 

	
 
			/// Returns Swift::IQRouter associated with this Component.
 

	
 
			/// \return Swift::IQRouter associated with this Component.
 
			Swift::IQRouter *getIQRouter() { return m_iqRouter; }
 

	
 
			/// Returns Swift::PresenceOracle associated with this Transport::Component.
 

	
 
			/// You can use it to check current resource connected for particular user.
 
			/// \return Swift::PresenceOracle associated with this Transport::Component.
 
			PresenceOracle *getPresenceOracle();
 

	
 
			/// Returns True if the component is in server mode.
 

	
 
			/// \return True if the component is in server mode.
 
			bool inServerMode() { return m_server != NULL; }
 

	
 
			/// Connects the Jabber server.
 
			/// Starts the Component.
 
			
 
			/// In server-mode, it starts listening on particular port for new client connections.
 
			/// In gateway-mode, it connects the XMPP server.
 
			void start();
 

	
 
			/// Stops the component.
 
			void stop();
 

	
 
			/// Returns Jabber ID of this transport.
 

	
 
			/// \return Jabber ID of this transport
 
			Swift::JID &getJID() { return m_jid; }
 

	
 
			/// Returns Swift::NetworkFactories which can be used to create new connections.
 

	
 
			/// \return Swift::NetworkFactories which can be used to create new connections.
 
			Swift::NetworkFactories *getNetworkFactories() { return m_factories; }
 

	
 
			/// Returns Transport Factory used to create basic Transport components.
 

	
 
			/// \return Transport Factory used to create basic Transport components.
 
			Factory *getFactory() { return m_factory; }
 

	
 
			/// This signal is emitted when server disconnects the transport because of some error.
 

	
 
			/// \param error disconnection error
 
			boost::signal<void (const Swift::ComponentError &error)> onConnectionError;
 

	
 
			/// This signal is emitted when transport successfully connects the server.
 
			boost::signal<void ()> onConnected;
 

	
 
			/// This signal is emitted when XML stanza is sent to server.
 

	
 
			/// \param xml xml stanza
 
			boost::signal<void (const std::string &xml)> onXMLOut;
 

	
 
			/// This signal is emitted when XML stanza is received from server.
 

	
 
			/// \param xml xml stanza
 
			boost::signal<void (const std::string &xml)> onXMLIn;
 

	
 
			Config *getConfig() { return m_config; }
 

	
 
			/// This signal is emitted when presence from XMPP user is received.
 

	
 
			/// It's emitted only for presences addressed to transport itself
 
			/// (for example to="j2j.domain.tld").
 
			/// \param presence presence data
 
			/// (for example to="j2j.domain.tld") and for presences comming to
 
			/// MUC (for example to="#chat%irc.freenode.org@irc.domain.tld")
 
			/// \param presence Presence.
 
			boost::signal<void (Swift::Presence::ref presence)> onUserPresenceReceived;
 

	
 
			/// Component class asks the XMPP clients automatically for their capabilities.
 
			/// This signal is emitted when capabilities have been received or changed.
 
			/// \param jid JID of the client for which we received capabilities
 
			/// \param info disco#info with response.
 
			boost::signal<void (const Swift::JID& jid, boost::shared_ptr<Swift::DiscoInfo> info)> onUserDiscoInfoReceived;
 

	
 
// 			boost::signal<void (boost::shared_ptr<Swift::DiscoInfo> info, Swift::ErrorPayload::ref error, const Swift::JID& jid)> onDiscoInfoResponse;
 

	
 
		private:
 
			void handleConnected();
 
			void handleConnectionError(const Swift::ComponentError &error);
 
			void handleServerStopped(boost::optional<Swift::BoostConnectionServer::Error> e);
 
			void handlePresence(Swift::Presence::ref presence);
 
			void handleDataRead(const Swift::SafeByteArray &data);
 
			void handleDataWritten(const Swift::SafeByteArray &data);
 

	
 
			void handleDiscoInfoResponse(boost::shared_ptr<Swift::DiscoInfo> info, Swift::ErrorPayload::ref error, const Swift::JID& jid);
 
			void handleCapsChanged(const Swift::JID& jid);
 

	
 
			Swift::NetworkFactories *m_factories;
 
			Swift::Component *m_component;
 
			Swift::Server *m_server;
 
			Swift::Timer::ref m_reconnectTimer;
 
			Swift::EntityCapsManager *m_entityCapsManager;
 
			Swift::CapsManager *m_capsManager;
 
			Swift::CapsMemoryStorage *m_capsMemoryStorage;
 
			PresenceOracle *m_presenceOracle;
 
			Swift::StanzaChannel *m_stanzaChannel;
 
			Swift::IQRouter *m_iqRouter;
 
			
 
			Transport::UserRegistry *m_userRegistry;
 
			StorageBackend *m_storageBackend;
 
			int m_reconnectCount;
 
			Config* m_config;
 
			std::string m_protocol;
 
			Swift::JID m_jid;
 
			Factory *m_factory;
 
			Swift::EventLoop *m_loop;
 

	
 
		friend class User;
 
		friend class UserRegistration;
 
		friend class NetworkPluginServer;
 
	};
 
}
include/transport/user.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <time.h>
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Disco/EntityCapsManager.h"
 
#include "Swiften/Disco/EntityCapsProvider.h"
 
#include "storagebackend.h"
 
#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
 
#include "Swiften/Elements/SpectrumErrorPayload.h"
 
#include "Swiften/Network/Timer.h"
 
#include "Swiften/Network/Connection.h"
 

	
 
namespace Transport {
 

	
 
class Component;
 
class RosterManager;
 
class ConversationManager;
 
class UserManager;
 
class PresenceOracle;
 
struct UserInfo;
 

	
 
/// Represents online XMPP user.
 
class User : public Swift::EntityCapsProvider {
 
	public:
 
		/// Creates new User class.
 
		/// \param jid XMPP JID associated with this user
 
		/// \param userInfo UserInfo struct with informations needed to connect
 
		/// this user to legacy network
 
		/// \param component Component associated with this user
 
		User(const Swift::JID &jid, UserInfo &userInfo, Component * component, UserManager *userManager);
 

	
 
		/// Destroyes User.
 
		virtual ~User();
 

	
 
		/// Returns JID of XMPP user who is currently connected using this User class.
 
		/// \return full JID
 
		const Swift::JID &getJID();
 

	
 
		/// Returns full JID which supports particular feature or invalid JID.
 
		/// \param feature disco#info feature.
 
		/// \return full JID which supports particular feature or invalid JID.
 
		std::vector<Swift::JID> getJIDWithFeature(const std::string &feature);
 

	
 
		Swift::DiscoInfo::ref getCaps(const Swift::JID &jid) const;
 

	
 
		/// Returns UserInfo struct with informations needed to connect the legacy network.
 
		/// \return UserInfo struct
 
		UserInfo &getUserInfo() { return m_userInfo; }
 

	
 
		RosterManager *getRosterManager() { return m_rosterManager; }
 

	
 
		ConversationManager *getConversationManager() { return m_conversationManager; }
 

	
 
		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, bool forceJoin = false);
 

	
 
		void handleSubscription(Swift::Presence::ref presence);
 

	
 
		void handleDiscoInfo(const Swift::JID& jid, boost::shared_ptr<Swift::DiscoInfo> info);
 

	
 
		time_t &getLastActivity() {
 
			return m_lastActivity;
 
		}
 

	
 
		void updateLastActivity() {
 
			m_lastActivity = time(NULL);
 
		}
 

	
 
		/// Returns language.
 
		/// \return language
 
		const char *getLang() { return "en"; }
 

	
 
		void handleDisconnected(const std::string &error, Swift::SpectrumErrorPayload::Error e = Swift::SpectrumErrorPayload::CONNECTION_ERROR_OTHER_ERROR);
 

	
 
		bool isReadyToConnect() {
 
			return m_readyForConnect;
 
		}
 

	
 
		void setConnected(bool connected);
 

	
 
		void sendCurrentPresence();
 

	
 
		void setIgnoreDisconnect(bool ignoreDisconnect);
 

	
 
		bool isConnected() {
 
			return m_connected;
 
		}
 

	
 
		int getResourceCount() {
 
			return m_resources;
 
		}
 

	
 
		void addUserSetting(const std::string &key, const std::string &value) {
 
			m_settings[key] = value;
 
		}
 

	
 
		const std::string &getUserSetting(const std::string &key) {
 
			return m_settings[key];
 
		}
include/transport/usermanager.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <string>
 
#include <map>
 
#include "Swiften/Swiften.h"
 
#include "transport/userregistry.h"
 
#include "Swiften/Elements/Message.h"
 
#include "Swiften/Elements/Presence.h"
 
#include "Swiften/Disco/EntityCapsProvider.h"
 
#include "Swiften/Elements/DiscoInfo.h"
 
#include "Swiften/Network/Timer.h"
 

	
 
namespace Transport {
 

	
 
class User;
 
class Component;
 
class StorageBackend;
 
class StorageResponder;
 
class RosterResponder;
 
class DiscoItemsResponder;
 

	
 
/// Manages online XMPP Users.
 

	
 
/// This class handles presences and creates User classes when new user connects.
 
/// It also removes the User class once the last user's resource disconnected.
 

	
 
/// Basic user creation process:
 
/**
 
	\msc
 
	Component,UserManager,User,StorageBackend,Slot;
 
	---  [ label = "Available presence received"];
 
	Component->UserManager [label="handlePresence(...)", URL="\ref UserManager::handlePresence()"];
 
	UserManager->StorageBackend [label="getUser(...)", URL="\ref StorageBackend::getUser()"];
 
	UserManager->User [label="User::User(...)", URL="\ref User"];
 
	UserManager->Slot [label="onUserCreated(...)", URL="\ref UserManager::onUserCreated()"];
 
	UserManager->User [label="handlePresence(...)", URL="\ref User::handlePresence()"];
 
	\endmsc
 
*/
 
class UserManager : public Swift::EntityCapsProvider {
 
	public:
 
		/// 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, DiscoItemsResponder *discoItemsResponder, StorageBackend *storageBackend = NULL);
 

	
 
		/// Destroys UserManager.
 
		~UserManager();
 

	
 
		/// Returns user according to his bare JID.
 
		/// \param barejid bare JID of user
 
		/// \return User class associated with this user
 
		User *getUser(const std::string &barejid);
 

	
 
		/// Returns map with all connected users.
 
		/// \return All connected users.
 
		const std::map<std::string, User *> &getUsers() {
 
			return m_users;
 
		}
 

	
 
		/// Returns number of online users.
 
		/// \return number of online users
 
		int getUserCount();
 

	
 
		/// Removes user. This function disconnects user and safely removes
 
		/// User class. This does *not* remove user from StorageBackend.
 
		/// \param user User class to remove
 
		void removeUser(User *user, bool onUserBehalf = true);
 

	
 
		void removeAllUsers(bool onUserBehalf = true);
 

	
 
		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;
 

	
 
		/// Called when User class is going to be removed
 
		/// \param user removed User class
 
		boost::signal<void (User *user)> onUserDestroyed;
 

	
 
		/// Returns true if user is connected.
 
		/// \return True if user is connected.
 
		bool isUserConnected(const std::string &barejid) const {
 
			return m_users.find(barejid) != m_users.end();
 
		}
 

	
 
		/// Returns pointer to UserRegistry.
 
		/// \return Pointer to UserRegistry.
 
		UserRegistry *getUserRegistry() {
 
			return m_userRegistry;
 
		}
 

	
 
		Component *getComponent() {
 
			return m_component;
 
		}
 

	
 
		/// Connects user manually.
 
		/// \param user JID of user.
 
		void connectUser(const Swift::JID &user);
 

	
 
		/// Disconnects user manually.
 
		/// \param user JID of user.
 
		void disconnectUser(const Swift::JID &user);
 

	
 
		void messageToXMPPSent() { m_sentToXMPP++; }
 
		void messageToBackendSent() { m_sentToBackend++; }
 

	
 
		unsigned long getMessagesToXMPP() { return m_sentToXMPP; }
 
		unsigned long getMessagesToBackend() { return m_sentToBackend; }
 
		
 

	
 
	private:
 
		void handlePresence(Swift::Presence::ref presence);
 
		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 handleMUCPresence(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);
 
		void addUser(User *user);
 

	
 
		long m_onlineBuddies;
 
		User *m_cachedUser;
 
		std::map<std::string, User *> m_users;
 
		Component *m_component;
 
		StorageBackend *m_storageBackend;
 
		StorageResponder *m_storageResponder;
 
		UserRegistry *m_userRegistry;
 
		Swift::Timer::ref m_removeTimer;
 
		unsigned long m_sentToXMPP;
 
		unsigned long m_sentToBackend;
 
		DiscoItemsResponder *m_discoItemsResponder;
 
		friend class RosterResponder;
 
};
 

	
 
}
include/transport/userregistration.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Queries/Responder.h"
 
#include "Swiften/Elements/InBandRegistrationPayload.h"
 
#include "Swiften/Elements/RosterPayload.h"
 
#include <boost/signal.hpp>
 

	
 
namespace Transport {
 

	
 
struct UserInfo;
 
class Component;
 
class StorageBackend;
 
class UserManager;
 
class Config;
 

	
 
/// Allows users to register the transport using service discovery.
 
class UserRegistration : public Swift::Responder<Swift::InBandRegistrationPayload> {
 
	public:
 
		/// Creates new UserRegistration handler.
 
		/// \param component Component associated with this class
 
		/// \param userManager UserManager associated with this class
 
		/// \param storageBackend StorageBackend where the registered users will be stored
 
		UserRegistration(Component *component, UserManager *userManager, StorageBackend *storageBackend);
 

	
 
		/// Destroys UserRegistration.
 
		~UserRegistration();
 

	
 
		/// Registers new user. This function stores user into database and subscribe user to transport.
 
		/// \param userInfo UserInfo struct with informations about registered user
 
		/// \return false if user is already registered
 
		bool registerUser(const UserInfo &userInfo);
 

	
 
		/// Unregisters user. This function removes all data about user from databa, unsubscribe all buddies
 
		/// managed by this transport and disconnects user if he's connected.
 
		/// \param barejid bare JID of user to unregister
 
		/// \return false if there is no such user registered
 
		bool unregisterUser(const std::string &barejid);
 

	
 
		/// Called when new user has been registered.
 
		/// \param userInfo UserInfo struct with informations about user
 
		boost::signal<void (const UserInfo &userInfo)> onUserRegistered;
 

	
 
		/// Called when user has been unregistered.
 
		/// \param userInfo UserInfo struct with informations about user
 
		boost::signal<void (const UserInfo &userInfo)> onUserUnregistered;
 

	
 
		/// Called when user's registration has been updated.
 
		/// \param userInfo UserInfo struct with informations about user
 
		boost::signal<void (const UserInfo &userInfo)> onUserUpdated;
 

	
 
	private:
 
		virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::InBandRegistrationPayload> payload);
 
		virtual bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::InBandRegistrationPayload> payload);
 

	
 
		void handleRegisterRemoteRosterResponse(boost::shared_ptr<Swift::RosterPayload> payload, Swift::ErrorPayload::ref error, const UserInfo &row);
 
		void handleUnregisterRemoteRosterResponse(boost::shared_ptr<Swift::RosterPayload> payload, Swift::ErrorPayload::ref error, const std::string &barejid);
 
		
 
		Component *m_component;
 
		StorageBackend *m_storageBackend;
 
		UserManager *m_userManager;
 
		Config *m_config;
 

	
 
};
 

	
 
}
include/transport/userregistry.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <string>
 
#include <map>
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Server/UserRegistry.h"
 
#include "Swiften/Network/NetworkFactories.h"
 
#include "Swiften/Network/Timer.h"
 
#include "Swiften/Network/TimerFactory.h"
 
#include "transport/config.h"
 

	
 
namespace Transport {
 

	
 
/// Validates passwords in Server mode.
 

	
 
/// Normal login workflow could work like following:
 
/**
 
	\msc
 
	UserManager,UserRegistry,ServerFromClientSession;
 
	ServerFromClientSession->UserRegistry [label="isValidUserPassword(...)", URL="\ref UserRegistry::isValidUserPassword()"];
 
	UserManager<-UserRegistry [label="onConnectUser(...)", URL="\ref UserRegistry::onConnectUser()"];
 
	---  [ label = "UserManager logins user and validates password"];
 
	UserManager->UserRegistry [label="onPasswordValid(...)", URL="\ref UserRegistry::onPasswordValid()"];
 
	ServerFromClientSession<-UserRegistry [label="handlePasswordValid(...)", URL="\ref ServerFromClientSession::handlePasswordValid()"];
 
	\endmsc
 
*/
 
/// User can of course disconnect during login process. In this case, stopLogin() method is called which informs upper layer
 
/// that user disconnected:
 
/**
 
	\msc
 
	UserManager,UserRegistry,ServerFromClientSession;
 
	ServerFromClientSession->UserRegistry [label="isValidUserPassword(...)", URL="\ref UserRegistry::isValidUserPassword()"];
 
	UserManager<-UserRegistry [label="onConnectUser(...)", URL="\ref UserRegistry::onConnectUser()"];
 
	---  [ label = "UserManager is logging in the user, but he disconnected"];
 
	ServerFromClientSession->UserRegistry [label="stopLogin(...)", URL="\ref UserRegistry::stopLogin()"];
 
	UserManager<-UserRegistry [label="onDisconnectUser(...)", URL="\ref UserRegistry::onDisconnectUser()"];
 
	---  [ label = "UserManager disconnects the user"];
 
	\endmsc
 
*/
 
class UserRegistry : public Swift::UserRegistry {
 
	public:
 
		/// Creates new UserRegistry.
 
		/// \param cfg Config file
 
		/// 	- service.admin_username - username for admin account
 
		/// 	- service.admin_password - password for admin account
 
		UserRegistry(Config *cfg, Swift::NetworkFactories *factories);
 

	
 
		/// Destructor.
 
		virtual ~UserRegistry();
 

	
 
		/// Called to detect wheter the password is valid.
 
		/// \param user JID of user who is logging in.
 
		/// \param session Session associated with this user.
 
		/// \param password Password used for this session.
 
		void isValidUserPassword(const Swift::JID& user, Swift::ServerFromClientSession *session, const Swift::SafeByteArray& password);
 

	
 
		/// Called when user disconnects during login process. Disconnects user from legacy network.
 
		/// \param user JID.
 
		/// \param session Session.
 
		void stopLogin(const Swift::JID& user, Swift::ServerFromClientSession *session);
 

	
 
		/// Informs user that the password is valid and finishes login process.
 
		/// \param user JID.
 
		void onPasswordValid(const Swift::JID &user);
 

	
 
		/// Informs user that the password is invalid and disconnects him.
 
		/// \param user JID.
 
		void onPasswordInvalid(const Swift::JID &user, const std::string &error = "");
 

	
 
		/// Removes session later.
 
		/// \param user JID.
 
		void removeLater(const Swift::JID &user);
 

	
 
		/// Returns current password for particular user
 
		/// \param barejid JID.
 
		const std::string getUserPassword(const std::string &barejid);
 

	
 
		/// Emitted when user wants to connect legacy network to validate the password.
 
		boost::signal<void (const Swift::JID &user)> onConnectUser;
 

	
 
		/// Emitted when user disconnected XMPP server and therefore should disconnect legacy network.
 
		boost::signal<void (const Swift::JID &user)> onDisconnectUser;
 

	
 
	private:
 
		typedef struct {
 
			std::string password;
 
			Swift::ServerFromClientSession *session;
 
		} Sess;
 

	
 
		void handleRemoveTimeout(const Swift::JID &user);
 

	
 
		mutable std::map<std::string, Sess> users;
 
		mutable Config *config;
 
		Swift::Timer::ref m_removeTimer;
 
		bool m_inRemoveLater;
 
};
 

	
 
}
include/transport/usersreconnecter.h
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <string>
 
#include <algorithm>
 
#include <vector>
 
#include "Swiften/Swiften.h"
 

	
 
#include "Swiften/Network/Timer.h"
 

	
 
namespace Transport {
 

	
 
class StorageBackend;
 
class Component;
 

	
 
/// Tries to reconnect users who have been online before crash/restart.
 
class UsersReconnecter {
 
	public:
 
		/// Creates new UsersReconnecter.
 
		/// \param component Transport instance associated with this roster.
 
		/// \param storageBackend StorageBackend from which the users will be fetched.
 
		UsersReconnecter(Component *component, StorageBackend *storageBackend);
 

	
 
		/// Destructor.
 
		virtual ~UsersReconnecter();
 

	
 
		void reconnectNextUser();
 

	
 
	private:
 
		void handleConnected();
 

	
 
		Component *m_component;
 
		StorageBackend *m_storageBackend;
 
		bool m_started;
 
		std::vector<std::string> m_users;
 
		Swift::Timer::ref m_nextUserTimer;
 
};
 

	
 
}
include/transport/vcardresponder.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <vector>
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Queries/Responder.h"
 
#include "Swiften/Elements/VCard.h"
 
#include "Swiften/Network/NetworkFactories.h"
 
#include "Swiften/Network/Timer.h"
 
#include <boost/signal.hpp>
 

	
 
namespace Transport {
 

	
 
class StorageBackend;
 
class UserManager;
 
class User;
 

	
 
class VCardResponder : public Swift::Responder<Swift::VCard> {
 
	public:
 
		VCardResponder(Swift::IQRouter *router, Swift::NetworkFactories *factories, UserManager *userManager);
 
		~VCardResponder();
 

	
 
		void sendVCard(unsigned int id, boost::shared_ptr<Swift::VCard> vcard);
 

	
 
		boost::signal<void (User *, const std::string &name, unsigned int id)> onVCardRequired;
 
		boost::signal<void (User *, boost::shared_ptr<Swift::VCard> vcard)> onVCardUpdated;
 

	
 
		void collectTimeouted();
 

	
 
	private:
 
		struct VCardData {
 
			Swift::JID from;
 
			Swift::JID to;
 
			std::string id;
 
			time_t received;
 
		};
 

	
 
		virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::VCard> payload);
 
		virtual bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::VCard> payload);
 
		UserManager *m_userManager;
 
		std::map<unsigned int, VCardData> m_queries;
 
		unsigned int m_id;
 
		Swift::Timer::ref m_collectTimer;
 
};
 

	
 
}
plugin/cpp/networkplugin.cpp
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/networkplugin.h"
 
#include "transport/memoryusage.h"
 
#include "transport/logging.h"
 

	
 
#include <sstream>
 

	
 
#ifndef WIN32
 
#include <arpa/inet.h>
 
#include <sys/types.h>
 
#include <unistd.h>
 
#else 
 
#include <winsock2.h>
 
#include <stdint.h>
 
#include <process.h>
 
#define getpid _getpid
 
#endif
 

	
 
DEFINE_LOGGER(logger, "NetworkPlugin");
 

	
 
namespace Transport {
 

	
 
#define WRAP(MESSAGE, TYPE) 	pbnetwork::WrapperMessage wrap; \
 
	wrap.set_type(TYPE); \
 
	wrap.set_payload(MESSAGE); \
 
	wrap.SerializeToString(&MESSAGE);
 

	
 
template <class T> std::string stringOf(T object) {
 
	std::ostringstream os;
 
	os << object;
 
	return (os.str());
 
}
 

	
 
NetworkPlugin::NetworkPlugin() {
 
	m_pingReceived = false;
 

	
 
	double shared;
 
#ifndef WIN32
 
	process_mem_usage(shared, m_init_res);
 
#endif
 
}
 

	
 
NetworkPlugin::~NetworkPlugin() {
 
}
 

	
 
void NetworkPlugin::sendConfig(const PluginConfig &cfg) {
 
	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";
 

	
 
	for (std::vector<std::string>::const_iterator it = cfg.m_extraFields.begin(); it != cfg.m_extraFields.end(); it++) {
 
		data += std::string("extraField=") + (*it) + "\n";
 
	}
 

	
 
	data += "[features]\n";
 
	data += std::string("muc=") + (cfg.m_supportMUC ? "1" : "0") + "\n";
 

	
 
	pbnetwork::BackendConfig m;
 
	m.set_config(data);
 

	
 
	std::string message;
 
	m.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BACKEND_CONFIG);
 

	
 
	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, const std::string &timestamp, bool headline) {
 
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, bool headline, bool pm) {
 
	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);
 
	m.set_headline(headline);
 
	m.set_pm(pm);
 

	
 
	std::string message;
 
	m.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE);
 

	
 
	send(message);
 
}
 

	
 
void NetworkPlugin::handleMessageAck(const std::string &user, const std::string &legacyName, const std::string &id) {
 
	pbnetwork::ConversationMessage m;
 
	m.set_username(user);
 
	m.set_buddyname(legacyName);
 
	m.set_message("");
 
	m.set_id(id);
 

	
 
	std::string message;
 
	m.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE_ACK);
 

	
 
	send(message);
 
}
 

	
 
void NetworkPlugin::handleAttention(const std::string &user, const std::string &buddyName, const std::string &msg) {
 
	pbnetwork::ConversationMessage m;
 
	m.set_username(user);
 
	m.set_buddyname(buddyName);
 
	m.set_message(msg);
 

	
 
	std::string message;
 
	m.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_ATTENTION);
 

	
 
	send(message);
 
}
 

	
 
void NetworkPlugin::handleVCard(const std::string &user, unsigned int id, const std::string &legacyName, const std::string &fullName, const std::string &nickname, const std::string &photo) {
 
	pbnetwork::VCard vcard;
 
	vcard.set_username(user);
 
	vcard.set_buddyname(legacyName);
 
	vcard.set_id(id);
 
	vcard.set_fullname(fullName);
 
	vcard.set_nickname(nickname);
 
	vcard.set_photo(photo);
 

	
 
	std::string message;
 
	vcard.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_VCARD);
 
	send(message);
 
}
 

	
 
void NetworkPlugin::handleSubject(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &nickname) {
 
	pbnetwork::ConversationMessage m;
 
	m.set_username(user);
 
	m.set_buddyname(legacyName);
 
	m.set_message(msg);
 
	m.set_nickname(nickname);
 

	
 
	std::string message;
 
	m.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_ROOM_SUBJECT_CHANGED);
 
// 	std::cout << "SENDING MESSAGE\n";
 

	
 
	send(message);
 
}
 

	
 
void NetworkPlugin::handleBuddyChanged(const std::string &user, const std::string &buddyName, const std::string &alias,
 
			const std::vector<std::string> &groups, pbnetwork::StatusType status, const std::string &statusMessage, const std::string &iconHash, bool blocked) {
 
	pbnetwork::Buddy buddy;
 
	buddy.set_username(user);
 
	buddy.set_buddyname(buddyName);
 
	buddy.set_alias(alias);
 
	for (std::vector<std::string>::const_iterator it = groups.begin(); it != groups.end(); it++) {
 
		buddy.add_group(*it);
 
	}
 
	buddy.set_status((pbnetwork::StatusType) status);
 
	buddy.set_statusmessage(statusMessage);
 
	buddy.set_iconhash(iconHash);
 
	buddy.set_blocked(blocked);
 

	
 
	std::string message;
 
	buddy.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED);
 

	
 
	send(message);
 
}
 

	
 
void NetworkPlugin::handleBuddyRemoved(const std::string &user, const std::string &buddyName) {
 
	pbnetwork::Buddy buddy;
 
	buddy.set_username(user);
 
	buddy.set_buddyname(buddyName);
spectrum/src/main.cpp
Show inline comments
 
#include "transport/config.h"
 
#include "transport/transport.h"
 
#include "transport/filetransfermanager.h"
 
#include "transport/usermanager.h"
 
#include "transport/sqlite3backend.h"
 
#include "transport/mysqlbackend.h"
 
#include "transport/pqxxbackend.h"
 
#include "transport/userregistration.h"
 
#include "transport/networkpluginserver.h"
 
#include "transport/admininterface.h"
 
#include "transport/statsresponder.h"
 
#include "transport/usersreconnecter.h"
 
#include "transport/util.h"
 
#include "transport/gatewayresponder.h"
 
#include "transport/logging.h"
 
#include "transport/discoitemsresponder.h"
 
#include "transport/adhocmanager.h"
 
#include "transport/settingsadhoccommand.h"
 
#include "Swiften/EventLoop/SimpleEventLoop.h"
 
#include "Swiften/Network/BoostNetworkFactories.h"
 
#include <boost/filesystem.hpp>
 
#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/ServiceWrapper.h"
 
#endif
 
#include <sys/stat.h>
 

	
 
using namespace Transport;
 
using namespace Transport::Util;
 

	
 
DEFINE_LOGGER(logger, "Spectrum");
 

	
 
Swift::SimpleEventLoop *eventLoop_ = NULL;
 
Component *component_ = NULL;
 
UserManager *userManager_ = NULL;
 
Config *config_ = NULL;
 

	
 
static void stop_spectrum() {
 
	userManager_->removeAllUsers(false);
 
	component_->stop();
 
	eventLoop_->stop();
 
}
 

	
 
static void spectrum_sigint_handler(int sig) {
 
	eventLoop_->postEvent(&stop_spectrum);
 
}
 

	
 
static void spectrum_sigterm_handler(int sig) {
 
	eventLoop_->postEvent(&stop_spectrum);
 
}
 

	
 
#ifdef WIN32
 
BOOL spectrum_control_handler( DWORD fdwCtrlType ) { 
 
	if (fdwCtrlType == CTRL_C_EVENT || fdwCtrlType == CTRL_CLOSE_EVENT) {
 
		eventLoop_->postEvent(&stop_spectrum);
 
		return TRUE;
 
	}
 
	return FALSE;
 
} 
 
#endif
 

	
 
static void removeOldIcons(std::string iconDir) {
 
	std::vector<std::string> dirs;
 
	dirs.push_back(iconDir);
 

	
 
	boost::thread thread(boost::bind(Util::removeEverythingOlderThan, dirs, time(NULL) - 3600*24*14));
 
}
 

	
 
#ifndef WIN32
 
static void daemonize(const char *cwd, const char *lock_file) {
 
	pid_t pid, sid;
 
	FILE* lock_file_f;
 
	char process_pid[20];
 
	/* already a daemon */
 
	if ( getppid() == 1 ) return;
 

	
 
	/* Fork off the parent process */
 
	pid = fork();
 
	if (pid < 0) {
 
		exit(1);
 
	}
 
	/* If we got a good PID, then we can exit the parent process. */
 
	if (pid > 0) {
 
		if (lock_file) {
 
			/* write our pid into it & close the file. */
 
			lock_file_f = fopen(lock_file, "w+");
 
			if (lock_file_f == NULL) {
 
				std::cerr << "Cannot create lock file " << lock_file << ". Exiting\n";
 
				exit(1);
 
			}
 
			sprintf(process_pid,"%d\n",pid);
 
			if (fwrite(process_pid,1,strlen(process_pid),lock_file_f) < strlen(process_pid)) {
 
				std::cerr << "Cannot write to lock file " << lock_file << ". Exiting\n";
 
				exit(1);
 
			}
 
			fclose(lock_file_f);
 
		}
 
		exit(0);
 
	}
 

	
 
	/* Change the file mode mask */
 
	umask(0);
 

	
 
	/* Create a new SID for the child process */
spectrum/src/sample.cfg
Show inline comments
 
[service]
 
jid = localhost
 
password = secret
 
server = 127.0.0.1
 
port = 5222
 
server_mode = 1
 
backend_host=localhost
 
pidfile=./test.pid
 
# < this option doesn't work yet
 
#backend_port=10001
 
#admin_jid=admin@localhost
 
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/swiften/spectrum2_swiften_backend
 
#backend=../..//backends/swiften/spectrum2_swiften_backend
 
#backend=../../backends/twitter/spectrum2_twitter_backend
 
#backend=/home/hanzz/code/libtransport/backends/libcommuni/spectrum2_libcommuni_backend
 
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
 
portfile=./$jid.port
 
irc_server=irc.freenode.org
 

	
 
[backend]
 
#default_avatar=catmelonhead.jpg
 
#no_vcard_fetch=true
 

	
 
[logging]
 
#config=logging.cfg # log4cxx/log4j logging configuration file
 
#backend_config=/home/hanzz/code/libtransport/spectrum/src/backend-logging.cfg # log4cxx/log4j logging configuration file for backends
 

	
 
[database]
 
type=sqlite3 # or "none" without database backend
 
database=users.sqlite
 
prefix=twitter
 
#type = mysql # or "none" without database backend.......................................................................................................................
 
#database = test
 
#prefix=
 
#user=root
 
#password=yourrootsqlpassword
 
#encryption_key=hanzzik
spectrum/src/sample2.cfg
Show inline comments
 
[service]
 
# 1 if Spectrum should run in server mode.
 
server_mode = 1
 

	
 
# The name of user/group Spectrum runs as.
 
#user=spectrum
 
#group=spectrum
 

	
 
# JID of Spectrum instance.
 
jid = localhost
 

	
 
# Password used to connect the XMPP server in gateway mode.
 
# In server mode, this option is ignored.
 
password = secret
 

	
 
# XMPP server to which Spectrum connects in gateway mode.
 
# In server mode, this option is ignored.
 
server = 127.0.0.1
 

	
 
# XMPP server port.
 
port = 5222
 

	
 
# Interface on which Spectrum listens for backends.
 
backend_host = localhost
 

	
 
# Port on which Spectrum listens for backends.
 
# By default Spectrum chooses random backend port and there's
 
# no need to change it normally
 
#backend_port=10001
 

	
 
# Full path to PKCS#12 cetficiate used for TLS in server mode.
 
#cert=
 

	
 
# Certificate password if any.
 
#cert_password= 
 

	
 
# Number of users per one legacy network backend.
 
users_per_backend=10
 

	
 
# Full path to backend binary.
 
backend=/usr/bin/spectrum2_libpurple_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
 
#backend=/usr/bin/xvfb-run -a -s "-screen 0 10x10x8" -f /tmp/x-skype-gw /usr/bin/spectrum2_skype_backend
 

	
 
# Libpurple protocol-id for spectrum_libpurple_backend
 
protocol=prpl-jabber
 
#protocol=prpl-msn
 
#protocol=prpl-icq
 

	
 
# prpl-any means that user sets his protocol in his JID which has to be
 
# in following format: protocol.username@domain.tld
 
# So for example: prpl-jabber.hanzz.k%gmail.com@domain.tld
 
#protocol=prpl-any
 

	
 
[identity]
 
# Name of Spectrum instance in service discovery
 
name=Spectrum Jabber Transport
 

	
 
# Type of transport ("msn", "icq", "xmpp").
 
# Check http://xmpp.org/registrar/disco-categories.html#gateway
 
type=xmpp
 

	
 
# Category of transport, default is "gateway
 
#category=gateway
 

	
 
[logging]
 
# log4cxx/log4j logging configuration file in ini format used for main spectrum2 instance.
 
config = /etc/spectrum2/logging.cfg
 

	
 
# log4cxx/log4j logging configuration file in ini format used for backends.
 
backend_config = /etc/spectrum2/backend-logging.cfg
 

	
 
[database]
 
# Database backend type
 
# "sqlite3", "mysql", "pqxx", or "none" without database backend
 
type = none
 

	
 
# For SQLite3: Full path to database
 
# For MySQL and PostgreSQL: name of database
 
# default database = /var/lib/spectrum2/$jid/database.sql
 
#database = jabber_transport
 

	
 
# Server.
 
#server = localhost
 

	
 
# Port.
 
#port = 0
 

	
 
# User.
 
#user = spectrum
 

	
 
# Paasword.
 
#password = secret
 

	
 
# Prefix used for tables
 
#prefix = jabber_
 

	
 
# Connection string (for pqxx only!)
 
# If you set this, it ignores every other database option except for type and prefix.
 
#connectionstring = host=localhost user=specturm password=secret
 

	
 
[registration]
 
# Enable public registrations
 
enable_public_registration=1
 

	
 
# Text to display upon user registration form
 
#username_label=Jabber JID (e.g. user@server.tld):
 
#instructions=Enter your remote jabber JID and password as well as your local username and password
 

	
 
# If True a local jabber account on <local_account_server> is needed 
 
# for transport registration, the idea is to enable public registration
 
# from other servers, but only for users, who have already local accounts
 
#require_local_account=1
 
#local_username_label=Local username (without @server.tld):
 
#local_account_server=localhost
 
#local_account_server_timeout=10000
 

	
spectrum_manager/src/main.cpp
Show inline comments
 
@@ -45,153 +45,156 @@ using namespace boost;
 
// 				if (cfg.load(itr->path().string()) == false) {
 
// 					std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
 
// 					continue;
 
// 				}
 
// 
 
// 				if (CONFIG_VECTOR(&cfg, "service.admin_jid").empty() || CONFIG_STRING(&cfg, "service.admin_password").empty()) {
 
// 					std::cerr << itr->path().string() << ": service.admin_jid or service.admin_password empty. This server can't be queried over XMPP.\n";
 
// 					continue;
 
// 				}
 
// 
 
// 				finished++;
 
// 				Swift::Client *client = new Swift::Client(CONFIG_VECTOR(&cfg, "service.admin_jid")[0], CONFIG_STRING(&cfg, "service.admin_password"), &networkFactories);
 
// 				client->setAlwaysTrustCertificates();
 
// 				client->onConnected.connect(boost::bind(&handleConnected, client, CONFIG_STRING(&cfg, "service.jid")));
 
// 				client->onDisconnected.connect(bind(&handleDisconnected, client, _1, CONFIG_STRING(&cfg, "service.jid")));
 
// 				client->onMessageReceived.connect(bind(&handleMessageReceived, client, _1, CONFIG_STRING(&cfg, "service.jid")));
 
// 				Swift::ClientOptions opt;
 
// 				opt.allowPLAINWithoutTLS = true;
 
// 				client->connect(opt);
 
// 			}
 
// 		}
 
// 	}
 
// 	catch (const filesystem_error& ex) {
 
// 		std::cerr << "boost filesystem error\n";
 
// 		exit(5);
 
// 	}
 
// }
 
 
 
int main(int argc, char **argv)
 
{
 
	ManagerConfig config;
 
	std::string config_file;
 
	std::vector<std::string> command;
 
	boost::program_options::variables_map vm;
 
 
	boost::program_options::options_description desc("Usage: spectrum [OPTIONS] <COMMAND>\n"
 
													 "       spectrum [OPTIONS] <instance_JID> <other>\nCommands:\n"
 
													 " start - start all local Spectrum2 instances\n"
 
													 " stop  - stop all local Spectrum2 instances\n"
 
													 " restart  - restart all local Spectrum2 instances\n"
 
													 " status - status of local Spectrum2 instances\n"
 
													 " <other> - send command to local Spectrum2 instance and print output\n"
 
													 "Allowed options");
 
	desc.add_options()
 
		("help,h", "Show help output")
 
		("config,c", boost::program_options::value<std::string>(&config_file)->default_value("/etc/spectrum2/spectrum_manager.cfg"), "Spectrum manager config file")
 
		("command", boost::program_options::value<std::vector<std::string> >(&command), "Command")
 
		;
 
	try
 
	{
 
		boost::program_options::positional_options_description p;
 
		p.add("command", -1);
 
		boost::program_options::store(boost::program_options::command_line_parser(argc, argv).
 
          options(desc).positional(p).run(), vm);
 
		boost::program_options::notify(vm);
 
 
		if(vm.count("help"))
 
		{
 
			std::cout << desc << "\n";
 
			return 1;
 
		}
 
	}
 
	catch (std::runtime_error& e)
 
	{
 
		std::cout << desc << "\n";
 
		return 2;
 
	}
 
	catch (...)
 
	{
 
		std::cout << desc << "\n";
 
		return 3;
 
	}
 
 
	if (!config.load(config_file)) {
 
		std::cerr << "Can't load configuration file.\n";
 
		return 4;
 
	}
 
 
	if (command.empty()) {
 
		std::cout << desc << "\n";
 
		return 1;
 
	}
 
 
	if (command[0] == "start") {
 
		return start_instances(&config);
 
	}
 
	else if (command[0] == "stop") {
 
		stop_instances(&config);
 
	}
 
	else if (command[0] == "status") {
 
		return show_status(&config);
 
	}
 
	else if (command[0] == "list") {
 
		std::vector<std::string> list = show_list(&config);
 
	}
 
	else if (command[0] == "restart") {
 
		return restart_instances(&config);
 
	}
 
	else if (command[0] == "server") {
 
		Server server(&config);
 
		if (server.start() == false) {
 
			std::cerr << "Can't set up server handler.\n";
 
			return 1;
 
		}
 
		while (1) { sleep(10); }
 
	}
 
	else {
 
		if (command.size() < 2) {
 
			std::cout << desc << "\n";
 
			return 11;
 
		}
 
		Swift::SimpleEventLoop eventLoop;
 
		Swift::BoostNetworkFactories networkFactories(&eventLoop);
 
 
		std::string jid = command[0];
 
		command.erase(command.begin());
 
		std::string cmd = boost::algorithm::join(command, " ");
 
 
		if (cmd == "start") {
 
			return start_instances(&config, jid);
 
		}
 
		else if (cmd == "stop") {
 
			stop_instances(&config, jid);
 
			return 0;
 
		}
 
		else if (cmd == "restart") {
 
			return restart_instances(&config, jid);
 
		}
 
 
		ask_local_server(&config, networkFactories, jid, cmd);
 
// 		std::string message = command;
 
// 		m = &message;
 
 
// 		ask_local_server(&config, networkFactories, message);
 
 
		eventLoop.runUntilEvents();
 
 
 
		struct timeval td_start,td_end;
 
		float elapsed = 0; 
 
		gettimeofday(&td_start, NULL);
 
	
 
		time_t started = time(NULL);
 
		while(get_response().empty()) {
 
			eventLoop.runUntilEvents();
 
		}
 
		if (!get_response().empty()) {
 
			gettimeofday(&td_end, NULL);
 
			elapsed = 1000000.0 * (td_end.tv_sec -td_start.tv_sec); \
 
			elapsed += (td_end.tv_usec - td_start.tv_usec); \
 
			elapsed = elapsed / 1000 / 1000; \
 
// 			std::cout << "Response received after " << (elapsed) << " seconds\n";
 
		}
 
	}
 
}
src/blockresponder.cpp
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "blockresponder.h"
 

	
 
#include <iostream>
 
#include <boost/bind.hpp>
 
#include "Swiften/Queries/IQRouter.h"
 
#include "transport/BlockPayload.h"
 
#include "Swiften/Swiften.h"
 
#include "transport/usermanager.h"
 
#include "transport/user.h"
 
#include "transport/buddy.h"
 
#include "transport/rostermanager.h"
 
#include "transport/logging.h"
 

	
 
using namespace Swift;
 
using namespace boost;
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "BlockResponder");
 

	
 
BlockResponder::BlockResponder(Swift::IQRouter *router, UserManager *userManager) : Swift::SetResponder<BlockPayload>(router) {
 
	m_userManager = userManager;
 
}
 

	
 
BlockResponder::~BlockResponder() {
 
	
 
}
 

	
 
bool BlockResponder::handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Transport::BlockPayload> info) {
 
	User *user = m_userManager->getUser(from.toBare().toString());
 
	if (!user) {
 
		LOG4CXX_WARN(logger, from.toBare().toString() << ": User is not logged in");
 
		return true;
 
	}
 

	
 
	Buddy *buddy = user->getRosterManager()->getBuddy(Buddy::JIDToLegacyName(to));
 
	if (!buddy) {
 
		LOG4CXX_WARN(logger, from.toBare().toString() << ": Buddy " << Buddy::JIDToLegacyName(to) << " does not exist");
 
		return true;
 
	}
 

	
 
	if (buddy->isBlocked()) {
 
		LOG4CXX_INFO(logger, from.toBare().toString() << ": Unblocking buddy " << Buddy::JIDToLegacyName(to));
 
	}
 
	else {
 
		LOG4CXX_INFO(logger, from.toBare().toString() << ": Blocking buddy " << Buddy::JIDToLegacyName(to));
 
	}
 

	
 
	onBlockToggled(buddy);
 

	
 
	return true;
 
}
 

	
 
}
src/blockresponder.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <vector>
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Queries/SetResponder.h"
 
#include "transport/BlockPayload.h"
 
#include <boost/signal.hpp>
 

	
 
namespace Transport {
 

	
 
class UserManager;
 
class Buddy;
 

	
 
class BlockResponder : public Swift::SetResponder<Transport::BlockPayload> {
 
	public:
 
		BlockResponder(Swift::IQRouter *router, UserManager *userManager);
 
		~BlockResponder();
 

	
 
		boost::signal<void (Buddy *)> onBlockToggled;
 

	
 
	private:
 
		virtual bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Transport::BlockPayload> payload);
 

	
 
		UserManager *m_userManager;
 
};
 

	
 
}
src/buddy.cpp
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/buddy.h"
 
#include "transport/rostermanager.h"
 
#include "transport/user.h"
 
#include "transport/transport.h"
 
#include "transport/BlockPayload.h"
 
#include "transport/usermanager.h"
 
#include "transport/discoitemsresponder.h"
 

	
 
#include "Swiften/Elements/VCardUpdate.h"
 

	
 
namespace Transport {
 

	
 
Buddy::Buddy(RosterManager *rosterManager, long id, BuddyFlag flags) : m_id(id), m_flags(flags), m_rosterManager(rosterManager),
 
	m_subscription(Ask) {
 
// 	m_rosterManager->setBuddy(this);
 
}
 

	
 
Buddy::~Buddy() {
 
// 	m_rosterManager->unsetBuddy(this);
 
}
 

	
 
void Buddy::sendPresence() {
 
	Swift::Presence::ref presence = generatePresenceStanza(255);
 
	if (presence) {
 
		m_rosterManager->getUser()->getComponent()->getStanzaChannel()->sendPresence(presence);
 
	}
 
}
 

	
 
void Buddy::generateJID() {
 
	m_jid = Swift::JID();
 
	m_jid = Swift::JID(getSafeName(), m_rosterManager->getUser()->getComponent()->getJID().toString(), "bot");
 
}
 

	
 
void Buddy::setID(long id) {
 
	m_id = id;
 
}
 

	
 
long Buddy::getID() {
 
	return m_id;
 
}
 

	
 
void Buddy::setFlags(BuddyFlag flags) {
 
	m_flags = flags;
 

	
 
	if (!getSafeName().empty()) {
 
		try {
 
			generateJID();
 
		} catch (...) {
 
		}
 
	}
 
}
 

	
 
BuddyFlag Buddy::getFlags() {
 
	return m_flags;
 
}
 

	
 
const Swift::JID &Buddy::getJID() {
 
	if (!m_jid.isValid() || m_jid.getNode().empty()) {
 
		generateJID();
 
	}
 
	return m_jid;
 
}
 

	
 
void Buddy::setSubscription(Subscription subscription) {
 
	m_subscription = subscription;
 
}
 

	
 
Buddy::Subscription Buddy::getSubscription() {
 
	return m_subscription;
 
}
 

	
 
Swift::Presence::ref Buddy::generatePresenceStanza(int features, bool only_new) {
 
	std::string alias = getAlias();
 
	std::string name = getSafeName();
 

	
 
	Swift::StatusShow s;
 
	std::string statusMessage;
 
	if (!getStatus(s, statusMessage))
 
		return Swift::Presence::ref();
 

	
 
	if (m_jid.getNode().empty()) {
 
		generateJID();
 
	}
 

	
 
	Swift::Presence::ref presence = Swift::Presence::create();
 
 	presence->setFrom(m_jid);
 
	presence->setTo(m_rosterManager->getUser()->getJID().toBare());
 
	presence->setType(Swift::Presence::Available);
 

	
 
	if (!statusMessage.empty())
 
		presence->setStatus(statusMessage);
 

	
 
	if (s.getType() == Swift::StatusShow::None)
 
		presence->setType(Swift::Presence::Unavailable);
 
	presence->setShow(s.getType());
 

	
 
	if (presence->getType() != Swift::Presence::Unavailable) {
 
		// caps
 
		
 
		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())));
 
// 		}
 
		if (isBlocked()) {
 
			presence->addPayload(boost::shared_ptr<Swift::Payload>(new Transport::BlockPayload ()));
src/config.cpp
Show inline comments
 
@@ -221,164 +221,165 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description
 
				}
 
				catch(...) {
 
					list.push_back(m_unregistered[opt.string_key].as<std::string>());
 
				}
 
				
 
				list.push_back(opt.value[0]);
 
				m_unregistered[opt.string_key] = variable_value(list, false);
 
			}
 
		}
 
		else if (opt.value[0].find("$jid") != std::string::npos) {
 
			boost::replace_all(opt.value[0], "$jid", jid);
 
		}
 
	}
 

	
 
	// Load configs passed by command line
 
	if (m_argc != 0 && m_argv) {
 
		basic_command_line_parser<char> parser = command_line_parser(m_argc, m_argv).options(opts).allow_unregistered();
 
		parsed_options parsed = parser.run();
 
		BOOST_FOREACH(option &opt, parsed.options) {
 
			if (opt.unregistered && !opt.value.empty()) {
 
				m_unregistered[opt.string_key] = variable_value(opt.value[0], false);
 
			}
 
		}
 
		store(parsed, m_variables);
 
	}
 

	
 
	store(parsed, m_variables);
 
	notify(m_variables);
 

	
 
	onConfigReloaded();
 

	
 
	return true;
 
}
 

	
 
bool Config::load(std::istream &ifs) {
 
	options_description opts("Transport options");
 
	return load(ifs, opts);
 
}
 

	
 
bool Config::load(const std::string &configfile, const std::string &jid) {
 
	try {
 
		options_description opts("Transport options");
 
		return load(configfile, opts, jid);
 
	} catch ( const boost::program_options::multiple_occurrences& e ) {
 
#if (BOOST_MAJOR_VERSION >= 1 && BOOST_MINOR_VERSION >= 42)
 
		std::cerr << configfile << " parsing error: " << e.what() << " from option: " << e.get_option_name() << std::endl;
 
#else
 
		std::cerr << configfile << " parsing error: " << e.what() << std::endl;
 
#endif
 
		return false;
 
	}
 
}
 

	
 
bool Config::reload() {
 
	if (m_file.empty()) {
 
		return false;
 
	}
 

	
 
	return load(m_file, m_jid);
 
}
 

	
 
Config::SectionValuesCont Config::getSectionValues(const std::string& sectionName) {
 
	SectionValuesCont sectionValues;
 

	
 
	std::string sectionSearchString = sectionName + ".";
 
	BOOST_FOREACH (const Variables::value_type & varItem, m_variables) {
 
		if (boost::istarts_with(varItem.first, sectionSearchString))
 
			sectionValues[varItem.first] = varItem.second;
 
	}
 

	
 
	BOOST_FOREACH (const UnregisteredCont::value_type & varItem, m_unregistered) {
 
		if (boost::istarts_with(varItem.first, sectionSearchString))
 
			sectionValues[varItem.first] = varItem.second;
 
	}
 

	
 
	return sectionValues;
 
}
 

	
 
std::string Config::getCommandLineArgs() const {
 
	std::ostringstream commandLineArgs;
 

	
 
	// Return the command-line arguments that were passed to us originally (but remove the initial .exe part)
 
	for (int i = 1; i < m_argc; ++i) 	{
 
		commandLineArgs << "\"" << m_argv[i] << "\" ";
 
	}
 

	
 
	return commandLineArgs.str();
 
}
 

	
 
void Config::updateBackendConfig(const std::string &backendConfig) {
 
	options_description opts("Backend options");
 
	opts.add_options()
 
		("registration.needPassword", value<bool>()->default_value(true), "")
 
		("registration.needRegistration", value<bool>()->default_value(false), "")
 
		("registration.extraField", value<std::vector<std::string> >()->multitoken(), "")
 
		("features.receipts", value<bool>()->default_value(false), "")
 
		("features.muc", value<bool>()->default_value(false), "")
 
	;
 

	
 
	std::stringstream ifs(backendConfig);
 
	parsed_options parsed = parse_config_file(ifs, opts, true);
 

	
 
	store(parsed, m_backendConfig);
 
	notify(m_backendConfig);
 

	
 
	onBackendConfigUpdated();
 
}
 

	
 
Config *Config::createFromArgs(int argc, char **argv, std::string &error, std::string &host, int &port) {
 
	std::string jid;
 
	std::ostringstream os;
 
	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")
 
		("no-daemonize,n", "Do not run spectrum as daemon")
 
		("no-debug,d", "Create coredumps on crash")
 
		("jid,j", boost::program_options::value<std::string>(&jid)->default_value(""), "Specify JID of transport manually")
 
		("config", boost::program_options::value<std::string>(&configFile)->default_value(""), "Config file")
 
		;
 

	
 
	os << desc;
 
	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"))
 
		{
 
			error = os.str();
 
			return NULL;
 
		}
 

	
 
		if(vm.count("config") == 0) {
 
			error = os.str();
 
			return NULL;
 
		}
 
	}
 
	catch (std::runtime_error& e)
 
	{
 
		error = std::string(e.what()) + "\n" + os.str();
 
		return NULL;
 
	}
 
	catch (...)
 
	{
 
		error = os.str();
 
		return NULL;
 
	}
 

	
 
	Config *config = new Config(argc, argv);
 
	if (!config->load(configFile)) {
 
		error = "Can't open " + configFile + " configuration file.\n";
 
		delete config;
 
		return NULL;
 
	}
 
	return config;
 
}
 

	
 
}
src/conversation.cpp
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include <iostream>
 
#include "transport/conversation.h"
 
#include "transport/conversationmanager.h"
 
#include "transport/user.h"
 
#include "transport/transport.h"
 
#include "transport/buddy.h"
 
#include "transport/rostermanager.h"
 

	
 
#include "Swiften/Elements/MUCItem.h"
 
#include "Swiften/Elements/MUCOccupant.h"
 
#include "Swiften/Elements/MUCUserPayload.h"
 
#include "Swiften/Elements/Delay.h"
 
#include "Swiften/Elements/MUCPayload.h"
 

	
 
namespace Transport {
 

	
 
Conversation::Conversation(ConversationManager *conversationManager, const std::string &legacyName, bool isMUC) : m_conversationManager(conversationManager) {
 
	m_legacyName = legacyName;
 
	m_muc = isMUC;
 
	m_jid = m_conversationManager->getUser()->getJID().toBare();
 
	m_sentInitialPresence = false;
 
}
 

	
 
Conversation::~Conversation() {
 
}
 

	
 
void Conversation::destroyRoom() {
 
	if (m_muc) {
 
		Swift::Presence::ref presence = Swift::Presence::create();
 
		std::string legacyName = m_legacyName;
 
		if (legacyName.find_last_of("@") != std::string::npos) {
 
			legacyName.replace(legacyName.find_last_of("@"), 1, "%"); // OK
 
		}
 
		presence->setFrom(Swift::JID(legacyName, m_conversationManager->getComponent()->getJID().toBare(), m_nickname));
 
		presence->setType(Swift::Presence::Unavailable);
 

	
 
		Swift::MUCItem item;
 
		item.affiliation = Swift::MUCOccupant::NoAffiliation;
 
		item.role = Swift::MUCOccupant::NoRole;
 
		item.actor = "Transport";
 
		item.reason = "Spectrum 2 transport is being shut down.";
 
		Swift::MUCUserPayload *p = new Swift::MUCUserPayload ();
 
		p->addItem(item);
 

	
 
		Swift::MUCUserPayload::StatusCode c;
 
		c.code = 332;
 
		p->addStatusCode(c);
 
		Swift::MUCUserPayload::StatusCode c2;
 
		c2.code = 307;
 
		p->addStatusCode(c2);
 

	
 
		presence->addPayload(boost::shared_ptr<Swift::Payload>(p));
 
		BOOST_FOREACH(const Swift::JID &jid, m_jids) {
 
			presence->setTo(jid);
 
			m_conversationManager->getComponent()->getStanzaChannel()->sendPresence(presence);
 
		}
 
	}
 
}
 

	
 
void Conversation::setRoom(const std::string &room) {
 
	m_room = room;
 
	m_legacyName = m_room + "/" + m_legacyName;
 
}
 

	
 
void Conversation::handleMessage(boost::shared_ptr<Swift::Message> &message, const std::string &nickname) {
 
	if (m_muc) {
 
		message->setType(Swift::Message::Groupchat);
 
	}
 
	else {
 
		if (message->getType() == Swift::Message::Headline) {
 
			if (m_conversationManager->getUser()->getUserSetting("send_headlines") != "1") {
 
				message->setType(Swift::Message::Chat);
 
			}
 
		}
 
		else {
 
			message->setType(Swift::Message::Chat);
 
		}
 
	}
 

	
 
	std::string n = nickname;
 
	if (n.empty() && !m_room.empty() && !m_muc) {
 
		n = m_nickname;
 
	}
 

	
 
	if (message->getType() != Swift::Message::Groupchat) {
 
		message->setTo(m_jid);
 
		// normal message
 
		if (n.empty()) {
 
			Buddy *buddy = m_conversationManager->getUser()->getRosterManager()->getBuddy(m_legacyName);
 
			if (buddy) {
 
				message->setFrom(buddy->getJID());
 
			}
 
			else {
 
				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
 
		else {
 
			if (m_room.empty()) {
 
				message->setFrom(Swift::JID(n, m_conversationManager->getComponent()->getJID().toBare(), "user"));
 
			}
 
			else {
 
				message->setFrom(Swift::JID(m_room, m_conversationManager->getComponent()->getJID().toBare(), n));
 
				std::string legacyName = m_room;
 
				if (legacyName.find_last_of("@") != std::string::npos) {
 
					legacyName.replace(legacyName.find_last_of("@"), 1, "%"); // OK
 
				}
 
				message->setFrom(Swift::JID(legacyName, m_conversationManager->getComponent()->getJID().toBare(), n));
 
			}
 
		}
 

	
 
		if (m_conversationManager->getComponent()->inServerMode() && m_conversationManager->getUser()->shouldCacheMessages()) {
 
			boost::posix_time::ptime timestamp = boost::posix_time::second_clock::universal_time();
 
			boost::shared_ptr<Swift::Delay> delay(boost::make_shared<Swift::Delay>());
 
			delay->setStamp(timestamp);
 
			message->addPayload(delay);
 
			m_cachedMessages.push_back(message);
 
			if (m_cachedMessages.size() > 100) {
 
				m_cachedMessages.pop_front();
 
			}
 
		}
 
		else {
 
			m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(message);
 
		}
 
	}
 
	else {
 
		std::string legacyName = m_legacyName;
 
		if (legacyName.find_last_of("@") != std::string::npos) {
 
			legacyName.replace(legacyName.find_last_of("@"), 1, "%"); // OK
 
		}
 

	
 
		std::string n = nickname;
 
		if (n.empty()) {
 
			n = " ";
 
		}
 

	
 
		message->setFrom(Swift::JID(legacyName, m_conversationManager->getComponent()->getJID().toBare(), n));
 

	
 
		if (m_jids.empty()) {
 
			boost::posix_time::ptime timestamp = boost::posix_time::second_clock::universal_time();
 
			boost::shared_ptr<Swift::Delay> delay(boost::make_shared<Swift::Delay>());
 
			delay->setStamp(timestamp);
 
			message->addPayload(delay);
 
			m_cachedMessages.push_back(message);
 
			if (m_cachedMessages.size() > 100) {
 
				m_cachedMessages.pop_front();
 
			}
 
		}
 
		else {
 
			BOOST_FOREACH(const Swift::JID &jid, m_jids) {
 
				message->setTo(jid);
 
				// 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);
 
			}
 
		}
 
	}
 
}
 

	
 
void Conversation::sendParticipants(const Swift::JID &to) {
 
	for (std::map<std::string, Participant>::iterator it = m_participants.begin(); it != m_participants.end(); it++) {
 
		Swift::Presence::ref presence = generatePresence(it->first, it->second.flag, it->second.status, it->second.statusMessage, "");
 
		presence->setTo(to);
 
		m_conversationManager->getComponent()->getStanzaChannel()->sendPresence(presence);
 
	}
 
}
 

	
 
void Conversation::sendCachedMessages(const Swift::JID &to) {
 
	for (std::list<boost::shared_ptr<Swift::Message> >::const_iterator it = m_cachedMessages.begin(); it != m_cachedMessages.end(); it++) {
 
		if (to.isValid()) {
 
			(*it)->setTo(to);
 
		}
 
		else {
 
			(*it)->setTo(m_jid.toBare());
 
		}
 
		m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(*it);
 
	}
 
	m_cachedMessages.clear();
 
}
 

	
 
Swift::Presence::ref Conversation::generatePresence(const std::string &nick, int flag, int status, const std::string &statusMessage, const std::string &newname) {
 
	std::string nickname = nick;
 
	Swift::Presence::ref presence = Swift::Presence::create();
 
	std::string legacyName = m_legacyName;
 
	if (m_muc) {
 
		if (legacyName.find_last_of("@") != std::string::npos) {
 
			legacyName.replace(legacyName.find_last_of("@"), 1, "%"); // OK
 
		}
 
	}
 
	presence->setFrom(Swift::JID(legacyName, m_conversationManager->getComponent()->getJID().toBare(), nickname));
 
	presence->setType(Swift::Presence::Available);
 

	
 
	if (!statusMessage.empty())
 
		presence->setStatus(statusMessage);
 

	
 
	Swift::StatusShow s((Swift::StatusShow::Type) status);
 

	
 
	if (s.getType() == Swift::StatusShow::None) {
 
		presence->setType(Swift::Presence::Unavailable);
 
	}
 

	
 
	presence->setShow(s.getType());
 

	
 
	Swift::MUCUserPayload *p = new Swift::MUCUserPayload ();
 
	if (m_nickname == nickname) {
 
		if (flag & PARTICIPANT_FLAG_CONFLICT) {
 
			delete p;
 
			presence->setType(Swift::Presence::Error);
 
			presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::MUCPayload()));
 
			presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::ErrorPayload(Swift::ErrorPayload::Conflict)));
 
			return presence;
 
		}
 
		else if (flag & PARTICIPANT_FLAG_NOT_AUTHORIZED) {
 
			delete p;
 
			presence->setType(Swift::Presence::Error);
 
			presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::MUCPayload()));
 
			presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::ErrorPayload(Swift::ErrorPayload::NotAuthorized, Swift::ErrorPayload::Auth)));
 
			return presence;
 
		}
 
		else {
 
			Swift::MUCUserPayload::StatusCode c;
 
			c.code = 110;
 
			p->addStatusCode(c);
 
			m_sentInitialPresence = true;
 
		}
 
	}
 

	
 

	
 
	Swift::MUCItem item;
 
	
 
	item.affiliation = Swift::MUCOccupant::Member;
 
	item.role = Swift::MUCOccupant::Participant;
 

	
 
	if (flag & Moderator) {
 
	if (flag & PARTICIPANT_FLAG_MODERATOR) {
 
		item.affiliation = Swift::MUCOccupant::Admin;
 
		item.role = Swift::MUCOccupant::Moderator;
 
	}
 

	
 
	if (!newname.empty()) {
 
		item.nick = newname;
 
		Swift::MUCUserPayload::StatusCode c;
 
		c.code = 303;
 
		p->addStatusCode(c);
 
		presence->setType(Swift::Presence::Unavailable);
 
	}
 
	
 
	p->addItem(item);
 
	presence->addPayload(boost::shared_ptr<Swift::Payload>(p));
 
	return presence;
 
}
 

	
 
void Conversation::handleParticipantChanged(const std::string &nick, int flag, int status, const std::string &statusMessage, const std::string &newname) {
 
void Conversation::handleParticipantChanged(const std::string &nick, Conversation::ParticipantFlag flag, int status, const std::string &statusMessage, const std::string &newname) {
 
	Swift::Presence::ref presence = generatePresence(nick, flag, status, statusMessage, newname);
 

	
 
	if (presence->getType() == Swift::Presence::Unavailable) {
 
		m_participants.erase(nick);
 
	}
 
	else {
 
		Participant p;
 
		p.flag = flag;
 
		p.status = status;
 
		p.statusMessage = statusMessage;
 
		m_participants[nick] = p;
 
	}
 

	
 

	
 
	BOOST_FOREACH(const Swift::JID &jid, m_jids) {
 
		presence->setTo(jid);
 
		m_conversationManager->getComponent()->getStanzaChannel()->sendPresence(presence);
 
	}
 
	if (!newname.empty()) {
 
		handleParticipantChanged(newname, flag, status, statusMessage);
 
	}
 

	
 
	if (m_sentInitialPresence && m_subject) {
 
		m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(m_subject);
 
		m_subject.reset();
 
	}
 
}
 

	
 
}
src/discoinforesponder.cpp
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "discoinforesponder.h"
 

	
 
#include <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"
 
#include "Swiften/Disco/CapsInfoGenerator.h"
 

	
 
using namespace Swift;
 
using namespace boost;
 

	
 
DEFINE_LOGGER(logger, "DiscoInfoResponder");
 

	
 
namespace Transport {
 

	
 
DiscoInfoResponder::DiscoInfoResponder(Swift::IQRouter *router, Config *config) : Swift::GetResponder<DiscoInfo>(router) {
 
	m_config = config;
 
	m_config->onBackendConfigUpdated.connect(boost::bind(&DiscoInfoResponder::updateBuddyFeatures, this));
 
	m_config->onBackendConfigUpdated.connect(boost::bind(&DiscoInfoResponder::updateFeatures, this));
 
	m_buddyInfo = NULL;
 
	m_transportInfo.addIdentity(DiscoInfo::Identity(CONFIG_STRING(m_config, "identity.name"),
 
													CONFIG_STRING(m_config, "identity.category"),
 
													CONFIG_STRING(m_config, "identity.type")));
 

	
 
	std::list<std::string> features;
 
	features.push_back("jabber:iq:register");
 
	features.push_back("jabber:iq:gateway");
 
	features.push_back("jabber:iq:private");
 
	features.push_back("http://jabber.org/protocol/disco#info");
 
	features.push_back("http://jabber.org/protocol/commands");
 
	setTransportFeatures(features);
 

	
 
	updateBuddyFeatures();
 
	updateFeatures();
 
}
 

	
 
DiscoInfoResponder::~DiscoInfoResponder() {
 
	delete m_buddyInfo;
 
}
 

	
 
void DiscoInfoResponder::updateBuddyFeatures() {
 
void DiscoInfoResponder::updateFeatures() {
 
	std::list<std::string> features2;
 
	features2.push_back("jabber:iq:register");
 
	features2.push_back("jabber:iq:gateway");
 
	features2.push_back("jabber:iq:private");
 
	features2.push_back("http://jabber.org/protocol/disco#info");
 
	features2.push_back("http://jabber.org/protocol/commands");
 
	if (CONFIG_BOOL_DEFAULTED(m_config, "features.muc", false)) {
 
		features2.push_back("http://jabber.org/protocol/muc");
 
	}
 
	setTransportFeatures(features2);
 

	
 
	std::list<std::string> features;
 
	features.push_back("http://jabber.org/protocol/disco#items");
 
	features.push_back("http://jabber.org/protocol/disco#info");
 
	features.push_back("http://jabber.org/protocol/chatstates");
 
	features.push_back("http://jabber.org/protocol/xhtml-im");
 
	if (CONFIG_BOOL_DEFAULTED(m_config, "features.receipts", false)) {
 
		features.push_back("urn:xmpp:receipts");
 
	}
 
	setBuddyFeatures(features);
 
}
 

	
 
void DiscoInfoResponder::setTransportFeatures(std::list<std::string> &features) {
 
	for (std::list<std::string>::iterator it = features.begin(); it != features.end(); it++) {
 
		if (!m_transportInfo.hasFeature(*it)) {
 
			m_transportInfo.addFeature(*it);
 
		}
 
	}
 
}
 

	
 
void DiscoInfoResponder::setBuddyFeatures(std::list<std::string> &f) {
 
	delete m_buddyInfo;
 
	m_buddyInfo = new Swift::DiscoInfo;
 
	m_buddyInfo->addIdentity(DiscoInfo::Identity(CONFIG_STRING(m_config, "identity.name"), "client", "pc"));
 

	
 
	for (std::list<std::string>::iterator it = f.begin(); it != f.end(); it++) {
 
		if (!m_buddyInfo->hasFeature(*it)) {
 
			m_buddyInfo->addFeature(*it);
 
		}
 
	}
 

	
 
	CapsInfoGenerator caps("spectrum");
 
	m_capsInfo = caps.generateCapsInfo(*m_buddyInfo);
 
	onBuddyCapsInfoChanged(m_capsInfo);
 
}
 

	
 
void DiscoInfoResponder::addRoom(const std::string &jid, const std::string &name) {
 
	std::string j = jid;
 
	boost::algorithm::to_lower(j);
 
	m_rooms[j] = name;
 
}
 

	
 
void DiscoInfoResponder::clearRooms() {
 
	m_rooms.clear();
 
}
 

	
 
void DiscoInfoResponder::addAdHocCommand(const std::string &node, const std::string &name) {
 
	m_commands[node] = node;
 
}
 

	
 
bool DiscoInfoResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::DiscoInfo> info) {
 
	// disco#info for transport
 
	if (to.getNode().empty()) {
 
		// 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() == "http://jabber.org/protocol/commands") {
 
			boost::shared_ptr<DiscoInfo> res(new DiscoInfo());
 
			res->addIdentity(DiscoInfo::Identity("Commands", "automation", "command-list"));
 
			res->setNode(info->getNode());
 
			sendResponse(from, to, id, res);
 
		}
 
		else {
 
			if (!info->getNode().empty()) {
 
				sendError(from, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel);
 
				return true;
 
			}
 

	
 
			boost::shared_ptr<DiscoInfo> res(new DiscoInfo(m_transportInfo));
 
			res->setNode(info->getNode());
 
			sendResponse(from, id, res);
 
		}
 
	}
 
	// disco#info for room
 
	else if (m_rooms.find(to.toBare().toString()) != m_rooms.end()) {
 
		boost::shared_ptr<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());
 
		sendResponse(from, to, id, res);
 
	}
 
	return true;
 
}
 

	
 
}
src/discoinforesponder.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <vector>
 
#include "Swiften/Swiften.h"
 
#include <list>
 
#include <boost/signal.hpp>
 
#include "Swiften/Queries/GetResponder.h"
 
#include "Swiften/Elements/DiscoInfo.h"
 
#include "Swiften/Elements/CapsInfo.h"
 

	
 
namespace Transport {
 

	
 
class Config;
 

	
 
class DiscoInfoResponder : public Swift::GetResponder<Swift::DiscoInfo> {
 
	public:
 
		DiscoInfoResponder(Swift::IQRouter *router, Config *config);
 
		~DiscoInfoResponder();
 

	
 
		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() {
 
				return m_capsInfo;
 
		}
 

	
 
	private:
 
		virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::DiscoInfo> payload);
 
		void updateBuddyFeatures();
 
		void updateFeatures();
 

	
 
		Swift::DiscoInfo m_transportInfo;
 
		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;
 
};
 

	
 
}
src/discoitemsresponder.cpp
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/discoitemsresponder.h"
 

	
 
#include <iostream>
 
#include <boost/bind.hpp>
 
#include "Swiften/Queries/IQRouter.h"
 
#include "Swiften/Swiften.h"
 
#include "transport/transport.h"
 
#include "transport/logging.h"
 
#include "discoinforesponder.h"
 

	
 
using namespace Swift;
 
using namespace boost;
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "DiscoItemsResponder");
 

	
 
DiscoItemsResponder::DiscoItemsResponder(Component *component) : Swift::GetResponder<DiscoItems>(component->getIQRouter()) {
 
	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();
 
}
 

	
 

	
 
bool DiscoItemsResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::DiscoItems> info) {
 
	LOG4CXX_INFO(logger, "get request received with node " << info->getNode());
 
	if (info->getNode() == "http://jabber.org/protocol/commands") {
 
		sendResponse(from, id, m_commands);
 
	}
 
	else if (to.getNode().empty()) {
 
		sendResponse(from, id, m_rooms);
 
	}
 
	else {
 
		sendResponse(from, id, boost::shared_ptr<DiscoItems>(new DiscoItems()));
 
	}
 
	return true;
 
}
 

	
 
}
src/filetransfermanager.cpp
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/filetransfermanager.h"
 
#include "transport/transport.h"
 
#include "transport/usermanager.h"
 
#include "transport/user.h"
 
#include "transport/buddy.h"
 
#include "transport/logging.h"
 
#include "Swiften/Network/ConnectionServerFactory.h"
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "FileTransferManager");
 

	
 
FileTransferManager::FileTransferManager(Component *component, UserManager *userManager) {
 
	m_component = component;
 
	m_userManager = userManager;
 

	
 
	m_jingleSessionManager = new Swift::JingleSessionManager(m_component->getIQRouter());
 
	m_connectivityManager = new Swift::ConnectivityManager(m_component->getNetworkFactories()->getNATTraverser());
 
	m_bytestreamRegistry = new Swift::SOCKS5BytestreamRegistry();
 
	m_bytestreamProxy = new Swift::SOCKS5BytestreamProxy(m_component->getNetworkFactories()->getConnectionFactory(), m_component->getNetworkFactories()->getTimerFactory());
 

	
 
	m_localCandidateGeneratorFactory = new Swift::DefaultLocalJingleTransportCandidateGeneratorFactory(m_connectivityManager, m_bytestreamRegistry, m_bytestreamProxy, "thishouldnotbeused");
 
	m_remoteCandidateSelectorFactory = new Swift::DefaultRemoteJingleTransportCandidateSelectorFactory(m_component->getNetworkFactories()->getConnectionFactory(), m_component->getNetworkFactories()->getTimerFactory());
 

	
 
	boost::shared_ptr<Swift::ConnectionServer> server = m_component->getNetworkFactories()->getConnectionServerFactory()->createConnectionServer(19645);
 
	server->start();
 
	m_bytestreamServer = new Swift::SOCKS5BytestreamServer(server, m_bytestreamRegistry);
 
	m_bytestreamServer->start();
 

	
 
	m_outgoingFTManager = new Swift::CombinedOutgoingFileTransferManager(m_jingleSessionManager, m_component->getIQRouter(),
 
																	m_userManager, m_remoteCandidateSelectorFactory, 
 
																	m_localCandidateGeneratorFactory, m_bytestreamRegistry, 
 
																	m_bytestreamProxy, m_component->getPresenceOracle(),
 
																	m_bytestreamServer);
 

	
 
// WARNING: Swiften crashes when this is uncommented... But we probably need it for working Jingle FT
 
// 	m_connectivityManager->addListeningPort(19645);
 
}
 

	
 
FileTransferManager::~FileTransferManager() {
 
	m_bytestreamServer->stop();
 
	delete m_outgoingFTManager;
 
	delete m_remoteCandidateSelectorFactory;
 
	delete m_localCandidateGeneratorFactory;
 
	delete m_jingleSessionManager;
 
	delete m_bytestreamRegistry;
 
	delete m_bytestreamServer;
 
	delete m_bytestreamProxy;
 
	delete m_connectivityManager;
 
}
 

	
 
FileTransferManager::Transfer FileTransferManager::sendFile(User *user, Buddy *buddy, boost::shared_ptr<Swift::ReadBytestream> byteStream, const Swift::StreamInitiationFileInfo &info) {
 
	FileTransferManager::Transfer transfer;
 
	transfer.from = buddy->getJID();
 
	transfer.to = user->getJID();
 
	transfer.readByteStream = byteStream;
 

	
 
	LOG4CXX_INFO(logger, "Starting FT from '" << transfer.from << "' to '" << transfer.to << "'")
 

	
 
	transfer.ft = m_outgoingFTManager->createOutgoingFileTransfer(transfer.from, transfer.to, transfer.readByteStream, info);
 
// 	if (transfer.ft) {
 
// 		m_filetransfers.push_back(ft);
 
// 		ft->onStateChange.connect(boost::bind(&User::handleFTStateChanged, this, _1, Buddy::JIDToLegacyName(from), info.getName(), info.getSize(), id));
 
// 		transfer.ft->start();
 
// 	}
 
	return transfer;
 
}
 

	
 
}
src/gatewayresponder.cpp
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/gatewayresponder.h"
 

	
 
#include <iostream>
 
#include <boost/bind.hpp>
 
#include "Swiften/Queries/IQRouter.h"
 
#include "Swiften/Elements/RawXMLPayload.h"
 
#include "Swiften/Swiften.h"
 
#include "transport/usermanager.h"
 
#include "transport/user.h"
 
#include "transport/transport.h"
 
#include "transport/logging.h"
 

	
 
using namespace Swift;
 
using namespace boost;
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "GatewayResponder");
 

	
 
GatewayResponder::GatewayResponder(Swift::IQRouter *router, UserManager *userManager) : Swift::Responder<GatewayPayload>(router) {
 
	m_userManager = userManager;
 
}
 

	
 
GatewayResponder::~GatewayResponder() {
 
}
 

	
 
bool GatewayResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::GatewayPayload> payload) {
 
	std::string prompt = CONFIG_STRING(m_userManager->getComponent()->getConfig(), "gateway_responder.prompt");
 
	std::string label = CONFIG_STRING(m_userManager->getComponent()->getConfig(), "gateway_responder.label");
 
	sendResponse(from, id, boost::shared_ptr<GatewayPayload>(new GatewayPayload(Swift::JID(), label, prompt)));
 
	return true;
 
}
 

	
 
bool GatewayResponder::handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::GatewayPayload> payload) {
 
	std::string prompt = payload->getPrompt();
 

	
 
	std::string escaped = Swift::JID::getEscapedNode(prompt);
 
	if (!CONFIG_BOOL_DEFAULTED(m_userManager->getComponent()->getConfig(), "service.jid_escaping", true)) {
 
		escaped = prompt;
 
		if (escaped.find_last_of("@") != std::string::npos) {
 
			escaped.replace(escaped.find_last_of("@"), 1, "%");
 
		}
 
	}
 
	// This code is here to workaround Gajim (and probably other clients bug too) bug
 
	// https://trac.gajim.org/ticket/7277
 
	if (prompt.find("\\40") != std::string::npos) {
 
		LOG4CXX_WARN(logger, from.toString() << " Received already escaped JID " << prompt << ". Not escaping again.");
 
		escaped = prompt;
 
	}
 

	
 
	std::string jid = escaped + "@" + m_userManager->getComponent()->getJID().toBare().toString();
 

	
 
	sendResponse(from, id, boost::shared_ptr<GatewayPayload>(new GatewayPayload(jid)));
 
	return true;
 
}
 

	
 
}
src/logging.cpp
Show inline comments
 
@@ -43,190 +43,194 @@
 
#define getpid _getpid
 
#endif
 

	
 
using namespace boost::filesystem;
 

	
 

	
 
namespace Transport {
 

	
 
namespace Logging {
 

	
 
#ifdef WITH_LOG4CXX
 
using namespace log4cxx;
 
static LoggerPtr root;
 

	
 

	
 
class intercept_stream : public std::streambuf{
 
public:
 
    intercept_stream(std::ostream& stream, char const* logger):
 
      _logger(log4cxx::Logger::getLogger(logger))
 
    {
 
		stream.rdbuf(this);
 
    }
 
    ~intercept_stream(){
 
    }
 
protected:
 
    virtual std::streamsize xsputn(const char *msg, std::streamsize count){
 

	
 
        //Output to log4cxx logger
 
        std::string s(msg, count - 1); // remove last \n
 
		LOG4CXX_INFO(_logger, s);
 
        return count;
 
    }
 

	
 
	int overflow(int c)
 
	{
 
		return 0;
 
	}
 

	
 
	int sync()
 
	{
 
		return 0;
 
	}
 

	
 
private:
 
    log4cxx::LoggerPtr _logger;
 
};
 

	
 
static intercept_stream* intercepter_cout;
 
static intercept_stream* intercepter_cerr;
 

	
 

	
 
static void initLogging(Config *config, std::string key, bool only_create_dir = false) {
 
	if (CONFIG_STRING(config, key).empty()) {
 
		if (only_create_dir) {
 
			return;
 
		}
 
		root = log4cxx::Logger::getRootLogger();
 
#ifdef _MSC_VER
 
		root->addAppender(new ConsoleAppender(new PatternLayout(L"%d %-5p %c: %m%n")));
 
#else
 
		root->addAppender(new ConsoleAppender(new PatternLayout("%d %-5p %c: %m%n")));
 
#endif
 
	}
 
	else {
 
		log4cxx::helpers::Properties p;
 

	
 
		log4cxx::helpers::FileInputStream *istream = NULL;
 
		try {
 
			istream = new log4cxx::helpers::FileInputStream(CONFIG_STRING(config, key));
 
		}
 
		catch(log4cxx::helpers::IOException &ex) {
 
			std::cerr << "Can't create FileInputStream logger instance: " << ex.what() << "\n";
 
		}
 
		catch (...) {
 
			std::cerr << "Can't create FileInputStream logger instance\n";
 
		}
 

	
 
		if (!istream) {
 
			return;
 
		}
 

	
 
		p.load(istream);
 
		LogString pid, jid, id;
 
		log4cxx::helpers::Transcoder::decode(boost::lexical_cast<std::string>(getpid()), pid);
 
		log4cxx::helpers::Transcoder::decode(CONFIG_STRING(config, "service.jid"), jid);
 
		log4cxx::helpers::Transcoder::decode(CONFIG_STRING_DEFAULTED(config, "service.backend_id", ""), id);
 
#ifdef _MSC_VER
 
		p.setProperty(L"pid", pid);
 
		p.setProperty(L"jid", jid);
 
		p.setProperty(L"id", id);
 
#else
 
		p.setProperty("pid", pid);
 
		p.setProperty("jid", jid);
 
		p.setProperty("id", id);
 
#endif
 

	
 
		std::string dir;
 
		std::vector<std::string> dirs;
 
		BOOST_FOREACH(const log4cxx::LogString &prop, p.propertyNames()) {
 
// 			if (boost::ends_with(prop, ".File")) {
 
			if (boost::ends_with(prop, ".File")) {
 
				std::string dir;
 
				log4cxx::helpers::Transcoder::encode(p.get(prop), dir);
 
				boost::replace_all(dir, "${jid}", jid);
 
				boost::replace_all(dir, "${pid}", pid);
 
				boost::replace_all(dir, "${id}", id);
 
				break;
 
// 			}
 
				dirs.push_back(dir);
 
			}
 
		}
 
		mode_t old_cmask;
 
		if (!dir.empty()) {
 
		// create directories
 
#ifndef WIN32
 
		old_cmask = umask(0007);
 
#endif
 

	
 
		BOOST_FOREACH(std::string &dir, dirs) {
 
			if (!dir.empty()) {
 
				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";
 
				}
 
			}
 
		}
 

	
 
#ifndef WIN32
 
		umask(old_cmask);
 
#endif
 

	
 
		if (only_create_dir) {
 
			return;
 
		}
 

	
 
		log4cxx::PropertyConfigurator::configure(p);
 

	
 
		// Change owner of main log file
 
#ifndef WIN32
 
	BOOST_FOREACH(std::string &dir, dirs) {
 
		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
 
	}
 
}
 

	
 
void initBackendLogging(Config *config) {
 
	initLogging(config, "logging.backend_config");
 

	
 
	redirect_stderr();
 
}
 

	
 
void initMainLogging(Config *config) {
 
	initLogging(config, "logging.config");
 
	initLogging(config, "logging.backend_config", true);
 
}
 

	
 
void redirect_stderr() {
 
	 intercepter_cerr = new intercept_stream(std::cerr, "cerr");
 
	 intercepter_cout = new intercept_stream(std::cout, "cout");
 
}
 

	
 
void shutdownLogging() {
 
	delete intercepter_cerr;
 
	delete intercepter_cout;
 
	log4cxx::LogManager::shutdown();
 
}
 

	
 
#else /* WITH_LOG4CXX */
 
void initBackendLogging(Config */*config*/) {
 
}
 

	
 
void initMainLogging(Config */*config*/) {
 
}
 

	
 
void shutdownLogging() {
 
	
 
}
 

	
 
void redirect_stderr() {
 
	
 
}
 
#endif /* WITH_LOG4CXX */
 

	
 
}
 

	
 
}
src/networkpluginserver.cpp
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/networkpluginserver.h"
 
#include "transport/user.h"
 
#include "transport/transport.h"
 
#include "transport/storagebackend.h"
 
#include "transport/rostermanager.h"
 
#include "transport/usermanager.h"
 
#include "transport/conversationmanager.h"
 
#include "transport/localbuddy.h"
 
#include "transport/config.h"
 
#include "transport/conversation.h"
 
#include "transport/vcardresponder.h"
 
#include "transport/rosterresponder.h"
 
#include "transport/memoryreadbytestream.h"
 
#include "transport/logging.h"
 
#include "transport/admininterface.h"
 
#include "blockresponder.h"
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Server/ServerStanzaChannel.h"
 
#include "Swiften/Elements/StreamError.h"
 
#include "Swiften/Network/BoostConnectionServer.h"
 
#include "Swiften/Network/ConnectionServerFactory.h"
 
#include "Swiften/Elements/AttentionPayload.h"
 
#include "Swiften/Elements/XHTMLIMPayload.h"
 
#include "Swiften/Elements/Delay.h"
 
#include "Swiften/Elements/DeliveryReceipt.h"
 
#include "Swiften/Elements/DeliveryReceiptRequest.h"
 
#include "Swiften/Elements/InvisiblePayload.h"
 
#include "Swiften/Elements/SpectrumErrorPayload.h"
 
#include "transport/protocol.pb.h"
 
#include "transport/util.h"
 
#include "transport/discoitemsresponder.h"
 

	
 
#include "boost/date_time/posix_time/posix_time.hpp"
 
#include "boost/signal.hpp"
 

	
 
#include "utf8.h"
 

	
 
#include <Swiften/FileTransfer/ReadBytestream.h>
 
#include <Swiften/Elements/StreamInitiationFileInfo.h>
 

	
 
#ifdef _WIN32
 
#include "windows.h"
 
#include <stdint.h>
 
#else
 
#include "sys/wait.h"
 
#include "sys/signal.h"
 
#include <sys/types.h>
 
#include <signal.h>
 
#include "popt.h"
 
#endif
 

	
 
using namespace Transport::Util;
 

	
 
namespace Transport {
 

	
 
static unsigned long backend_id;
 
static unsigned long bytestream_id;
 

	
 
DEFINE_LOGGER(logger, "NetworkPluginServer");
 

	
 
static NetworkPluginServer *_server;
 

	
 
class NetworkConversation : public Conversation {
 
	public:
 
		NetworkConversation(ConversationManager *conversationManager, const std::string &legacyName, bool muc = false) : Conversation(conversationManager, legacyName, muc) {
 
		}
 

	
 
		// Called when there's new message to legacy network from XMPP network
 
		void sendMessage(boost::shared_ptr<Swift::Message> &message) {
 
			onMessageToSend(this, message);
 
		}
 

	
 
		boost::signal<void (NetworkConversation *, boost::shared_ptr<Swift::Message> &)> onMessageToSend;
 
};
 

	
 
class NetworkFactory : public Factory {
 
	public:
 
		NetworkFactory(NetworkPluginServer *nps) {
 
			m_nps = nps;
 
		}
 

	
 
		virtual ~NetworkFactory() {}
 

	
 
		// Creates new conversation (NetworkConversation in this case)
 
		Conversation *createConversation(ConversationManager *conversationManager, const std::string &legacyName, bool isMuc) {
 
			NetworkConversation *nc = new NetworkConversation(conversationManager, legacyName, isMuc);
 
			nc->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, m_nps, _1, _2));
 
			return nc;
 
		}
 

	
 
		// Creates new LocalBuddy
 
		Buddy *createBuddy(RosterManager *rosterManager, const BuddyInfo &buddyInfo) {
 
			LocalBuddy *buddy = new LocalBuddy(rosterManager, buddyInfo.id, buddyInfo.legacyName, buddyInfo.alias, buddyInfo.groups, (BuddyFlag) buddyInfo.flags);
 
			if (!buddy->isValid()) {
 
				delete buddy;
 
				return NULL;
 
			}
 
			if (buddyInfo.subscription == "both") {
 
				buddy->setSubscription(Buddy::Both);
 
			}
 
			else {
 
				buddy->setSubscription(Buddy::Ask);
 
			}
 
			if (buddyInfo.settings.find("icon_hash") != buddyInfo.settings.end())
 
				buddy->setIconHash(buddyInfo.settings.find("icon_hash")->second.s);
 
			return buddy;
 
		}
 

	
 
	private:
 
		NetworkPluginServer *m_nps;
 
};
 

	
 
// Wraps google protobuf payload into WrapperMessage and serialize it to string
 
#define WRAP(MESSAGE, TYPE) 	pbnetwork::WrapperMessage wrap; \
 
	wrap.set_type(TYPE); \
 
	wrap.set_payload(MESSAGE); \
 
	wrap.SerializeToString(&MESSAGE);
 

	
 
// Executes new backend
 
static unsigned long exec_(const std::string& exePath, const char *host, const char *port, const char *log_id, const char *cmdlineArgs) {
 
	// BACKEND_ID is replaced with unique ID. The ID is increasing for every backend.
 
	std::string finalExePath = boost::replace_all_copy(exePath, "BACKEND_ID", boost::lexical_cast<std::string>(backend_id++));	
 

	
 
#ifdef _WIN32
 
	// Add host and port.
 
	std::ostringstream fullCmdLine;
 
	fullCmdLine << "\"" << finalExePath << "\" --host " << host << " --port " << port;
 

	
 
	if (cmdlineArgs)
 
		fullCmdLine << " " << cmdlineArgs;
 

	
 
@@ -526,262 +532,274 @@ void NetworkPluginServer::handleChatStatePayload(const std::string &data, Swift:
 
	pbnetwork::Buddy payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	// We're not creating new Conversation just because of chatstates.
 
	// Some networks/clients spams with chatstates a lot and it leads to bigger memory usage.
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname());
 
	if (!conv) {
 
		return;
 
	}
 

	
 
	// Forward chatstate
 
	boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
	msg->addPayload(boost::make_shared<Swift::ChatState>(type));
 

	
 
	conv->handleMessage(msg);
 
}
 

	
 
void NetworkPluginServer::handleBuddyChangedPayload(const std::string &data) {
 
	pbnetwork::Buddy payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(payload.buddyname());
 
	if (buddy) {
 
		handleBuddyPayload(buddy, payload);
 
	}
 
	else {
 
		if (payload.buddyname() == user->getUserInfo().uin) {
 
			return;
 
		}
 

	
 
		std::vector<std::string> groups;
 
		for (int i = 0; i < payload.group_size(); i++) {
 
			groups.push_back(payload.group(i));
 
		}
 
		if (CONFIG_BOOL_DEFAULTED(m_config, "service.jid_escaping", true)) {
 
			buddy = new LocalBuddy(user->getRosterManager(), -1, payload.buddyname(), payload.alias(), groups, BUDDY_JID_ESCAPING);
 
		}
 
		else {
 
			buddy = new LocalBuddy(user->getRosterManager(), -1, payload.buddyname(), payload.alias(), groups, BUDDY_NO_FLAG);
 
		}
 
		if (!buddy->isValid()) {
 
			delete buddy;
 
			return;
 
		}
 

	
 
		buddy->setBlocked(payload.blocked());
 
		user->getRosterManager()->setBuddy(buddy);
 
		buddy->setStatus(Swift::StatusShow((Swift::StatusShow::Type) payload.status()), payload.statusmessage());
 
		buddy->setIconHash(payload.iconhash());
 
	}
 
}
 

	
 
void NetworkPluginServer::handleBuddyRemovedPayload(const std::string &data) {
 
	pbnetwork::Buddy payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	user->getRosterManager()->removeBuddy(payload.buddyname());
 
}
 

	
 
void NetworkPluginServer::handleParticipantChangedPayload(const std::string &data) {
 
	pbnetwork::Participant payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.room());
 
	if (!conv) {
 
		return;
 
	}
 

	
 
	conv->handleParticipantChanged(payload.nickname(), payload.flag(), payload.status(), payload.statusmessage(), payload.newname());
 
	conv->handleParticipantChanged(payload.nickname(), (Conversation::ParticipantFlag) payload.flag(), payload.status(), payload.statusmessage(), payload.newname());
 
}
 

	
 
void NetworkPluginServer::handleRoomChangedPayload(const std::string &data) {
 
	pbnetwork::Room payload;
 
	if (payload.ParseFromString(data) == false) {
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.room());
 
	if (!conv) {
 
		return;
 
	}
 

	
 
	conv->setNickname(payload.nickname());
 
}
 

	
 
void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool subject) {
 
	pbnetwork::ConversationMessage payload;
 

	
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	// Message from legacy network triggers network acticity
 
	user->updateLastActivity();
 

	
 
	// Set proper body.
 
	boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
	if (subject) {
 
		msg->setSubject(payload.message());
 
	}
 
	else {
 
		msg->setBody(payload.message());
 
	}
 

	
 
	if (payload.headline()) {
 
		msg->setType(Swift::Message::Headline);
 
	}
 

	
 
	// Add xhtml-im payload.
 
	if (CONFIG_BOOL(m_config, "service.enable_xhtml") && !payload.xhtml().empty()) {
 
		msg->addPayload(boost::make_shared<Swift::XHTMLIMPayload>(payload.xhtml()));
 
	}
 

	
 
	if (!payload.timestamp().empty()) {
 
		boost::posix_time::ptime timestamp = boost::posix_time::from_iso_string(payload.timestamp());
 
		boost::shared_ptr<Swift::Delay> delay(boost::make_shared<Swift::Delay>());
 
		delay->setStamp(timestamp);
 
		msg->addPayload(delay);
 
	}
 

	
 
	
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname());
 

	
 
	// We can't create Conversation for payload with nickname, because this means the message is from room,
 
	// but this user is not in any room, so it's OK to just reject this message
 
	if (!conv && !payload.nickname().empty()) {
 
		return;
 
	}
 

	
 
	if (conv && payload.pm()) {
 
		conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname() + "/" + payload.nickname());
 
		if (!conv) {
 
			conv = new NetworkConversation(user->getConversationManager(), payload.nickname());
 
			std::string name = payload.buddyname();
 
			conv->setRoom(name);
 
			conv->setNickname(payload.buddyname() + "/" + payload.nickname());
 

	
 
			user->getConversationManager()->addConversation(conv);
 
			conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2));
 
		}
 
	}
 

	
 
	// Create new Conversation if it does not exist
 
	if (!conv) {
 
		conv = new NetworkConversation(user->getConversationManager(), payload.buddyname());
 
		user->getConversationManager()->addConversation(conv);
 
		conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2));
 
	}
 

	
 
	// Forward it
 
	conv->handleMessage(msg, payload.nickname());
 
	m_userManager->messageToXMPPSent();
 
}
 

	
 
void NetworkPluginServer::handleConvMessageAckPayload(const std::string &data) {
 
	pbnetwork::ConversationMessage payload;
 

	
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	if (payload.id().empty()) {
 
		LOG4CXX_WARN(logger, "Received message ack with empty ID, not forwarding to XMPP.");
 
		return;
 
	}
 

	
 
	boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
	msg->addPayload(boost::make_shared<Swift::DeliveryReceipt>(payload.id()));
 

	
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname());
 

	
 
	// Receipts don't create conversation
 
	if (!conv) {
 
		return;
 
	}
 

	
 
	// Forward it
 
	conv->handleMessage(msg);
 
}
 

	
 
void NetworkPluginServer::handleAttentionPayload(const std::string &data) {
 
	pbnetwork::ConversationMessage payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
	msg->setBody(payload.message());
 
	msg->addPayload(boost::make_shared<Swift::AttentionPayload>());
 

	
 
	// Attentions trigger new Conversation creation
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname());
 
	if (!conv) {
 
		conv = new NetworkConversation(user->getConversationManager(), payload.buddyname());
 
		user->getConversationManager()->addConversation(conv);
 
		conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2));
 
	}
 

	
 
	conv->handleMessage(msg);
 
}
 

	
 
void NetworkPluginServer::handleStatsPayload(Backend *c, const std::string &data) {
 
	pbnetwork::Stats payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	c->res = payload.res();
 
	c->init_res = payload.init_res();
 
	c->shared = payload.shared();
 
	c->id = payload.id();
 
}
 

	
 
void NetworkPluginServer::handleFTStartPayload(const std::string &data) {
 
	pbnetwork::File payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	LOG4CXX_INFO(logger, "handleFTStartPayload " << payload.filename() << " " << payload.buddyname());
 
	
 
	LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(payload.buddyname());
 
@@ -1154,477 +1172,509 @@ void NetworkPluginServer::pingTimeout() {
 
#endif
 
		}
 

	
 
		if ((*it)->users.size() == 0) {
 
			LOG4CXX_INFO(logger, "Disconnecting backend " << (*it) << " (ID=" << (*it)->id << "). There are no users.");
 
			toRemove.push_back(*it);
 
		}
 
	}
 

	
 
	BOOST_FOREACH(Backend *b, toRemove) {
 
		handleSessionFinished(b);
 
	}
 

	
 
	m_pingTimer->start();
 
}
 

	
 
void NetworkPluginServer::collectBackend() {
 
	// Stop accepting new users to backend with the biggest memory usage. This prevents backends
 
	// which are leaking to eat whole memory by connectin new users to legacy network.
 
	LOG4CXX_INFO(logger, "Collect backend called, finding backend which will be set to die");
 
	unsigned long max = 0;
 
	Backend *backend = NULL;
 
	for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
 
		if ((*it)->res > max) {
 
			max = (*it)->res;
 
			backend = (*it);
 
		}
 
	}
 

	
 
	if (backend) {
 
		if (m_collectTimer) {
 
			m_collectTimer->start();
 
		}
 
		LOG4CXX_INFO(logger, "Backend " << backend << " (ID=" << backend->id << ") is set to die");
 
		backend->acceptUsers = false;
 
	}
 
}
 

	
 
bool NetworkPluginServer::moveToLongRunBackend(User *user) {
 
	// Check if user has already some backend
 
	Backend *old = (Backend *) user->getData();
 
	if (!old) {
 
		LOG4CXX_INFO(logger, "User " << user->getJID().toString() << " does not have old backend. Not moving.");
 
		return true;
 
	}
 

	
 
	// if he's already on long run, do nothing
 
	if (old->longRun) {
 
		LOG4CXX_INFO(logger, "User " << user->getJID().toString() << " is already on long-running backend. Not moving.");
 
		return true;
 
	}
 

	
 
	// Get free longrun backend, if there's no longrun backend, create one and wait
 
	// for its connection
 
	Backend *backend = getFreeClient(false, true);
 
	if (!backend) {
 
		LOG4CXX_INFO(logger, "No free long-running backend for user " << user->getJID().toString() << ". Will try later");
 
		return false;
 
	}
 

	
 
	// old backend will trigger disconnection which has to be ignored to keep user online
 
	user->setIgnoreDisconnect(true);
 

	
 
	// remove user from the old backend
 
	// If backend is empty, it will be collected by pingTimeout
 
	old->users.remove(user);
 

	
 
	// switch to new backend and connect
 
	user->setData(backend);
 
	backend->users.push_back(user);
 

	
 
	// connect him
 
	handleUserReadyToConnect(user);
 
	return true;
 
}
 

	
 
void NetworkPluginServer::handleUserCreated(User *user) {
 
	// Get free backend to handle this user or spawn new one if there's no free one.
 
	Backend *c = getFreeClient();
 

	
 
	// Add user to queue if there's no free backend to handle him so far.
 
	if (!c) {
 
		LOG4CXX_INFO(logger, "There is no backend to handle user " << user->getJID().toString() << ". Adding him to queue.");
 
		m_waitingUsers.push_back(user);
 
		return;
 
	}
 

	
 
	// Associate users with backend
 
	user->setData(c);
 
	c->users.push_back(user);
 

	
 
	// Don't forget to disconnect these in handleUserDestroyed!!!
 
	user->onReadyToConnect.connect(boost::bind(&NetworkPluginServer::handleUserReadyToConnect, this, user));
 
	user->onPresenceChanged.connect(boost::bind(&NetworkPluginServer::handleUserPresenceChanged, this, user, _1));
 
	user->onRoomJoined.connect(boost::bind(&NetworkPluginServer::handleRoomJoined, this, user, _1, _2, _3, _4));
 
	user->onRoomLeft.connect(boost::bind(&NetworkPluginServer::handleRoomLeft, this, user, _1));
 

	
 
	user->getRosterManager()->onBuddyAdded.connect(boost::bind(&NetworkPluginServer::handleUserBuddyAdded, this, user, _1));
 
	user->getRosterManager()->onBuddyRemoved.connect(boost::bind(&NetworkPluginServer::handleUserBuddyRemoved, this, user, _1));
 
}
 

	
 
void NetworkPluginServer::handleUserReadyToConnect(User *user) {
 
	UserInfo userInfo = user->getUserInfo();
 

	
 
	pbnetwork::Login login;
 
	login.set_user(user->getJID().toBare());
 
	login.set_legacyname(userInfo.uin);
 
	login.set_password(userInfo.password);
 

	
 
	std::string message;
 
	login.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LOGIN);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleUserPresenceChanged(User *user, Swift::Presence::ref presence) {
 
	if (presence->getShow() == Swift::StatusShow::None)
 
		return;
 

	
 
	UserInfo userInfo = user->getUserInfo();
 

	
 
	pbnetwork::Status status;
 
	status.set_username(user->getJID().toBare());
 

	
 
	bool isInvisible = presence->getPayload<Swift::InvisiblePayload>() != NULL;
 
	if (isInvisible) {
 
		LOG4CXX_INFO(logger, "This presence is invisible");
 
		status.set_status((pbnetwork::STATUS_INVISIBLE));
 
	}
 
	else {
 
		status.set_status((pbnetwork::StatusType) presence->getShow());
 
	}
 

	
 
	status.set_statusmessage(presence->getStatus());
 

	
 
	std::string message;
 
	status.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_STATUS_CHANGED);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleRoomJoined(User *user, const Swift::JID &who, const std::string &r, const std::string &nickname, const std::string &password) {
 
	UserInfo userInfo = user->getUserInfo();
 

	
 
	pbnetwork::Room room;
 
	room.set_username(user->getJID().toBare());
 
	room.set_nickname(nickname);
 
	room.set_room(r);
 
	room.set_password(password);
 

	
 
	std::string message;
 
	room.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_JOIN_ROOM);
 
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleRoomLeft(User *user, const std::string &r) {
 
	UserInfo userInfo = user->getUserInfo();
 

	
 
	pbnetwork::Room room;
 
	room.set_username(user->getJID().toBare());
 
	room.set_nickname("");
 
	room.set_room(r);
 
	room.set_password("");
 

	
 
	std::string message;
 
	room.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LEAVE_ROOM);
 
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleUserDestroyed(User *user) {
 
	m_waitingUsers.remove(user);
 
	UserInfo userInfo = user->getUserInfo();
 

	
 
	user->onReadyToConnect.disconnect(boost::bind(&NetworkPluginServer::handleUserReadyToConnect, this, user));
 
	user->onPresenceChanged.disconnect(boost::bind(&NetworkPluginServer::handleUserPresenceChanged, this, user, _1));
 
	user->onRoomJoined.disconnect(boost::bind(&NetworkPluginServer::handleRoomJoined, this, user, _1, _2, _3, _4));
 
	user->onRoomLeft.disconnect(boost::bind(&NetworkPluginServer::handleRoomLeft, this, user, _1));
 

	
 
	user->getRosterManager()->onBuddyAdded.disconnect(boost::bind(&NetworkPluginServer::handleUserBuddyAdded, this, user, _1));
 
	user->getRosterManager()->onBuddyRemoved.disconnect(boost::bind(&NetworkPluginServer::handleUserBuddyRemoved, this, user, _1));
 

	
 
	pbnetwork::Logout logout;
 
	logout.set_user(user->getJID().toBare());
 
	logout.set_legacyname(userInfo.uin);
 

	
 
	std::string message;
 
	logout.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LOGOUT);
 
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
	c->users.remove(user);
 

	
 
	// If backend should handle only one user, it must not accept another one before 
 
	// we kill it, so set up willDie to true
 
	if (c->users.size() == 0 && CONFIG_INT(m_config, "service.users_per_backend") == 1) {
 
		LOG4CXX_INFO(logger, "Backend " << c->id << " will die, because the last user disconnected");
 
		c->willDie = true;
 
	}
 
}
 

	
 
void NetworkPluginServer::handleMessageReceived(NetworkConversation *conv, boost::shared_ptr<Swift::Message> &msg) {
 
	conv->getConversationManager()->getUser()->updateLastActivity();
 
	boost::shared_ptr<Swift::ChatState> statePayload = msg->getPayload<Swift::ChatState>();
 
	if (statePayload) {
 
		pbnetwork::WrapperMessage_Type type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED;
 
		switch (statePayload->getChatState()) {
 
			case Swift::ChatState::Active:
 
				type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_STOPPED_TYPING;
 
				break;
 
			case Swift::ChatState::Composing:
 
				type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPING;
 
				break;
 
			case Swift::ChatState::Paused:
 
				type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPED;
 
				break;
 
			default:
 
				break;
 
		}
 
		if (type != pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED) {
 
			pbnetwork::Buddy buddy;
 
			buddy.set_username(conv->getConversationManager()->getUser()->getJID().toBare());
 
			buddy.set_buddyname(conv->getLegacyName());
 

	
 
			std::string message;
 
			buddy.SerializeToString(&message);
 

	
 
			WRAP(message, type);
 

	
 
			Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
 
			if (!c) {
 
				return;
 
			}
 
			send(c->connection, message);
 
		}
 
	}
 

	
 
	boost::shared_ptr<Swift::AttentionPayload> attentionPayload = msg->getPayload<Swift::AttentionPayload>();
 
	if (attentionPayload) {
 
		pbnetwork::ConversationMessage m;
 
		m.set_username(conv->getConversationManager()->getUser()->getJID().toBare());
 
		m.set_buddyname(conv->getLegacyName());
 
		m.set_message(msg->getBody());
 

	
 
		std::string message;
 
		m.SerializeToString(&message);
 

	
 
		WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_ATTENTION);
 

	
 
		Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
 
		send(c->connection, message);
 
		return;
 
	}
 

	
 
	if (!msg->getSubject().empty()) {
 
		pbnetwork::ConversationMessage m;
 
		m.set_username(conv->getConversationManager()->getUser()->getJID().toBare());
 
		m.set_buddyname(conv->getLegacyName());
 
		m.set_message(msg->getSubject());
 

	
 
		std::string message;
 
		m.SerializeToString(&message);
 

	
 
		WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_ROOM_SUBJECT_CHANGED);
 

	
 
		Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
 
		send(c->connection, message);
 
		return;
 
	}
 
	
 

	
 
	std::string xhtml;
 
	boost::shared_ptr<Swift::XHTMLIMPayload> xhtmlPayload = msg->getPayload<Swift::XHTMLIMPayload>();
 
	if (xhtmlPayload) {
 
		xhtml = xhtmlPayload->getBody();
 
	}
 

	
 
	// Send normal message
 
	if (!msg->getBody().empty() || !xhtml.empty()) {
 
		pbnetwork::ConversationMessage m;
 
		m.set_username(conv->getConversationManager()->getUser()->getJID().toBare());
 
		m.set_buddyname(conv->getLegacyName());
 
		m.set_message(msg->getBody());
 
		m.set_xhtml(xhtml);
 
		boost::shared_ptr<Swift::DeliveryReceiptRequest> receiptPayload = msg->getPayload<Swift::DeliveryReceiptRequest>();
 
		if (receiptPayload && !msg->getID().empty()) {
 
			m.set_id(msg->getID());
 
		}
 

	
 
		std::string message;
 
		m.SerializeToString(&message);
 

	
 
		WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE);
 

	
 
		Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
 
		if (!c) {
 
			return;
 
		}
 
		send(c->connection, message);
 
	}
 
}
 

	
 
void NetworkPluginServer::handleBuddyRemoved(Buddy *b) {
 
	User *user = b->getRosterManager()->getUser();
 

	
 
	pbnetwork::Buddy buddy;
 
	buddy.set_username(user->getJID().toBare());
 
	buddy.set_buddyname(b->getName());
 
	buddy.set_alias(b->getAlias());
 
	BOOST_FOREACH(const std::string &g, b->getGroups()) {
 
		buddy.add_group(g);
 
	}
 
	buddy.set_status(pbnetwork::STATUS_NONE);
 

	
 
	std::string message;
 
	buddy.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_REMOVED);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleBuddyUpdated(Buddy *b, const Swift::RosterItemPayload &item) {
 
	User *user = b->getRosterManager()->getUser();
 

	
 
	dynamic_cast<LocalBuddy *>(b)->setAlias(item.getName());
 
	dynamic_cast<LocalBuddy *>(b)->setGroups(item.getGroups());
 

	
 
	pbnetwork::Buddy buddy;
 
	buddy.set_username(user->getJID().toBare());
 
	buddy.set_buddyname(b->getName());
 
	buddy.set_alias(b->getAlias());
 
	BOOST_FOREACH(const std::string &g, b->getGroups()) {
 
		buddy.add_group(g);
 
	}
 
	buddy.set_status(pbnetwork::STATUS_NONE);
 

	
 
	std::string message;
 
	buddy.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleBuddyAdded(Buddy *buddy, const Swift::RosterItemPayload &item) {
 
	handleBuddyUpdated(buddy, item);
 
}
 

	
 
void NetworkPluginServer::handleUserBuddyAdded(User *user, Buddy *b) {
 
	pbnetwork::Buddy buddy;
 
	buddy.set_username(user->getJID().toBare());
 
	buddy.set_buddyname(b->getName());
 
	buddy.set_alias(b->getAlias());
 
	BOOST_FOREACH(const std::string &g, b->getGroups()) {
 
		buddy.add_group(g);
 
	}
 
	buddy.set_status(pbnetwork::STATUS_NONE);
 

	
 
	std::string message;
 
	buddy.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleUserBuddyRemoved(User *user, Buddy *b) {
 
	handleBuddyRemoved(b);
 
}
 

	
 
void NetworkPluginServer::handleBlockToggled(Buddy *b) {
 
	User *user = b->getRosterManager()->getUser();
 

	
 
	pbnetwork::Buddy buddy;
 
	buddy.set_username(user->getJID().toBare());
 
	buddy.set_buddyname(b->getName());
 
	buddy.set_alias(b->getAlias());
 
	BOOST_FOREACH(const std::string &g, b->getGroups()) {
 
		buddy.add_group(g);
 
	}
 
	buddy.set_status(pbnetwork::STATUS_NONE);
 
	buddy.set_blocked(!b->isBlocked());
 

	
 
	std::string message;
 
	buddy.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 

	
 
void NetworkPluginServer::handleVCardUpdated(User *user, boost::shared_ptr<Swift::VCard> v) {
 
	pbnetwork::VCard vcard;
 
	vcard.set_username(user->getJID().toBare());
 
	vcard.set_buddyname("");
 
	vcard.set_id(0);
 
	vcard.set_photo(&v->getPhoto()[0], v->getPhoto().size());
 
	vcard.set_nickname(v->getNickname());
 

	
 
	std::string message;
 
	vcard.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_VCARD);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleVCardRequired(User *user, const std::string &name, unsigned int id) {
 
	pbnetwork::VCard vcard;
 
	vcard.set_username(user->getJID().toBare());
 
	vcard.set_buddyname(name);
 
	vcard.set_id(id);
 

	
 
	std::string message;
 
	vcard.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_VCARD);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleFTAccepted(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID) {
 
	pbnetwork::File f;
 
	f.set_username(user->getJID().toBare());
 
	f.set_buddyname(buddyName);
 
	f.set_filename(fileName);
 
	f.set_size(size);
 
	f.set_ftid(ftID);
 

	
 
	std::string message;
 
	f.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_START);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleFTRejected(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size) {
 
	pbnetwork::File f;
 
	f.set_username(user->getJID().toBare());
 
	f.set_buddyname(buddyName);
 
	f.set_filename(fileName);
 
	f.set_size(size);
 
	f.set_ftid(0);
 

	
 
	std::string message;
 
	f.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_FINISH);
src/presenceoracle.cpp
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/presenceoracle.h"
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Elements/MUCPayload.h"
 

	
 
#include <boost/bind.hpp>
 

	
 
using namespace Swift;
 

	
 
namespace Transport {
 

	
 
PresenceOracle::PresenceOracle(StanzaChannel* stanzaChannel) {
 
	stanzaChannel_ = stanzaChannel;
 
	stanzaChannel_->onPresenceReceived.connect(boost::bind(&PresenceOracle::handleIncomingPresence, this, _1));
 
	stanzaChannel_->onAvailableChanged.connect(boost::bind(&PresenceOracle::handleStanzaChannelAvailableChanged, this, _1));
 
}
 

	
 
PresenceOracle::~PresenceOracle() {
 
	stanzaChannel_->onPresenceReceived.disconnect(boost::bind(&PresenceOracle::handleIncomingPresence, this, _1));
 
	stanzaChannel_->onAvailableChanged.disconnect(boost::bind(&PresenceOracle::handleStanzaChannelAvailableChanged, this, _1));
 
}
 

	
 
void PresenceOracle::handleStanzaChannelAvailableChanged(bool available) {
 
	if (available) {
 
		entries_.clear();
 
	}
 
}
 

	
 
void PresenceOracle::clearPresences(const Swift::JID& bareJID) {
 
	std::map<JID, boost::shared_ptr<Presence> > jidMap = entries_[bareJID];
 
	jidMap.clear();
 
	entries_[bareJID] = jidMap;
 
}
 

	
 
void PresenceOracle::handleIncomingPresence(Presence::ref presence) {
 
	// ignore presences for some contact, we're checking only presences for the transport itself here.
 
	bool isMUC = presence->getPayload<MUCPayload>() != NULL || *presence->getTo().getNode().c_str() == '#';
 
	// filter out login/logout presence spam
 
	if (!presence->getTo().getNode().empty() && isMUC == false)
 
	if (!presence->getTo().getNode().empty())
 
		return;
 

	
 
	JID bareJID(presence->getFrom().toBare());
 
	if (presence->getType() == Presence::Subscribe || presence->getType() == Presence::Subscribed) {
 
	}
 
	else {
 
		Presence::ref passedPresence = presence;
 
		if (!isMUC) {
 
		if (presence->getType() == Presence::Unsubscribe || presence->getType() == Presence::Unsubscribed) {
 
			/* 3921bis says that we don't follow up with an unavailable, so simulate this ourselves */
 
			passedPresence = Presence::ref(new Presence());
 
			passedPresence->setType(Presence::Unavailable);
 
			passedPresence->setFrom(bareJID);
 
			passedPresence->setStatus(presence->getStatus());
 
		}
 
		std::map<JID, boost::shared_ptr<Presence> > jidMap = entries_[bareJID];
 
		if (passedPresence->getFrom().isBare() && presence->getType() == Presence::Unavailable) {
 
			/* Have a bare-JID only presence of offline */
 
			jidMap.clear();
 
		} else if (passedPresence->getType() == Presence::Available) {
 
			/* Don't have a bare-JID only offline presence once there are available presences */
 
			jidMap.erase(bareJID);
 
		}
 
		if (passedPresence->getType() == Presence::Unavailable && jidMap.size() > 1) {
 
			jidMap.erase(passedPresence->getFrom());
 
		} else {
 
			jidMap[passedPresence->getFrom()] = passedPresence;
 
		}
 
		entries_[bareJID] = jidMap;
 
		}
 
		onPresenceChange(passedPresence);
 
	}
 
}
 

	
 
Presence::ref PresenceOracle::getLastPresence(const JID& jid) const {
 
	PresencesMap::const_iterator i = entries_.find(jid.toBare());
 
	if (i == entries_.end()) {
 
		return Presence::ref();
 
	}
 
	PresenceMap presenceMap = i->second;
 
	PresenceMap::const_iterator j = presenceMap.find(jid);
 
	if (j != presenceMap.end()) {
 
		return j->second;
 
	}
 
	else {
 
		return Presence::ref();
 
	}
 
}
 

	
 
std::vector<Presence::ref> PresenceOracle::getAllPresence(const JID& bareJID) const {
 
	std::vector<Presence::ref> results;
 
	PresencesMap::const_iterator i = entries_.find(bareJID);
 
	if (i == entries_.end()) {
 
		return results;
 
	}
 
	PresenceMap presenceMap = i->second;
 
	PresenceMap::const_iterator j = presenceMap.begin();
 
	for (; j != presenceMap.end(); ++j) {
 
		Presence::ref current = j->second;
 
		results.push_back(current);
 
	}
 
	return results;
 
}
 

	
 
Presence::ref PresenceOracle::getHighestPriorityPresence(const JID& bareJID) const {
 
	PresencesMap::const_iterator i = entries_.find(bareJID);
 
	if (i == entries_.end()) {
 
		return Presence::ref();
 
	}
 
	PresenceMap presenceMap = i->second;
 
	PresenceMap::const_iterator j = presenceMap.begin();
 
	Presence::ref highest;
 
	for (; j != presenceMap.end(); ++j) {
 
		Presence::ref current = j->second;
 
		if (!highest
 
				|| current->getPriority() > highest->getPriority()
 
				|| (current->getPriority() == highest->getPriority()
 
						&& StatusShow::typeToAvailabilityOrdering(current->getShow()) > StatusShow::typeToAvailabilityOrdering(highest->getShow()))) {
 
			highest = current;
 
		}
 

	
 
	}
 
	return highest;
 
}
 

	
 
}
src/rostermanager.cpp
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/rostermanager.h"
 
#include "transport/rosterstorage.h"
 
#include "transport/storagebackend.h"
 
#include "transport/buddy.h"
 
#include "transport/usermanager.h"
 
#include "transport/buddy.h"
 
#include "transport/user.h"
 
#include "transport/logging.h"
 
#include "Swiften/Roster/SetRosterRequest.h"
 
#include "Swiften/Elements/RosterPayload.h"
 
#include "Swiften/Elements/RosterItemPayload.h"
 
#include "Swiften/Elements/RosterItemExchangePayload.h"
 
#include "Swiften/Elements/Nickname.h"
 
#include "Swiften/Queries/IQRouter.h"
 
#include <boost/foreach.hpp>
 
#include <boost/make_shared.hpp>
 

	
 
#include <map>
 
#include <iterator>
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "RosterManager");
 

	
 
RosterManager::RosterManager(User *user, Component *component){
 
	m_rosterStorage = NULL;
 
	m_user = user;
 
	m_component = component;
 
	m_setBuddyTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(1000);
 
	m_RIETimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(5000);
 
	m_RIETimer->onTick.connect(boost::bind(&RosterManager::sendRIE, this));
 

	
 
	m_supportRemoteRoster = false;
 

	
 
	if (!m_component->inServerMode()) {
 
		m_remoteRosterRequest = AddressedRosterRequest::ref(new AddressedRosterRequest(m_component->getIQRouter(), m_user->getJID().toBare()));
 
		m_remoteRosterRequest->onResponse.connect(boost::bind(&RosterManager::handleRemoteRosterResponse, this, _1, _2));
 
		m_remoteRosterRequest->send();
 
	}
 
}
 

	
 
RosterManager::~RosterManager() {
 
	m_setBuddyTimer->stop();
 
	m_RIETimer->stop();
 
	if (m_rosterStorage) {
 
		m_rosterStorage->storeBuddies();
 
	}
 

	
 
	sendUnavailablePresences(m_user->getJID().toBare());
 

	
 
	if (m_remoteRosterRequest) {
 
		m_remoteRosterRequest->onResponse.disconnect_all_slots();
 
		m_component->getIQRouter()->removeHandler(m_remoteRosterRequest);
 
	}
 

	
 
	for (std::map<std::string, Buddy *, std::less<std::string>, boost::pool_allocator< std::pair<std::string, Buddy *> > >::iterator it = m_buddies.begin(); it != m_buddies.end(); it++) {
 
		Buddy *buddy = (*it).second;
 
		if (!buddy) {
 
			continue;
 
		}
 
		delete buddy;
 
	}
 

	
 
	if (m_requests.size() != 0) {
 
		LOG4CXX_INFO(logger, m_user->getJID().toString() <<  ": Removing " << m_requests.size() << " unresponded IQs");
 
		BOOST_FOREACH(Swift::SetRosterRequest::ref request, m_requests) {
 
			request->onResponse.disconnect_all_slots();
 
			m_component->getIQRouter()->removeHandler(request);
 
		}
 
		m_requests.clear();
 
	}
 

	
 
	boost::singleton_pool<boost::pool_allocator_tag, sizeof(unsigned int)>::release_memory();
 

	
 
	if (m_rosterStorage)
 
		delete m_rosterStorage;
 
}
 

	
 
void RosterManager::setBuddy(Buddy *buddy) {
 
// 	m_setBuddyTimer->onTick.connect(boost::bind(&RosterManager::setBuddyCallback, this, buddy));
 
// 	m_setBuddyTimer->start();
 
	setBuddyCallback(buddy);
 
}
 

	
 
void RosterManager::removeBuddy(const std::string &name) {
 
	Buddy *buddy = getBuddy(name);
 
	if (!buddy) {
 
		LOG4CXX_WARN(logger, m_user->getJID().toString() << ": Tried to remove unknown buddy " << name);
 
		return;
 
	}
 

	
 
	if (m_component->inServerMode() || m_remoteRosterRequest) {
 
		sendBuddyRosterRemove(buddy);
 
	}
 
	else {
 
		sendBuddyUnsubscribePresence(buddy);
 
	}
 

	
 
	if (m_rosterStorage)
 
		m_rosterStorage->removeBuddy(buddy);
 

	
 
	unsetBuddy(buddy);
 
	delete buddy;
 
}
 

	
 
void RosterManager::sendBuddyRosterRemove(Buddy *buddy) {
 
	Swift::RosterPayload::ref p = Swift::RosterPayload::ref(new Swift::RosterPayload());
 
	Swift::RosterItemPayload item;
 
	item.setJID(buddy->getJID().toBare());
 
	item.setSubscription(Swift::RosterItemPayload::Remove);
 

	
 
	p->addItem(item);
 

	
 
	// In server mode we have to send pushes to all resources, but in gateway-mode we send it only to bare JID
 
	if (m_component->inServerMode()) {
 
		std::vector<Swift::Presence::ref> presences = m_component->getPresenceOracle()->getAllPresence(m_user->getJID().toBare());
 
		BOOST_FOREACH(Swift::Presence::ref presence, presences) {
 
			Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(p, presence->getFrom(), m_component->getIQRouter());
 
			request->send();
 
		}
 
	}
 
	else {
 
		Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(p, m_user->getJID().toBare(), m_component->getIQRouter());
 
		request->send();
 
	}
 
}
 

	
 
void RosterManager::sendBuddyRosterPush(Buddy *buddy) {
 
	// user can't receive anything in server mode if he's not logged in.
 
	// He will ask for roster later (handled in rosterreponsder.cpp)
 
	if (m_component->inServerMode() && (!m_user->isConnected() || m_user->shouldCacheMessages()))
 
		return;
 

	
 
	Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload());
 
	Swift::RosterItemPayload item;
 
	item.setJID(buddy->getJID().toBare());
 
	if (buddy->getAlias().empty()) {
 
		item.setName(buddy->getJID().toBare().toString());
 
	}
 
	else {
 
	item.setName(buddy->getAlias());
 
	}
 
	item.setGroups(buddy->getGroups());
 
	item.setSubscription(Swift::RosterItemPayload::Both);
 

	
 
	payload->addItem(item);
 

	
 
	// In server mode we have to send pushes to all resources, but in gateway-mode we send it only to bare JID
 
	if (m_component->inServerMode()) {
 
		std::vector<Swift::Presence::ref> presences = m_component->getPresenceOracle()->getAllPresence(m_user->getJID().toBare());
 
		BOOST_FOREACH(Swift::Presence::ref presence, presences) {
 
			Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, presence->getFrom(), m_component->getIQRouter());
 
			request->onResponse.connect(boost::bind(&RosterManager::handleBuddyRosterPushResponse, this, _1, request, buddy->getName()));
 
			request->send();
 
			m_requests.push_back(request);
 
		}
 
	}
 
	else {
 
		Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, m_user->getJID().toBare(), m_component->getIQRouter());
 
		request->onResponse.connect(boost::bind(&RosterManager::handleBuddyRosterPushResponse, this, _1, request, buddy->getName()));
 
		request->send();
 
		m_requests.push_back(request);
 
	}
 

	
 
	if (buddy->getSubscription() != Buddy::Both) {
 
		buddy->setSubscription(Buddy::Both);
 
		storeBuddy(buddy);
 
	}
 
}
 

	
 
void RosterManager::sendBuddyUnsubscribePresence(Buddy *buddy) {
 
	Swift::Presence::ref response = Swift::Presence::create();
 
	response->setTo(m_user->getJID());
 
	response->setFrom(buddy->getJID());
 
	response->setType(Swift::Presence::Unsubscribe);
 
	m_component->getStanzaChannel()->sendPresence(response);
 

	
 
	response = Swift::Presence::create();
 
	response->setTo(m_user->getJID());
 
	response->setFrom(buddy->getJID());
 
	response->setType(Swift::Presence::Unsubscribed);
 
	m_component->getStanzaChannel()->sendPresence(response);
 
}
 

	
 
void RosterManager::sendBuddySubscribePresence(Buddy *buddy) {
 
	Swift::Presence::ref response = Swift::Presence::create();
 
	response->setTo(m_user->getJID());
 
	response->setFrom(buddy->getJID());
 
	response->setType(Swift::Presence::Subscribe);
 
	if (!buddy->getAlias().empty()) {
 
		response->addPayload(boost::make_shared<Swift::Nickname>(buddy->getAlias()));
 
	}
 
	m_component->getStanzaChannel()->sendPresence(response);
 
}
 

	
 
void RosterManager::handleBuddyChanged(Buddy *buddy) {
 
}
 

	
 
void RosterManager::setBuddyCallback(Buddy *buddy) {
 
	LOG4CXX_INFO(logger, "Associating buddy " << buddy->getName() << " with " << m_user->getJID().toString());
 
	m_buddies[buddy->getName()] = buddy;
 
	onBuddySet(buddy);
 

	
 
	// In server mode the only way is to send jabber:iq:roster push.
 
	// In component mode we send RIE or Subscribe presences, based on features.
 
	if (m_component->inServerMode()) {
 
		sendBuddyRosterPush(buddy);
 
	}
 
	else {
 
		if (buddy->getSubscription() == Buddy::Both) {
 
			LOG4CXX_INFO(logger, m_user->getJID().toString() << ": Not forwarding this buddy, because subscription=both");
 
			return;
 
		}
 

	
 
		if (m_supportRemoteRoster) {
 
			sendBuddyRosterPush(buddy);
 
		}
 
		else {
 
			m_RIETimer->start();
 
		}
 
	}
 

	
 
	if (m_rosterStorage)
 
		m_rosterStorage->storeBuddy(buddy);
 
}
 

	
 
void RosterManager::unsetBuddy(Buddy *buddy) {
 
	m_buddies.erase(buddy->getName());
 
	if (m_rosterStorage)
 
		m_rosterStorage->removeBuddyFromQueue(buddy);
 
	onBuddyUnset(buddy);
 
}
 

	
 
void RosterManager::storeBuddy(Buddy *buddy) {
 
	if (m_rosterStorage) {
 
		m_rosterStorage->storeBuddy(buddy);
 
	}
 
}
 

	
 
void RosterManager::handleBuddyRosterPushResponse(Swift::ErrorPayload::ref error, Swift::SetRosterRequest::ref request, const std::string &key) {
 
	LOG4CXX_INFO(logger, "handleBuddyRosterPushResponse called for buddy " << key);
 
	if (m_buddies[key] != NULL) {
 
		Swift::Presence::ref presence = m_buddies[key]->generatePresenceStanza(255);
 
		if (presence && presence->getType() == Swift::Presence::Available) {
 
			m_component->getStanzaChannel()->sendPresence(presence);
 
		}
 
	}
 
	else {
 
		LOG4CXX_WARN(logger, "handleBuddyRosterPushResponse called for unknown buddy " << key);
 
	}
 

	
 
	m_requests.remove(request);
 
	request->onResponse.disconnect_all_slots();
 
}
 

	
 
void RosterManager::handleRemoteRosterResponse(boost::shared_ptr<Swift::RosterPayload> payload, Swift::ErrorPayload::ref error) {
 
	m_remoteRosterRequest.reset();
 
	if (error) {
 
		m_supportRemoteRoster = false;
 
		LOG4CXX_INFO(logger, m_user->getJID().toString() << ": This server does not support remote roster protoXEP");
 
		return;
 
	}
 

	
 
	LOG4CXX_INFO(logger, m_user->getJID().toString() << ": This server supports remote roster protoXEP");
 
	m_supportRemoteRoster = true;
 

	
 
	//If we receive empty RosterPayload on login (not register) initiate full RosterPush
 
	if(!m_buddies.empty() && payload->getItems().empty()){
 
			LOG4CXX_INFO(logger, "Received empty Roster upon login. Pushing full Roster.");
 
			for(std::map<std::string, Buddy *, std::less<std::string>, boost::pool_allocator< std::pair<std::string, Buddy *> > >::const_iterator c_it = m_buddies.begin();
 
					c_it != m_buddies.end(); c_it++) {
 
				sendBuddyRosterPush(c_it->second);
 
			}
 
	}
 
	return;
 

	
 
	BOOST_FOREACH(const Swift::RosterItemPayload &item, payload->getItems()) {
 
		std::string legacyName = Buddy::JIDToLegacyName(item.getJID());
 
		if (m_buddies.find(legacyName) != m_buddies.end()) {
 
			continue;
 
		}
 

	
 
		if (legacyName.empty()) {
 
			continue;
 
		}
 

	
 
		BuddyInfo buddyInfo;
 
		buddyInfo.id = -1;
 
		buddyInfo.alias = item.getName();
 
		buddyInfo.legacyName = legacyName;
 
		buddyInfo.subscription = "both";
 
		buddyInfo.flags = Buddy::buddyFlagsFromJID(item.getJID());
 
		buddyInfo.groups = item.getGroups();
 

	
 
		Buddy *buddy = m_component->getFactory()->createBuddy(this, buddyInfo);
 
		if (buddy) {
 
			setBuddy(buddy);
 
		}
 
	}
 
}
 

	
 
Buddy *RosterManager::getBuddy(const std::string &name) {
 
	return m_buddies[name];
 
}
 

	
 
void RosterManager::sendRIE() {
 
	m_RIETimer->stop();
 

	
 
	// Check the feature, because proper resource could logout during RIETimer.
 
	std::vector<Swift::JID> jidWithRIE = m_user->getJIDWithFeature("http://jabber.org/protocol/rosterx");
 

	
 
	// fallback to normal subscribe
 
	if (jidWithRIE.empty()) {
 
		for (std::map<std::string, Buddy *, std::less<std::string>, boost::pool_allocator< std::pair<std::string, Buddy *> > >::iterator it = m_buddies.begin(); it != m_buddies.end(); it++) {
 
			Buddy *buddy = (*it).second;
 
			if (!buddy) {
 
				continue;
 
			}
 
			sendBuddySubscribePresence(buddy);
 
		}
 
		return;
 
	}
 

	
 
	Swift::RosterItemExchangePayload::ref payload = Swift::RosterItemExchangePayload::ref(new Swift::RosterItemExchangePayload());
 
	for (std::map<std::string, Buddy *, std::less<std::string>, boost::pool_allocator< std::pair<std::string, Buddy *> > >::iterator it = m_buddies.begin(); it != m_buddies.end(); it++) {
 
		Buddy *buddy = (*it).second;
 
		if (!buddy) {
 
			continue;
 
		}
 
		Swift::RosterItemExchangePayload::Item item;
 
		item.setJID(buddy->getJID().toBare());
 
		item.setName(buddy->getAlias());
 
		item.setAction(Swift::RosterItemExchangePayload::Item::Add);
 
		item.setGroups(buddy->getGroups());
 

	
 
		payload->addItem(item);
 
	}
 

	
 
	BOOST_FOREACH(Swift::JID &jid, jidWithRIE) {
 
		LOG4CXX_INFO(logger, "Sending RIE stanza to " << jid.toString());
 
		boost::shared_ptr<Swift::GenericRequest<Swift::RosterItemExchangePayload> > request(new Swift::GenericRequest<Swift::RosterItemExchangePayload>(Swift::IQ::Set, jid, payload, m_component->getIQRouter()));
 
		request->send();
 
	}
 
}
 

	
 
void RosterManager::handleSubscription(Swift::Presence::ref presence) {
 
	std::string legacyName = Buddy::JIDToLegacyName(presence->getTo());
 
	if (legacyName.empty()) {
 
		return;
 
	}
 
	
 
	// For server mode the subscription changes are handler in rosterresponder.cpp
 
	// using roster pushes.
 
	if (m_component->inServerMode()) {
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setTo(presence->getFrom().toBare());
 
		response->setFrom(presence->getTo().toBare());
 
		Buddy *buddy = getBuddy(Buddy::JIDToLegacyName(presence->getTo()));
 
		if (buddy) {
 
			LOG4CXX_INFO(logger, m_user->getJID().toString() << ": Subscription received and buddy " << Buddy::JIDToLegacyName(presence->getTo()) << " is already there => answering");
 
			switch (presence->getType()) {
 
				case Swift::Presence::Subscribe:
 
					onBuddyAdded(buddy);
 
					response->setType(Swift::Presence::Subscribed);
 
					break;
 
				case Swift::Presence::Unsubscribe:
 
					onBuddyRemoved(buddy);
 
					removeBuddy(buddy->getName());
 
					buddy = NULL;
 
					response->setType(Swift::Presence::Unsubscribed);
 
@@ -397,192 +404,193 @@ void RosterManager::handleSubscription(Swift::Presence::ref presence) {
 
					buddyInfo.subscription = "both";
 
					buddyInfo.flags = Buddy::buddyFlagsFromJID(presence->getTo());
 
					LOG4CXX_INFO(logger, m_user->getJID().toString() << ": Subscription received for new buddy " << buddyInfo.legacyName << " => adding to legacy network");
 

	
 
					buddy = m_component->getFactory()->createBuddy(this, buddyInfo);
 
					setBuddy(buddy);
 
					onBuddyAdded(buddy);
 
					response->setType(Swift::Presence::Subscribed);
 
					break;
 
				case Swift::Presence::Subscribed:
 
// 					onBuddyAdded(buddy);
 
					return;
 
				// buddy is not 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());
 

	
 
					buddy = m_component->getFactory()->createBuddy(this, buddyInfo);
 
					onBuddyRemoved(buddy);
 
					delete buddy;
 
					response->setType(Swift::Presence::Unsubscribed);
 
					break;
 
				default:
 
					return;
 
			}
 
			m_component->getStanzaChannel()->sendPresence(response);
 
		}
 
	}
 
	else {
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		Swift::Presence::ref currentPresence;
 
		response->setTo(presence->getFrom().toBare());
 
		response->setFrom(presence->getTo().toBare());
 

	
 
		Buddy *buddy = getBuddy(Buddy::JIDToLegacyName(presence->getTo()));
 
		if (buddy) {
 
			switch (presence->getType()) {
 
				// buddy is already there, so nothing to do, just answer
 
				case Swift::Presence::Subscribe:
 
					onBuddyAdded(buddy);
 
					response->setType(Swift::Presence::Subscribed);
 
					currentPresence = buddy->generatePresenceStanza(255);
 
					if (currentPresence) {
 
						currentPresence->setTo(presence->getFrom());
 
						m_component->getStanzaChannel()->sendPresence(currentPresence);
 
					}
 
					if (buddy->getSubscription() != Buddy::Both) {
 
						buddy->setSubscription(Buddy::Both);
 
						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:
 
					response->setType(Swift::Presence::Unsubscribe);
 
					// We set both here, because this Unsubscribed can be response to
 
					// subscribe presence and we don't want that unsubscribe presence
 
					// to be send later again
 
					if (buddy->getSubscription() != Buddy::Both) {
 
						buddy->setSubscription(Buddy::Both);
 
						storeBuddy(buddy);
 
					}
 
					break;
 
				case Swift::Presence::Subscribed:
 
					if (buddy->getSubscription() != Buddy::Both) {
 
						buddy->setSubscription(Buddy::Both);
 
						storeBuddy(buddy);
 
					}
 
					return;
 
				default:
 
					return;
 
			}
 
		}
 
		else {
 
			BuddyInfo buddyInfo;
 
			switch (presence->getType()) {
 
				// buddy is not in roster, so add him
 
				case Swift::Presence::Subscribe:
 
					buddyInfo.id = -1;
 
					buddyInfo.alias = "";
 
					buddyInfo.legacyName = Buddy::JIDToLegacyName(presence->getTo());
 
					buddyInfo.subscription = "both";
 
					buddyInfo.flags = Buddy::buddyFlagsFromJID(presence->getTo());
 

	
 
					buddy = m_component->getFactory()->createBuddy(this, buddyInfo);
 
					setBuddy(buddy);
 
					onBuddyAdded(buddy);
 
					LOG4CXX_INFO(logger, m_user->getJID().toString() << ": Subscription received for new buddy " << buddyInfo.legacyName << " => adding to legacy network");
 
					response->setType(Swift::Presence::Subscribed);
 
					break;
 
				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);
 
					
 
					buddy = m_component->getFactory()->createBuddy(this, buddyInfo);
 
					onBuddyRemoved(buddy);
 
					delete buddy;
 
					buddy = NULL;
 
					break;
 
				// just send response
 
				case Swift::Presence::Unsubscribed:
 
					response->setType(Swift::Presence::Unsubscribe);
 
					break;
 
				// just send response
 
				default:
 
					return;
 
			}
 
		}
 

	
 
		m_component->getStanzaChannel()->sendPresence(response);
 

	
 
		// We have to act as buddy and send its subscribe/unsubscribe just to be sure...
 
		switch (response->getType()) {
 
			case Swift::Presence::Unsubscribed:
 
				response->setType(Swift::Presence::Unsubscribe);
 
				m_component->getStanzaChannel()->sendPresence(response);
 
				break;
 
			case Swift::Presence::Subscribed:
 
				response->setType(Swift::Presence::Subscribe);
 
				m_component->getStanzaChannel()->sendPresence(response);
 
				break;
 
			default:
 
				break;
 
		}
 
	}
 
}
 

	
 
void RosterManager::setStorageBackend(StorageBackend *storageBackend) {
 
	if (m_rosterStorage || !storageBackend) {
 
		return;
 
	}
 
	RosterStorage *storage = new RosterStorage(m_user, storageBackend);
 

	
 
	std::list<BuddyInfo> roster;
 
	storageBackend->getBuddies(m_user->getUserInfo().id, roster);
 

	
 
	for (std::list<BuddyInfo>::const_iterator it = roster.begin(); it != roster.end(); it++) {
 
		Buddy *buddy = m_component->getFactory()->createBuddy(this, *it);
 
		if (buddy) {
 
			LOG4CXX_INFO(logger, m_user->getJID().toString() << ": Adding cached buddy " << buddy->getName() << " fom database");
 
			m_buddies[buddy->getName()] = buddy;
 
			onBuddySet(buddy);
 
		}
 
	}
 

	
 
	m_rosterStorage = storage;
 
}
 

	
 
Swift::RosterPayload::ref RosterManager::generateRosterPayload() {
 
	Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload());
 

	
 
	for (std::map<std::string, Buddy *, std::less<std::string>, boost::pool_allocator< std::pair<std::string, Buddy *> > >::iterator it = m_buddies.begin(); it != m_buddies.end(); it++) {
 
		Buddy *buddy = (*it).second;
 
		if (!buddy) {
 
			continue;
 
		}
 
		Swift::RosterItemPayload item;
 
		item.setJID(buddy->getJID().toBare());
 
		item.setName(buddy->getAlias());
 
		item.setGroups(buddy->getGroups());
 
		item.setSubscription(Swift::RosterItemPayload::Both);
 
		payload->addItem(item);
 
	}
 
	return payload;
 
}
 

	
 
void RosterManager::sendCurrentPresences(const Swift::JID &to) {
 
	for (std::map<std::string, Buddy *, std::less<std::string>, boost::pool_allocator< std::pair<std::string, Buddy *> > >::iterator it = m_buddies.begin(); it != m_buddies.end(); it++) {
 
		Buddy *buddy = (*it).second;
 
		if (!buddy) {
 
			continue;
 
		}
 
		Swift::Presence::ref presence = buddy->generatePresenceStanza(255);
 
		if (presence && presence->getType() == Swift::Presence::Available) {
 
			presence->setTo(to);
 
			m_component->getStanzaChannel()->sendPresence(presence);
 
		}
 
	}
 
}
src/rosterresponder.cpp
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/rosterresponder.h"
 

	
 
#include <iostream>
 
#include <boost/bind.hpp>
 
#include "Swiften/Queries/IQRouter.h"
 
#include "Swiften/Swiften.h"
 
#include "transport/user.h"
 
#include "transport/usermanager.h"
 
#include "transport/rostermanager.h"
 
#include "transport/buddy.h"
 
#include "transport/logging.h"
 

	
 
using namespace Swift;
 
using namespace boost;
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "RosterResponder");
 

	
 
RosterResponder::RosterResponder(Swift::IQRouter *router, UserManager *userManager) : Swift::Responder<RosterPayload>(router) {
 
	m_userManager = userManager;
 
}
 

	
 
RosterResponder::~RosterResponder() {
 
}
 

	
 
bool RosterResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::RosterPayload> payload) {
 
	// Get means we're in server mode and user wants to fetch his roster.
 
	// For now we send empty reponse, but TODO: Get buddies from database and send proper stored roster.
 
	User *user = m_userManager->getUser(from.toBare().toString());
 
	if (!user) {
 
		sendResponse(from, id, boost::shared_ptr<RosterPayload>(new RosterPayload()));
 
		LOG4CXX_WARN(logger, from.toBare().toString() << ": User is not logged in");
 
		return true;
 
	}
 
	sendResponse(from, id, user->getRosterManager()->generateRosterPayload());
 
	user->getRosterManager()->sendCurrentPresences(from);
 
	return true;
 
}
 

	
 
bool RosterResponder::handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::RosterPayload> payload) {
 
	sendResponse(from, id, boost::shared_ptr<RosterPayload>(new RosterPayload()));
 

	
 
	User *user = m_userManager->getUser(from.toBare().toString());
 
	if (!user) {
 
		LOG4CXX_WARN(logger, from.toBare().toString() << ": User is not logged in");
 
		return true;
 
	}
 

	
 
	if (payload->getItems().size() == 0) {
 
		LOG4CXX_WARN(logger, from.toBare().toString() << ": Roster push with no item");
 
		return true;
 
	}
 

	
 
	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) {
 
			LOG4CXX_INFO(logger, from.toBare().toString() << ": Removing buddy " << buddy->getName());
 
			onBuddyRemoved(buddy);
 

	
 
			// send roster push here
 
			Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, user->getJID().toBare(), user->getComponent()->getIQRouter());
 
			request->send();
 
		}
 
		else {
 
			LOG4CXX_INFO(logger, from.toBare().toString() << ": Updating buddy " << buddy->getName());
 
			onBuddyUpdated(buddy, item);
 
		}
 
	}
 
	else if (item.getSubscription() != Swift::RosterItemPayload::Remove) {
 
		// Roster push for this new buddy is sent by RosterManager
 
		BuddyInfo buddyInfo;
 
		buddyInfo.id = -1;
 
		buddyInfo.alias = item.getName();
 
		buddyInfo.legacyName = Buddy::JIDToLegacyName(item.getJID());
 
		buddyInfo.subscription = "both";
 
		buddyInfo.flags = Buddy::buddyFlagsFromJID(item.getJID());
 
		LOG4CXX_INFO(logger, from.toBare().toString() << ": Adding buddy " << buddyInfo.legacyName);
 

	
 
		buddy = user->getComponent()->getFactory()->createBuddy(user->getRosterManager(), buddyInfo);
 
		user->getRosterManager()->setBuddy(buddy);
 
		onBuddyAdded(buddy, item);
 
	}
 

	
 
	return true;
 
}
 

	
 
}
src/rosterstorage.cpp
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/rosterstorage.h"
 
#include "transport/buddy.h"
 
#include "transport/user.h"
 
#include "transport/storagebackend.h"
 
#include "transport/logging.h"
 

	
 
#include "Swiften/Network/NetworkFactories.h"
 

	
 
DEFINE_LOGGER(logger, "RosterStorage");
 

	
 
namespace Transport {
 

	
 
// static void save_settings(gpointer k, gpointer v, gpointer data) {
 
// 	PurpleValue *value = (PurpleValue *) v;
 
// 	std::string key((char *) k);
 
// 	SaveData *s = (SaveData *) data;
 
// 	AbstractUser *user = s->user;
 
// 	long id = s->id;
 
// 	if (purple_value_get_type(value) == PURPLE_TYPE_BOOLEAN) {
 
// 		if (purple_value_get_boolean(value))
 
// 			Transport::instance()->sql()->addBuddySetting(user->storageId(), id, key, "1", purple_value_get_type(value));
 
// 		else
 
// 			Transport::instance()->sql()->addBuddySetting(user->storageId(), id, key, "0", purple_value_get_type(value));
 
// 	}
 
// 	else if (purple_value_get_type(value) == PURPLE_TYPE_STRING) {
 
// 		const char *str = purple_value_get_string(value);
 
// 		Transport::instance()->sql()->addBuddySetting(user->storageId(), id, key, str ? str : "", purple_value_get_type(value));
 
// 	}
 
// }
 
// 
 
// static gboolean storeAbstractSpectrumBuddy(gpointer key, gpointer v, gpointer data) {
 
// 	AbstractUser *user = (AbstractUser *) data;
 
// 	AbstractSpectrumBuddy *s_buddy = (AbstractSpectrumBuddy *) v;
 
// 	if (s_buddy->getFlags() & SPECTRUM_BUDDY_IGNORE)
 
// 		return TRUE;
 
// 	
 
// 	// save PurpleBuddy
 
// 	std::string alias = s_buddy->getAlias();
 
// 	std::string name = s_buddy->getName();
 
// 	long id = s_buddy->getId();
 
// 
 
// 	// Buddy is not in DB
 
// 	if (id != -1) {
 
// 		Transport::instance()->sql()->addBuddy(user->storageId(), name, s_buddy->getSubscription(), s_buddy->getGroup(), alias, s_buddy->getFlags());
 
// 	}
 
// 	else {
 
// 		id = Transport::instance()->sql()->addBuddy(user->storageId(), name, s_buddy->getSubscription(), s_buddy->getGroup(), alias, s_buddy->getFlags());
 
// 		s_buddy->setId(id);
 
// 	}
 
// 	Log("buddyListSaveNode", id << " " << name << " " << alias << " " << s_buddy->getSubscription());
 
// 	if (s_buddy->getBuddy() && id != -1) {
 
// 		PurpleBuddy *buddy = s_buddy->getBuddy();
 
// 		SaveData *s = new SaveData;
 
// 		s->user = user;
 
// 		s->id = id;
 
// 		g_hash_table_foreach(buddy->node.settings, save_settings, s);
 
// 		delete s;
 
// 	}
 
// 	return TRUE;
 
// }
 

	
 
RosterStorage::RosterStorage(User *user, StorageBackend *storageBackend) {
 
	m_user = user;
 
	m_storageBackend = storageBackend;
 
	m_storageTimer = m_user->getComponent()->getNetworkFactories()->getTimerFactory()->createTimer(5000);
 
	m_storageTimer->onTick.connect(boost::bind(&RosterStorage::storeBuddies, this));
 
}
 

	
 
RosterStorage::~RosterStorage() {
 
	m_storageTimer->stop();
 
}
 

	
 
void RosterStorage::removeBuddy(Buddy *buddy) {
 
	if (buddy->getID() != -1) {
 
		m_storageBackend->removeBuddy(buddy->getID());
 
	}
 
}
 

	
 
void RosterStorage::storeBuddy(Buddy *buddy) {
 
	if (!buddy) {
 
		return;
 
	}
 
	if (buddy->getName().empty()) {
 
		return;
 
	}
 

	
 
	m_buddies[buddy->getName()] = buddy;
 
	m_storageTimer->start();
 
}
 

	
 
bool RosterStorage::storeBuddies() {
 
	if (m_buddies.size() == 0) {
 
		return false;
 
	}
 
	
 
	m_storageBackend->beginTransaction();
 

	
 
	for (std::map<std::string, Buddy *>::const_iterator it = m_buddies.begin(); it != m_buddies.end(); it++) {
 
		Buddy *buddy = (*it).second;
 
		BuddyInfo buddyInfo;
 
		buddyInfo.alias = buddy->getAlias();
 
		buddyInfo.legacyName = buddy->getName();
 
		buddyInfo.groups = buddy->getGroups();
 
		buddyInfo.subscription = buddy->getSubscription() == Buddy::Ask ? "ask" : "both";
src/statsresponder.cpp
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/statsresponder.h"
 

	
 
#include <iostream>
 
#include <boost/bind.hpp>
 
#include "Swiften/Queries/IQRouter.h"
 
#include "transport/BlockPayload.h"
 
#include "Swiften/Swiften.h"
 
#include "transport/usermanager.h"
 
#include "transport/user.h"
 
#include "transport/buddy.h"
 
#include "transport/rostermanager.h"
 
#include "transport/memoryusage.h"
 
#include "transport/conversationmanager.h"
 
#include "transport/rostermanager.h"
 
#include "transport/usermanager.h"
 
#include "transport/networkpluginserver.h"
 
#include "transport/logging.h"
 

	
 
using namespace Swift;
 
using namespace boost;
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "StatsResponder");
 

	
 
StatsResponder::StatsResponder(Component *component, UserManager *userManager, NetworkPluginServer *server, StorageBackend *storageBackend) : Swift::Responder<StatsPayload>(component->getIQRouter()) {
 
	m_component = component;
 
	m_userManager = userManager;
 
	m_server = server;
 
	m_storageBackend = storageBackend;
 
	m_start = time(0);
 
}
 

	
 
StatsResponder::~StatsResponder() {
 
	
 
}
 

	
 
unsigned long StatsResponder::usedMemory() {
 
	double shared = 0;
 
	double rss = 0;
 
#ifndef WIN32
 
	process_mem_usage(shared, rss);
 
#endif
 
	rss -= shared;
 

	
 
	const std::list <NetworkPluginServer::Backend *> &backends = m_server->getBackends();
 
	BOOST_FOREACH(NetworkPluginServer::Backend * backend, backends) {
 
		rss += backend->res - backend->shared;
 
	}
 

	
 
	return (unsigned long) rss;
 
}
 

	
 
bool StatsResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<StatsPayload> stats) {
 
	boost::shared_ptr<StatsPayload> response(new StatsPayload());
 

	
 
	if (stats->getItems().empty()) {
 
		response->addItem(StatsPayload::Item("uptime"));
 
		response->addItem(StatsPayload::Item("users/online"));
 
		response->addItem(StatsPayload::Item("contacts/online"));
 
		response->addItem(StatsPayload::Item("contacts/total"));
 
		response->addItem(StatsPayload::Item("messages/from-xmpp"));
 
		response->addItem(StatsPayload::Item("messages/to-xmpp"));
 
		response->addItem(StatsPayload::Item("backends/running"));
 
		response->addItem(StatsPayload::Item("backends/crashed"));
 
		response->addItem(StatsPayload::Item("memory-usage"));
 
	}
 
	else {
 
		unsigned long contactsOnline = 0;
 
		unsigned long contactsTotal = 0;
 

	
 
		Swift::StatusShow s;
 
		std::string statusMessage;
 
		for (std::map<std::string, User *>::const_iterator it = m_userManager->getUsers().begin(); it != m_userManager->getUsers().end(); it++) {
 
			if (!(*it).second) {
 
				continue;
 
			}
 
			const RosterManager::BuddiesMap &buddies = (*it).second->getRosterManager()->getBuddies();
 
			contactsTotal += buddies.size();
 
			for(RosterManager::BuddiesMap::const_iterator bt = buddies.begin(); bt != buddies.end(); bt++) {
 
				if (!(*bt).second) {
 
					continue;
 
				}
 
				if (!(*bt).second->getStatus(s, statusMessage))
 
					continue;
 
				if (s.getType() != Swift::StatusShow::None) {
 
					contactsOnline++;
 
				}
 
			}
 
		}
 

	
 
		BOOST_FOREACH(const StatsPayload::Item &item, stats->getItems()) {
 
			if (item.getName() == "uptime") {
 
				response->addItem(StatsPayload::Item("uptime", "seconds", boost::lexical_cast<std::string>(time(0) - m_start)));
 
			}
 
			else if (item.getName() == "users/online") {
 
				response->addItem(StatsPayload::Item("users/online", "users", boost::lexical_cast<std::string>(m_userManager->getUserCount())));
 
			}
 
			else if (item.getName() == "backends/running") {
 
				response->addItem(StatsPayload::Item("backends/running", "backends", boost::lexical_cast<std::string>(m_server->getBackendCount())));
 
			}
 
			else if (item.getName() == "backends/crashed") {
 
				response->addItem(StatsPayload::Item("backends/crashed", "backends", boost::lexical_cast<std::string>(m_server->getCrashedBackends().size())));
src/storagebackend.cpp
Show inline comments
 
#include "transport/storagebackend.h"
 
#include "transport/config.h"
 

	
 
#include "transport/sqlite3backend.h"
 
#include "transport/mysqlbackend.h"
 
#include "transport/pqxxbackend.h"
 
#include "Swiften/StringCodecs/Base64.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") {
 
		storageBackend = new SQLite3Backend(config);
 
	}
 
#else
 
	if (CONFIG_STRING(config, "database.type") == "sqlite3") {
 
		error = "Libtransport is not compiled with sqlite3 backend support.";
 
	}
 
#endif
 

	
 
#ifdef WITH_MYSQL
 
	if (CONFIG_STRING(config, "database.type") == "mysql") {
 
		storageBackend = new MySQLBackend(config);
 
	}
 
#else
 
	if (CONFIG_STRING(config, "database.type") == "mysql") {
 
		error = "Spectrum2 is not compiled with mysql backend support.";
 
	}
 
#endif
 

	
 
#ifdef WITH_PQXX
 
	if (CONFIG_STRING(config, "database.type") == "pqxx") {
 
		storageBackend = new PQXXBackend(config);
 
	}
 
#else
 
	if (CONFIG_STRING(config, "database.type") == "pqxx") {
 
		error = "Spectrum2 is not compiled with pqxx backend support.";
 
	}
 
#endif
 

	
 
	if (CONFIG_STRING(config, "database.type") != "mysql" && CONFIG_STRING(config, "database.type") != "sqlite3"
 
		&& CONFIG_STRING(config, "database.type") != "pqxx" && CONFIG_STRING(config, "database.type") != "none") {
 
		error = "Unknown storage backend " + CONFIG_STRING(config, "database.type");
 
	}
 

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

	
 
}
src/storageresponder.cpp
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "storageresponder.h"
 

	
 
#include <iostream>
 
#include <boost/bind.hpp>
 
#include "Swiften/Queries/IQRouter.h"
 
#include "Swiften/Elements/RawXMLPayload.h"
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Elements/Storage.h"
 
#include "Swiften/Elements/Storage.h"
 
#include "Swiften/Serializer/PayloadSerializers/StorageSerializer.h"
 
#include "transport/usermanager.h"
 
#include "transport/user.h"
 
#include "transport/logging.h"
 

	
 
using namespace Swift;
 
using namespace boost;
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "StorageResponder");
 

	
 
StorageResponder::StorageResponder(Swift::IQRouter *router, StorageBackend *storageBackend, UserManager *userManager) : Swift::Responder<PrivateStorage>(router) {
 
	m_storageBackend = storageBackend;
 
	m_userManager = userManager;
 
}
 

	
 
StorageResponder::~StorageResponder() {
 
}
 

	
 
bool StorageResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::PrivateStorage> payload) {
 
	User *user = m_userManager->getUser(from.toBare().toString());
 
	if (!user) {
 
		LOG4CXX_WARN(logger, from.toBare().toString() << ": User is not logged in");
 
		sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Cancel);
 
		return true;
 
	}
 

	
 
	int type = 0;
 
	std::string value = "";
 
	m_storageBackend->getUserSetting(user->getUserInfo().id, "storage", type, value);
 
	LOG4CXX_INFO(logger, from.toBare().toString() << ": Sending jabber:iq:storage");
 

	
 
	sendResponse(from, id, boost::shared_ptr<PrivateStorage>(new PrivateStorage(boost::shared_ptr<RawXMLPayload>(new RawXMLPayload(value)))));
 
	return true;
 
}
 

	
 
bool StorageResponder::handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::PrivateStorage> payload) {
 
	User *user = m_userManager->getUser(from.toBare().toString());
 
	if (!user) {
 
		sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Cancel);
 
		LOG4CXX_WARN(logger, from.toBare().toString() << ": User is not logged in");
 
		return true;
 
	}
 

	
 
	boost::shared_ptr<Storage> storage = boost::dynamic_pointer_cast<Storage>(payload->getPayload());
 

	
 
	if (storage) {
 
		StorageSerializer serializer;
 
		std::string value = serializer.serializePayload(boost::dynamic_pointer_cast<Storage>(payload->getPayload()));
 
		m_storageBackend->updateUserSetting(user->getUserInfo().id, "storage", value);
 
		LOG4CXX_INFO(logger, from.toBare().toString() << ": Storing jabber:iq:storage");
 
		sendResponse(from, id, boost::shared_ptr<PrivateStorage>());
 
	}
 
	else {
 
		LOG4CXX_INFO(logger, from.toBare().toString() << ": Unknown element. Libtransport does not support serialization of this.");
 
		sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Cancel);
 
	}
 
	return true;
 
}
 

	
 
}
src/storageresponder.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <vector>
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Queries/Responder.h"
 
#include "Swiften/Elements/RosterPayload.h"
 
#include "Swiften/Elements/PrivateStorage.h"
 

	
 
namespace Transport {
 

	
 
class StorageBackend;
 
class UserManager;
 

	
 
class StorageResponder : public Swift::Responder<Swift::PrivateStorage> {
 
	public:
 
		StorageResponder(Swift::IQRouter *router, StorageBackend *storageBackend, UserManager *userManager);
 
		~StorageResponder();
 

	
 
	private:
 
		virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::PrivateStorage> payload);
 
		virtual bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::PrivateStorage> payload);
 
		StorageBackend *m_storageBackend;
 
		UserManager *m_userManager;
 
};
 

	
 
}
src/tests/conversationmanager.cpp
Show inline comments
 
#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/usermanager.h"
 
#include "transport/conversationmanager.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 ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
 
	CPPUNIT_TEST_SUITE(ConversationManagerTest);
 
	CPPUNIT_TEST(handleNormalMessages);
 
	CPPUNIT_TEST(handleNormalMessagesHeadline);
 
	CPPUNIT_TEST(handleGroupchatMessages);
 
	CPPUNIT_TEST(handleGroupchatMessagesBouncer);
 
	CPPUNIT_TEST(handleGroupchatMessagesBouncerLeave);
 
	CPPUNIT_TEST(handleGroupchatMessagesTwoResources);
 
	CPPUNIT_TEST(handleChatstateMessages);
 
	CPPUNIT_TEST(handleSubjectMessages);
 
	CPPUNIT_TEST(handleParticipantChanged);
 
	CPPUNIT_TEST(handleParticipantChangedTwoResources);
 
	CPPUNIT_TEST(handlePMFromXMPP);
 
	CPPUNIT_TEST(handleGroupchatRemoved);
 
	CPPUNIT_TEST(handleNicknameConflict);
 
	CPPUNIT_TEST(handleNotAuthorized);
 
	CPPUNIT_TEST_SUITE_END();
 

	
 
	public:
 
		TestingConversation *m_conv;
 
		boost::shared_ptr<Swift::Message> m_msg;
 

	
 
		void setUp (void) {
 
			m_conv = NULL;
 
			m_msg.reset();
 
			setMeUp();
 
			connectUser();
 
			add2Buddies();
 
			factory->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2));
 
			received.clear();
 
		}
 

	
 
		void tearDown (void) {
 
			received.clear();
 
			disconnectUser();
 
			factory->onMessageToSend.disconnect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2));
 
			tearMeDown();
 
		}
 

	
 
	void handleMessageReceived(TestingConversation *_conv, boost::shared_ptr<Swift::Message> &_msg) {
 
		m_conv = _conv;
 
		m_msg = _msg;
 
	}
 

	
 
	void handleChatstateMessages() {
 
		User *user = userManager->getUser("user@localhost");
 

	
 
		TestingConversation *conv = new TestingConversation(user->getConversationManager(), "buddy1");
 
		user->getConversationManager()->addConversation(conv);
 
		conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2));
 

	
 
		boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
		msg->addPayload(boost::make_shared<Swift::ChatState>(Swift::ChatState::Composing));
 

	
 
		// Forward it
 
		conv->handleMessage(msg);
 
		loop->processEvents();
 
		
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[0])));
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[0]))->getPayload<Swift::ChatState>());
 
		received.clear();
 

	
 
		// send response
 
		msg->setFrom("user@localhost/resource");
 
		msg->setTo("buddy1@localhost/bot");
 
		injectMessage(msg);
 
		loop->processEvents();
 
		
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 
		CPPUNIT_ASSERT(m_msg);
 
		CPPUNIT_ASSERT(m_msg->getPayload<Swift::ChatState>());
 

	
 
		received.clear();
 
	}
 

	
 
	void handleSubjectMessages() {
 
		User *user = userManager->getUser("user@localhost");
 
		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);
 

	
 
		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");
 
		conv->handleParticipantChanged("nickname", Conversation::PARTICIPANT_FLAG_MODERATOR, Swift::StatusShow::Away, "my status message");
 
		loop->processEvents();
 
		
 
		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("#room@localhost");
 
		injectMessage(msg);
 
		loop->processEvents();
 
		
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 
		CPPUNIT_ASSERT(m_msg);
 
		CPPUNIT_ASSERT_EQUAL(std::string("subject"), m_msg->getSubject());
 

	
 
		received.clear();
 
	}
 

	
 
	void handleNormalMessages() {
 
		User *user = userManager->getUser("user@localhost");
 

	
 
		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<>!");
 

	
 
		// Forward it
 
		conv->handleMessage(msg);
 
		loop->processEvents();
 
		
 
		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\\40test@localhost/bot"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getFrom().toString());
 
		
 
		received.clear();
 

	
 
		// send response
 
		msg->setFrom("user@localhost/resource");
 
		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());
 

	
 
		// send another message from legacy network, should be sent to user@localhost/resource now
 
		boost::shared_ptr<Swift::Message> msg2(new Swift::Message());
 
		msg2->setBody("hi there!");
 

	
 
		// Forward it
 
		conv->handleMessage(msg2);
 
		loop->processEvents();
 

	
 
		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/resource"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getTo().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%test@localhost/bot"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getFrom().toString());
 
		
 
		received.clear();
 
	}
 

	
 
	void handleNormalMessagesHeadline() {
 
		User *user = userManager->getUser("user@localhost");
 
		user->addUserSetting("send_headlines", "1");
 

	
 
		TestingConversation *conv = new TestingConversation(user->getConversationManager(), "buddy1@test");
 
@@ -250,349 +252,387 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe
 
		conv->handleMessage(msg);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[0])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::Message::Chat, dynamic_cast<Swift::Message *>(getStanza(received[0]))->getType());
 
		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\\40test@localhost/bot"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getFrom().toString());
 
	}
 

	
 
	void handleGroupchatMessages() {
 
		User *user = userManager->getUser("user@localhost");
 
		TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true);
 
		user->getConversationManager()->addConversation(conv);
 
		conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2));
 
		conv->setNickname("nickname");
 
		conv->addJID("user@localhost/resource");
 

	
 
		// reset resources should not touch this resource
 
		user->getConversationManager()->resetResources();
 

	
 
		boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
		msg->setBody("hi there!");
 

	
 
		// Forward it
 
		conv->handleMessage(msg, "anotheruser");
 

	
 
		loop->processEvents();
 
		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/resource"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getTo().toString());
 
		CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast<Swift::Message *>(getStanza(received[0]))->getFrom().toString());
 

	
 
		received.clear();
 

	
 
		// send response
 
		msg->setFrom("user@localhost/resource");
 
		msg->setTo("#room@localhost");
 
		msg->setBody("response!");
 
		msg->setType(Swift::Message::Groupchat);
 
		injectMessage(msg);
 
		loop->processEvents();
 
		
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 
		CPPUNIT_ASSERT(m_msg);
 
		CPPUNIT_ASSERT_EQUAL(std::string("response!"), m_msg->getBody());
 
	}
 

	
 
	void handleGroupchatMessagesBouncer() {
 
		User *user = userManager->getUser("user@localhost");
 
		user->addUserSetting("stay_connected", "1");
 
		TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true);
 
		user->getConversationManager()->addConversation(conv);
 
		conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2));
 
		conv->setNickname("nickname");
 
		conv->addJID("user@localhost/resource");
 

	
 
		CPPUNIT_ASSERT(!user->shouldCacheMessages());
 

	
 
		// disconnectUser
 
		userManager->disconnectUser("user@localhost");
 
		dynamic_cast<Swift::DummyTimerFactory *>(factories->getTimerFactory())->setTime(10);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT(user->shouldCacheMessages());
 

	
 
		// reset resources should not touch this resource
 
		user->getConversationManager()->resetResources();
 

	
 
		boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
		msg->setBody("hi there!");
 
		conv->handleMessage(msg, "anotheruser");
 

	
 
		boost::shared_ptr<Swift::Message> msg2(new Swift::Message());
 
		msg2->setBody("hi there2!");
 
		conv->handleMessage(msg2, "anotheruser");
 

	
 
		loop->processEvents();
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 

	
 
		userRegistry->isValidUserPassword(Swift::JID("user@localhost/resource"), serverFromClientSession.get(), Swift::createSafeByteArray("password"));
 
		userRegistry->onPasswordValid(Swift::JID("user@localhost/resource"));
 
		loop->processEvents();
 

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

	
 
		CPPUNIT_ASSERT_EQUAL(4, (int) received.size());
 
		CPPUNIT_ASSERT_EQUAL(3, (int) received.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[1])));
 
		CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), 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("#room@localhost/anotheruser"), dynamic_cast<Swift::Message *>(getStanza(received[1]))->getFrom().toString());
 

	
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[2])));
 
		CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast<Swift::Message *>(getStanza(received[2]))->getBody());
 
		CPPUNIT_ASSERT_EQUAL(std::string("hi there2!"), dynamic_cast<Swift::Message *>(getStanza(received[2]))->getBody());
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Message *>(getStanza(received[2]))->getTo().toString());
 
		CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast<Swift::Message *>(getStanza(received[2]))->getFrom().toString());
 

	
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[3])));
 
		CPPUNIT_ASSERT_EQUAL(std::string("hi there2!"), dynamic_cast<Swift::Message *>(getStanza(received[3]))->getBody());
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Message *>(getStanza(received[3]))->getTo().toString());
 
		CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast<Swift::Message *>(getStanza(received[3]))->getFrom().toString());
 

	
 
	}
 

	
 
	void handleGroupchatMessagesBouncerLeave() {
 
		User *user = userManager->getUser("user@localhost");
 
		user->addUserSetting("stay_connected", "1");
 
		TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true);
 
		user->getConversationManager()->addConversation(conv);
 
		conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2));
 
		conv->setNickname("nickname");
 
		conv->addJID("user@localhost/resource");
 

	
 
		CPPUNIT_ASSERT(!user->shouldCacheMessages());
 

	
 
		Swift::Presence::ref response3 = Swift::Presence::create();
 
		response3->setType(Swift::Presence::Unavailable);
 
		response3->setTo("#room@localhost/hanzz");
 
		response3->setFrom("user@localhost/resource");
 

	
 
		Swift::MUCPayload *payload3 = new Swift::MUCPayload();
 
		payload3->setPassword("password");
 
		response3->addPayload(boost::shared_ptr<Swift::Payload>(payload3));
 
		injectPresence(response3);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT(!user->shouldCacheMessages());
 

	
 
		// reset resources should not touch this resource
 
		user->getConversationManager()->resetResources();
 

	
 
		boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
		msg->setBody("hi there!");
 
		conv->handleMessage(msg, "anotheruser");
 

	
 
		boost::shared_ptr<Swift::Message> msg2(new Swift::Message());
 
		msg2->setBody("hi there2!");
 
		conv->handleMessage(msg2, "anotheruser");
 

	
 
		loop->processEvents();
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 

	
 
		userRegistry->isValidUserPassword(Swift::JID("user@localhost/resource"), serverFromClientSession.get(), Swift::createSafeByteArray("password"));
 
		userRegistry->onPasswordValid(Swift::JID("user@localhost/resource"));
 
		loop->processEvents();
 

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

	
 
		CPPUNIT_ASSERT_EQUAL(4, (int) received.size());
 
		CPPUNIT_ASSERT_EQUAL(3, (int) received.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[1])));
 
		CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), 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("#room@localhost/anotheruser"), dynamic_cast<Swift::Message *>(getStanza(received[1]))->getFrom().toString());
 

	
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[2])));
 
		CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast<Swift::Message *>(getStanza(received[2]))->getBody());
 
		CPPUNIT_ASSERT_EQUAL(std::string("hi there2!"), dynamic_cast<Swift::Message *>(getStanza(received[2]))->getBody());
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Message *>(getStanza(received[2]))->getTo().toString());
 
		CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast<Swift::Message *>(getStanza(received[2]))->getFrom().toString());
 

	
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[3])));
 
		CPPUNIT_ASSERT_EQUAL(std::string("hi there2!"), dynamic_cast<Swift::Message *>(getStanza(received[3]))->getBody());
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Message *>(getStanza(received[3]))->getTo().toString());
 
		CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast<Swift::Message *>(getStanza(received[3]))->getFrom().toString());
 

	
 
	}
 

	
 
	void handleGroupchatMessagesTwoResources() {
 
		connectSecondResource();
 
		received2.clear();
 
		User *user = userManager->getUser("user@localhost");
 
		TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true);
 
		user->getConversationManager()->addConversation(conv);
 
		conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2));
 
		conv->setNickname("nickname");
 
		conv->addJID("user@localhost/resource");
 
		conv->addJID("user@localhost/resource2");
 

	
 
		// reset resources should not touch this resource
 
		user->getConversationManager()->resetResources();
 

	
 
		boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
		msg->setBody("hi there!");
 

	
 
		// Forward it
 
		conv->handleMessage(msg, "anotheruser");
 

	
 
		loop->processEvents();
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received2.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received2[0])));
 
		CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast<Swift::Message *>(getStanza(received2[0]))->getBody());
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource2"), dynamic_cast<Swift::Message *>(getStanza(received2[0]))->getTo().toString());
 
		CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast<Swift::Message *>(getStanza(received2[0]))->getFrom().toString());
 

	
 
		received.clear();
 

	
 
		// send response
 
		msg->setFrom("user@localhost/resource2");
 
		msg->setTo("#room@localhost");
 
		msg->setBody("response!");
 
		msg->setType(Swift::Message::Groupchat);
 
		injectMessage(msg);
 
		loop->processEvents();
 
		
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 
		CPPUNIT_ASSERT(m_msg);
 
		CPPUNIT_ASSERT_EQUAL(std::string("response!"), m_msg->getBody());
 
	}
 

	
 
	void handleParticipantChanged() {
 
		User *user = userManager->getUser("user@localhost");
 
		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");
 

	
 
		// normal presence
 
		conv->handleParticipantChanged("anotheruser", 0, Swift::StatusShow::Away, "my status message");
 
		conv->handleParticipantChanged("anotheruser", Conversation::PARTICIPANT_FLAG_NONE, Swift::StatusShow::Away, "my status message");
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received[0])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getShow());
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getTo().toString());
 
		CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getFrom().toString());
 
		CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::MUCUserPayload>());
 
		CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Member, *getStanza(received[0])->getPayload<Swift::MUCUserPayload>()->getItems()[0].affiliation);
 
		CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Participant, *getStanza(received[0])->getPayload<Swift::MUCUserPayload>()->getItems()[0].role);
 

	
 
		received.clear();
 

	
 
		// this user presence - status code 110
 
		conv->handleParticipantChanged("nickname", 1, Swift::StatusShow::Away, "my status message");
 
		conv->handleParticipantChanged("nickname", Conversation::PARTICIPANT_FLAG_MODERATOR, Swift::StatusShow::Away, "my status message");
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received[0])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getShow());
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getTo().toString());
 
		CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/nickname"), dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getFrom().toString());
 
		CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::MUCUserPayload>());
 
		CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Admin, *getStanza(received[0])->getPayload<Swift::MUCUserPayload>()->getItems()[0].affiliation);
 
		CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Moderator, *getStanza(received[0])->getPayload<Swift::MUCUserPayload>()->getItems()[0].role);
 
		CPPUNIT_ASSERT_EQUAL(110, getStanza(received[0])->getPayload<Swift::MUCUserPayload>()->getStatusCodes()[0].code);
 

	
 
		received.clear();
 

	
 
		// renamed - status code 303
 
		conv->handleParticipantChanged("anotheruser", 1, Swift::StatusShow::Away, "my status message", "hanzz");
 
		conv->handleParticipantChanged("anotheruser", Conversation::PARTICIPANT_FLAG_MODERATOR, Swift::StatusShow::Away, "my status message", "hanzz");
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(2, (int) received.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received[0])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::Presence::Unavailable, dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getType());
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getTo().toString());
 
		CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getFrom().toString());
 
		CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::MUCUserPayload>());
 
		CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Admin, *getStanza(received[0])->getPayload<Swift::MUCUserPayload>()->getItems()[0].affiliation);
 
		CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Moderator, *getStanza(received[0])->getPayload<Swift::MUCUserPayload>()->getItems()[0].role);
 
		CPPUNIT_ASSERT_EQUAL(std::string("hanzz"), *getStanza(received[0])->getPayload<Swift::MUCUserPayload>()->getItems()[0].nick);
 
		CPPUNIT_ASSERT_EQUAL(303, getStanza(received[0])->getPayload<Swift::MUCUserPayload>()->getStatusCodes()[0].code);
 
	}
 

	
 
	void handleParticipantChangedTwoResources() {
 
		connectSecondResource();
 
		received2.clear();
 
		User *user = userManager->getUser("user@localhost");
 
		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");
 
		conv->addJID("user@localhost/resource2");
 

	
 
		// normal presence
 
		conv->handleParticipantChanged("anotheruser", 0, Swift::StatusShow::Away, "my status message");
 
		conv->handleParticipantChanged("anotheruser", Conversation::PARTICIPANT_FLAG_NONE, Swift::StatusShow::Away, "my status message");
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received2.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received2[0])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast<Swift::Presence *>(getStanza(received2[0]))->getShow());
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource2"), dynamic_cast<Swift::Presence *>(getStanza(received2[0]))->getTo().toString());
 
		CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast<Swift::Presence *>(getStanza(received2[0]))->getFrom().toString());
 
		CPPUNIT_ASSERT(getStanza(received2[0])->getPayload<Swift::MUCUserPayload>());
 
		CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Member, *getStanza(received2[0])->getPayload<Swift::MUCUserPayload>()->getItems()[0].affiliation);
 
		CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Participant, *getStanza(received2[0])->getPayload<Swift::MUCUserPayload>()->getItems()[0].role);
 
	}
 

	
 
	void handlePMFromXMPP() {
 
		User *user = userManager->getUser("user@localhost");
 
		TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true);
 
		user->getConversationManager()->addConversation(conv);
 
		conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2));
 
		conv->setNickname("nickname");
 
		conv->setJID("user@localhost/resource");
 

	
 
		conv->handleParticipantChanged("anotheruser", 0, Swift::StatusShow::Away, "my status message");
 
		conv->handleParticipantChanged("anotheruser", Conversation::PARTICIPANT_FLAG_NONE, Swift::StatusShow::Away, "my status message");
 
		loop->processEvents();
 

	
 
		received.clear();
 
		boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
		msg->setBody("hi there!");
 
		msg->setFrom("user@localhost/resource");
 
		msg->setTo("#room@localhost/anotheruser");
 
		msg->setBody("hi there!");
 
		injectMessage(msg);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 
		CPPUNIT_ASSERT(m_msg);
 
		CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), m_msg->getBody());
 
		CPPUNIT_ASSERT_EQUAL(std::string("#room/anotheruser"), m_conv->getLegacyName());
 

	
 
		Conversation *pmconv = user->getConversationManager()->getConversation("#room/anotheruser");
 

	
 
		boost::shared_ptr<Swift::Message> msg2(new Swift::Message());
 
		msg2->setBody("response!");
 

	
 
		pmconv->handleMessage(msg2);
 
	}
 

	
 
	void handleGroupchatRemoved() {
 
		User *user = userManager->getUser("user@localhost");
 
		TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true);
 
		conv->setNickname("nickname");
 
		conv->addJID("user@localhost/resource");
 
		received.clear();
 
		conv->destroyRoom();
 
		delete conv;
 

	
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received[0])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::None, dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getShow());
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getTo().toString());
 
		CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/nickname"), dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getFrom().toString());
 
		CPPUNIT_ASSERT_EQUAL(332, getStanza(received[0])->getPayload<Swift::MUCUserPayload>()->getStatusCodes()[0].code);
 
	}
 

	
 
	void handleNicknameConflict() {
 
		User *user = userManager->getUser("user@localhost");
 
		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");
 

	
 
		// normal presence
 
		conv->handleParticipantChanged("nickname", Conversation::PARTICIPANT_FLAG_CONFLICT, Swift::StatusShow::Away, "my status message");
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received[0])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::Presence::Error, dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getType());
 
		CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::ErrorPayload>());
 
		CPPUNIT_ASSERT_EQUAL(Swift::ErrorPayload::Conflict, getStanza(received[0])->getPayload<Swift::ErrorPayload>()->getCondition());
 
	}
 

	
 
	void handleNotAuthorized() {
 
		User *user = userManager->getUser("user@localhost");
 
		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");
 

	
 
		// normal presence
 
		conv->handleParticipantChanged("nickname", Conversation::PARTICIPANT_FLAG_NOT_AUTHORIZED, Swift::StatusShow::Away, "my status message");
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received[0])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::Presence::Error, dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getType());
 
		CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::ErrorPayload>());
 
		CPPUNIT_ASSERT_EQUAL(Swift::ErrorPayload::NotAuthorized, getStanza(received[0])->getPayload<Swift::ErrorPayload>()->getCondition());
 
	}
 

	
 
};
 

	
 
CPPUNIT_TEST_SUITE_REGISTRATION (ConversationManagerTest);
src/tests/rostermanager.cpp
Show inline comments
 
#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/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 RosterManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
 
	CPPUNIT_TEST_SUITE(RosterManagerTest);
 
	CPPUNIT_TEST(setBuddy);
 
	CPPUNIT_TEST(setBuddyNoAlias);
 
	CPPUNIT_TEST(sendCurrentPresences);
 
	CPPUNIT_TEST(sendUnavailablePresences);
 
	CPPUNIT_TEST(sendCurrentPresence);
 
	CPPUNIT_TEST(sendBuddySubscribePresence);
 
	CPPUNIT_TEST(removeBuddy);
 
	CPPUNIT_TEST(subscribeExistingBuddy);
 
	CPPUNIT_TEST(subscribeNewBuddy);
 
	CPPUNIT_TEST(unsubscribeExistingBuddy);
 
	CPPUNIT_TEST(unsubscribeNewBuddy);
 
	CPPUNIT_TEST_SUITE_END();
 

	
 
	public:
 
		std::string m_buddy;
 

	
 
		void setUp (void) {
 
			m_buddy.clear();
 
			setMeUp();
 
			connectUser();
 
			User *user = userManager->getUser("user@localhost");
 
			user->getRosterManager()->onBuddyAdded.connect(boost::bind(&RosterManagerTest::handleBuddyAdded, this, _1));
 
			user->getRosterManager()->onBuddyRemoved.connect(boost::bind(&RosterManagerTest::handleBuddyRemoved, this, _1));
 
			received.clear();
 
		}
 

	
 
		void tearDown (void) {
 
			received.clear();
 
			disconnectUser();
 
			tearMeDown();
 
		}
 

	
 
	void handleBuddyAdded(Buddy *buddy) {
 
		m_buddy = buddy->getName();
 
	}
 

	
 
	void handleBuddyRemoved(Buddy *buddy) {
 
		m_buddy = buddy->getName();
 
	}
 

	
 
	void sendBuddySubscribePresence() {
 
		add2Buddies();
 
		received.clear();
 
		User *user = userManager->getUser("user@localhost");
 
		user->getRosterManager()->sendBuddySubscribePresence(user->getRosterManager()->getBuddy("buddy1"));
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 
		CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::Nickname>());
 
		CPPUNIT_ASSERT_EQUAL(std::string("Buddy 1"), getStanza(received[0])->getPayload<Swift::Nickname>()->getNickname());
 
		
 
	}
 

	
 
	void setBuddyNoAlias() {
 
		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", "", grp, BUDDY_JID_ESCAPING);
 
		user->getRosterManager()->setBuddy(buddy);
 

	
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 

	
 
		Swift::RosterPayload::ref payload1 = getStanza(received[0])->getPayload<Swift::RosterPayload>();
 
		CPPUNIT_ASSERT(payload1);
 
		CPPUNIT_ASSERT_EQUAL(1, (int) payload1->getItems().size());
 
		Swift::RosterItemPayload item = payload1->getItems()[0];
 
		CPPUNIT_ASSERT_EQUAL(std::string("buddy1"), Buddy::JIDToLegacyName(item.getJID()));
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), item.getName());
 
	}
 

	
 
	void setBuddy() {
 
		add2Buddies();
 
		CPPUNIT_ASSERT_EQUAL(4, (int) received.size());
 

	
 
		Swift::RosterPayload::ref payload1 = getStanza(received[0])->getPayload<Swift::RosterPayload>();
 
		CPPUNIT_ASSERT(payload1);
 
		CPPUNIT_ASSERT_EQUAL(1, (int) payload1->getItems().size());
 
		Swift::RosterItemPayload item = payload1->getItems()[0];
 
		CPPUNIT_ASSERT_EQUAL(std::string("buddy1"), Buddy::JIDToLegacyName(item.getJID()));
 
		CPPUNIT_ASSERT_EQUAL(std::string("Buddy 1"), item.getName());
 

	
 
		Swift::RosterPayload::ref payload2 = getStanza(received[2])->getPayload<Swift::RosterPayload>();
 
		CPPUNIT_ASSERT(payload2);
 
		CPPUNIT_ASSERT_EQUAL(1, (int) payload2->getItems().size());
 
		item = payload2->getItems()[0];
 
		CPPUNIT_ASSERT_EQUAL(std::string("buddy2"), Buddy::JIDToLegacyName(item.getJID()));
 
		CPPUNIT_ASSERT_EQUAL(std::string("Buddy 2"), item.getName());
 

	
 
		// send responses back
 
		injectIQ(Swift::IQ::createResult(getStanza(received[0])->getFrom(), getStanza(received[0])->getTo(), getStanza(received[0])->getID()));
 
		injectIQ(Swift::IQ::createResult(getStanza(received[2])->getFrom(), getStanza(received[2])->getTo(), getStanza(received[2])->getID()));
 

	
 
		// we should get presences
 
		CPPUNIT_ASSERT_EQUAL(6, (int) received.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received[4])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast<Swift::Presence *>(getStanza(received[4]))->getShow());
 
		CPPUNIT_ASSERT_EQUAL(std::string("status1"), dynamic_cast<Swift::Presence *>(getStanza(received[4]))->getStatus());
 

	
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received[5])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast<Swift::Presence *>(getStanza(received[5]))->getShow());
 
		CPPUNIT_ASSERT_EQUAL(std::string("status2"), dynamic_cast<Swift::Presence *>(getStanza(received[5]))->getStatus());
 
	}
 

	
 
	void sendCurrentPresences() {
 
		setBuddy();
 
		received.clear();
 

	
 
		User *user = userManager->getUser("user@localhost");
 
		user->getRosterManager()->sendCurrentPresences("user@localhost/resource");
 

	
 
		CPPUNIT_ASSERT_EQUAL(2, (int) received.size());
 

	
 
		for (int i = 0; i < 2; i++) {
 
			CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received[i])));
 
			CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast<Swift::Presence *>(getStanza(received[i]))->getShow());
 
			CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Presence *>(getStanza(received[i]))->getTo().toString());
 
		}
 
	}
 

	
 
	void sendUnavailablePresences() {
 
		setBuddy();
 
		received.clear();
 

	
 
		User *user = userManager->getUser("user@localhost");
 
		user->getRosterManager()->sendUnavailablePresences("user@localhost/resource");
 

	
 
		CPPUNIT_ASSERT_EQUAL(3, (int) received.size());
 

	
 
		for (int i = 0; i < 3; i++) {
 
			CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received[i])));
 
			CPPUNIT_ASSERT_EQUAL(Swift::Presence::Unavailable, dynamic_cast<Swift::Presence *>(getStanza(received[i]))->getType());
 
			CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Presence *>(getStanza(received[i]))->getTo().toString());
 
		}
 
	}
 

	
 
	void sendCurrentPresence() {
 
		setBuddy();
 
		received.clear();
 

	
 
		User *user = userManager->getUser("user@localhost");
 
		user->getRosterManager()->sendCurrentPresence("buddy1@localhost", "user@localhost/resource");
 

	
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received[0])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getShow());
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getTo().toString());
 

	
 
		received.clear();
 
		user->getRosterManager()->sendCurrentPresence("buddy_unknown@localhost", "user@localhost/resource");
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received[0])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::Presence::Unavailable, dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getType());
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getTo().toString());
 
	}
 

	
 
	void removeBuddy() {
 
		add2Buddies();
 
		CPPUNIT_ASSERT_EQUAL(4, (int) received.size());
 

	
 
		received.clear();
 
		User *user = userManager->getUser("user@localhost");
 
		user->getRosterManager()->removeBuddy("buddy1");
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
 

	
 
		Swift::RosterPayload::ref payload1 = getStanza(received[0])->getPayload<Swift::RosterPayload>();
 
		CPPUNIT_ASSERT(payload1);
src/tests/user.cpp
Show inline comments
 
@@ -32,440 +32,434 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
 
	CPPUNIT_TEST(handlePresenceLeaveRoomBouncer);
 
	CPPUNIT_TEST(handlePresenceLeaveRoomTwoResourcesBouncer);
 
	CPPUNIT_TEST(handlePresenceLeaveRoomTwoResourcesOneDisconnectsBouncer);
 
	CPPUNIT_TEST(leaveJoinedRoom);
 
	CPPUNIT_TEST(joinRoomBeforeConnected);
 
	CPPUNIT_TEST(handleDisconnected);
 
	CPPUNIT_TEST(handleDisconnectedReconnect);
 
	CPPUNIT_TEST(joinRoomHandleDisconnectedRejoin);
 
	CPPUNIT_TEST_SUITE_END();
 

	
 
	public:
 
		std::string room;
 
		std::string roomNickname;
 
		std::string roomPassword;
 
		bool readyToConnect;
 
		bool disconnected;
 
		Swift::Presence::ref changedPresence;
 

	
 
		void setUp (void) {
 
			disconnected = false;
 
			readyToConnect = false;
 
			changedPresence = Swift::Presence::ref();
 
			room = "";
 
			roomNickname = "";
 
			roomPassword = "";
 

	
 
			setMeUp();
 
			userManager->onUserCreated.connect(boost::bind(&UserTest::handleUserCreated, this, _1));
 
			connectUser();
 
			received.clear();
 
		}
 

	
 
		void tearDown (void) {
 
			received.clear();
 
			if (!disconnected) {
 
				disconnectUser();
 
			}
 
			userManager->removeAllUsers();
 
			tearMeDown();
 
		}
 

	
 
	void handleUserCreated(User *user) {
 
		user->onReadyToConnect.connect(boost::bind(&UserTest::handleUserReadyToConnect, this, user));
 
		user->onPresenceChanged.connect(boost::bind(&UserTest::handleUserPresenceChanged, this, user, _1));
 
		user->onRoomJoined.connect(boost::bind(&UserTest::handleRoomJoined, this, user, _1, _2, _3, _4));
 
		user->onRoomLeft.connect(boost::bind(&UserTest::handleRoomLeft, this, user, _1));
 
	}
 

	
 
	void handleUserReadyToConnect(User *user) {
 
		readyToConnect = true;
 
	}
 

	
 
	void handleUserPresenceChanged(User *user, Swift::Presence::ref presence) {
 
		changedPresence = presence;
 
	}
 

	
 
	void handleRoomJoined(User *user, const std::string &jid, const std::string &r, const std::string &nickname, const std::string &password) {
 
		room = r;
 
		roomNickname = nickname;
 
		roomPassword = password;
 
	}
 

	
 
	void handleRoomLeft(User *user, const std::string &r) {
 
		room = r;
 
	}
 

	
 

	
 
	void sendCurrentPresence() {
 
		User *user = userManager->getUser("user@localhost");
 
		user->sendCurrentPresence();
 

	
 
		// We're not forwarding current presence in server-mode
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 
	}
 

	
 
	void handlePresence() {
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setTo("localhost");
 
		response->setFrom("user@localhost/resource");
 
		response->setShow(Swift::StatusShow::Away);
 

	
 
		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(changedPresence);
 
		CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, changedPresence->getShow());
 
	}
 

	
 
	void handlePresenceJoinRoom() {
 
		User *user = userManager->getUser("user@localhost");
 

	
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setTo("#room@localhost/hanzz");
 
		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"), room);
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 
		CPPUNIT_ASSERT_EQUAL(std::string("room"), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string("hanzz"), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string("password"), roomPassword);
 

	
 
		room = "";
 
		roomNickname = "";
 
		roomPassword = "";
 

	
 
		// simulate that backend joined the room
 
		TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true);
 
		TestingConversation *conv = new TestingConversation(user->getConversationManager(), "room", true);
 
		conv->addJID("user@localhost/resource");
 
		user->getConversationManager()->addConversation(conv);
 

	
 
		received.clear();
 
		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(0, (int) received.size());
 

	
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 
	}
 

	
 
	void handlePresenceJoinRoomTwoResources() {
 
		handlePresenceJoinRoom();
 
		User *user = userManager->getUser("user@localhost");
 

	
 
		// Add 1 participant
 
		Conversation *conv = user->getConversationManager()->getConversation("#room");
 
		conv->handleParticipantChanged("anotheruser", 0, Swift::StatusShow::Away, "my status message");
 
		Conversation *conv = user->getConversationManager()->getConversation("room");
 
		conv->handleParticipantChanged("anotheruser", Conversation::PARTICIPANT_FLAG_NONE, Swift::StatusShow::Away, "my status message");
 

	
 
		// Connect 2nd resource
 
		connectSecondResource();
 
		received2.clear();
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setTo("#room@localhost/hanzz");
 
		response->setTo("room@localhost/hanzz");
 
		response->setFrom("user@localhost/resource2");
 

	
 
		Swift::MUCPayload *payload = new Swift::MUCPayload();
 
		payload->setPassword("password");
 
		response->addPayload(boost::shared_ptr<Swift::Payload>(payload));
 
		injectPresence(response);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 

	
 
		CPPUNIT_ASSERT_EQUAL(2, (int) received2.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received2[1])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast<Swift::Presence *>(getStanza(received2[1]))->getShow());
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource2"), dynamic_cast<Swift::Presence *>(getStanza(received2[1]))->getTo().toString());
 
		CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast<Swift::Presence *>(getStanza(received2[1]))->getFrom().toString());
 
		CPPUNIT_ASSERT(getStanza(received2[1])->getPayload<Swift::MUCUserPayload>());
 
		CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Member, *getStanza(received2[1])->getPayload<Swift::MUCUserPayload>()->getItems()[0].affiliation);
 
		CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Participant, *getStanza(received2[1])->getPayload<Swift::MUCUserPayload>()->getItems()[0].role);
 
		CPPUNIT_ASSERT_EQUAL(1, (int) received2.size());
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received2[0])));
 
		CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast<Swift::Presence *>(getStanza(received2[0]))->getShow());
 
		CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource2"), dynamic_cast<Swift::Presence *>(getStanza(received2[0]))->getTo().toString());
 
		CPPUNIT_ASSERT_EQUAL(std::string("room@localhost/anotheruser"), dynamic_cast<Swift::Presence *>(getStanza(received2[0]))->getFrom().toString());
 
		CPPUNIT_ASSERT(getStanza(received2[0])->getPayload<Swift::MUCUserPayload>());
 
		CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Member, *getStanza(received2[0])->getPayload<Swift::MUCUserPayload>()->getItems()[0].affiliation);
 
		CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Participant, *getStanza(received2[0])->getPayload<Swift::MUCUserPayload>()->getItems()[0].role);
 
	}
 

	
 
	void handlePresenceLeaveRoom() {
 
		received.clear();
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setTo("#room@localhost/hanzz");
 
		response->setTo("room@localhost/hanzz");
 
		response->setFrom("user@localhost/resource");
 
		response->setType(Swift::Presence::Unavailable);
 

	
 
		Swift::MUCPayload *payload = new Swift::MUCPayload();
 
		payload->setPassword("password");
 
		response->addPayload(boost::shared_ptr<Swift::Payload>(payload));
 
		injectPresence(response);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 

	
 
		CPPUNIT_ASSERT_EQUAL(std::string("#room"), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 
// 		CPPUNIT_ASSERT_EQUAL(std::string("room"), room);
 
// 		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
// 		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 
	}
 

	
 
	void handlePresenceLeaveRoomTwoResources() {
 
		handlePresenceJoinRoomTwoResources();
 
		received.clear();
 

	
 
		// User is still connected from resource2, so he should not leave the room
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setTo("#room@localhost/hanzz");
 
		response->setTo("room@localhost/hanzz");
 
		response->setFrom("user@localhost/resource");
 
		response->setType(Swift::Presence::Unavailable);
 

	
 
		Swift::MUCPayload *payload = new Swift::MUCPayload();
 
		payload->setPassword("password");
 
		response->addPayload(boost::shared_ptr<Swift::Payload>(payload));
 
		injectPresence(response);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 

	
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 

	
 
		// disconnect also from resource
 
		// User is still connected from resource2, so he should not leave the room
 
		response = Swift::Presence::create();
 
		response->setTo("#room@localhost/hanzz");
 
		response->setTo("room@localhost/hanzz");
 
		response->setFrom("user@localhost/resource2");
 
		response->setType(Swift::Presence::Unavailable);
 

	
 
		payload = new Swift::MUCPayload();
 
		payload->setPassword("password");
 
		response->addPayload(boost::shared_ptr<Swift::Payload>(payload));
 
		injectPresence(response);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 

	
 
		CPPUNIT_ASSERT_EQUAL(std::string("#room"), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string("room"), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 
	}
 

	
 
	void handlePresenceLeaveRoomTwoResourcesOneDisconnects() {
 
		handlePresenceJoinRoomTwoResources();
 
		received.clear();
 
		User *user = userManager->getUser("user@localhost");
 

	
 
		// User is still connected from resource2, so he should not leave the room
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setTo("localhost/hanzz");
 
		response->setFrom("user@localhost/resource");
 
		response->setType(Swift::Presence::Unavailable);
 
		injectPresence(response);
 
		loop->processEvents();
 

	
 

	
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 

	
 
		Conversation *conv = user->getConversationManager()->getConversation("#room");
 
		Conversation *conv = user->getConversationManager()->getConversation("room");
 
		CPPUNIT_ASSERT_EQUAL(1, (int) conv->getJIDs().size());
 
		CPPUNIT_ASSERT_EQUAL(Swift::JID("user@localhost/resource2"), conv->getJIDs().front());
 
	}
 

	
 
	void handlePresenceLeaveRoomBouncer() {
 
		User *user = userManager->getUser("user@localhost");
 
		user->addUserSetting("stay_connected", "1");
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setTo("#room@localhost/hanzz");
 
		response->setTo("room@localhost/hanzz");
 
		response->setFrom("user@localhost/resource");
 
		response->setType(Swift::Presence::Unavailable);
 

	
 
		Swift::MUCPayload *payload = new Swift::MUCPayload();
 
		payload->setPassword("password");
 
		response->addPayload(boost::shared_ptr<Swift::Payload>(payload));
 
		injectPresence(response);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 

	
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 
	}
 

	
 
	void handlePresenceLeaveRoomTwoResourcesBouncer() {
 
		User *user = userManager->getUser("user@localhost");
 
		user->addUserSetting("stay_connected", "1");
 
		handlePresenceJoinRoomTwoResources();
 
		received.clear();
 

	
 
		// User is still connected from resource2, so he should not leave the room
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setTo("#room@localhost/hanzz");
 
		response->setTo("room@localhost/hanzz");
 
		response->setFrom("user@localhost/resource");
 
		response->setType(Swift::Presence::Unavailable);
 

	
 
		Swift::MUCPayload *payload = new Swift::MUCPayload();
 
		payload->setPassword("password");
 
		response->addPayload(boost::shared_ptr<Swift::Payload>(payload));
 
		injectPresence(response);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 

	
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 

	
 
		room = "something";
 
		// disconnect also from resource
 
		// User is still connected from resource2, so he should not leave the room
 
		response = Swift::Presence::create();
 
		response->setTo("#room@localhost/hanzz");
 
		response->setTo("room@localhost/hanzz");
 
		response->setFrom("user@localhost/resource2");
 
		response->setType(Swift::Presence::Unavailable);
 

	
 
		payload = new Swift::MUCPayload();
 
		payload->setPassword("password");
 
		response->addPayload(boost::shared_ptr<Swift::Payload>(payload));
 
		injectPresence(response);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 

	
 
		CPPUNIT_ASSERT_EQUAL(std::string("something"), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 
	}
 

	
 
	void handlePresenceLeaveRoomTwoResourcesOneDisconnectsBouncer() {
 
		room = "something";
 
		handlePresenceJoinRoomTwoResources();
 
		received.clear();
 
		User *user = userManager->getUser("user@localhost");
 

	
 
		// User is still connected from resource2, so he should not leave the room
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setTo("localhost/hanzz");
 
		response->setFrom("user@localhost/resource");
 
		response->setType(Swift::Presence::Unavailable);
 
		injectPresence(response);
 
		loop->processEvents();
 

	
 

	
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
 

	
 
		Conversation *conv = user->getConversationManager()->getConversation("#room");
 
		Conversation *conv = user->getConversationManager()->getConversation("room");
 
		CPPUNIT_ASSERT_EQUAL(1, (int) conv->getJIDs().size());
 
		CPPUNIT_ASSERT_EQUAL(Swift::JID("user@localhost/resource2"), conv->getJIDs().front());
 
	}
 

	
 
	void leaveJoinedRoom() {
 
		User *user = userManager->getUser("user@localhost");
 
		handlePresenceJoinRoom();
 

	
 
		CPPUNIT_ASSERT(user->getConversationManager()->getConversation("#room"));
 
		CPPUNIT_ASSERT(user->getConversationManager()->getConversation("room"));
 

	
 
		received.clear();
 
		handlePresenceLeaveRoom();
 

	
 
		CPPUNIT_ASSERT(!user->getConversationManager()->getConversation("#room"));
 
		CPPUNIT_ASSERT(!user->getConversationManager()->getConversation("room"));
 
	}
 

	
 
	void handleDisconnected() {
 
		User *user = userManager->getUser("user@localhost");
 
		user->handleDisconnected("Connection error", Swift::SpectrumErrorPayload::CONNECTION_ERROR_AUTHENTICATION_FAILED);
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT(streamEnded);
 
		user = userManager->getUser("user@localhost");
 
		CPPUNIT_ASSERT(!user);
 

	
 
		CPPUNIT_ASSERT_EQUAL(2, (int) received.size());
 
		Swift::Message *m = dynamic_cast<Swift::Message *>(getStanza(received[0]));
 
		CPPUNIT_ASSERT_EQUAL(std::string("Connection error"), m->getBody());
 

	
 
		CPPUNIT_ASSERT(dynamic_cast<Swift::StreamError *>(received[1].get()));
 
		CPPUNIT_ASSERT_EQUAL(std::string("Connection error"), dynamic_cast<Swift::StreamError *>(received[1].get())->getText());
 

	
 
		disconnected = true;
 
	}
 

	
 
	void handleDisconnectedReconnect() {
 
		readyToConnect = false;
 
		User *user = userManager->getUser("user@localhost");
 
		user->handleDisconnected("Connection error");
 
		loop->processEvents();
 

	
 
		CPPUNIT_ASSERT(!streamEnded);
 
		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->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(0, (int) received.size());
 
		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("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("room"), room);
 
		CPPUNIT_ASSERT_EQUAL(std::string("hanzz"), roomNickname);
 
		CPPUNIT_ASSERT_EQUAL(std::string("password"), roomPassword);
 
		CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
 
	}
 

	
 
};
 

	
 
CPPUNIT_TEST_SUITE_REGISTRATION (UserTest);
src/tests/userregistry.cpp
Show inline comments
 
#include "transport/userregistry.h"
 
#include "transport/config.h"
 
#include <cppunit/TestFixture.h>
 
#include <cppunit/extensions/HelperMacros.h>
 
#include <Swiften/EventLoop/DummyEventLoop.h>
 
#include <Swiften/Server/Server.h>
 
#include <Swiften/Network/DummyNetworkFactories.h>
 
#include <Swiften/Network/DummyConnectionServer.h>
 
#include <Swiften/Network/ConnectionFactory.h>
 
#include <Swiften/Network/DummyTimerFactory.h>
 

	
 
using namespace Transport;
 

	
 
class UserRegistryTest : public CPPUNIT_NS :: TestFixture {
 
	CPPUNIT_TEST_SUITE(UserRegistryTest);
 
	CPPUNIT_TEST(login);
 
	CPPUNIT_TEST(loginInvalidPassword);
 
	CPPUNIT_TEST(loginTwoClientsValidPasswords);
 
	CPPUNIT_TEST(loginTwoClientsDifferentPasswords);
 
	CPPUNIT_TEST(loginDisconnect);
 
	CPPUNIT_TEST(removeLater);
 
	CPPUNIT_TEST_SUITE_END();
 

	
 
	public:
 
		void setUp (void) {
 
			state1 = Init;
 
			state2 = Init;
 
			std::istringstream ifs;
 
			cfg = new Config();
 
			cfg->load(ifs);
 

	
 
			loop = new Swift::DummyEventLoop();
 
			factories = new Swift::DummyNetworkFactories(loop);
 

	
 
			userRegistry = new UserRegistry(cfg, factories);
 
			userRegistry->onConnectUser.connect(bind(&UserRegistryTest::handleConnectUser, this, _1));
 
			userRegistry->onDisconnectUser.connect(bind(&UserRegistryTest::handleDisconnectUser, this, _1));
 

	
 
			server = new Swift::Server(loop, factories, userRegistry, "localhost", 5222);
 
			server->start();
 
			connectionServer = server->getConnectionServer();
 

	
 
			client1 = factories->getConnectionFactory()->createConnection();
 
			dynamic_cast<Swift::DummyConnectionServer *>(connectionServer.get())->acceptConnection(client1);
 

	
 
			dynamic_cast<Swift::DummyConnection *>(client1.get())->onDataSent.connect(boost::bind(&UserRegistryTest::handleDataReceived, this, _1, client1));
 

	
 
			client2 = factories->getConnectionFactory()->createConnection();
 
			dynamic_cast<Swift::DummyConnectionServer *>(connectionServer.get())->acceptConnection(client2);
 

	
 
			dynamic_cast<Swift::DummyConnection *>(client2.get())->onDataSent.connect(boost::bind(&UserRegistryTest::handleDataReceived, this, _1, client2));
 

	
 
			loop->processEvents();
 
		}
 

	
 
		void tearDown (void) {
 
			delete server;
 
			dynamic_cast<Swift::DummyConnection *>(client1.get())->onDataSent.disconnect(boost::bind(&UserRegistryTest::handleDataReceived, this, _1, client1));
 
			client1.reset();
 
			dynamic_cast<Swift::DummyConnection *>(client2.get())->onDataSent.disconnect(boost::bind(&UserRegistryTest::handleDataReceived, this, _1, client2));
 
			client2.reset();
 
			connectionServer.reset();
 
			delete userRegistry;
 
			delete factories;
 
			delete loop;
 
			delete cfg;
 
			received1.clear();
 
			received2.clear();
 
		}
 

	
 
		void send(boost::shared_ptr<Swift::Connection> conn, const std::string &data) {
 
			dynamic_cast<Swift::DummyConnection *>(conn.get())->receive(Swift::createSafeByteArray(data));
 
			loop->processEvents();
 
		}
 

	
 
		void sendCredentials(boost::shared_ptr<Swift::Connection> conn, const std::string &username, const std::string &password, const std::string &b64) {
 
			std::vector<std::string> &received = conn == client1 ? received1 : received2;
 
			send(conn, "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='localhost' version='1.0'>");
 
			CPPUNIT_ASSERT_EQUAL(2, (int) received.size());
 
			CPPUNIT_ASSERT(received[0].find("<?xml version=\"1.0\"?>") == 0);
 
			CPPUNIT_ASSERT(received[1].find("PLAIN") != std::string::npos);
 
			received.clear();
 

	
 
			// username:test
 
			send(conn, "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>" + b64 + "</auth>");
 
			if (conn == client1)
 
				CPPUNIT_ASSERT_EQUAL(Connecting, state1);
 
// 			else
 
// 				CPPUNIT_ASSERT_EQUAL(Connecting, state2);
 
			CPPUNIT_ASSERT_EQUAL(password, userRegistry->getUserPassword(username));
 
			CPPUNIT_ASSERT_EQUAL(std::string(""), userRegistry->getUserPassword("unknown@localhost"));
 
		}
 

	
 
		void bindSession(boost::shared_ptr<Swift::Connection> conn) {
 
			std::vector<std::string> &received = conn == client1 ? received1 : received2;
 

	
 
			send(conn, "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='localhost' version='1.0'>");
 
			CPPUNIT_ASSERT_EQUAL(2, (int) received.size());
 
			CPPUNIT_ASSERT(received[0].find("<?xml version=\"1.0\"?>") == 0);
 
			CPPUNIT_ASSERT(received[1].find("urn:ietf:params:xml:ns:xmpp-bind") != std::string::npos);
 
			CPPUNIT_ASSERT(received[1].find("urn:ietf:params:xml:ns:xmpp-session") != std::string::npos);
 
			
 
		}
 

	
 
		void handleDataReceived(const Swift::SafeByteArray &data, boost::shared_ptr<Swift::Connection> conn) {
 
			if (conn == client1) {
src/transport.cpp
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/transport.h"
 
#include <boost/bind.hpp>
 
#include <boost/smart_ptr/make_shared.hpp>
 
#include <boost/algorithm/string/predicate.hpp>
 
#include "transport/storagebackend.h"
 
#include "transport/factory.h"
 
#include "transport/userregistry.h"
 
#include "transport/logging.h"
 
#include "storageparser.h"
 
#ifdef _WIN32
 
#include <Swiften/TLS/CAPICertificate.h>
 
#include "Swiften/TLS/Schannel/SchannelServerContext.h"
 
#include "Swiften/TLS/Schannel/SchannelServerContextFactory.h"
 
#else
 
#include "Swiften/TLS/PKCS12Certificate.h"
 
#include "Swiften/TLS/CertificateWithKey.h"
 
#include "Swiften/TLS/OpenSSL/OpenSSLServerContext.h"
 
#include "Swiften/TLS/OpenSSL/OpenSSLServerContextFactory.h"
 
#endif
 
#include "Swiften/Parser/PayloadParsers/AttentionParser.h"
 
#include "Swiften/Serializer/PayloadSerializers/AttentionSerializer.h"
 
#include "Swiften/Parser/PayloadParsers/XHTMLIMParser.h"
 
#include "Swiften/Serializer/PayloadSerializers/XHTMLIMSerializer.h"
 
#include "Swiften/Parser/PayloadParsers/StatsParser.h"
 
#include "Swiften/Serializer/PayloadSerializers/StatsSerializer.h"
 
#include "Swiften/Parser/PayloadParsers/GatewayPayloadParser.h"
 
#include "Swiften/Serializer/PayloadSerializers/GatewayPayloadSerializer.h"
 
#include "Swiften/Serializer/PayloadSerializers/SpectrumErrorSerializer.h"
 
#include "Swiften/Parser/PayloadParsers/MUCPayloadParser.h"
 
#include "transport/BlockParser.h"
 
#include "transport/BlockSerializer.h"
 
#include "Swiften/Parser/PayloadParsers/InvisibleParser.h"
 
#include "Swiften/Serializer/PayloadSerializers/InvisibleSerializer.h"
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Parser/GenericPayloadParserFactory.h"
 

	
 
using namespace Swift;
 
using namespace boost;
 

	
 
namespace Transport {
 
	
 
DEFINE_LOGGER(logger, "Component");
 
DEFINE_LOGGER(logger_xml, "Component.XML");
 

	
 
Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, Config *config, Factory *factory, Transport::UserRegistry *userRegistry) {
 
	m_component = NULL;
 
	m_userRegistry = NULL;
 
	m_server = NULL;
 
	m_reconnectCount = 0;
 
	m_config = config;
 
	m_factory = factory;
 
	m_loop = loop;
 
	m_userRegistry = userRegistry;
 

	
 
	m_jid = Swift::JID(CONFIG_STRING(m_config, "service.jid"));
 

	
 
	m_factories = factories;
 

	
 
	m_reconnectTimer = m_factories->getTimerFactory()->createTimer(3000);
 
	m_reconnectTimer->onTick.connect(bind(&Component::start, this)); 
 

	
 
	if (CONFIG_BOOL(m_config, "service.server_mode")) {
 
		LOG4CXX_INFO(logger, "Creating component in server mode on port " << CONFIG_INT(m_config, "service.port"));
 
		m_server = new Swift::Server(loop, m_factories, m_userRegistry, m_jid, CONFIG_INT(m_config, "service.port"));
 
		if (!CONFIG_STRING(m_config, "service.cert").empty()) {
 
#ifndef _WIN32
 
//TODO: fix
 
			LOG4CXX_INFO(logger, "Using PKCS#12 certificate " << CONFIG_STRING(m_config, "service.cert"));
 
			LOG4CXX_INFO(logger, "SSLv23_server_method used.");
 
			TLSServerContextFactory *f = new OpenSSLServerContextFactory();
 
			CertificateWithKey::ref certificate = boost::make_shared<PKCS12Certificate>(CONFIG_STRING(m_config, "service.cert"), createSafeByteArray(CONFIG_STRING(m_config, "service.cert_password")));
 
			m_server->addTLSEncryption(f, certificate);
 
#endif
 
			
 
		}
 
		else {
 
			LOG4CXX_WARN(logger, "No PKCS#12 certificate used. TLS is disabled.");
 
		}
 
// 		m_server->start();
 
		m_stanzaChannel = m_server->getStanzaChannel();
 
		m_iqRouter = m_server->getIQRouter();
 

	
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<StorageParser>("private", "jabber:iq:private"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::AttentionParser>("attention", "urn:xmpp:attention:0"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::XHTMLIMParser>("html", "http://jabber.org/protocol/xhtml-im"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Transport::BlockParser>("block", "urn:xmpp:block:0"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::InvisibleParser>("invisible", "urn:xmpp:invisible:0"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::StatsParser>("query", "http://jabber.org/protocol/stats"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::GatewayPayloadParser>("query", "jabber:iq:gateway"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::MUCPayloadParser>("x", "http://jabber.org/protocol/muc"));
 

	
 
		m_server->addPayloadSerializer(new Swift::AttentionSerializer());
 
		m_server->addPayloadSerializer(new Swift::XHTMLIMSerializer());
 
		m_server->addPayloadSerializer(new Transport::BlockSerializer());
 
		m_server->addPayloadSerializer(new Swift::InvisibleSerializer());
 
		m_server->addPayloadSerializer(new Swift::StatsSerializer());
 
		m_server->addPayloadSerializer(new Swift::SpectrumErrorSerializer());
 
		m_server->addPayloadSerializer(new Swift::GatewayPayloadSerializer());
 

	
 
		m_server->onDataRead.connect(boost::bind(&Component::handleDataRead, this, _1));
 
		m_server->onDataWritten.connect(boost::bind(&Component::handleDataWritten, this, _1));
 
	}
 
	else {
 
		LOG4CXX_INFO(logger, "Creating component in gateway mode");
 
		m_component = new Swift::Component(loop, m_factories, m_jid, CONFIG_STRING(m_config, "service.password"));
 
		m_component->setSoftwareVersion("Spectrum", SPECTRUM_VERSION);
 
		m_component->onConnected.connect(bind(&Component::handleConnected, this));
 
		m_component->onError.connect(boost::bind(&Component::handleConnectionError, this, _1));
 
		m_component->onDataRead.connect(boost::bind(&Component::handleDataRead, this, _1));
 
		m_component->onDataWritten.connect(boost::bind(&Component::handleDataWritten, this, _1));
 

	
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<StorageParser>("private", "jabber:iq:private"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::AttentionParser>("attention", "urn:xmpp:attention:0"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::XHTMLIMParser>("html", "http://jabber.org/protocol/xhtml-im"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Transport::BlockParser>("block", "urn:xmpp:block:0"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::InvisibleParser>("invisible", "urn:xmpp:invisible:0"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::StatsParser>("query", "http://jabber.org/protocol/stats"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::GatewayPayloadParser>("query", "jabber:iq:gateway"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::MUCPayloadParser>("x", "http://jabber.org/protocol/muc"));
 

	
 
		m_component->addPayloadSerializer(new Swift::AttentionSerializer());
 
		m_component->addPayloadSerializer(new Swift::XHTMLIMSerializer());
 
		m_component->addPayloadSerializer(new Transport::BlockSerializer());
 
		m_component->addPayloadSerializer(new Swift::InvisibleSerializer());
 
		m_component->addPayloadSerializer(new Swift::StatsSerializer());
 
		m_component->addPayloadSerializer(new Swift::SpectrumErrorSerializer());
 
		m_component->addPayloadSerializer(new Swift::GatewayPayloadSerializer());
 

	
 
		m_stanzaChannel = m_component->getStanzaChannel();
 
		m_iqRouter = m_component->getIQRouter();
 
	}
 
@@ -182,146 +182,145 @@ Swift::StanzaChannel *Component::getStanzaChannel() {
 
}
 

	
 
Transport::PresenceOracle *Component::getPresenceOracle() {
 
	return m_presenceOracle;
 
}
 

	
 
void Component::start() {
 
	if (m_component && !m_component->isAvailable()) {
 
		LOG4CXX_INFO(logger, "Connecting XMPP server " << CONFIG_STRING(m_config, "service.server") << " port " << CONFIG_INT(m_config, "service.port"));
 
		if (CONFIG_INT(m_config, "service.port") == 5222) {
 
			LOG4CXX_WARN(logger, "Port 5222 is usually used for client connections, not for component connections! Are you sure you are using right port?");
 
		}
 
		m_reconnectCount++;
 
		m_component->connect(CONFIG_STRING(m_config, "service.server"), CONFIG_INT(m_config, "service.port"));
 
		m_reconnectTimer->stop();
 
	}
 
	else if (m_server) {
 
		LOG4CXX_INFO(logger, "Starting component in server mode on port " << CONFIG_INT(m_config, "service.port"));
 
		m_server->start();
 

	
 
		//Type casting to BoostConnectionServer since onStopped signal is not defined in ConnectionServer
 
		//Ideally, onStopped must be defined in ConnectionServer
 
		if (boost::dynamic_pointer_cast<Swift::BoostConnectionServer>(m_server->getConnectionServer())) {
 
			boost::dynamic_pointer_cast<Swift::BoostConnectionServer>(m_server->getConnectionServer())->onStopped.connect(boost::bind(&Component::handleServerStopped, this, _1));
 
		}
 
		
 
		// We're connected right here, because we're in server mode...
 
		handleConnected();
 
	}
 
}
 

	
 
void Component::stop() {
 
	if (m_component) {
 
		m_reconnectCount = 0;
 
		// TODO: Call this once swiften will fix assert(!session_);
 
// 		m_component->disconnect();
 
		m_reconnectTimer->stop();
 
	}
 
	else if (m_server) {
 
		LOG4CXX_INFO(logger, "Stopping component in server mode on port " << CONFIG_INT(m_config, "service.port"));
 
		m_server->stop();
 
	}
 
}
 

	
 
void Component::handleConnected() {
 
	onConnected();
 
	m_reconnectCount = 0;
 
	m_reconnectTimer->stop();
 
}
 

	
 
void Component::handleServerStopped(boost::optional<Swift::BoostConnectionServer::Error> e) {
 
	if(e != NULL ) {
 
		if(*e == Swift::BoostConnectionServer::Conflict) {
 
			LOG4CXX_INFO(logger, "Port "<< CONFIG_INT(m_config, "service.port") << " already in use! Stopping server..");
 
			if (CONFIG_INT(m_config, "service.port") == 5347) {
 
				LOG4CXX_INFO(logger, "Port 5347 is usually used for components. You are using server_mode=1. Are you sure you don't want to use server_mode=0 and run spectrum as component?");
 
			}
 
		}
 
		if(*e == Swift::BoostConnectionServer::UnknownError)
 
			LOG4CXX_INFO(logger, "Unknown error occured! Stopping server..");
 
		exit(1);
 
	}
 
}
 

	
 

	
 
void Component::handleConnectionError(const ComponentError &error) {
 
	onConnectionError(error);
 
// 	if (m_reconnectCount == 2)
 
// 		Component::instance()->userManager()->removeAllUsers();
 
	std::string str = "Unknown error";
 
	switch (error.getType()) {
 
		case ComponentError::UnknownError: str = "Unknown error"; break;
 
		case ComponentError::ConnectionError: str = "Connection error"; break;
 
		case ComponentError::ConnectionReadError: str = "Connection read error"; break;
 
		case ComponentError::ConnectionWriteError: str = "Connection write error"; break;
 
		case ComponentError::XMLError: str = "XML Error"; break;
 
		case ComponentError::AuthenticationFailedError: str = "Authentication failed error"; break;
 
		case ComponentError::UnexpectedElementError: str = "Unexpected element error"; break;
 
	}
 
	LOG4CXX_INFO(logger, "Disconnected from XMPP server. Error: " << str);
 

	
 
	m_reconnectTimer->start();
 
}
 

	
 
void Component::handleDataRead(const Swift::SafeByteArray &data) {
 
	std::string d = safeByteArrayToString(data);
 
	if (!boost::starts_with(d, "<auth")) {
 
		LOG4CXX_INFO(logger_xml, "XML IN " << d);
 
	}
 
}
 

	
 
void Component::handleDataWritten(const Swift::SafeByteArray &data) {
 
	LOG4CXX_INFO(logger_xml, "XML OUT " << safeByteArrayToString(data));
 
}
 

	
 
void Component::handlePresence(Swift::Presence::ref presence) {
 
	bool isMUC = presence->getPayload<MUCPayload>() != NULL || *presence->getTo().getNode().c_str() == '#';
 
	// filter out login/logout presence spam
 
	if (!presence->getTo().getNode().empty() && isMUC == false)
 
	if (!presence->getTo().getNode().empty())
 
		return;
 

	
 
	// filter out bad presences
 
	if (!presence->getFrom().isValid()) {
 
		return;
 
	}
 

	
 
	switch (presence->getType()) {
 
		case Presence::Error:
 
		case Presence::Subscribe:
 
		case Presence::Subscribed:
 
		case Presence::Unsubscribe:
 
		case Presence::Unsubscribed:
 
			return;
 
		default:
 
			break;
 
	};
 

	
 
	// check if we have this client's capabilities and ask for them
 
	if (presence->getType() != Swift::Presence::Unavailable) {
 
		boost::shared_ptr<CapsInfo> capsInfo = presence->getPayload<CapsInfo>();
 
		if (capsInfo && capsInfo->getHash() == "sha-1") {
 
			/*haveFeatures = */m_entityCapsManager->getCaps(presence->getFrom()) != DiscoInfo::ref();
 
		}
 
#ifdef SUPPORT_LEGACY_CAPS
 
		else {
 
			GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(presence->getFrom(), m_iqRouter);
 
			discoInfoRequest->onResponse.connect(boost::bind(&Component::handleDiscoInfoResponse, this, _1, _2, presence->getFrom()));
 
			discoInfoRequest->send();
 
		}
 
#endif
 
	}
 

	
 
	onUserPresenceReceived(presence);
 
}
 

	
 
void Component::handleDiscoInfoResponse(boost::shared_ptr<Swift::DiscoInfo> info, Swift::ErrorPayload::ref error, const Swift::JID& jid) {
 
#ifdef SUPPORT_LEGACY_CAPS
 
	onUserDiscoInfoReceived(jid, info);
 
#endif
 
}
 

	
 
void Component::handleCapsChanged(const Swift::JID& jid) {
 
	onUserDiscoInfoReceived(jid, m_entityCapsManager->getCaps(jid));
 
}
 

	
 
}
src/user.cpp
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/user.h"
 
#include "transport/transport.h"
 
#include "transport/storagebackend.h"
 
#include "transport/rostermanager.h"
 
#include "transport/usermanager.h"
 
#include "transport/conversationmanager.h"
 
#include "transport/presenceoracle.h"
 
#include "transport/logging.h"
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Server/ServerStanzaChannel.h"
 
#include "Swiften/Elements/StreamError.h"
 
#include "Swiften/Elements/MUCPayload.h"
 
#include "Swiften/Elements/SpectrumErrorPayload.h"
 
#include <boost/foreach.hpp>
 
#include <stdio.h>
 
#include <stdlib.h>
 

	
 
using namespace boost;
 

	
 
#define foreach         BOOST_FOREACH
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "User");
 

	
 
User::User(const Swift::JID &jid, UserInfo &userInfo, Component *component, UserManager *userManager) {
 
	m_jid = jid.toBare();
 
	m_data = NULL;
 

	
 
	m_cacheMessages = false;
 
	m_component = component;
 
	m_presenceOracle = component->m_presenceOracle;
 
	m_entityCapsManager = component->m_entityCapsManager;
 
	m_userManager = userManager;
 
	m_userInfo = userInfo;
 
	m_connected = false;
 
	m_readyForConnect = false;
 
	m_ignoreDisconnect = false;
 
	m_resources = 0;
 
	m_reconnectCounter = 0;
 

	
 
	m_reconnectTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(5000);
 
	m_reconnectTimer->onTick.connect(boost::bind(&User::onConnectingTimeout, this)); 
 

	
 
	m_rosterManager = new RosterManager(this, m_component);
 
	m_conversationManager = new ConversationManager(this, m_component);
 
	LOG4CXX_INFO(logger, m_jid.toString() << ": Created");
 
	updateLastActivity();
 
}
 

	
 
User::~User(){
 
	LOG4CXX_INFO(logger, m_jid.toString() << ": Destroying");
 
	if (m_component->inServerMode()) {
 
		dynamic_cast<Swift::ServerStanzaChannel *>(m_component->getStanzaChannel())->finishSession(m_jid, boost::shared_ptr<Swift::Element>());
 
	}
 

	
 
	m_reconnectTimer->stop();
 
	delete m_rosterManager;
 
	delete m_conversationManager;
 
}
 

	
 
const Swift::JID &User::getJID() {
 
	return m_jid;
 
}
 

	
 
std::vector<Swift::JID> User::getJIDWithFeature(const std::string &feature) {
 
	std::vector<Swift::JID> jid;
 
	std::vector<Swift::Presence::ref> presences = m_presenceOracle->getAllPresence(m_jid);
 

	
 
	foreach(Swift::Presence::ref presence, presences) {
 
		if (presence->getType() == Swift::Presence::Unavailable)
 
			continue;
 

	
 
		Swift::DiscoInfo::ref discoInfo = m_entityCapsManager->getCaps(presence->getFrom());
 
		if (!discoInfo) {
 
#ifdef SUPPORT_LEGACY_CAPS
 
			if (m_legacyCaps.find(presence->getFrom()) != m_legacyCaps.end()) {
 
				discoInfo = m_legacyCaps[presence->getFrom()];
 
			}
 
			else {
 
				continue;
 
			}
 

	
 
			if (!discoInfo) {
 
				continue;
 
			}
 
#else
 
			continue;
 
#endif
 
		}
 

	
 
		if (discoInfo->hasFeature(feature)) {
 
			LOG4CXX_INFO(logger, m_jid.toString() << ": Found JID with " << feature << " feature: " << presence->getFrom().toString());
 
			jid.push_back(presence->getFrom());
 
		}
 
	}
 

	
 
	if (jid.empty()) {
 
		LOG4CXX_INFO(logger, m_jid.toString() << ": No JID with " << feature << " feature " << m_legacyCaps.size());
 
	}
 
	return jid;
 
}
 

	
 
Swift::DiscoInfo::ref User::getCaps(const Swift::JID &jid) const {
 
	Swift::DiscoInfo::ref discoInfo = m_entityCapsManager->getCaps(jid);
 
#ifdef SUPPORT_LEGACY_CAPS
 
	if (!discoInfo) {
 
		std::map<Swift::JID, Swift::DiscoInfo::ref>::const_iterator it = m_legacyCaps.find(jid);
 
		if (it != m_legacyCaps.end()) {
 
			discoInfo = it->second;
 
		}
 
	}
 
#endif
 
	return discoInfo;
 
}
 

	
 
void User::sendCurrentPresence() {
 
	if (m_component->inServerMode()) {
 
		return;
 
	}
 

	
 
	std::vector<Swift::Presence::ref> presences = m_presenceOracle->getAllPresence(m_jid);
 
	foreach(Swift::Presence::ref presence, presences) {
 
		if (presence->getType() == Swift::Presence::Unavailable) {
 
			continue;
 
		}
 

	
 
		if (m_connected) {
 
			Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare());
 
			if (highest) {
 
				Swift::Presence::ref response = Swift::Presence::create(highest);
 
				response->setTo(presence->getFrom());
 
				response->setFrom(m_component->getJID());
 
				m_component->getStanzaChannel()->sendPresence(response);
 
			}
 
			else {
 
				Swift::Presence::ref response = Swift::Presence::create();
 
				response->setTo(presence->getFrom());
 
				response->setFrom(m_component->getJID());
 
				response->setType(Swift::Presence::Unavailable);
 
				m_component->getStanzaChannel()->sendPresence(response);
 
			}
 
		}
 
		else {
 
			Swift::Presence::ref response = Swift::Presence::create();
 
			response->setTo(presence->getFrom());
 
			response->setFrom(m_component->getJID());
 
			response->setType(Swift::Presence::Unavailable);
 
			response->setStatus("Connecting");
 
			m_component->getStanzaChannel()->sendPresence(response);
 
		}
 
	}
 
}
 

	
 
void User::setConnected(bool connected) {
 
	m_connected = connected;
 
	m_reconnectCounter = 0;
 
	setIgnoreDisconnect(false);
 
	updateLastActivity();
 

	
 
	sendCurrentPresence();
 

	
 
	if (m_connected) {
 
		BOOST_FOREACH(Swift::Presence::ref &presence, m_joinedRooms) {
 
			handlePresence(presence, true);
 
		}
 
	}
 
}
 

	
 
void User::setCacheMessages(bool cacheMessages) {
 
	if (m_component->inServerMode() && !m_cacheMessages && cacheMessages) {
 
		m_conversationManager->sendCachedChatMessages();
 
	}
 
	m_cacheMessages = cacheMessages;
 
}
 

	
 
void User::handlePresence(Swift::Presence::ref presence, bool forceJoin) {
 

	
 
	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) {
 
			
 
			// Forward status message to legacy network, but only if it's sent from active resource
 
// 					if (m_activeResource == presence->getFrom().getResource().getUTF8String()) {
 
// 						forwardStatus(presenceShow, stanzaStatus);
 
// 					}
 
			boost::shared_ptr<Swift::CapsInfo> capsInfo = presence->getPayload<Swift::CapsInfo>();
 
			if (capsInfo && capsInfo->getHash() == "sha-1") {
 
				if (m_entityCapsManager->getCaps(presence->getFrom()) != Swift::DiscoInfo::ref()) {
 
					LOG4CXX_INFO(logger, m_jid.toString() << ": Ready to be connected to legacy network");
 
					m_readyForConnect = true;
 
					onReadyToConnect();
 
				}
 
				else {
 
					m_reconnectTimer->start();
 
				}
 
			}
 
			else if (m_component->inServerMode()) {
 
				LOG4CXX_INFO(logger, m_jid.toString() << ": Ready to be connected to legacy network");
 
				m_readyForConnect = true;
 
				onReadyToConnect();
 
			}
 
			else {
 
				m_reconnectTimer->start();
 
			}
 
		}
 
	}
 

	
 
	
 
	if (!presence->getTo().getNode().empty()) {
 
		bool isMUC = presence->getPayload<Swift::MUCPayload>() != NULL || *presence->getTo().getNode().c_str() == '#';
 
	if (isMUC) {
 
		if (presence->getType() == Swift::Presence::Unavailable) {
 
			std::string room = Buddy::JIDToLegacyName(presence->getTo());
 
			Conversation *conv = m_conversationManager->getConversation(room);
 
			if (conv) {
 
				conv->removeJID(presence->getFrom());
 
				if (!conv->getJIDs().empty()) {
 
					return;
 
				}
 
			}
 
			else {
 
				return;
 
			}
 

	
 
			if (getUserSetting("stay_connected") != "1") {
 
				LOG4CXX_INFO(logger, m_jid.toString() << ": Going to left room " << room);
 
				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;
 
				}
 
			}
 
		}
 
		else {
 
		else if (isMUC) {
 
			// force connection to legacy network to let backend to handle auto-join on connect.
 
			if (!m_readyForConnect) {
 
				LOG4CXX_INFO(logger, m_jid.toString() << ": Ready to be connected to legacy network");
 
				m_readyForConnect = true;
 
				onReadyToConnect();
 
			}
 

	
 
			std::string room = Buddy::JIDToLegacyName(presence->getTo());
 
			std::string password = "";
 
			if (presence->getPayload<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()) {
 
					LOG4CXX_INFO(logger, m_jid.toString() << ": User has already tried to join room " << room << " as " << presence->getTo().getResource());
 
				}
 
				else {
 
					conv->addJID(presence->getFrom());
 
					conv->sendParticipants(presence->getFrom());
 
					conv->sendCachedMessages(presence->getFrom());
 
				}
 

	
 
				if (forceJoin) {
 
					onRoomJoined(presence->getFrom(), room, presence->getTo().getResource(), password);
 
				}
 
				return;
 
			}
 

	
 
			bool isInJoined = false;
 
			BOOST_FOREACH(Swift::Presence::ref &p, m_joinedRooms) {
 
				if (p->getTo() == presence->getTo()) {
 
					isInJoined = true;
 
				}
 
			}
 
			if (!isInJoined) {
 
				m_joinedRooms.push_back(presence);
 
			}
 

	
 
			if (!m_connected) {
 
				LOG4CXX_INFO(logger, m_jid.toString() << ": Joining room " << room << " postponed, because use is not connected to legacy network yet.");
 
				return;
 
			}
 

	
 
			LOG4CXX_INFO(logger, m_jid.toString() << ": Going to join room " << room << " as " << presence->getTo().getResource());
 

	
 
			conv = m_component->getFactory()->createConversation(m_conversationManager, room, true);
 
			m_conversationManager->addConversation(conv);
 
			conv->setNickname(presence->getTo().getResource());
 
			conv->addJID(presence->getFrom());
 

	
 
			onRoomJoined(presence->getFrom(), room, presence->getTo().getResource(), password);
 
		}
 
		return;
 
	}
 

	
 
	int currentResourcesCount = m_presenceOracle->getAllPresence(m_jid).size();
 

	
 
	m_conversationManager->resetResources();
 

	
 

	
 
	if (presence->getType() == Swift::Presence::Unavailable) {
 
		m_conversationManager->removeJID(presence->getFrom());
 

	
 
		std::string presences;
 
		std::vector<Swift::Presence::ref> ps = m_presenceOracle->getAllPresence(m_jid);
 
		BOOST_FOREACH(Swift::Presence::ref p, ps) {
 
			if (p != presence) {
 
				presences += p->getFrom().toString() + " ";
 
			}
 
		};
 

	
 
		if (!presences.empty()) {
 
			LOG4CXX_INFO(logger, m_jid.toString() << ": User is still connected from following clients: " << presences);
 
		}
 
		else {
 
			LOG4CXX_INFO(logger, m_jid.toString() << ": Last client disconnected");
 
		}
 
	}
 

	
 

	
 
	// User wants to disconnect this resource
 
	if (!m_component->inServerMode()) {
 
		if (presence->getType() == Swift::Presence::Unavailable) {
 
				// Send unavailable presences for online contacts
 
				m_rosterManager->sendUnavailablePresences(presence->getFrom());
 

	
 
				// Send unavailable presence for transport contact itself
 
				Swift::Presence::ref response = Swift::Presence::create();
 
				response->setTo(presence->getFrom());
 
				response->setFrom(m_component->getJID());
 
				response->setType(Swift::Presence::Unavailable);
 
				m_component->getStanzaChannel()->sendPresence(response);
 
		}
 
		else {
 
			sendCurrentPresence();
 
		}
 
	}
 

	
 
	// This resource is new, so we have to send buddies presences
 
	if (presence->getType() != Swift::Presence::Unavailable && currentResourcesCount != m_resources) {
 
		m_rosterManager->sendCurrentPresences(presence->getFrom());
 
	}
 

	
 
	m_resources = currentResourcesCount;
 

	
 
	// Change legacy network presence
 
	if (m_readyForConnect) {
 
		Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare());
 
		if (highest) {
 
			if (highest->getType() == Swift::Presence::Unavailable && getUserSetting("stay_connected") == "1") {
 
				m_resources = 0;
 
				m_conversationManager->clearJIDs();
 
				setCacheMessages(true);
 
				return;
 
			}
 
			Swift::Presence::ref response = Swift::Presence::create(highest);
 
			response->setTo(m_jid);
 
			response->setFrom(m_component->getJID());
 
			LOG4CXX_INFO(logger, m_jid.toString() << ": Changing legacy network presence to " << response->getType());
 
			onPresenceChanged(highest);
 
			setCacheMessages(false);
 
		}
 
		else {
 
			if (getUserSetting("stay_connected") == "1") {
 
				m_resources = 0;
 
				m_conversationManager->clearJIDs();
 
				setCacheMessages(true);
 
				return;
 
			}
 
			Swift::Presence::ref response = Swift::Presence::create();
 
			response->setTo(m_jid.toBare());
 
			response->setFrom(m_component->getJID());
 
			response->setType(Swift::Presence::Unavailable);
 
			onPresenceChanged(response);
 
		}
 
	}
 
}
 

	
 
void User::handleSubscription(Swift::Presence::ref presence) {
 
	m_rosterManager->handleSubscription(presence);
 
}
 

	
 
void User::handleDiscoInfo(const Swift::JID& jid, boost::shared_ptr<Swift::DiscoInfo> info) {
 
	LOG4CXX_INFO(logger, jid.toString() << ": got disco#info");
 
#ifdef SUPPORT_LEGACY_CAPS
 
	Swift::DiscoInfo::ref discoInfo = m_entityCapsManager->getCaps(jid);
 
	// This is old legacy cap which is not stored in entityCapsManager,
 
	// we have to store it in our user class.
 
	if (!discoInfo) {
 
		LOG4CXX_INFO(logger, jid.toString() << ": LEGACY");
 
		m_legacyCaps[jid] = info;
 
	}
 
#endif
 

	
 
	onConnectingTimeout();
 
}
 

	
 
void User::onConnectingTimeout() {
 
	if (m_connected || m_readyForConnect)
 
		return;
 
	m_reconnectTimer->stop();
 
	m_readyForConnect = true;
 
	onReadyToConnect();
 

	
 
	Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare());
 
	if (highest) {
 
		LOG4CXX_INFO(logger, m_jid.toString() << ": Changing legacy network presence to " << highest->getType());
 
		onPresenceChanged(highest);
 
	}
 
}
 

	
 
void User::setIgnoreDisconnect(bool ignoreDisconnect) {
 
	m_ignoreDisconnect = ignoreDisconnect;
src/usermanager.cpp
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/usermanager.h"
 
#include "transport/user.h"
 
#include "transport/transport.h"
 
#include "transport/storagebackend.h"
 
#include "transport/conversationmanager.h"
 
#include "transport/rostermanager.h"
 
#include "transport/userregistry.h"
 
#include "transport/logging.h"
 
#include "transport/discoitemsresponder.h"
 
#include "storageresponder.h"
 

	
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Server/ServerStanzaChannel.h"
 
#include "Swiften/Elements/StreamError.h"
 
#include "Swiften/Elements/MUCPayload.h"
 
#include "Swiften/Elements/ChatState.h"
 
#ifndef __FreeBSD__
 
#include "malloc.h"
 
#endif
 
// #include "valgrind/memcheck.h"
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "UserManager");
 

	
 
UserManager::UserManager(Component *component, UserRegistry *userRegistry, DiscoItemsResponder *discoItemsResponder, StorageBackend *storageBackend) {
 
	m_cachedUser = NULL;
 
	m_onlineBuddies = 0;
 
	m_sentToXMPP = 0;
 
	m_sentToBackend = 0;
 
	m_component = component;
 
	m_storageBackend = storageBackend;
 
	m_storageResponder = NULL;
 
	m_userRegistry = userRegistry;
 
	m_discoItemsResponder = discoItemsResponder;
 

	
 
	if (m_storageBackend) {
 
		m_storageResponder = new StorageResponder(component->getIQRouter(), m_storageBackend, this);
 
		m_storageResponder->start();
 
	}
 

	
 
	component->onUserPresenceReceived.connect(bind(&UserManager::handlePresence, this, _1));
 
	component->onUserDiscoInfoReceived.connect(bind(&UserManager::handleDiscoInfo, this, _1, _2));
 
	m_component->getStanzaChannel()->onMessageReceived.connect(bind(&UserManager::handleMessageReceived, this, _1));
 
	m_component->getStanzaChannel()->onPresenceReceived.connect(bind(&UserManager::handleGeneralPresenceReceived, this, _1));
 

	
 
	m_userRegistry->onConnectUser.connect(bind(&UserManager::connectUser, this, _1));
 
	m_userRegistry->onDisconnectUser.connect(bind(&UserManager::disconnectUser, this, _1));
 

	
 
	m_removeTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(1);
 
}
 

	
 
UserManager::~UserManager(){
 
	if (m_storageResponder) {
 
		m_storageResponder->stop();
 
		delete m_storageResponder;
 
	}
 
}
 

	
 
void UserManager::addUser(User *user) {
 
	m_users[user->getJID().toBare().toString()] = user;
 
	if (m_storageBackend) {
 
		m_storageBackend->setUserOnline(user->getUserInfo().id, true);
 
	}
 
	onUserCreated(user);
 
}
 

	
 
User *UserManager::getUser(const std::string &barejid){
 
	if (m_cachedUser && barejid == m_cachedUser->getJID().toBare().toString()) {
 
		return m_cachedUser;
 
	}
 

	
 
	if (m_users.find(barejid) != m_users.end()) {
 
		User *user = m_users[barejid];
 
		m_cachedUser = user;
 
		return user;
 
	}
 
	return NULL;
 
}
 

	
 
Swift::DiscoInfo::ref UserManager::getCaps(const Swift::JID &jid) const {
 
	std::map<std::string, User *>::const_iterator it = m_users.find(jid.toBare().toString());
 
	if (it == m_users.end()) {
 
		return Swift::DiscoInfo::ref();
 
	}
 

	
 
	User *user = it->second;
 
	return user->getCaps(jid);
 
}
 

	
 
void UserManager::removeUser(User *user, bool onUserBehalf) {
 
	m_users.erase(user->getJID().toBare().toString());
 
	if (m_cachedUser == user)
 
		m_cachedUser = NULL;
 

	
 
	if (m_component->inServerMode()) {
 
		disconnectUser(user->getJID());
 
	}
 
	else {
 
		// User could be disconnected by User::handleDisconnect() method, but
 
		// Transport::PresenceOracle could still contain his last presence.
 
		// We have to clear all received presences for this user in PresenceOracle.
 
		m_component->getPresenceOracle()->clearPresences(user->getJID().toBare());
 
	}
 

	
 
	if (m_storageBackend && onUserBehalf) {
 
		m_storageBackend->setUserOnline(user->getUserInfo().id, false);
 
	}
 

	
 
	onUserDestroyed(user);
 
	delete user;
 
#ifndef WIN32
 
@@ -136,374 +137,393 @@ void UserManager::removeUser(User *user, bool onUserBehalf) {
 
}
 

	
 
void UserManager::removeAllUsers(bool onUserBehalf) {
 
	while(m_users.begin() != m_users.end()) {
 
		removeUser((*m_users.begin()).second, onUserBehalf);
 
	}
 
}
 

	
 
int UserManager::getUserCount() {
 
	return m_users.size();
 
}
 

	
 
void UserManager::handleDiscoInfo(const Swift::JID& jid, boost::shared_ptr<Swift::DiscoInfo> info) {
 
	User *user = getUser(jid.toBare().toString());
 
	if (!user) {
 
		return;
 
	}
 

	
 
	user->handleDiscoInfo(jid, info);
 
}
 

	
 
void UserManager::handlePresence(Swift::Presence::ref presence) {
 
	std::string barejid = presence->getTo().toBare().toString();
 
	std::string userkey = presence->getFrom().toBare().toString();
 

	
 
	User *user = getUser(userkey);
 
	// Create user class if it's not there
 
	if (!user) {
 
		// Admin user is not legacy network user, so do not create User class instance for him
 
		if (m_component->inServerMode()) {
 
			std::vector<std::string> const &x = CONFIG_VECTOR(m_component->getConfig(),"service.admin_jid");
 
			if (std::find(x.begin(), x.end(), presence->getFrom().toBare().toString()) != x.end()) {
 
				// Send admin contact to the user.
 
				Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload());
 
				Swift::RosterItemPayload item;
 
				item.setJID(m_component->getJID());
 
				item.setName("Admin");
 
				item.setSubscription(Swift::RosterItemPayload::Both);
 
				payload->addItem(item);
 

	
 
				Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, presence->getFrom(), m_component->getIQRouter());
 
				request->send();
 

	
 
				Swift::Presence::ref response = Swift::Presence::create();
 
				response->setTo(presence->getFrom());
 
				response->setFrom(m_component->getJID());
 
				m_component->getStanzaChannel()->sendPresence(response);
 
				return;
 
		    }
 
		}
 

	
 
		UserInfo res;
 
		bool registered = m_storageBackend ? m_storageBackend->getUser(userkey, res) : false;
 

	
 
		// No user and unavailable presence -> answer with unavailable
 
		if (presence->getType() == Swift::Presence::Unavailable || presence->getType() == Swift::Presence::Probe) {
 
			Swift::Presence::ref response = Swift::Presence::create();
 
			response->setTo(presence->getFrom());
 
			response->setFrom(presence->getTo());
 
			response->setType(Swift::Presence::Unavailable);
 
			m_component->getStanzaChannel()->sendPresence(response);
 

	
 
			// bother him with probe presence, just to be
 
			// sure he is subscribed to us.
 
			if (/*registered && */presence->getType() == Swift::Presence::Probe) {
 
				Swift::Presence::ref response = Swift::Presence::create();
 
				response->setTo(presence->getFrom());
 
				response->setFrom(presence->getTo());
 
				response->setType(Swift::Presence::Probe);
 
				m_component->getStanzaChannel()->sendPresence(response);
 
			}
 

	
 
			// Set user offline in database
 
			if (m_storageBackend) {
 
				UserInfo res;
 
				bool registered = m_storageBackend->getUser(userkey, res);
 
				if (registered) {
 
					m_storageBackend->setUserOnline(res.id, false);
 
				}
 
			}
 
			return;
 
		}
 

	
 
		// In server mode, we don't need registration normally, but for networks like IRC
 
		// or Twitter where there's no real authorization using password, we have to force
 
		// registration otherwise some data (like bookmarked rooms) could leak.
 
		if (m_component->inServerMode()) {
 
			if (!registered) {
 
				// If we need registration, stop login process because user is not registered
 
				if (CONFIG_BOOL_DEFAULTED(m_component->getConfig(), "registration.needRegistration", false)) {
 
					m_userRegistry->onPasswordInvalid(presence->getFrom());
 
					return;
 
				}
 
				res.password = "";
 
				res.uin = presence->getFrom().getNode();
 
				res.jid = userkey;
 
				if (res.uin.find_last_of("%") != std::string::npos) { // OK
 
				while (res.uin.find_last_of("%") != std::string::npos) { // OK
 
					res.uin.replace(res.uin.find_last_of("%"), 1, "@"); // OK
 
				}
 
				if (m_storageBackend) {
 
					// store user and getUser again to get user ID.
 
					m_storageBackend->setUser(res);
 
					registered = m_storageBackend->getUser(userkey, res);
 
				}
 
				else {
 
					registered = true;
 
				}
 
			}
 
			res.password = m_userRegistry->getUserPassword(userkey);
 
		}
 

	
 
		// We allow auto_register feature in gateway-mode. This allows IRC user to register
 
		// the transport just by joining the room.
 
		if (!m_component->inServerMode()) {
 
			if (!registered && (CONFIG_BOOL(m_component->getConfig(), "registration.auto_register") ||
 
				!CONFIG_BOOL_DEFAULTED(m_component->getConfig(), "registration.needRegistration", true))) {
 
				res.password = "";
 
				res.jid = userkey;
 

	
 
				bool isMUC = presence->getPayload<Swift::MUCPayload>() != NULL || *presence->getTo().getNode().c_str() == '#';
 
				if (isMUC) {
 
					res.uin = presence->getTo().getResource();
 
				}
 
				else {
 
					res.uin = presence->getFrom().toString();
 
				}
 
				LOG4CXX_INFO(logger, "Auto-registering user " << userkey << " with uin=" << res.uin);
 

	
 
				if (m_storageBackend) {
 
					// store user and getUser again to get user ID.
 
					m_storageBackend->setUser(res);
 
					registered = m_storageBackend->getUser(userkey, res);
 
				}
 
				else {
 
					registered = true;
 
				}
 
			}
 
		}
 

	
 
		// Unregistered users are not able to login
 
		if (!registered) {
 
			LOG4CXX_WARN(logger, "Unregistered user " << userkey << " tried to login");
 
			return;
 
		}
 

	
 
		if (CONFIG_BOOL(m_component->getConfig(), "service.vip_only") && res.vip == false) {
 
			if (!CONFIG_STRING(m_component->getConfig(), "service.vip_message").empty()) {
 
				boost::shared_ptr<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";
 
			int type = (int) TYPE_BOOLEAN;
 
			m_storageBackend->getUserSetting(res.id, "enable_transport", type, value);
 
			transport_enabled = value == "1";
 
		}
 
		// User can disabled the transport using adhoc commands
 
		if (!transport_enabled) {
 
			LOG4CXX_INFO(logger, "User " << userkey << " has disabled transport, not logging");
 
			return;
 
		}
 

	
 
		// Create new user class and set storagebackend
 
		user = new User(presence->getFrom(), res, m_component, this);
 
		user->getRosterManager()->setStorageBackend(m_storageBackend);
 
		addUser(user);
 
	}
 

	
 
	// User can be handleDisconnected in addUser callback, so refresh the pointer
 
	user = getUser(userkey);
 
	if (!user) {
 
		m_userRegistry->onPasswordInvalid(presence->getFrom());
 
		return;
 
	}
 

	
 
	// Handle this presence
 
	user->handlePresence(presence);
 

	
 
	// Unavailable MUC presence should not trigger whole account disconnection, so block it here.
 
	bool isMUC = presence->getPayload<Swift::MUCPayload>() != NULL || *presence->getTo().getNode().c_str() == '#';
 
	if (isMUC)
 
		return;
 

	
 
	// Unavailable presence could remove this user, because he could be unavailable
 
	if (presence->getType() == Swift::Presence::Unavailable) {
 
		if (user) {
 
			if (user->getUserSetting("stay_connected") == "1") {
 
				return;
 
			}
 

	
 
			Swift::Presence::ref highest = m_component->getPresenceOracle()->getHighestPriorityPresence(presence->getFrom().toBare());
 
			// There's no presence for this user, so disconnect
 
			if (!highest || (highest && highest->getType() == Swift::Presence::Unavailable)) {
 
				m_removeTimer->onTick.connect(boost::bind(&UserManager::handleRemoveTimeout, this, user->getJID().toBare().toString(), user, false));
 
				m_removeTimer->start();
 
			}
 
		}
 
	}
 
}
 

	
 
void UserManager::handleRemoveTimeout(const std::string jid, User *u, bool reconnect) {
 
	m_removeTimer->onTick.disconnect(boost::bind(&UserManager::handleRemoveTimeout, this, jid, u, reconnect));
 

	
 
	// Maybe this User instance has been deleted in mean time and we would remove new one,
 
	// so better check for it and ignore deletion if "u" does not exist anymore.
 
	User *user = getUser(jid);
 
	if (user != u) {
 
		return;
 
	}
 

	
 
	// Remove user
 
	if (user) {
 
		removeUser(user);
 
	}
 

	
 
	// Connect the user again when we're reconnecting.
 
	if (reconnect) {
 
		connectUser(jid);
 
	}
 
}
 

	
 
void UserManager::handleMessageReceived(Swift::Message::ref message) {
 
	if (message->getType() == Swift::Message::Error) {
 
		return;
 
	}
 

	
 
	// Do not count chatstate notification...
 
	boost::shared_ptr<Swift::ChatState> statePayload = message->getPayload<Swift::ChatState>();
 
	if (!statePayload) {
 
		messageToBackendSent();
 
	}
 

	
 
	if (message->getBody().empty() && !statePayload && message->getSubject().empty()) {
 
		return;
 
	}
 

	
 
	User *user = getUser(message->getFrom().toBare().toString());
 
	if (!user){
 
		return;
 
	}
 

	
 
	user->getConversationManager()->handleMessageReceived(message);
 
}
 

	
 
void UserManager::handleGeneralPresenceReceived(Swift::Presence::ref presence) {
 
	switch(presence->getType()) {
 
		case Swift::Presence::Subscribe:
 
		case Swift::Presence::Subscribed:
 
		case Swift::Presence::Unsubscribe:
 
		case Swift::Presence::Unsubscribed:
 
			handleSubscription(presence);
 
			break;
 
		case Swift::Presence::Available:
 
		case Swift::Presence::Unavailable:
 
			handleMUCPresence(presence);
 
			break;
 
		case Swift::Presence::Probe:
 
			handleProbePresence(presence);
 
			break;
 
		case Swift::Presence::Error:
 
			handleErrorPresence(presence);
 
			break;
 
		default:
 
			break;
 
	};
 
}
 

	
 
void UserManager::handleMUCPresence(Swift::Presence::ref presence) {
 
	// Don't let RosterManager to handle presences for us
 
	if (presence->getTo().getNode().empty()) {
 
		return;
 
	}
 

	
 
	if (presence->getType() == Swift::Presence::Available) {
 
		handlePresence(presence);
 
	}
 
	else if (presence->getType() == Swift::Presence::Unavailable) {
 
		std::string userkey = presence->getFrom().toBare().toString();
 
		User *user = getUser(userkey);
 
		if (user) {
 
			user->handlePresence(presence);
 
		}
 
	}
 
}
 

	
 
void UserManager::handleProbePresence(Swift::Presence::ref presence) {
 
	// Don't let RosterManager to handle presences for us
 
	if (presence->getTo().getNode().empty()) {
 
		return;
 
	}
 

	
 
	User *user = getUser(presence->getFrom().toBare().toString());
 

	
 
 	if (user) {
 
 		user->getRosterManager()->sendCurrentPresence(presence->getTo(), presence->getFrom());
 
 	}
 
 	else {
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setFrom(presence->getTo());
 
		response->setTo(presence->getFrom());
 
		response->setType(Swift::Presence::Unavailable);
 
		m_component->getStanzaChannel()->sendPresence(response);
 
	}
 
}
 

	
 
void UserManager::handleErrorPresence(Swift::Presence::ref presence) {
 
	// Don't let RosterManager to handle presences for us
 
	if (!presence->getTo().getNode().empty()) {
 
		return;
 
	}
 

	
 
	if (!presence->getPayload<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();
 
		response->setFrom(presence->getTo().toBare());
 
		response->setTo(presence->getFrom().toBare());
 
		response->setType(Swift::Presence::Subscribed);
 
		m_component->getStanzaChannel()->sendPresence(response);
 

	
 
// 		response = Swift::Presence::create();
 
// 		response->setFrom(presence->getTo());
 
// 		response->setTo(presence->getFrom());
 
// 		response->setType(Swift::Presence::Subscribe);
 
// 		m_component->getStanzaChannel()->sendPresence(response);
 
		return;
 
	}
 
	else if (presence->getType() == Swift::Presence::Unsubscribed && presence->getTo().getNode().empty()) {
 
		std::string userkey = presence->getFrom().toBare().toString();
 
		UserInfo res;
 
		bool registered = m_storageBackend ? m_storageBackend->getUser(userkey, res) : false;
 
		if (registered) {
 
			Swift::Presence::ref response = Swift::Presence::create();
 
			response->setFrom(presence->getTo().toBare());
 
			response->setTo(presence->getFrom().toBare());
 
			response->setType(Swift::Presence::Subscribe);
 
			m_component->getStanzaChannel()->sendPresence(response);
 
		}
 
		return;
 
	}
 

	
 
	// Don't let RosterManager to handle presences for us
 
	if (presence->getTo().getNode().empty()) {
 
		return;
 
	}
 

	
 
	User *user = getUser(presence->getFrom().toBare().toString());
 

	
 
 	if (user) {
 
 		user->handleSubscription(presence);
 
 	}
 
 	else if (presence->getType() == Swift::Presence::Unsubscribe) {
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setFrom(presence->getTo());
 
		response->setTo(presence->getFrom());
 
		response->setType(Swift::Presence::Unsubscribed);
 
		m_component->getStanzaChannel()->sendPresence(response);
 
	}
 
// 	else {
 
// // 		Log(presence->getFrom().toString().getUTF8String(), "Subscribe presence received, but this user is not logged in");
src/userregistration.cpp
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/userregistration.h"
 
#include "transport/usermanager.h"
 
#include "transport/storagebackend.h"
 
#include "transport/transport.h"
 
#include "transport/rostermanager.h"
 
#include "transport/user.h"
 
#include "transport/logging.h"
 
#include "Swiften/Elements/ErrorPayload.h"
 
#include "Swiften/EventLoop/SimpleEventLoop.h"
 
#include "Swiften/Network/BoostNetworkFactories.h"
 
#include "Swiften/Client/Client.h"
 
#include <boost/shared_ptr.hpp>
 
#include <boost/thread.hpp>
 
#include <boost/date_time/posix_time/posix_time.hpp>
 
#include <boost/regex.hpp> 
 

	
 
using namespace Swift;
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "UserRegistration");
 

	
 
UserRegistration::UserRegistration(Component *component, UserManager *userManager, StorageBackend *storageBackend) : Swift::Responder<Swift::InBandRegistrationPayload>(component->m_iqRouter) {
 
	m_component = component;
 
	m_config = m_component->m_config;
 
	m_storageBackend = storageBackend;
 
	m_userManager = userManager;
 
}
 

	
 
UserRegistration::~UserRegistration(){
 
}
 

	
 
bool UserRegistration::registerUser(const UserInfo &row) {
 
	UserInfo user;
 
	bool registered = m_storageBackend->getUser(row.jid, user);
 
	// This user is already registered
 
	if (registered)
 
		return false;
 

	
 
	m_storageBackend->setUser(row);
 

	
 
	//same as in unregisterUser but here we have to pass UserInfo to handleRegisterRRResponse
 
	AddressedRosterRequest::ref request = AddressedRosterRequest::ref(new AddressedRosterRequest(m_component->getIQRouter(),row.jid));
 
	request->onResponse.connect(boost::bind(&UserRegistration::handleRegisterRemoteRosterResponse, this, _1, _2, row));
 
	request->send();
 

	
 
	return true;
 
}
 

	
 
bool UserRegistration::unregisterUser(const std::string &barejid) {
 
	UserInfo userInfo;
 
	bool registered = m_storageBackend->getUser(barejid, userInfo);
 
	// This user is not registered
 
	if (!registered)
 
		return false;
 

	
 
	onUserUnregistered(userInfo);
 

	
 
	// We have to check if server supports remoteroster XEP and use it if it's supported or fallback to unsubscribe otherwise
 
	AddressedRosterRequest::ref request = AddressedRosterRequest::ref(new AddressedRosterRequest(m_component->getIQRouter(), barejid));
 
	request->onResponse.connect(boost::bind(&UserRegistration::handleUnregisterRemoteRosterResponse, this, _1, _2, barejid));
 
	request->send();
 

	
 
	return true;
 
}
 

	
 
void UserRegistration::handleRegisterRemoteRosterResponse(boost::shared_ptr<Swift::RosterPayload> payload, Swift::ErrorPayload::ref remoteRosterNotSupported /*error*/, const UserInfo &row){
 
	if (remoteRosterNotSupported || !payload) {
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setFrom(m_component->getJID());
 
		response->setTo(Swift::JID(row.jid));
 
		response->setType(Swift::Presence::Subscribe);
 
		m_component->getStanzaChannel()->sendPresence(response);
 
	}
 
	else{
 
		Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload());
 
		Swift::RosterItemPayload item;
 
		item.setJID(m_component->getJID());
 
		item.setSubscription(Swift::RosterItemPayload::Both);
 
		payload->addItem(item);
 
		Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, row.jid, m_component->getIQRouter());
 
		request->send();
 
	}
 
	onUserRegistered(row);
 

	
 
	std::vector<std::string> const &x = CONFIG_VECTOR(m_component->getConfig(),"registration.notify_jid");
 
	BOOST_FOREACH(const std::string &notify_jid, x) {
 
		boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
		msg->setBody(std::string("registered: ") + row.jid);
 
		msg->setTo(notify_jid);
 
		msg->setFrom(m_component->getJID());
 
		m_component->getStanzaChannel()->sendMessage(msg);
 
	}
 
}
 

	
 
void UserRegistration::handleUnregisterRemoteRosterResponse(boost::shared_ptr<Swift::RosterPayload> payload, Swift::ErrorPayload::ref remoteRosterNotSupported /*error*/, const std::string &barejid) {
 
	UserInfo userInfo;
 
	bool registered = m_storageBackend->getUser(barejid, userInfo);
 
	// This user is not registered
 
	if (!registered)
 
		return;
 

	
 
	if (remoteRosterNotSupported || !payload) {
 
		std::list <BuddyInfo> roster;
 
		m_storageBackend->getBuddies(userInfo.id, roster);
 
		for(std::list<BuddyInfo>::iterator u = roster.begin(); u != roster.end() ; u++){
 
			std::string name = (*u).legacyName;
src/userregistry.cpp
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include <string>
 
#include <map>
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Server/UserRegistry.h"
 
#include "transport/userregistry.h"
 
#include "transport/logging.h"
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "UserRegistry");
 

	
 
UserRegistry::UserRegistry(Config *cfg, Swift::NetworkFactories *factories) {
 
	config = cfg;
 
	m_removeTimer = factories->getTimerFactory()->createTimer(1);
 
	m_inRemoveLater = false;
 
}
 

	
 
UserRegistry::~UserRegistry() { m_removeTimer->stop(); }
 

	
 
void UserRegistry::isValidUserPassword(const Swift::JID& user, Swift::ServerFromClientSession *session, const Swift::SafeByteArray& password) {
 
	std::vector<std::string> const &x = CONFIG_VECTOR(config,"service.admin_jid");
 
	if (std::find(x.begin(), x.end(), user.toBare().toString()) != x.end()) {
 
		if (Swift::safeByteArrayToString(password) == CONFIG_STRING(config, "service.admin_password")) {
 
			session->handlePasswordValid();
 
		}
 
		else {
 
			session->handlePasswordInvalid();
 
		}
 
		return;
 
	}
 
	std::string key = user.toBare().toString();
 

	
 
	// Users try to connect twice
 
	if (users.find(key) != users.end()) {
 
		// Kill the first session
 
		LOG4CXX_INFO(logger, key << ": Removing previous session and making this one active");
 
		Swift::ServerFromClientSession *tmp = users[key].session;
 
		users[key].session = session;
 
		tmp->handlePasswordInvalid();
 
	}
 

	
 
	LOG4CXX_INFO(logger, key << ": Connecting this user to find if password is valid");
 

	
 
	users[key].password = Swift::safeByteArrayToString(password);
 
	users[key].session = session;
 
	onConnectUser(user);
 

	
 
	return;
 
}
 

	
 
void UserRegistry::stopLogin(const Swift::JID& user, Swift::ServerFromClientSession *session) {
 
	std::string key = user.toBare().toString();
 
	if (users.find(key) != users.end()) {
 
		if (users[key].session == session) {
 
			LOG4CXX_INFO(logger, key << ": Stopping login process (user probably disconnected while logging in)");
 
			users.erase(key);
 
		}
 
		else {
 
			LOG4CXX_WARN(logger, key << ": Stopping login process (user probably disconnected while logging in), but this is not active session");
 
		}
 
	}
 
	else {
 
		LOG4CXX_WARN(logger, key << ": Stopping login process (user probably disconnected while logging in) for invalid user");
 
	}
 

	
 
	// ::removeLater can be called only by libtransport, not by Swift and libtransport
 
	// takes care about user disconnecting itself, so don't call our signal.
 
	if (!m_inRemoveLater)
 
		onDisconnectUser(user);
 
}
 

	
 
void UserRegistry::onPasswordValid(const Swift::JID &user) {
 
	std::string key = user.toBare().toString();
 
	if (users.find(key) != users.end()) {
 
		LOG4CXX_INFO(logger, key << ": Password is valid");
 
		users[key].session->handlePasswordValid();
 
		users.erase(key);
 
	}
 
	else {
 
		LOG4CXX_INFO(logger, key << ": onPasswordValid called for invalid user");
 
	}
 
}
 

	
 
void UserRegistry::onPasswordInvalid(const Swift::JID &user, const std::string &error) {
 
	std::string key = user.toBare().toString();
 
	if (users.find(key) != users.end()) {
 
		LOG4CXX_INFO(logger, key << ": Password is invalid");
 
		LOG4CXX_INFO(logger, key << ": Password is invalid or there was an error when connecting the legacy network");
 
		users[key].session->handlePasswordInvalid(error);
 
		users.erase(key);
 
	}
 
	else {
 
		LOG4CXX_INFO(logger, key << ": onPasswordInvalid called for invalid user");
 
	}
 
}
 

	
 
void UserRegistry::handleRemoveTimeout(const Swift::JID &user) {
 
	m_inRemoveLater = true;
 
	onPasswordInvalid(user);
 
	m_inRemoveLater = false;
 
}
 

	
 
void UserRegistry::removeLater(const Swift::JID &user) {
 
	m_removeTimer->onTick.connect(boost::bind(&UserRegistry::handleRemoveTimeout, this, user));
 
	m_removeTimer->start();
 
}
 

	
 
const std::string UserRegistry::getUserPassword(const std::string &barejid) {
 
	if (users.find(barejid) != users.end())
 
		return users[barejid].password;
 
	return "";
 
}
 

	
 
}
src/usersreconnecter.cpp
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/usersreconnecter.h"
 

	
 
#include <iostream>
 
#include <boost/bind.hpp>
 
#include "Swiften/Queries/IQRouter.h"
 
#include "Swiften/Swiften.h"
 
#include "transport/storagebackend.h"
 
#include "transport/transport.h"
 
#include "transport/logging.h"
 

	
 
#include "Swiften/Network/NetworkFactories.h"
 

	
 
using namespace Swift;
 
using namespace boost;
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "UserReconnecter");
 

	
 
UsersReconnecter::UsersReconnecter(Component *component, StorageBackend *storageBackend) {
 
	m_component = component;
 
	m_storageBackend = storageBackend;
 
	m_started = false;
 

	
 
	m_nextUserTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(1000);
 
	m_nextUserTimer->onTick.connect(boost::bind(&UsersReconnecter::reconnectNextUser, this));
 

	
 
	m_component->onConnected.connect(bind(&UsersReconnecter::handleConnected, this));
 
}
 

	
 
UsersReconnecter::~UsersReconnecter() {
 
	m_component->onConnected.disconnect(bind(&UsersReconnecter::handleConnected, this));
 
	m_nextUserTimer->stop();
 
	m_nextUserTimer->onTick.disconnect(boost::bind(&UsersReconnecter::reconnectNextUser, this));
 
}
 

	
 
void UsersReconnecter::reconnectNextUser() {
 
	if (m_users.empty()) {
 
		LOG4CXX_INFO(logger, "All users reconnected, stopping UserReconnecter.");
 
		return;
 
	}
 

	
 
	std::string user = m_users.back();
 
	m_users.pop_back();
 

	
 
	LOG4CXX_INFO(logger, "Sending probe presence to " << user);
 
	Swift::Presence::ref response = Swift::Presence::create();
 
	try {
 
		response->setTo(user);
 
	}
 
	catch (...) { return; }
 
	
 
	response->setFrom(m_component->getJID());
 
	response->setType(Swift::Presence::Probe);
 

	
 
	m_component->getStanzaChannel()->sendPresence(response);
 
	m_nextUserTimer->start();
 
}
 

	
 
void UsersReconnecter::handleConnected() {
 
	if (m_started)
 
		return;
 

	
 
	LOG4CXX_INFO(logger, "Starting UserReconnecter.");
 
	m_started = true;
 

	
 
	m_storageBackend->getOnlineUsers(m_users);
 

	
 
	reconnectNextUser();
 
}
 

	
 

	
 
}
src/vcardresponder.cpp
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/vcardresponder.h"
 

	
 
#include <iostream>
 
#include <boost/bind.hpp>
 
#include "Swiften/Queries/IQRouter.h"
 
#include "Swiften/Swiften.h"
 
#include "transport/user.h"
 
#include "transport/usermanager.h"
 
#include "transport/rostermanager.h"
 
#include "transport/transport.h"
 
#include "transport/logging.h"
 

	
 
using namespace Swift;
 
using namespace boost;
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "VCardResponder");
 

	
 
VCardResponder::VCardResponder(Swift::IQRouter *router, Swift::NetworkFactories *factories, UserManager *userManager) : Swift::Responder<VCard>(router) {
 
	m_id = 0;
 
	m_userManager = userManager;
 
	m_collectTimer = factories->getTimerFactory()->createTimer(20000);
 
	m_collectTimer->onTick.connect(boost::bind(&VCardResponder::collectTimeouted, this));
 
	m_collectTimer->start();
 
}
 

	
 
VCardResponder::~VCardResponder() {
 
}
 

	
 
void VCardResponder::sendVCard(unsigned int id, boost::shared_ptr<Swift::VCard> vcard) {
 
	if (m_queries.find(id) == m_queries.end()) {
 
		LOG4CXX_WARN(logger, "Unexpected VCard from legacy network with id " << id);
 
		return;
 
	}
 

	
 
	LOG4CXX_INFO(logger, m_queries[id].from.toString() << ": Forwarding VCard of " << m_queries[id].to.toString() << " from legacy network");
 

	
 
	sendResponse(m_queries[id].from, m_queries[id].to, m_queries[id].id, vcard);
 
	m_queries.erase(id);
 
}
 

	
 
void VCardResponder::collectTimeouted() {
 
	time_t now = time(NULL);
 

	
 
	std::vector<unsigned int> candidates;
 
	for(std::map<unsigned int, VCardData>::iterator it = m_queries.begin(); it != m_queries.end(); it++) {
 
		if (now - (*it).second.received > 40) {
 
			candidates.push_back((*it).first);
 
		}
 
	}
 

	
 
	if (candidates.size() != 0) {
 
		LOG4CXX_INFO(logger, "Removing " << candidates.size() << " timeouted VCard requests");
 
	}
 

	
 
	BOOST_FOREACH(unsigned int id, candidates) {
 
		sendVCard(id, boost::shared_ptr<Swift::VCard>(new Swift::VCard()));
 
	}
 
	m_collectTimer->start();
 
}
 

	
 
bool VCardResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::VCard> payload) {
 
	User *user = m_userManager->getUser(from.toBare().toString());
 
	if (!user) {
 
		LOG4CXX_WARN(logger, from.toBare().toString() << ": User is not logged in");
 
		return false;
 
	}
 

	
 
	Swift::JID to_ = to;
 

	
 
	std::string name = to_.getUnescapedNode();
 
	if (name.empty()) {
 
		to_ = user->getJID();
 
	}
 

	
 
	name = Buddy::JIDToLegacyName(to_);
 

	
 
	LOG4CXX_INFO(logger, from.toBare().toString() << ": Requested VCard of " << name);
 

	
 
	m_queries[m_id].from = from;
 
	m_queries[m_id].to = to;
 
	m_queries[m_id].id = id; 
 
	m_queries[m_id].received = time(NULL);
 
	onVCardRequired(user, name, m_id++);
 
	return true;
 
}
 

	
 
bool VCardResponder::handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::VCard> payload) {
 
	if (!to.getNode().empty()) {
 
		LOG4CXX_WARN(logger, from.toBare().toString() << ": Tried to set VCard of somebody else");
 
		return false;
 
	}
 

	
 
	User *user = m_userManager->getUser(from.toBare().toString());
 
	if (!user) {
 
		LOG4CXX_WARN(logger, from.toBare().toString() << ": User is not logged in");
 
		return false;
 
	}
 

	
 
	LOG4CXX_INFO(logger, from.toBare().toString() << ": Setting VCard");
 
	onVCardUpdated(user, payload);
0 comments (0 inline, 0 general)