From 158abda4e9486196b9ad3bcf2ec28e2d273b5540 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Tue, 20 Nov 2012 14:29:59 +0400 Subject: [PATCH 1/5] Multiple Windows services support --- spectrum/src/main.cpp | 53 +++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/spectrum/src/main.cpp b/spectrum/src/main.cpp index 0b312bf2..21f6a1c4 100644 --- a/spectrum/src/main.cpp +++ b/spectrum/src/main.cpp @@ -266,7 +266,13 @@ int main(int argc, char **argv) bool no_daemon = false; std::string config_file; std::string jid; - +#ifdef WIN32 + std::string install_service_name, uninstall_service_name, run_service_name; + // determine the name of the currently executing file + char szFilePath[MAX_PATH]; + GetModuleFileNameA(NULL, szFilePath, sizeof(szFilePath)); + std::string exe_file(szFilePath); +#endif setlocale(LC_ALL, ""); #ifndef WIN32 #ifndef __FreeBSD__ @@ -297,8 +303,9 @@ int main(int argc, char **argv) ; #ifdef WIN32 desc.add_options() - ("install-service,i", "Install spectrum as Windows service") - ("uninstall-service,u", "Uninstall Windows service"); + ("install-service,i", boost::program_options::value(&install_service_name)->default_value(""), "Install spectrum as Windows service") + ("uninstall-service,u", boost::program_options::value(&uninstall_service_name)->default_value(""), "Uninstall Windows service") + ("run-as-service,r", boost::program_options::value(&run_service_name)->default_value(""), "stub for Windows Service Manager"); #endif try { @@ -327,41 +334,38 @@ int main(int argc, char **argv) if(vm.count("no-daemonize")) { no_daemon = true; } -#ifdef WIN32 - if (vm.count("install-service")) { - ServiceWrapper ntservice("Spectrum2"); +#ifdef WIN32 + if (!install_service_name.empty()) { + // build command line for Service Manager + std::string service_path = exe_file + std::string(" --config ") + vm["config"].as() + + std::string(" --run-as-service ") + install_service_name; + + ServiceWrapper ntservice((char *)install_service_name.c_str()); if (!ntservice.IsInstalled()) { - // determine the name of the currently executing file - char szFilePath[MAX_PATH]; - GetModuleFileNameA(NULL, szFilePath, sizeof(szFilePath)); - std::string exe_file(szFilePath); - std::string config_file = exe_file.replace(exe_file.end() - 4, exe_file.end(), ".cfg"); - std::string service_path = std::string(szFilePath) + std::string(" --config ") + config_file; - if (ntservice.Install((char *)service_path.c_str())) { - std::cout << "Successfully installed" << std::endl; + std::cout << "Successfully installed " << install_service_name << std::endl; return 0; } else { std::cout << "Error installing service, are you an Administrator?" << std::endl; return 1; } } else { - std::cout << "Already installed" << std::endl; + std::cout << "Already installed " << install_service_name << std::endl; return 1; } } - if (vm.count("uninstall-service")) { - ServiceWrapper ntservice("Spectrum2"); + if (!uninstall_service_name.empty()) { + ServiceWrapper ntservice((char *)uninstall_service_name.c_str()); if (ntservice.IsInstalled()) { if (ntservice.UnInstall()) { - std::cout << "Successfully removed" << std::endl; + std::cout << "Successfully removed " << uninstall_service_name << std::endl; return 0; } else { std::cout << "Error removing service, are you an Administrator?" << std::endl; return 1; } } else { - std::cout << "Service not installed" << std::endl; + std::cout << "Service not installed: " << uninstall_service_name << std::endl; return 1; } } @@ -441,9 +445,14 @@ int main(int argc, char **argv) } #endif #ifdef WIN32 - ServiceWrapper ntservice("Spectrum2"); - if (ntservice.IsInstalled()) { - ntservice.RunService(); + if (!run_service_name.empty()) { + ServiceWrapper ntservice((char *)run_service_name.c_str()); + if (ntservice.IsInstalled()) { + ntservice.RunService(); + } else { + std::cerr << "Service not installed: " << run_service_name << std::endl; + return 1; + } } else { mainloop(); } From 08a5e487db0c2f288eb0a56a11b3917b8fb33922 Mon Sep 17 00:00:00 2001 From: Jan Kaluza Date: Tue, 27 Nov 2012 10:33:04 +0100 Subject: [PATCH 2/5] Better error message when backends can't be started --- backends/twitter/main.cpp | 3 ++- src/networkpluginserver.cpp | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/backends/twitter/main.cpp b/backends/twitter/main.cpp index 5cbf2def..f68f25ab 100644 --- a/backends/twitter/main.cpp +++ b/backends/twitter/main.cpp @@ -39,7 +39,8 @@ int main (int argc, char* argv[]) { StorageBackend *storagebackend; storagebackend = StorageBackend::createBackend(cfg, error); if (storagebackend == NULL) { - LOG4CXX_ERROR(logger, "Error creating StorageBackend! " << error) + LOG4CXX_ERROR(logger, "Error creating StorageBackend! " << error); + LOG4CXX_ERROR(logger, "Twitter backend needs storage backend configured to work! " << error); return -2; } diff --git a/src/networkpluginserver.cpp b/src/networkpluginserver.cpp index bcea34f7..5061d1ce 100644 --- a/src/networkpluginserver.cpp +++ b/src/networkpluginserver.cpp @@ -332,7 +332,13 @@ void NetworkPluginServer::start() { if (result != 0) { if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) { - LOG4CXX_ERROR(logger, "Backend can not be started, exit_code=" << WEXITSTATUS(status) << ", possible error: " << strerror(WEXITSTATUS(status))); + if (status == 254) { + LOG4CXX_ERROR(logger, "Backend can not be started, because it needs database to store data, but the database backend is not configured."); + } + else { + LOG4CXX_ERROR(logger, "Backend can not be started, exit_code=" << WEXITSTATUS(status) << ", possible error: " << strerror(WEXITSTATUS(status))); + } + LOG4CXX_ERROR(logger, "Check backend log for more details"); continue; } } From 110a366bfb8667978c79f1cdffd275f93667a40c Mon Sep 17 00:00:00 2001 From: Jan Kaluza Date: Tue, 27 Nov 2012 10:50:49 +0100 Subject: [PATCH 3/5] Added NetworkPlugin::StorageBackendNeeded instead of -2 constant --- backends/twitter/main.cpp | 2 +- include/transport/networkplugin.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/backends/twitter/main.cpp b/backends/twitter/main.cpp index f68f25ab..bb28d601 100644 --- a/backends/twitter/main.cpp +++ b/backends/twitter/main.cpp @@ -41,7 +41,7 @@ int main (int argc, char* argv[]) { if (storagebackend == NULL) { LOG4CXX_ERROR(logger, "Error creating StorageBackend! " << error); LOG4CXX_ERROR(logger, "Twitter backend needs storage backend configured to work! " << error); - return -2; + return NetworkPlugin::StorageBackendNeeded; } else if (!storagebackend->connect()) { diff --git a/include/transport/networkplugin.h b/include/transport/networkplugin.h index 6c4ababd..a5e123f7 100644 --- a/include/transport/networkplugin.h +++ b/include/transport/networkplugin.h @@ -35,6 +35,7 @@ namespace Transport { /// development. class NetworkPlugin { public: + enum ExitCode { StorageBackendNeeded = -2 }; class PluginConfig { public: From 8b6973539f23ea00dfb950f7e109f2d5f2139d51 Mon Sep 17 00:00:00 2001 From: Jan Kaluza Date: Tue, 27 Nov 2012 12:51:19 +0100 Subject: [PATCH 4/5] Postpone room subject forwarding if we haven't sent initial presences yet --- backends/libcommuni/ircnetworkplugin.cpp | 12 ++++++++++++ backends/libcommuni/ircnetworkplugin.h | 1 + backends/libcommuni/session.cpp | 10 +++++++++- include/transport/conversation.h | 2 ++ plugin/cpp/networkplugin.cpp | 2 +- src/conversation.cpp | 12 ++++++++++++ src/tests/conversationmanager.cpp | 25 ++++++++++++++++-------- src/usermanager.cpp | 3 ++- 8 files changed, 56 insertions(+), 11 deletions(-) diff --git a/backends/libcommuni/ircnetworkplugin.cpp b/backends/libcommuni/ircnetworkplugin.cpp index f2fc4574..bda4cc4d 100644 --- a/backends/libcommuni/ircnetworkplugin.cpp +++ b/backends/libcommuni/ircnetworkplugin.cpp @@ -11,6 +11,7 @@ DEFINE_LOGGER(logger, "IRCNetworkPlugin"); IRCNetworkPlugin::IRCNetworkPlugin(Config *config, Swift::QtEventLoop *loop, const std::string &host, int port) { this->config = config; m_currentServer = 0; + m_firstPing = true; m_socket = new QTcpSocket(); m_socket->connectToHost(FROM_UTF8(host), port); connect(m_socket, SIGNAL(readyRead()), this, SLOT(readData())); @@ -48,6 +49,17 @@ void IRCNetworkPlugin::readData() { if (availableBytes == 0) return; + if (m_firstPing) { + m_firstPing = false; + // Users can join the network without registering if we allow + // one user to connect multiple IRC networks. + if (m_servers.empty()) { + NetworkPlugin::PluginConfig cfg; + cfg.setNeedRegistration(false); + sendConfig(cfg); + } + } + std::string d = std::string(m_socket->readAll().data(), availableBytes); handleDataRead(d); } diff --git a/backends/libcommuni/ircnetworkplugin.h b/backends/libcommuni/ircnetworkplugin.h index 043089cd..d4a84d73 100644 --- a/backends/libcommuni/ircnetworkplugin.h +++ b/backends/libcommuni/ircnetworkplugin.h @@ -46,4 +46,5 @@ class IRCNetworkPlugin : public QObject, public NetworkPlugin { std::vector m_servers; int m_currentServer; std::string m_identify; + bool m_firstPing; }; \ No newline at end of file diff --git a/backends/libcommuni/session.cpp b/backends/libcommuni/session.cpp index 91a7fee8..cb0b6c92 100644 --- a/backends/libcommuni/session.cpp +++ b/backends/libcommuni/session.cpp @@ -189,6 +189,7 @@ void MyIrcSession::on_messageReceived(IrcMessage *message) { void MyIrcSession::on_numericMessageReceived(IrcMessage *message) { QString channel; QStringList members; + std::string nick; IrcNumericMessage *m = (IrcNumericMessage *) message; switch (m->code()) { @@ -196,7 +197,14 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) { m_topicData = TO_UTF8(m->parameters().value(2)); break; case 333: - np->handleSubject(user, TO_UTF8(m->parameters().value(1)) + suffix, m_topicData, TO_UTF8(m->parameters().value(2))); + nick = TO_UTF8(m->parameters().value(2)); + if (nick.find("!") != std::string::npos) { + nick = nick.substr(0, nick.find("!")); + } + if (nick.find("/") != std::string::npos) { + nick = nick.substr(0, nick.find("/")); + } + np->handleSubject(user, TO_UTF8(m->parameters().value(1)) + suffix, m_topicData, nick); break; case 353: channel = m->parameters().value(2); diff --git a/include/transport/conversation.h b/include/transport/conversation.h index e137b566..ff109aa2 100644 --- a/include/transport/conversation.h +++ b/include/transport/conversation.h @@ -148,6 +148,8 @@ class Conversation { Swift::JID m_jid; std::list m_jids; std::map m_participants; + boost::shared_ptr m_subject; + bool m_sentInitialPresence; }; } diff --git a/plugin/cpp/networkplugin.cpp b/plugin/cpp/networkplugin.cpp index b5622326..3dde9c53 100644 --- a/plugin/cpp/networkplugin.cpp +++ b/plugin/cpp/networkplugin.cpp @@ -63,7 +63,7 @@ NetworkPlugin::~NetworkPlugin() { } void NetworkPlugin::sendConfig(const PluginConfig &cfg) { - std::string data = "[registration]"; + std::string data = "[registration]\n"; data += std::string("needPassword=") + (cfg.m_needPassword ? "1" : "0") + "\n"; data += std::string("needRegistration=") + (cfg.m_needRegistration ? "1" : "0") + "\n"; diff --git a/src/conversation.cpp b/src/conversation.cpp index 67a5c386..9cd8f739 100644 --- a/src/conversation.cpp +++ b/src/conversation.cpp @@ -33,6 +33,7 @@ Conversation::Conversation(ConversationManager *conversationManager, const std:: // m_conversationManager->addConversation(this); m_muc = isMUC; m_jid = m_conversationManager->getUser()->getJID().toBare(); + m_sentInitialPresence = false; } Conversation::~Conversation() { @@ -128,6 +129,11 @@ void Conversation::handleMessage(boost::shared_ptr &message, con BOOST_FOREACH(const Swift::JID &jid, m_jids) { message->setTo(jid); message->setFrom(Swift::JID(legacyName, m_conversationManager->getComponent()->getJID().toBare(), n)); + // Subject has to be sent after our own presence (the one with code 110) + if (!message->getSubject().empty() && m_sentInitialPresence == false) { + m_subject = message; + return; + } m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(message); } } @@ -169,6 +175,7 @@ Swift::Presence::ref Conversation::generatePresence(const std::string &nick, int Swift::MUCUserPayload::StatusCode c; c.code = 110; p->addStatusCode(c); + m_sentInitialPresence = true; } @@ -217,6 +224,11 @@ void Conversation::handleParticipantChanged(const std::string &nick, int flag, i if (!newname.empty()) { handleParticipantChanged(newname, flag, status, statusMessage); } + + if (m_sentInitialPresence && m_subject) { + m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(m_subject); + m_subject.reset(); + } } } diff --git a/src/tests/conversationmanager.cpp b/src/tests/conversationmanager.cpp index ef31975d..c182146c 100644 --- a/src/tests/conversationmanager.cpp +++ b/src/tests/conversationmanager.cpp @@ -94,26 +94,35 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe void handleSubjectMessages() { User *user = userManager->getUser("user@localhost"); - - TestingConversation *conv = new TestingConversation(user->getConversationManager(), "buddy1"); - user->getConversationManager()->addConversation(conv); + TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true); + conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2)); + conv->setNickname("nickname"); + conv->addJID("user@localhost/resource"); boost::shared_ptr msg(new Swift::Message()); msg->setSubject("subject"); + msg->setType(Swift::Message::Groupchat); - // Forward it conv->handleMessage(msg); loop->processEvents(); + + // No response, because presence with code 110 has not been sent yet and we must not send + // subject before this one. + CPPUNIT_ASSERT_EQUAL(0, (int) received.size()); + + // this user presence - status code 110 + conv->handleParticipantChanged("nickname", 1, Swift::StatusShow::Away, "my status message"); + loop->processEvents(); - CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); - CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); - CPPUNIT_ASSERT_EQUAL(std::string("subject"), dynamic_cast(getStanza(received[0]))->getSubject()); + CPPUNIT_ASSERT_EQUAL(2, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[1]))); + CPPUNIT_ASSERT_EQUAL(std::string("subject"), dynamic_cast(getStanza(received[1]))->getSubject()); received.clear(); // send response msg->setFrom("user@localhost/resource"); - msg->setTo("buddy1@localhost/bot"); + msg->setTo("#room@localhost"); injectMessage(msg); loop->processEvents(); diff --git a/src/usermanager.cpp b/src/usermanager.cpp index 4e6e4b01..fd4305ce 100644 --- a/src/usermanager.cpp +++ b/src/usermanager.cpp @@ -247,7 +247,8 @@ void UserManager::handlePresence(Swift::Presence::ref presence) { // We allow auto_register feature in gateway-mode. This allows IRC user to register // the transport just by joining the room. if (!m_component->inServerMode()) { - if (!registered && CONFIG_BOOL(m_component->getConfig(), "registration.auto_register")) { + if (!registered && (CONFIG_BOOL(m_component->getConfig(), "registration.auto_register") || + !CONFIG_BOOL_DEFAULTED(m_component->getConfig(), "registration.needRegistration", true))) { res.password = ""; res.jid = userkey; From 827bac538d8df2b45250a211e50b8c608e90d134 Mon Sep 17 00:00:00 2001 From: Jan Kaluza Date: Tue, 27 Nov 2012 12:53:46 +0100 Subject: [PATCH 5/5] Fix qt-moc error while parsing boost headers --- backends/libcommuni/CMakeLists.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/backends/libcommuni/CMakeLists.txt b/backends/libcommuni/CMakeLists.txt index 38e7507b..699b02bd 100644 --- a/backends/libcommuni/CMakeLists.txt +++ b/backends/libcommuni/CMakeLists.txt @@ -1,10 +1,14 @@ cmake_minimum_required(VERSION 2.6) FILE(GLOB SRC *.cpp) FILE(GLOB HEADERS *.h) -QT4_WRAP_CPP(SRC ${HEADERS}) +QT4_WRAP_CPP(SRC ${HEADERS} OPTIONS -DBOOST_TT_HAS_OPERATOR_HPP_INCLUDED) ADD_EXECUTABLE(spectrum2_libcommuni_backend ${SRC}) -target_link_libraries(spectrum2_libcommuni_backend ${IRC_LIBRARY} ${QT_LIBRARIES} transport pthread) +if (NOT WIN32) + target_link_libraries(spectrum2_libcommuni_backend ${IRC_LIBRARY} ${QT_LIBRARIES} transport pthread) +else () + target_link_libraries(spectrum2_libcommuni_backend ${IRC_LIBRARY} ${QT_LIBRARIES} transport) +endif() INSTALL(TARGETS spectrum2_libcommuni_backend RUNTIME DESTINATION bin)