This commit is contained in:
Vitaly Takmazov 2012-01-11 16:24:00 +04:00
commit 316a26474f
25 changed files with 815 additions and 39 deletions

View file

@ -53,6 +53,9 @@ find_package(log4cxx)
set(event_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
find_package(event)
set(pqxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
find_package(pqxx)
find_package(Doxygen)
INCLUDE(FindQt4)
@ -101,6 +104,16 @@ else (MYSQL_FOUND)
message("MySQL : no (install mysql-devel)")
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 "")
message("PostgreSQL : no (install libpqxx-devel)")
endif (PQXX_FOUND)
if (PROTOBUF_FOUND)
ADD_DEFINITIONS(-DWITH_PROTOBUF)
include_directories(${PROTOBUF_INCLUDE_DIRS})

View file

@ -1,3 +1,11 @@
Version 2.0.0-beta (X-X-X):
General:
* Fixed registration from Pidgin.
* Unsubscribe presence sent to some buddy doesn't disconnect the account.
* Remote Roster requests are not sent to resources, but to bare JID.
* Added automatic reconnection in case of non-fatal error.
* Added more error messages.
version 2.0.0 alpha (2011-12-06):
General:
* First Spectrum 2.0.0 alpha release, check more on

View file

@ -26,6 +26,7 @@ MyIrcSession::MyIrcSession(const std::string &user, NetworkPlugin *np, const std
this->np = np;
this->user = user;
this->suffix = suffix;
m_connected = false;
rooms = 0;
connect(this, SIGNAL(disconnected()), SLOT(on_disconnected()));
connect(this, SIGNAL(connected()), SLOT(on_connected()));
@ -33,6 +34,7 @@ MyIrcSession::MyIrcSession(const std::string &user, NetworkPlugin *np, const std
}
void MyIrcSession::on_connected() {
m_connected = true;
if (suffix.empty()) {
np->handleConnected(user);
}
@ -51,6 +53,7 @@ void MyIrcSession::on_connected() {
void MyIrcSession::on_disconnected() {
if (suffix.empty())
np->handleDisconnected(user, 0, "");
m_connected = false;
}
bool MyIrcSession::correctNickname(std::string &nickname) {
@ -156,6 +159,9 @@ void MyIrcSession::on_messageReceived(IrcMessage *message) {
}
void MyIrcSession::on_numericMessageReceived(IrcMessage *message) {
QString channel;
QStringList members;
IrcNumericMessage *m = (IrcNumericMessage *) message;
switch (m->code()) {
case 332:
@ -165,8 +171,8 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) {
np->handleSubject(user, m->parameters().value(1).toStdString() + suffix, m_topicData, m->parameters().value(2).toStdString());
break;
case 353:
QString channel = m->parameters().value(2);
QStringList members = m->parameters().value(3).split(" ");
channel = m->parameters().value(2);
members = m->parameters().value(3).split(" ");
for (int i = 0; i < members.size(); i++) {
bool flags = 0;
@ -176,6 +182,13 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) {
np->handleParticipantChanged(user, nickname, channel.toStdString() + suffix,(int) flags, pbnetwork::STATUS_ONLINE);
}
break;
case 432:
if (m_connected) {
np->handleDisconnected(user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Erroneous Nickname");
}
break;
default:
break;
}
//qDebug() << "numeric message received:" << receiver() << origin << code << params;
@ -208,5 +221,6 @@ void MyIrcSession::onMessageReceived(IrcMessage *message) {
case IrcMessage::Numeric:
on_numericMessageReceived(message);
break;
default:break;
}
}

View file

@ -65,6 +65,7 @@ protected:
std::string m_identify;
std::list<std::string> m_autoJoin;
std::string m_topicData;
bool m_connected;
};
//class MyIrcBuffer : public Irc::Buffer

View file

@ -441,6 +441,16 @@ static void * requestInput(const char *title, const char *primary,const char *se
((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);
}
@ -604,11 +614,13 @@ class SpectrumNetworkPlugin : public NetworkPlugin {
return;
}
LOG4CXX_INFO(logger, "Creating account with name '" << name.c_str() << "' and protocol '" << protocol << "'");
if (purple_accounts_find(name.c_str(), protocol.c_str()) != NULL){
if (purple_accounts_find(name.c_str(), protocol.c_str()) != NULL) {
LOG4CXX_INFO(logger, "Using previously created account with name '" << name.c_str() << "' and protocol '" << protocol << "'");
account = purple_accounts_find(name.c_str(), protocol.c_str());
}
else {
LOG4CXX_INFO(logger, "Creating account with name '" << name.c_str() << "' and protocol '" << protocol << "'");
account = purple_account_new(name.c_str(), protocol.c_str());
purple_accounts_add(account);
}
@ -1078,6 +1090,8 @@ static void buddyListNewNode(PurpleBlistNode *node) {
PurpleBuddy *buddy = (PurpleBuddy *) node;
PurpleAccount *account = purple_buddy_get_account(buddy);
LOG4CXX_INFO(logger, "Buddy updated " << np->m_accounts[account] << " " << purple_buddy_get_name(buddy) << " " << getAlias(buddy));
// Status
pbnetwork::StatusType status = pbnetwork::STATUS_NONE;
std::string message;

View file

@ -0,0 +1,16 @@
FIND_PATH(PQXX_INCLUDE_DIR pqxx/pqxx PATHS)
MARK_AS_ADVANCED(PQXX_INCLUDE_DIR)
FIND_LIBRARY(PQXX_LIBRARY pqxx )
MARK_AS_ADVANCED(PQXX_LIBRARY)
FIND_LIBRARY(PQ_LIBRARY pq )
MARK_AS_ADVANCED(PQ_LIBRARY)
if(PQXX_LIBRARY AND PQ_LIBRARY AND PQXX_INCLUDE_DIR)
set( PQXX_FOUND 1 )
message( STATUS "Found pqxx: ${PQXX_LIBRARY}, ${PQ_LIBRARY}, ${PQXX_INCLUDE_DIR}")
else()
message(STATUS "Could NOT find pqxx and pq library")
endif()

View file

@ -18,13 +18,12 @@
#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
#include <Swiften/Base/IDGenerator.h>
#include <Swiften/Elements/Presence.h>
#include <Swiften/Presence/PresenceOracle.h>
#include <Swiften/Base/foreach.h>
namespace Swift {
CombinedOutgoingFileTransferManager::CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, PresenceOracle *presOracle, SOCKS5BytestreamServer *bytestreamServer) : jsManager(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy), presenceOracle(presOracle), bytestreamServer(bytestreamServer) {
CombinedOutgoingFileTransferManager::CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, Transport::PresenceOracle *presOracle, SOCKS5BytestreamServer *bytestreamServer) : jsManager(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy), presenceOracle(presOracle), bytestreamServer(bytestreamServer) {
idGenerator = new IDGenerator();
}

View file

@ -11,6 +11,8 @@
#include <Swiften/JID/JID.h>
#include "transport/presenceoracle.h"
namespace Swift {
class JingleSessionManager;
@ -30,7 +32,7 @@ class PresenceOracle;
class CombinedOutgoingFileTransferManager {
public:
CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, PresenceOracle* presOracle, SOCKS5BytestreamServer *server);
CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, Transport::PresenceOracle* presOracle, SOCKS5BytestreamServer *server);
~CombinedOutgoingFileTransferManager();
boost::shared_ptr<OutgoingFileTransfer> createOutgoingFileTransfer(const JID& from, const JID& to, boost::shared_ptr<ReadBytestream>, const StreamInitiationFileInfo&);
@ -46,7 +48,7 @@ private:
IDGenerator *idGenerator;
SOCKS5BytestreamRegistry* bytestreamRegistry;
SOCKS5BytestreamProxy* bytestreamProxy;
PresenceOracle* presenceOracle;
Transport::PresenceOracle* presenceOracle;
SOCKS5BytestreamServer *bytestreamServer;
};

View file

@ -0,0 +1,109 @@
/**
* 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 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::work &txn, const std::string &query, bool show_error = true);
Config *m_config;
std::string m_prefix;
pqxx::connection *m_conn;
};
}
#endif

View file

@ -0,0 +1,57 @@
/**
* 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 <map>
#include <string>
#include <Swiften/Elements/Presence.h>
#include <Swiften/Client/StanzaChannel.h>
#include <Swiften/Base/boost_bsignals.h>
namespace Transport {
class PresenceOracle {
public:
PresenceOracle(Swift::StanzaChannel* stanzaChannel);
~PresenceOracle();
Swift::Presence::ref getLastPresence(const Swift::JID&) const;
Swift::Presence::ref getHighestPriorityPresence(const Swift::JID& bareJID) const;
std::vector<Swift::Presence::ref> getAllPresence(const Swift::JID& bareJID) const;
public:
boost::signal<void (Swift::Presence::ref)> onPresenceChange;
private:
void handleIncomingPresence(Swift::Presence::ref presence);
void handleStanzaChannelAvailableChanged(bool);
private:
typedef std::map<Swift::JID, Swift::Presence::ref> PresenceMap;
typedef std::map<Swift::JID, PresenceMap> PresencesMap;
PresencesMap entries_;
Swift::StanzaChannel* stanzaChannel_;
};
}

View file

@ -27,7 +27,6 @@
#include "Swiften/Disco/EntityCapsManager.h"
#include "Swiften/Disco/CapsManager.h"
#include "Swiften/Disco/CapsMemoryStorage.h"
#include "Swiften/Presence/PresenceOracle.h"
#include "Swiften/Network/BoostTimerFactory.h"
#include "Swiften/Network/BoostIOServiceThread.h"
#include "Swiften/Server/UserRegistry.h"
@ -37,6 +36,7 @@
#include <boost/bind.hpp>
#include "transport/config.h"
#include "transport/factory.h"
#include "transport/presenceoracle.h"
namespace Transport {
// typedef enum { CLIENT_FEATURE_ROSTERX = 2,
@ -92,7 +92,7 @@ namespace Transport {
/// You can use it to check current resource connected for particular user.
/// \return Swift::PresenceOracle associated with this Transport::Component.
Swift::PresenceOracle *getPresenceOracle();
PresenceOracle *getPresenceOracle();
/// Returns True if the component is in server mode.
@ -179,7 +179,7 @@ namespace Transport {
Swift::EntityCapsManager *m_entityCapsManager;
Swift::CapsManager *m_capsManager;
Swift::CapsMemoryStorage *m_capsMemoryStorage;
Swift::PresenceOracle *m_presenceOracle;
PresenceOracle *m_presenceOracle;
Swift::StanzaChannel *m_stanzaChannel;
Swift::IQRouter *m_iqRouter;

View file

@ -22,7 +22,6 @@
#include <time.h>
#include "Swiften/Swiften.h"
#include "Swiften/Presence/PresenceOracle.h"
#include "Swiften/Disco/EntityCapsManager.h"
#include "Swiften/Disco/EntityCapsProvider.h"
#include "storagebackend.h"
@ -35,6 +34,7 @@ class Component;
class RosterManager;
class ConversationManager;
class UserManager;
class PresenceOracle;
struct UserInfo;
/// Represents online XMPP user.
@ -125,7 +125,7 @@ class User : public Swift::EntityCapsProvider {
UserManager *m_userManager;
ConversationManager *m_conversationManager;
Swift::EntityCapsManager *m_entityCapsManager;
Swift::PresenceOracle *m_presenceOracle;
PresenceOracle *m_presenceOracle;
UserInfo m_userInfo;
void *m_data;
bool m_connected;

View file

@ -40,6 +40,8 @@ std::string serializeGroups(const std::vector<std::string> &groups);
std::vector<std::string> deserializeGroups(std::string &groups);
int getRandomPort(const std::string &s);
}
}

View file

@ -5,6 +5,7 @@
#include "transport/logger.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"
@ -379,7 +380,23 @@ int main(int argc, char **argv)
}
#endif
if (CONFIG_STRING(&config, "database.type") != "mysql" && CONFIG_STRING(&config, "database.type") != "sqlite3") {
#ifdef WITH_PQXX
if (CONFIG_STRING(&config, "database.type") == "pqxx") {
storageBackend = new PQXXBackend(&config);
if (!storageBackend->connect()) {
std::cerr << "Can't connect to database. Check the log to find out the reason.\n";
return -1;
}
}
#else
if (CONFIG_STRING(&config, "database.type") == "pqxx") {
std::cerr << "Spectrum2 is not compiled with pqxx backend.\n";
return -2;
}
#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") {
std::cerr << "Unknown storage backend " << CONFIG_STRING(&config, "database.type") << "\n";
return -2;
}

View file

@ -24,7 +24,9 @@ port = 5222
backend_host = localhost
# Port on which Spectrum listens for backends.
backend_port=10001
# 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=

View file

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 2.6)
FILE(GLOB SRC *.cpp)
ADD_EXECUTABLE(spectrum2_manager ${SRC} ../../src/config.cpp)
ADD_EXECUTABLE(spectrum2_manager ${SRC} ../../src/config.cpp ../../src/util.cpp)
target_link_libraries(spectrum2_manager ${SWIFTEN_LIBRARY})

View file

@ -35,9 +35,9 @@ if (CMAKE_COMPILER_IS_GNUCXX)
endif()
if (WIN32)
TARGET_LINK_LIBRARIES(transport ${SQLITE3_LIBRARIES} ${MYSQL_LIBRARIES} ${SWIFTEN_LIBRARY} ${PROTOBUF_LIBRARIES} ${LOG4CXX_LIBRARIES})
TARGET_LINK_LIBRARIES(transport ${PQXX_LIBRARY} ${PQ_LIBRARY} ${SQLITE3_LIBRARIES} ${MYSQL_LIBRARIES} ${SWIFTEN_LIBRARY} ${PROTOBUF_LIBRARIES} ${LOG4CXX_LIBRARIES})
else (WIN32)
TARGET_LINK_LIBRARIES(transport ${SQLITE3_LIBRARIES} ${MYSQL_LIBRARIES} ${SWIFTEN_LIBRARY} ${PROTOBUF_LIBRARIES} ${LOG4CXX_LIBRARIES} ${POPT_LIBRARY})
TARGET_LINK_LIBRARIES(transport ${PQXX_LIBRARY} ${PQ_LIBRARY} ${SQLITE3_LIBRARIES} ${MYSQL_LIBRARIES} ${SWIFTEN_LIBRARY} ${PROTOBUF_LIBRARIES} ${LOG4CXX_LIBRARIES} ${POPT_LIBRARY})
endif(WIN32)
SET_TARGET_PROPERTIES(transport PROPERTIES

View file

@ -19,6 +19,7 @@
*/
#include "transport/config.h"
#include "transport/util.h"
#include <fstream>
#ifdef _MSC_VER
#include <direct.h>
@ -120,13 +121,7 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description
else if (opt.string_key == "service.backend_port") {
found_backend_port = true;
if (opt.value[0] == "0") {
unsigned long r = 0;
BOOST_FOREACH(char c, _jid) {
r += (int) c;
}
srand(time(NULL) + r);
int randomPort = 30000 + rand() % 10000;
opt.value[0] = boost::lexical_cast<std::string>(randomPort);
opt.value[0] = boost::lexical_cast<std::string>(Util::getRandomPort(_jid.empty() ? jid : _jid));
}
}
else if (opt.string_key == "service.working_dir") {
@ -148,14 +143,9 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description
parsed.options.push_back(boost::program_options::basic_option<char>("service.pidfile", value));
}
if (!found_backend_port) {
unsigned long r = 0;
BOOST_FOREACH(char c, _jid) {
r += (int) c;
}
srand(time(NULL) + r);
int randomPort = 30000 + rand() % 10000;
std::vector<std::string> value;
value.push_back(boost::lexical_cast<std::string>(randomPort));
std::string p = boost::lexical_cast<std::string>(Util::getRandomPort(_jid.empty() ? jid : _jid));
value.push_back(p);
parsed.options.push_back(boost::program_options::basic_option<char>("service.backend_port", value));
}

373
src/pqxxbackend.cpp Normal file
View file

@ -0,0 +1,373 @@
/**
* 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
*/
#ifdef WITH_PQXX
#include "transport/pqxxbackend.h"
#include "transport/util.h"
#include <boost/bind.hpp>
#include "log4cxx/logger.h"
using namespace log4cxx;
using namespace boost;
namespace Transport {
static LoggerPtr logger = Logger::getLogger("PQXXBackend");
PQXXBackend::PQXXBackend(Config *config) {
m_config = config;
m_prefix = CONFIG_STRING(m_config, "database.prefix");
}
PQXXBackend::~PQXXBackend(){
disconnect();
}
void PQXXBackend::disconnect() {
LOG4CXX_INFO(logger, "Disconnecting");
delete m_conn;
}
bool PQXXBackend::connect() {
LOG4CXX_INFO(logger, "Connecting PostgreSQL server " << CONFIG_STRING(m_config, "database.server") << ", user " <<
CONFIG_STRING(m_config, "database.user") << ", database " << CONFIG_STRING(m_config, "database.database") <<
", port " << CONFIG_INT(m_config, "database.port")
);
std::string str = "dbname=";
str += CONFIG_STRING(m_config, "database.database") + " ";
str += "user=" + CONFIG_STRING(m_config, "database.user") + " ";
m_conn = new pqxx::connection(str);
createDatabase();
return true;
}
bool PQXXBackend::createDatabase() {
int exist = exec("SELECT * FROM " + m_prefix + "buddies_settings LIMIT 1;", false);
if (!exist) {
exec("CREATE TABLE " + m_prefix + "buddies_settings ("
"user_id integer NOT NULL,"
"buddy_id integer NOT NULL,"
"var varchar(50) NOT NULL,"
"type smallint NOT NULL,"
"value varchar(255) NOT NULL,"
"PRIMARY KEY (buddy_id,var)"
");");
exec("CREATE TYPE Subscription AS ENUM ('to','from','both','ask','none');");
exec("CREATE TABLE " + m_prefix + "buddies ("
"id SERIAL,"
"user_id integer NOT NULL,"
"uin varchar(255) NOT NULL,"
"subscription Subscription NOT NULL,"
"nickname varchar(255) NOT NULL,"
"groups varchar(255) NOT NULL,"
"flags smallint NOT NULL DEFAULT '0',"
"PRIMARY KEY (id),"
"UNIQUE (user_id,uin)"
");");
exec("CREATE TABLE " + m_prefix + "users ("
"id SERIAL,"
"jid varchar(255) NOT NULL,"
"uin varchar(4095) NOT NULL,"
"password varchar(255) NOT NULL,"
"language varchar(25) NOT NULL,"
"encoding varchar(50) NOT NULL default 'utf8',"
"last_login timestamp,"
"vip boolean NOT NULL default '0',"
"online boolean NOT NULL default '0',"
"PRIMARY KEY (id),"
"UNIQUE (jid)"
");");
exec("CREATE TABLE " + m_prefix + "users_settings ("
"user_id integer NOT NULL,"
"var varchar(50) NOT NULL,"
"type smallint NOT NULL,"
"value varchar(255) NOT NULL,"
"PRIMARY KEY (user_id,var)"
");");
exec("CREATE TABLE " + m_prefix + "db_version ("
"ver integer NOT NULL default '1',"
"UNIQUE (ver)"
");");
// exec("INSERT INTO db_version (ver) VALUES ('2');");
}
return true;
}
bool PQXXBackend::exec(const std::string &query, bool show_error) {
pqxx::work txn(*m_conn);
return exec(txn, query, show_error);
}
bool PQXXBackend::exec(pqxx::work &txn, const std::string &query, bool show_error) {
try {
txn.exec(query);
txn.commit();
}
catch (std::exception& e) {
if (show_error)
LOG4CXX_ERROR(logger, e.what());
return false;
}
return true;
}
void PQXXBackend::setUser(const UserInfo &user) {
std::string encrypted = user.password;
if (!CONFIG_STRING(m_config, "database.encryption_key").empty()) {
encrypted = Util::encryptPassword(encrypted, CONFIG_STRING(m_config, "database.encryption_key"));
}
pqxx::work txn(*m_conn);
exec(txn, "UPDATE " + m_prefix + "users SET uin=" + txn.quote(user.uin) + ", password=" + txn.quote(encrypted) + ";"
"INSERT INTO " + m_prefix + "users (jid, uin, password, language, encoding, last_login, vip) VALUES "
"(" + txn.quote(user.jid) + ","
+ txn.quote(user.uin) + ","
+ txn.quote(encrypted) + ","
+ txn.quote(user.language) + ","
+ txn.quote(user.encoding) + ","
+ "NOW(),"
+ txn.quote(user.vip) +")");
}
bool PQXXBackend::getUser(const std::string &barejid, UserInfo &user) {
try {
pqxx::work txn(*m_conn);
pqxx::result r = txn.exec("SELECT id, jid, uin, password, encoding, language, vip FROM " + m_prefix + "users WHERE jid="
+ txn.quote(barejid));
if (r.size() == 0) {
return false;
}
user.id = r[0][0].as<int>();
user.jid = r[0][1].as<std::string>();
user.uin = r[0][2].as<std::string>();
user.password = r[0][3].as<std::string>();
user.encoding = r[0][4].as<std::string>();
user.language = r[0][5].as<std::string>();
user.vip = r[0][6].as<bool>();
}
catch (std::exception& e) {
LOG4CXX_ERROR(logger, e.what());
return false;
}
return true;
}
void PQXXBackend::setUserOnline(long id, bool online) {
try {
pqxx::work txn(*m_conn);
exec(txn, "UPDATE " + m_prefix + "users SET online=" + txn.quote(online) + ", last_login=NOW() WHERE id=" + txn.quote(id));
}
catch (std::exception& e) {
LOG4CXX_ERROR(logger, e.what());
}
}
bool PQXXBackend::getOnlineUsers(std::vector<std::string> &users) {
try {
pqxx::work txn(*m_conn);
pqxx::result r = txn.exec("SELECT jid FROM " + m_prefix + "users WHERE online=1");
for (pqxx::result::const_iterator it = r.begin(); it != r.end(); it++) {
users.push_back((*it)[0].as<std::string>());
}
}
catch (std::exception& e) {
LOG4CXX_ERROR(logger, e.what());
return false;
}
return true;
}
long PQXXBackend::addBuddy(long userId, const BuddyInfo &buddyInfo) {
// "INSERT INTO " + m_prefix + "buddies (user_id, uin, subscription, groups, nickname, flags) VALUES (?, ?, ?, ?, ?, ?)"
// std::string groups = Util::serializeGroups(buddyInfo.groups);
// *m_addBuddy << userId << buddyInfo.legacyName << buddyInfo.subscription;
// *m_addBuddy << groups;
// *m_addBuddy << buddyInfo.alias << buddyInfo.flags;
// EXEC(m_addBuddy, addBuddy(userId, buddyInfo));
// long id = (long) mysql_insert_id(&m_conn);
// INSERT OR REPLACE INTO " + m_prefix + "buddies_settings (user_id, buddy_id, var, type, value) VALUES (?, ?, ?, ?, ?)
// if (!buddyInfo.settings.find("icon_hash")->second.s.empty()) {
// *m_updateBuddySetting << userId << id << buddyInfo.settings.find("icon_hash")->first << (int) TYPE_STRING << buddyInfo.settings.find("icon_hash")->second.s << buddyInfo.settings.find("icon_hash")->second.s;
// EXEC(m_updateBuddySetting, addBuddy(userId, buddyInfo));
// }
return 0;
}
void PQXXBackend::updateBuddy(long userId, const BuddyInfo &buddyInfo) {
// "UPDATE " + m_prefix + "buddies SET groups=?, nickname=?, flags=?, subscription=? WHERE user_id=? AND uin=?"
// std::string groups = Util::serializeGroups(buddyInfo.groups);
// *m_updateBuddy << groups;
// *m_updateBuddy << buddyInfo.alias << buddyInfo.flags << buddyInfo.subscription;
// *m_updateBuddy << userId << buddyInfo.legacyName;
// EXEC(m_updateBuddy, updateBuddy(userId, buddyInfo));
}
bool PQXXBackend::getBuddies(long id, std::list<BuddyInfo> &roster) {
// SELECT id, uin, subscription, nickname, groups, flags FROM " + m_prefix + "buddies WHERE user_id=? ORDER BY id ASC
// *m_getBuddies << id;
// "SELECT buddy_id, type, var, value FROM " + m_prefix + "buddies_settings WHERE user_id=? ORDER BY buddy_id ASC"
// *m_getBuddiesSettings << id;
// SettingVariableInfo var;
// long buddy_id = -1;
// std::string key;
// EXEC(m_getBuddies, getBuddies(id, roster));
// if (!exec_ok)
// return false;
// while (m_getBuddies->fetch() == 0) {
// BuddyInfo b;
// std::string group;
// *m_getBuddies >> b.id >> b.legacyName >> b.subscription >> b.alias >> group >> b.flags;
// if (!group.empty()) {
// b.groups = Util::deserializeGroups(group);
// }
// roster.push_back(b);
// }
// EXEC(m_getBuddiesSettings, getBuddies(id, roster));
// if (!exec_ok)
// return false;
// BOOST_FOREACH(BuddyInfo &b, roster) {
// if (buddy_id == b.id) {
//// std::cout << "Adding buddy info setting " << key << "\n";
// b.settings[key] = var;
// buddy_id = -1;
// }
// while(buddy_id == -1 && m_getBuddiesSettings->fetch() == 0) {
// std::string val;
// *m_getBuddiesSettings >> buddy_id >> var.type >> key >> val;
// switch (var.type) {
// case TYPE_BOOLEAN:
// var.b = atoi(val.c_str());
// break;
// case TYPE_STRING:
// var.s = val;
// break;
// default:
// if (buddy_id == b.id) {
// buddy_id = -1;
// }
// continue;
// break;
// }
// if (buddy_id == b.id) {
//// std::cout << "Adding buddy info setting " << key << "=" << val << "\n";
// b.settings[key] = var;
// buddy_id = -1;
// }
// }
// }
// while(m_getBuddiesSettings->fetch() == 0) {
// // TODO: probably remove those settings, because there's no buddy for them.
// // It should not happend, but one never know...
// }
return true;
}
bool PQXXBackend::removeUser(long id) {
// *m_removeUser << (int) id;
// EXEC(m_removeUser, removeUser(id));
// if (!exec_ok)
// return false;
// *m_removeUserSettings << (int) id;
// EXEC(m_removeUserSettings, removeUser(id));
// if (!exec_ok)
// return false;
// *m_removeUserBuddies << (int) id;
// EXEC(m_removeUserBuddies, removeUser(id));
// if (!exec_ok)
// return false;
// *m_removeUserBuddiesSettings << (int) id;
// EXEC(m_removeUserBuddiesSettings, removeUser(id));
// if (!exec_ok)
// return false;
return true;
}
void PQXXBackend::getUserSetting(long id, const std::string &variable, int &type, std::string &value) {
//// "SELECT type, value FROM " + m_prefix + "users_settings WHERE user_id=? AND var=?"
// *m_getUserSetting << id << variable;
// EXEC(m_getUserSetting, getUserSetting(id, variable, type, value));
// if (m_getUserSetting->fetch() != 0) {
//// "INSERT INTO " + m_prefix + "users_settings (user_id, var, type, value) VALUES (?,?,?,?)"
// *m_setUserSetting << id << variable << type << value;
// EXEC(m_setUserSetting, getUserSetting(id, variable, type, value));
// }
// else {
// *m_getUserSetting >> type >> value;
// }
}
void PQXXBackend::updateUserSetting(long id, const std::string &variable, const std::string &value) {
//// "UPDATE " + m_prefix + "users_settings SET value=? WHERE user_id=? AND var=?"
// *m_updateUserSetting << value << id << variable;
// EXEC(m_updateUserSetting, updateUserSetting(id, variable, value));
}
void PQXXBackend::beginTransaction() {
// exec("START TRANSACTION;");
}
void PQXXBackend::commitTransaction() {
// exec("COMMIT;");
}
}
#endif

136
src/presenceoracle.cpp Normal file
View file

@ -0,0 +1,136 @@
/**
* 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 <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::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)
return;
JID bareJID(presence->getFrom().toBare());
if (presence->getType() == Presence::Subscribe || presence->getType() == Presence::Subscribed) {
}
else {
Presence::ref passedPresence = presence;
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;
}
}

View file

@ -71,11 +71,19 @@ bool StorageResponder::handleSetRequest(const Swift::JID& from, const Swift::JID
return true;
}
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>());
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;
}

View file

@ -45,6 +45,7 @@
#include "log4cxx/consoleappender.h"
#include "log4cxx/patternlayout.h"
#include "log4cxx/propertyconfigurator.h"
#include "Swiften/Swiften.h"
using namespace Swift;
using namespace boost;
@ -137,7 +138,7 @@ Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories,
m_entityCapsManager = new EntityCapsManager(m_capsManager, m_stanzaChannel);
m_entityCapsManager->onCapsChanged.connect(boost::bind(&Component::handleCapsChanged, this, _1));
m_presenceOracle = new PresenceOracle(m_stanzaChannel);
m_presenceOracle = new Transport::PresenceOracle(m_stanzaChannel);
m_presenceOracle->onPresenceChange.connect(bind(&Component::handlePresence, this, _1));
m_discoInfoResponder = new DiscoInfoResponder(m_iqRouter, m_config);
@ -170,7 +171,7 @@ Swift::StanzaChannel *Component::getStanzaChannel() {
return m_stanzaChannel;
}
Swift::PresenceOracle *Component::getPresenceOracle() {
Transport::PresenceOracle *Component::getPresenceOracle() {
return m_presenceOracle;
}

View file

@ -24,6 +24,7 @@
#include "transport/rostermanager.h"
#include "transport/usermanager.h"
#include "transport/conversationmanager.h"
#include "transport/presenceoracle.h"
#include "Swiften/Swiften.h"
#include "Swiften/Server/ServerStanzaChannel.h"
#include "Swiften/Elements/StreamError.h"

View file

@ -286,6 +286,10 @@ bool UserRegistration::handleSetRequest(const Swift::JID& from, const Swift::JID
else if (textSingle->getName() == "encoding") {
encoding = textSingle->getValue();
}
// Pidgin sends it as textSingle, not sure why...
else if (textSingle->getName() == "password") {
payload->setPassword(textSingle->getValue());
}
continue;
}

View file

@ -128,6 +128,15 @@ std::vector<std::string> deserializeGroups(std::string &groups) {
return ret;
}
int getRandomPort(const std::string &s) {
unsigned long r = 0;
BOOST_FOREACH(char c, s) {
r += (int) c;
}
srand(time(NULL) + r);
return 30000 + rand() % 10000;
}
}
}