diff --git a/ChangeLog b/ChangeLog index 9ef83b48..94cc0f64 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,13 +13,25 @@ Version 2.0.0-beta3 (2012-XX-XX): * Added Munin plugin (Thanks to Askovpen). * Added support for more admin_jid JIDs (Thanks to Askovpen). * Fixed allowed_servers option. + * Show error in server-mode when server port is already used. + * Fixed bug when backend could freeze on exit. + * Options from config file can now be set also using command line like + --service.jid=domain.tld . + * Do not send password in IQ-get registration response. Libpurple: * prpl-gg: Fetch the contact list properly (#252). + * Added support for prpl-novell as it was in spectrum1. Skype: * Log more errors. + Libyahoo2: + * Added new Yahoo backend based on libyahoo2. + + Swiften: + * Added new XMPP backend based on Swiften library. + Backend API: * Added Python NetworkPlugin class, so it is now easier to write backends in Python (Thanks to Sarang). diff --git a/backends/libcommuni/main.cpp b/backends/libcommuni/main.cpp index 9b717d4c..cab69429 100644 --- a/backends/libcommuni/main.cpp +++ b/backends/libcommuni/main.cpp @@ -78,7 +78,7 @@ int main (int argc, char* argv[]) { Swift::QtEventLoop eventLoop; - if (config.getUnregistered().find("service.irc_server") == config.getUnregistered().end()) { + if (!CONFIG_HAS_KEY(&config, "service.irc_server")) { np = new IRCNetworkPlugin(&config, &eventLoop, host, port); } else { diff --git a/backends/libcommuni/singleircnetworkplugin.cpp b/backends/libcommuni/singleircnetworkplugin.cpp index cce79c7b..378df949 100644 --- a/backends/libcommuni/singleircnetworkplugin.cpp +++ b/backends/libcommuni/singleircnetworkplugin.cpp @@ -10,13 +10,19 @@ DEFINE_LOGGER(logger, "SingleIRCNetworkPlugin"); SingleIRCNetworkPlugin::SingleIRCNetworkPlugin(Config *config, Swift::QtEventLoop *loop, const std::string &host, int port) { this->config = config; - m_server = config->getUnregistered().find("service.irc_server")->second; + if (CONFIG_HAS_KEY(config, "service.irc_server")) { + m_server = CONFIG_STRING(config, "service.irc_server"); + } + else { + LOG4CXX_ERROR(logger, "No [service] irc_server defined, exiting..."); + exit(-1); + } m_socket = new QTcpSocket(); m_socket->connectToHost(FROM_UTF8(host), port); connect(m_socket, SIGNAL(readyRead()), this, SLOT(readData())); - if (config->getUnregistered().find("service.irc_identify") != config->getUnregistered().end()) { - m_identify = config->getUnregistered().find("service.irc_identify")->second; + if (CONFIG_HAS_KEY(config, "service.irc_identify")) { + m_identify = CONFIG_STRING(config, "service.irc_identify"); } else { m_identify = "NickServ identify $name $password"; diff --git a/backends/smstools3/main.cpp b/backends/smstools3/main.cpp index 2e61868c..32799348 100644 --- a/backends/smstools3/main.cpp +++ b/backends/smstools3/main.cpp @@ -124,8 +124,8 @@ class SMSNetworkPlugin : public NetworkPlugin { void handleSMSDir() { std::string dir = "/var/spool/sms/incoming/"; - if (config->getUnregistered().find("backend.incoming_dir") != config->getUnregistered().end()) { - dir = config->getUnregistered().find("backend.incoming_dir")->second; + if (CONFIG_HAS_KEY(config, "backend.incoming_dir")) { + dir = CONFIG_STRING(config, "backend.incoming_dir"); } LOG4CXX_INFO(logger, "Checking directory " << dir << " for incoming SMS."); diff --git a/include/transport/config.h b/include/transport/config.h index 903480e9..4ced7c98 100644 --- a/include/transport/config.h +++ b/include/transport/config.h @@ -77,7 +77,7 @@ class Config { bool reload(); bool hasKey(const std::string &key) { - return m_variables.find(key) != m_variables.end(); + return m_variables.find(key) != m_variables.end() || m_unregistered.find(key) != m_unregistered.end(); } /// Returns value of variable defined by key. @@ -85,16 +85,15 @@ class Config { /// For variables in sections you can use "section.variable" key format. /// \param key config variable name const boost::program_options::variable_value &operator[] (const std::string &key) { - return m_variables[key]; + if (m_variables.find(key) != m_variables.end()) { + return m_variables[key]; + } + return m_unregistered[key]; } /// Returns path to config file from which data were loaded. const std::string &getConfigFile() { return m_file; } - const std::map &getUnregistered() { - return m_unregistered; - } - /// This signal is emitted when config is loaded/reloaded. boost::signal onConfigReloaded; @@ -102,7 +101,7 @@ class Config { int m_argc; char **m_argv; Variables m_variables; - std::map m_unregistered; + std::map m_unregistered; std::string m_file; }; diff --git a/include/transport/networkplugin.h b/include/transport/networkplugin.h index 9f440152..f54269a0 100644 --- a/include/transport/networkplugin.h +++ b/include/transport/networkplugin.h @@ -75,6 +75,11 @@ class NetworkPlugin { 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") diff --git a/include/transport/networkpluginserver.h b/include/transport/networkpluginserver.h index 5572780a..7e43ce33 100644 --- a/include/transport/networkpluginserver.h +++ b/include/transport/networkpluginserver.h @@ -89,6 +89,7 @@ class NetworkPluginServer { 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 handleParticipantChangedPayload(const std::string &payload); void handleRoomChangedPayload(const std::string &payload); diff --git a/include/transport/rostermanager.h b/include/transport/rostermanager.h index 64f05fa7..fd649b57 100644 --- a/include/transport/rostermanager.h +++ b/include/transport/rostermanager.h @@ -69,6 +69,10 @@ class RosterManager { /// \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); @@ -107,7 +111,11 @@ class RosterManager { void sendBuddyRosterPush(Buddy *buddy); + void sendBuddyRosterRemove(Buddy *buddy); + void sendBuddySubscribePresence(Buddy *buddy); + + void sendBuddyUnsubscribePresence(Buddy *buddy); void sendCurrentPresences(const Swift::JID &to); diff --git a/plugin/cpp/networkplugin.cpp b/plugin/cpp/networkplugin.cpp index 43d82653..4455c37e 100644 --- a/plugin/cpp/networkplugin.cpp +++ b/plugin/cpp/networkplugin.cpp @@ -165,6 +165,19 @@ void NetworkPlugin::handleBuddyChanged(const std::string &user, const std::strin send(message); } +void NetworkPlugin::handleBuddyRemoved(const std::string &user, const std::string &buddyName) { + pbnetwork::Buddy buddy; + buddy.set_username(user); + buddy.set_buddyname(buddyName); + + std::string message; + buddy.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_REMOVED); + + send(message); +} + void NetworkPlugin::handleBuddyTyping(const std::string &user, const std::string &buddyName) { pbnetwork::Buddy buddy; buddy.set_username(user); diff --git a/src/config.cpp b/src/config.cpp index f17efb70..e761e523 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -195,7 +195,7 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description BOOST_FOREACH(option &opt, parsed.options) { if (opt.unregistered) { - m_unregistered[opt.string_key] = opt.value[0]; + m_unregistered[opt.string_key] = variable_value(opt.value[0], false); } else if (opt.value[0].find("$jid") != std::string::npos) { boost::replace_all(opt.value[0], "$jid", jid); diff --git a/src/networkpluginserver.cpp b/src/networkpluginserver.cpp index fdd68e68..c9b6f23c 100644 --- a/src/networkpluginserver.cpp +++ b/src/networkpluginserver.cpp @@ -264,6 +264,7 @@ NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, U LOG4CXX_INFO(logger, "Listening on host " << CONFIG_STRING(m_config, "service.backend_host") << " port " << CONFIG_STRING(m_config, "service.backend_port")); unsigned long pid = exec_(CONFIG_STRING(m_config, "service.backend"), CONFIG_STRING(m_config, "service.backend_host").c_str(), CONFIG_STRING(m_config, "service.backend_port").c_str(), m_config->getConfigFile().c_str()); + LOG4CXX_INFO(logger, "Tried to spawn first backend with pid " << pid); LOG4CXX_INFO(logger, "Backend should now connect to Spectrum2 instance. Spectrum2 won't accept any connection before backend connects"); #ifndef _WIN32 @@ -509,8 +510,6 @@ void NetworkPluginServer::handleBuddyChangedPayload(const std::string &data) { if (!user) return; - LOG4CXX_INFO(logger, "HANDLE BUDDY CHANGED " << payload.buddyname() << "-" << payload.alias()); - LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(payload.buddyname()); if (buddy) { handleBuddyPayload(buddy, payload); @@ -524,6 +523,20 @@ void NetworkPluginServer::handleBuddyChangedPayload(const std::string &data) { } } +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) { @@ -828,6 +841,9 @@ void NetworkPluginServer::handleDataRead(Backend *c, boost::shared_ptrgetJID().toString() << ": Tried to remove unknown buddy " << name); + return; + } + + if (m_component->inServerMode() || m_remoteRosterRequest) { + sendBuddyRosterRemove(buddy); + } + else { + sendBuddyUnsubscribePresence(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 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) @@ -142,6 +182,20 @@ void RosterManager::sendBuddyRosterPush(Buddy *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());