diff --git a/include/transport/transport.h b/include/transport/transport.h index 5c702753..2f59dd76 100644 --- a/include/transport/transport.h +++ b/include/transport/transport.h @@ -93,6 +93,8 @@ namespace Transport { /// \return Jabber ID of this transport Swift::JID &getJID() { return m_jid; } + Swift::BoostNetworkFactories *getFactories() { return m_factories; } + /// This signal is emitted when server disconnects the transport because of some error. /// \param error disconnection error boost::signal onConnectionError; @@ -114,6 +116,8 @@ namespace Transport { /// \param presence presence data boost::signal onUserPresenceReceived; +// boost::signal info, Swift::ErrorPayload::ref error, const Swift::JID& jid)> onDiscoInfoResponse; + private: void handleConnected(); void handleConnectionError(const Swift::ComponentError &error); @@ -125,8 +129,8 @@ namespace Transport { void handleDataRead(const std::string &data); void handleDataWritten(const std::string &data); - void handleDiscoInfoResponse(boost::shared_ptr info, Swift::ErrorPayload::ref error, const Swift::JID& jid); -// void handleCapsChanged(const Swift::JID& jid); +// void handleDiscoInfoResponse(boost::shared_ptr info, Swift::ErrorPayload::ref error, const Swift::JID& jid); + void handleCapsChanged(const Swift::JID& jid); Swift::BoostNetworkFactories *m_factories; Swift::Component *m_component; diff --git a/include/transport/user.h b/include/transport/user.h index 3475e078..eac38e1d 100644 --- a/include/transport/user.h +++ b/include/transport/user.h @@ -52,6 +52,9 @@ class User { /// \return UserInfo struct UserInfo &getUserInfo() { return m_userInfo; } + 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); @@ -60,12 +63,20 @@ class User { /// \return language const char *getLang() { return "en"; } + boost::signal onReadyToConnect; + private: + void onConnectingTimeout(); + Swift::JID m_jid; Component *m_component; Swift::EntityCapsManager *m_entityCapsManager; Swift::PresenceOracle *m_presenceOracle; UserInfo m_userInfo; + void *m_data; + bool m_connected; + bool m_readyForConnect; + Swift::Timer::ref m_reconnectTimer; }; } diff --git a/include/transport/usermanager.h b/include/transport/usermanager.h index 79108133..20bd13b3 100644 --- a/include/transport/usermanager.h +++ b/include/transport/usermanager.h @@ -68,6 +68,7 @@ class UserManager { private: void handlePresence(Swift::Presence::ref presence); +// void handleDiscoInfoResponse(boost::shared_ptr info, Swift::ErrorPayload::ref error, const Swift::JID& jid); void addUser(User *user); long m_onlineBuddies; diff --git a/spectrum/src/main.cpp b/spectrum/src/main.cpp index aff0d4fe..10a500cd 100644 --- a/spectrum/src/main.cpp +++ b/spectrum/src/main.cpp @@ -8,9 +8,12 @@ #include "transport/logger.h" #include "transport/sqlite3backend.h" #include "transport/userregistration.h" +#include "transport/user.h" +#include "transport/storagebackend.h" #include "spectrumeventloop.h" #include "geventloop.h" +#define Log(X, STRING) std::cout << "[SPECTRUM] " << X << " " << STRING << "\n"; using namespace Transport; @@ -61,13 +64,39 @@ static PurpleCoreUiOps coreUiOps = NULL }; +static void printDebug(PurpleDebugLevel level, const char *category, const char *arg_s) { + std::string c("[LIBPURPLE"); + + if (category) { + c.push_back('/'); + c.append(category); + } + + c.push_back(']'); + + std::cout << c << " " << arg_s; +} + +/* + * Ops.... + */ +static PurpleDebugUiOps debugUiOps = +{ + printDebug, + NULL, + NULL, + NULL, + NULL, + NULL +}; + static bool initPurple(Config &cfg) { bool ret; purple_util_set_user_dir("./"); // if (m_configuration.logAreas & LOG_AREA_PURPLE) -// purple_debug_set_ui_ops(&debugUiOps); + purple_debug_set_ui_ops(&debugUiOps); purple_core_set_ui_ops(&coreUiOps); purple_eventloop_set_ui_ops(getEventLoopUiOps()); @@ -119,6 +148,118 @@ static bool initPurple(Config &cfg) { return ret; } +static void handleUserReadyToConnect(User *user) { + PurpleAccount *account = (PurpleAccount *) user->getData(); + purple_account_set_enabled(account, "spectrum", TRUE); + + const PurpleStatusType *status_type = purple_account_get_status_type_with_primitive(account, PURPLE_STATUS_AVAILABLE); + if (status_type != NULL) { + purple_account_set_status(account, purple_status_type_get_id(status_type), TRUE, NULL); + } +} + +static void handleUserCreated(User *user, UserManager *userManager, Config *config) { + UserInfo userInfo = user->getUserInfo(); + PurpleAccount *account = NULL; + const char *protocol = CONFIG_STRING(config, "service.protocol").c_str(); + if (purple_accounts_find(userInfo.uin.c_str(), protocol) != NULL){ + Log(userInfo.jid, "this account already exists"); + account = purple_accounts_find(userInfo.uin.c_str(), protocol); + User *u = (User *) account->ui_data; + if (u && u != user) { + Log(userInfo.jid, "This account is already connected by another jid " << user->getJID()); + return; + } + } + else { + Log(userInfo.jid, "creating new account"); + account = purple_account_new(userInfo.uin.c_str(), protocol); + + purple_accounts_add(account); + } +// Transport::instance()->collector()->stopCollecting(m_account); + +// PurplePlugin *plugin = purple_find_prpl(protocol); +// PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); +// for (GList *l = prpl_info->protocol_options; l != NULL; l = l->next) { +// PurpleAccountOption *option = (PurpleAccountOption *) l->data; +// purple_account_remove_setting(account, purple_account_option_get_setting(option)); +// } +// +// std::map &settings = Transport::instance()->getConfiguration().purple_account_settings; +// for (std::map ::iterator it = settings.begin(); it != settings.end(); it++) { +// PurpleAccountSettingValue v = (*it).second; +// std::string key((*it).first); +// switch (v.type) { +// case PURPLE_PREF_BOOLEAN: +// purple_account_set_bool(m_account, key.c_str(), v.b); +// break; +// +// case PURPLE_PREF_INT: +// purple_account_set_int(m_account, key.c_str(), v.i); +// break; +// +// case PURPLE_PREF_STRING: +// if (v.str) +// purple_account_set_string(m_account, key.c_str(), v.str); +// else +// purple_account_remove_setting(m_account, key.c_str()); +// break; +// +// case PURPLE_PREF_STRING_LIST: +// // TODO: +// break; +// +// default: +// continue; +// } +// } + + purple_account_set_string(account, "encoding", userInfo.encoding.empty() ? CONFIG_STRING(config, "registration.encoding").c_str() : userInfo.encoding.c_str()); + purple_account_set_bool(account, "use_clientlogin", false); +// purple_account_set_bool(account, "require_tls", Transport::instance()->getConfiguration().require_tls); +// purple_account_set_bool(account, "use_ssl", Transport::instance()->getConfiguration().require_tls); + purple_account_set_bool(account, "direct_connect", false); +// purple_account_set_bool(account, "check-mail", purple_value_get_boolean(getSetting("enable_notify_email"))); + + account->ui_data = user; + user->setData(account); + + user->onReadyToConnect.connect(boost::bind(&handleUserReadyToConnect, user)); + +// Transport::instance()->protocol()->onPurpleAccountCreated(m_account); + +// m_loadingBuddiesFromDB = true; +// loadRoster(); +// m_loadingBuddiesFromDB = false; + +// m_connectionStart = time(NULL); +// m_readyForConnect = false; + purple_account_set_password(account, userInfo.password.c_str()); +// Log(m_jid, "UIN:" << m_username << " USER_ID:" << m_userID); +} + +static void handleUserDestroyed(User *user, UserManager *userManager, Config *config) { + PurpleAccount *account = (PurpleAccount *) user->getData(); + if (account) { + purple_account_set_enabled(account, "spectrum", FALSE); + + // Remove conversations. + // This has to be called before m_account->ui_data = NULL;, because it uses + // ui_data to call SpectrumMessageHandler::purpleConversationDestroyed() callback. + GList *iter; + for (iter = purple_get_conversations(); iter; ) { + PurpleConversation *conv = (PurpleConversation*) iter->data; + iter = iter->next; + if (purple_conversation_get_account(conv) == account) + purple_conversation_destroy(conv); + } + + account->ui_data = NULL; +// Transport::instance()->collector()->collect(m_account); + } +} + int main(int argc, char **argv) { GError *error = NULL; GOptionContext *context; @@ -183,7 +324,7 @@ int main(int argc, char **argv) { #endif Config config; if (!config.load(argv[1])) { - std::cout << "Can't open sample.cfg configuration file.\n"; + std::cout << "Can't open " << argv[1] << " configuration file.\n"; return 1; } @@ -192,8 +333,22 @@ int main(int argc, char **argv) { SpectrumEventLoop eventLoop; Component transport(&eventLoop, &config); Logger logger(&transport); + + SQLite3Backend sql(&config); + logger.setStorageBackend(&sql); + if (!sql.connect()) { + std::cout << "Can't connect to database.\n"; + } + + UserManager userManager(&transport, &sql); + userManager.onUserCreated.connect(boost::bind(&handleUserCreated, _1, &userManager, &config)); + userManager.onUserDestroyed.connect(boost::bind(&handleUserDestroyed, _1, &userManager, &config)); + + UserRegistration userRegistration(&transport, &userManager, &sql); + logger.setUserRegistration(&userRegistration); + logger.setUserManager(&userManager); + transport.connect(); - eventLoop.run(); } diff --git a/spectrum/src/sample.cfg b/spectrum/src/sample.cfg index fc90fdf8..6de70472 100644 --- a/spectrum/src/sample.cfg +++ b/spectrum/src/sample.cfg @@ -3,6 +3,7 @@ jid = icq.localhost password = secret server = 127.0.0.1 port = 8888 +protocol=prpl-jabber [database] database = test.sql diff --git a/src/transport.cpp b/src/transport.cpp index 2860b65b..a1d8a37f 100644 --- a/src/transport.cpp +++ b/src/transport.cpp @@ -52,7 +52,7 @@ Component::Component(Swift::EventLoop *loop, Config *config) { m_capsMemoryStorage = new CapsMemoryStorage(); m_capsManager = new CapsManager(m_capsMemoryStorage, m_component->getStanzaChannel(), m_component->getIQRouter()); m_entityCapsManager = new EntityCapsManager(m_capsManager, m_component->getStanzaChannel()); -// m_entityCapsManager->onCapsChanged.connect(boost::bind(&Component::handleCapsChanged, this, _1)); + m_entityCapsManager->onCapsChanged.connect(boost::bind(&Component::handleCapsChanged, this, _1)); m_presenceOracle = new PresenceOracle(m_component->getStanzaChannel()); m_presenceOracle->onPresenceChange.connect(bind(&Component::handlePresence, this, _1)); @@ -169,11 +169,11 @@ void Component::handlePresence(Swift::Presence::ref presence) { haveFeatures = m_entityCapsManager->getCaps(presence->getFrom()) != DiscoInfo::ref(); std::cout << "has capsInfo " << haveFeatures << "\n"; } - else { - GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(presence->getFrom(), m_component->getIQRouter()); - discoInfoRequest->onResponse.connect(boost::bind(&Component::handleDiscoInfoResponse, this, _1, _2, presence->getFrom())); - discoInfoRequest->send(); - } +// else { +// GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(presence->getFrom(), m_component->getIQRouter()); +// discoInfoRequest->onResponse.connect(boost::bind(&Component::handleDiscoInfoResponse, this, _1, _2, presence->getFrom())); +// discoInfoRequest->send(); +// } } onUserPresenceReceived(presence); @@ -223,35 +223,9 @@ void Component::handleSubscription(Swift::Presence::ref presence) { // } } -void Component::handleDiscoInfoResponse(boost::shared_ptr discoInfo, Swift::ErrorPayload::ref error, const Swift::JID& jid) { -// AbstractUser *user = Transport::instance()->userManager()->getUserByJID(jid.toBare().toString().getUTF8String()); -// -// std::string resource = jid.getResource().getUTF8String(); -// if (user && user->hasResource(resource)) { -// if (user->getResource(resource).caps == 0) { -// int capabilities = 0; -// -// for (std::vector< String >::const_iterator it = discoInfo->getFeatures().begin(); it != discoInfo->getFeatures().end(); ++it) { -// if (*it == "http://jabber.org/protocol/rosterx") { -// capabilities |= CLIENT_FEATURE_ROSTERX; -// } -// else if (*it == "http://jabber.org/protocol/xhtml-im") { -// capabilities |= CLIENT_FEATURE_XHTML_IM; -// } -// else if (*it == "http://jabber.org/protocol/si/profile/file-transfer") { -// capabilities |= CLIENT_FEATURE_FILETRANSFER; -// } -// else if (*it == "http://jabber.org/protocol/chatstates") { -// capabilities |= CLIENT_FEATURE_CHATSTATES; -// } -// } -// -// user->setResource(resource, -256, capabilities); -// if (user->readyForConnect()) { -// user->connect(); -// } -// } -// } +void Component::handleCapsChanged(const Swift::JID& jid) { + bool haveFeatures = m_entityCapsManager->getCaps(jid) != DiscoInfo::ref(); + std::cout << "has capsInfo " << haveFeatures << "\n"; } } diff --git a/src/user.cpp b/src/user.cpp index a4949f8f..d2dd0886 100644 --- a/src/user.cpp +++ b/src/user.cpp @@ -32,6 +32,11 @@ User::User(const Swift::JID &jid, UserInfo &userInfo, Component *component) { m_presenceOracle = component->m_presenceOracle; m_entityCapsManager = component->m_entityCapsManager; m_userInfo = userInfo; + m_connected = false; + m_readyForConnect = false; + + m_reconnectTimer = m_component->getFactories()->getTimerFactory()->createTimer(10000); + m_reconnectTimer->onTick.connect(boost::bind(&User::onConnectingTimeout, this)); } User::~User(){ @@ -44,8 +49,31 @@ const Swift::JID &User::getJID() { void User::handlePresence(Swift::Presence::ref presence) { Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare()); + + if (!m_connected) { + // we are not connected to legacy network, so we should do it when disco#info arrive :) + if (m_readyForConnect == false) { + + // Forward status message to legacy network, but only if it's sent from active resource +// if (m_activeResource == presence->getFrom().getResource().getUTF8String()) { +// forwardStatus(presenceShow, stanzaStatus); +// } + boost::shared_ptr capsInfo = presence->getPayload(); + if (capsInfo && capsInfo->getHash() == "sha-1") { + if (m_entityCapsManager->getCaps(presence->getFrom()) != Swift::DiscoInfo::ref()) { + m_readyForConnect = true; + onReadyToConnect(); + } + } + else { + m_reconnectTimer->start(); + } + } + } + + if (highest) { - highest->setTo(presence->getFrom()); + highest->setTo(presence->getFrom().toBare()); highest->setFrom(m_component->getJID()); m_component->getComponent()->sendPresence(highest); } @@ -58,4 +86,12 @@ void User::handlePresence(Swift::Presence::ref presence) { } } +void User::onConnectingTimeout() { + if (m_connected || m_readyForConnect) + return; + m_reconnectTimer->stop(); + m_readyForConnect = true; + onReadyToConnect(); +} + } diff --git a/src/usermanager.cpp b/src/usermanager.cpp index bc8e641a..1369c2ee 100644 --- a/src/usermanager.cpp +++ b/src/usermanager.cpp @@ -32,6 +32,7 @@ UserManager::UserManager(Component *component, StorageBackend *storageBackend) { m_storageBackend = storageBackend; component->onUserPresenceReceived.connect(bind(&UserManager::handlePresence, this, _1)); +// component->onDiscoInfoResponse.connect(bind(&UserManager::handleDiscoInfoResponse, this, _1, _2, _3)); } UserManager::~UserManager(){