diff --git a/backends/libcommuni/ircnetworkplugin.cpp b/backends/libcommuni/ircnetworkplugin.cpp index 2e419952..80e2df5d 100644 --- a/backends/libcommuni/ircnetworkplugin.cpp +++ b/backends/libcommuni/ircnetworkplugin.cpp @@ -121,10 +121,9 @@ MyIrcSession *IRCNetworkPlugin::createSession(const std::string &user, const std } } + session->createBufferModel(); LOG4CXX_INFO(logger, user << ": Connecting " << hostname << " as " << nickname << ", port=" << session->port() << ", suffix=" << suffix); - session->open(); - return session; } @@ -137,6 +136,7 @@ void IRCNetworkPlugin::handleLoginRequest(const std::string &user, const std::st } m_sessions[user] = createSession(user, m_servers[m_currentServer], legacyName, password, ""); + m_sessions[user]->open(); } else { // We are waiting for first room join to connect user to IRC network, because we don't know which @@ -233,12 +233,14 @@ void IRCNetworkPlugin::handleJoinRoomRequest(const std::string &user, const std: std::string target = getTargetName(room); LOG4CXX_INFO(logger, user << ": Session name: " << session << ", Joining room " << target); + bool createdSession = false; if (m_sessions[session] == NULL) { if (m_servers.empty()) { // in gateway mode we want to login this user to network according to legacyName if (room.find("@") != std::string::npos) { // suffix is %irc.freenode.net to let MyIrcSession return #room%irc.freenode.net m_sessions[session] = createSession(user, room.substr(room.find("@") + 1), nickname, "", room.substr(room.find("@"))); + createdSession = true; } else { LOG4CXX_WARN(logger, user << ": There's no proper server defined in room to which this user wants to join: " << room); @@ -251,13 +253,18 @@ void IRCNetworkPlugin::handleJoinRoomRequest(const std::string &user, const std: } } - m_sessions[session]->addAutoJoinChannel(target, password); m_sessions[session]->sendCommand(IrcCommand::createJoin(FROM_UTF8(target), FROM_UTF8(password))); m_sessions[session]->rooms += 1; + if (createdSession) { + m_sessions[session]->open(); + } + // update nickname, because we have nickname per session, no nickname per room. - handleRoomNicknameChanged(user, target, TO_UTF8(m_sessions[session]->nickName())); - handleParticipantChanged(user, nickname, target, 0, pbnetwork::STATUS_ONLINE, "", TO_UTF8(m_sessions[session]->nickName())); + if (nickname != TO_UTF8(m_sessions[session]->nickName())) { + handleRoomNicknameChanged(user, room, TO_UTF8(m_sessions[session]->nickName())); + handleParticipantChanged(user, nickname, room, 0, pbnetwork::STATUS_ONLINE, "", TO_UTF8(m_sessions[session]->nickName())); + } } void IRCNetworkPlugin::handleLeaveRoomRequest(const std::string &user, const std::string &room) { @@ -269,7 +276,6 @@ void IRCNetworkPlugin::handleLeaveRoomRequest(const std::string &user, const std return; m_sessions[session]->sendCommand(IrcCommand::createPart(FROM_UTF8(target))); - m_sessions[session]->removeAutoJoinChannel(target); m_sessions[session]->rooms -= 1; if (m_sessions[session]->rooms <= 0 && m_servers.empty()) { @@ -279,3 +285,18 @@ void IRCNetworkPlugin::handleLeaveRoomRequest(const std::string &user, const std m_sessions.erase(session); } } + +void IRCNetworkPlugin::handleStatusChangeRequest(const std::string &user, int status, const std::string &statusMessage) { + if (m_sessions[user] == NULL) { + return; + } + + if (status == pbnetwork::STATUS_AWAY) { + LOG4CXX_INFO(logger, user << ": User is now away."); + m_sessions[user]->sendCommand(IrcCommand::createAway(statusMessage.empty() ? "Away" : FROM_UTF8(statusMessage))); + } + else { + LOG4CXX_INFO(logger, user << ": User is not away anymore."); + m_sessions[user]->sendCommand(IrcCommand::createAway("")); + } +} diff --git a/backends/libcommuni/ircnetworkplugin.h b/backends/libcommuni/ircnetworkplugin.h index 91704c34..6a84e93b 100644 --- a/backends/libcommuni/ircnetworkplugin.h +++ b/backends/libcommuni/ircnetworkplugin.h @@ -49,8 +49,14 @@ class IRCNetworkPlugin : public QObject, public NetworkPlugin { void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id); + void handleStatusChangeRequest(const std::string &user, int status, const std::string &statusMessage); + void tryNextServer(); + Config *getConfig() { + return m_config; + } + public slots: void readData(); void sendData(const std::string &string); diff --git a/backends/libcommuni/session.cpp b/backends/libcommuni/session.cpp index 10735562..39ecc35c 100644 --- a/backends/libcommuni/session.cpp +++ b/backends/libcommuni/session.cpp @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include "backports.h" #include "ircnetworkplugin.h" @@ -34,8 +36,6 @@ DEFINE_LOGGER(logger, "IRCConnection"); -// static bool sentList; - MyIrcSession::MyIrcSession(const std::string &user, IRCNetworkPlugin *np, const std::string &suffix, QObject* parent) : IrcConnection(parent) { m_np = np; @@ -43,35 +43,136 @@ MyIrcSession::MyIrcSession(const std::string &user, IRCNetworkPlugin *np, const m_suffix = suffix; m_connected = false; rooms = 0; + m_bufferModel = NULL; 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); + m_awayTimer->start(1 * 1000); } MyIrcSession::~MyIrcSession() { delete m_awayTimer; } +void MyIrcSession::createBufferModel() { + m_bufferModel = new IrcBufferModel(this); + connect(m_bufferModel, SIGNAL(added(IrcBuffer*)), this, SLOT(onBufferAdded(IrcBuffer*))); + connect(m_bufferModel, SIGNAL(removed(IrcBuffer*)), this, SLOT(onBufferRemoved(IrcBuffer*))); + + // keep the command parser aware of the context +// connect(m_bufferModel, SIGNAL(channelsChanged(QStringList)), parser, SLOT(setChannels(QStringList))); + + // create a server buffer for non-targeted messages... + IrcBuffer* serverBuffer = m_bufferModel->add(host()); + // ...and connect it to IrcBufferModel::messageIgnored() + // TODO: Make this configurable, so users can show the MOTD and other stuff as in normal + // IRC client. + connect(m_bufferModel, SIGNAL(messageIgnored(IrcMessage*)), serverBuffer, SLOT(receiveMessage(IrcMessage*))); +} + +void MyIrcSession::onBufferAdded(IrcBuffer* buffer) { + LOG4CXX_INFO(logger, m_user << ": Created IrcBuffer " << TO_UTF8(buffer->name())); + + connect(buffer, SIGNAL(messageReceived(IrcMessage*)), this, SLOT(onMessageReceived(IrcMessage*))); + + if (buffer->isChannel()) { + QVariantMap userData; + userData["awayCycle"] = boost::lexical_cast(CONFIG_STRING_DEFAULTED(m_np->getConfig(), "service.irc_away_timeout", "12")) + m_userModels.size(); + userData["awayTick"] = 0; + buffer->setUserData(userData); + } + + // create a model for buffer users + IrcUserModel* userModel = new IrcUserModel(buffer); + connect(userModel, SIGNAL(added(IrcUser*)), this, SLOT(onIrcUserAdded(IrcUser*))); + connect(userModel, SIGNAL(removed(IrcUser*)), this, SLOT(onIrcUserRemoved(IrcUser*))); + m_userModels.insert(buffer, userModel); +} + +void MyIrcSession::onBufferRemoved(IrcBuffer* buffer) { + LOG4CXX_INFO(logger, m_user << ": Removed IrcBuffer " << TO_UTF8(buffer->name())); + // the buffer specific models and documents are no longer needed + delete m_userModels.take(buffer); +} + +bool MyIrcSession::hasIrcUser(const std::string &channel_, const std::string &name) { + std::string channel = channel_; + if (channel[0] != '#') { + channel = "#" + channel; + } + + IrcBuffer *buffer = m_bufferModel->find(FROM_UTF8(channel)); + if (!buffer) { + LOG4CXX_ERROR(logger, m_user << ": Cannot find IrcBuffer '" << channel << "'"); + return false; + } + + IrcUserModel *userModel = m_userModels.value(buffer); + if (!userModel) { + LOG4CXX_ERROR(logger, m_user << ": Cannot find UserModel for IrcBuffer " << channel); + return false; + } + + return userModel->contains(FROM_UTF8(name)); +} + +void MyIrcSession::sendUserToFrontend(IrcUser *user, pbnetwork::StatusType statusType, const std::string &statusMessage, const std::string &newNick) { + std::string target = "#" + TO_UTF8(user->channel()->name().toLower()) + m_suffix; + int op = user->mode() == "o"; + + if (statusType != pbnetwork::STATUS_NONE) { + if (user->isAway()) { + statusType = pbnetwork::STATUS_AWAY; + } + if (newNick.empty()) { + LOG4CXX_INFO(logger, m_user << ": IrcUser connected: " << target << "/" << TO_UTF8(user->name())); + } + else { + LOG4CXX_INFO(logger, m_user << ": IrcUser changed nickname: " << target << "/" << TO_UTF8(user->name()) << ", newNick=" << newNick); + } + } + else { + LOG4CXX_INFO(logger, m_user << ": IrcUser disconnected: " << target << "/" << TO_UTF8(user->name())); + } + + m_np->handleParticipantChanged(m_user, TO_UTF8(user->name()), target, op, statusType, statusMessage, newNick); +} + +void MyIrcSession::onIrcUserChanged(bool dummy) { + IrcUser *user = dynamic_cast(QObject::sender()); + if (!user) { + return; + } + + LOG4CXX_INFO(logger, m_user << ": IrcUser " << TO_UTF8(user->name()) << " changed."); + sendUserToFrontend(user, pbnetwork::STATUS_ONLINE); +} + +void MyIrcSession::onIrcUserChanged(const QString &) { + onIrcUserChanged(false); +} + +void MyIrcSession::onIrcUserAdded(IrcUser *user) { + sendUserToFrontend(user, pbnetwork::STATUS_ONLINE); + connect(user, SIGNAL(modeChanged(const QString&)), this, SLOT(onIrcUserChanged(const QString&))); + connect(user, SIGNAL(awayChanged(bool)), this, SLOT(onIrcUserChanged(bool))); + +} + +void MyIrcSession::onIrcUserRemoved(IrcUser *user) { + sendUserToFrontend(user, pbnetwork::STATUS_NONE); + disconnect(user, SIGNAL(modeChanged(const QString&)), this, SLOT(onIrcUserChanged(const QString&))); + disconnect(user, SIGNAL(awayChanged(bool)), this, SLOT(onIrcUserChanged(bool))); +} + void MyIrcSession::on_connected() { m_connected = true; if (m_suffix.empty()) { m_np->handleConnected(m_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) { @@ -104,8 +205,11 @@ void MyIrcSession::on_socketError(QAbstractSocket::SocketError error) { }; if (!m_suffix.empty()) { - for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) { - m_np->handleParticipantChanged(m_user, TO_UTF8(nickName()), it->second->getChannel() + m_suffix, pbnetwork::PARTICIPANT_FLAG_ROOM_NOT_FOUND, pbnetwork::STATUS_NONE, reason); + foreach (IrcBuffer *buffer, m_bufferModel->buffers()) { + if (!buffer->isChannel()) { + continue; + } + m_np->handleParticipantChanged(m_user, TO_UTF8(nickName()), TO_UTF8(buffer->title()) + m_suffix, pbnetwork::PARTICIPANT_FLAG_ROOM_NOT_FOUND, pbnetwork::STATUS_NONE, reason); } } else { @@ -123,95 +227,81 @@ void MyIrcSession::on_disconnected() { m_connected = false; } -bool MyIrcSession::correctNickname(std::string &nickname) { - bool flags = 0; - if (!nickname.empty()) { - 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; - } +void MyIrcSession::correctNickname(std::string &nick) { + if (nick.empty()) { + return; + } + + switch(nick.at(0)) { + case '@': + case '+': + case '~': + case '&': + case '%': + nick.erase(0, 1); + break; + default: break; } - return flags; } -void MyIrcSession::on_joined(IrcMessage *message) { - IrcJoinMessage *m = (IrcJoinMessage *) message; - std::string nickname = TO_UTF8(m->nick()); - bool op = correctNickname(nickname); - getIRCBuddy(TO_UTF8(m->channel().toLower()), nickname).setOp(op); - m_np->handleParticipantChanged(m_user, nickname, TO_UTF8(m->channel().toLower()) + m_suffix, op, pbnetwork::STATUS_ONLINE); - LOG4CXX_INFO(logger, m_user << ": " << nickname << " joined " << TO_UTF8(m->channel().toLower()) + m_suffix); +IrcUser *MyIrcSession::getIrcUser(IrcBuffer *buffer, std::string &nick) { + correctNickname(nick); + IrcUserModel *userModel = m_userModels.value(buffer); + if (!userModel) { + LOG4CXX_ERROR(logger, m_user << ": Cannot find UserModel for IrcBuffer " << TO_UTF8(buffer->name())); + return NULL; + } + + return userModel->find(FROM_UTF8(nick)); } +IrcUser *MyIrcSession::getIrcUser(IrcBuffer *buffer, IrcMessage *message) { + std::string nick = TO_UTF8(message->nick()); + return getIrcUser(buffer, nick); +} void MyIrcSession::on_parted(IrcMessage *message) { - IrcPartMessage *m = (IrcPartMessage *) message; - std::string nickname = TO_UTF8(m->nick()); - bool op = correctNickname(nickname); - removeIRCBuddy(TO_UTF8(m->channel().toLower()), nickname); - LOG4CXX_INFO(logger, m_user << ": " << nickname << " parted " << TO_UTF8(m->channel().toLower()) + m_suffix); - m_np->handleParticipantChanged(m_user, nickname, TO_UTF8(m->channel().toLower()) + m_suffix, op, pbnetwork::STATUS_NONE, TO_UTF8(m->reason())); + // TODO: We currently use onIrcUserRemoved, but this does not allow sending + // part/quit message. We should use this method instead and write version + // of sendUserToFrontend which takes nickname instead of IrcUser just for + // part/quit messages. +// IrcPartMessage *m = (IrcPartMessage *) message; +// IrcBuffer *buffer = dynamic_cast(QObject::sender()); +// IrcUser *user = getIrcUser(buffer, message); +// if (!user) { +// LOG4CXX_ERROR(logger, m_user << ": Part: IrcUser " << TO_UTF8(message->nick()) << " not in channel " << TO_UTF8(buffer->name())); +// return; +// } +// +// sendUserToFrontend(user, pbnetwork::STATUS_NONE, TO_UTF8(m->reason())); } void MyIrcSession::on_quit(IrcMessage *message) { - IrcQuitMessage *m = (IrcQuitMessage *) message; - std::string nickname = TO_UTF8(m->nick()); - bool op = correctNickname(nickname); - - for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) { - if (!hasIRCBuddy(it->second->getChannel(), nickname)) { - continue; - } - removeIRCBuddy(it->second->getChannel(), nickname); - LOG4CXX_INFO(logger, m_user << ": " << nickname << " quit " << it->second->getChannel() + m_suffix); - m_np->handleParticipantChanged(m_user, nickname, it->second->getChannel() + m_suffix, op, pbnetwork::STATUS_NONE, TO_UTF8(m->reason())); - } + // TODO: We currently use onIrcUserRemoved, but this does not allow sending + // part/quit message. We should use this method instead and write version + // of sendUserToFrontend which takes nickname instead of IrcUser just for + // part/quit messages. +// IrcQuitMessage *m = (IrcQuitMessage *) message; +// IrcBuffer *buffer = dynamic_cast(QObject::sender()); +// IrcUser *user = getIrcUser(buffer, message); +// if (!user) { +// LOG4CXX_ERROR(logger, m_user << ": Quit: IrcUser " << TO_UTF8(message->nick()) << " not in channel " << TO_UTF8(buffer->name())); +// return; +// } +// +// sendUserToFrontend(user, pbnetwork::STATUS_NONE, TO_UTF8(m->reason())); } void MyIrcSession::on_nickChanged(IrcMessage *message) { IrcNickMessage *m = (IrcNickMessage *) message; - std::string nickname = TO_UTF8(m->nick()); - correctNickname(nickname); - - 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); - LOG4CXX_INFO(logger, m_user << ": " << nickname << " changed nickname to " << TO_UTF8(m->nick())); - m_np->handleParticipantChanged(m_user, nickname, it->second->getChannel() + m_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; - - correctNickname(nickname); - - if (!hasIRCBuddy(TO_UTF8(m->target().toLower()), nickname)) { + IrcBuffer *buffer = dynamic_cast(QObject::sender()); + IrcUser *user = getIrcUser(buffer, message); + if (!user) { + LOG4CXX_ERROR(logger, m_user << ": NickChanged: IrcUser " << TO_UTF8(message->nick()) << " not in channel " << TO_UTF8(buffer->name())); return; } - IRCBuddy &buddy = getIRCBuddy(TO_UTF8(m->target().toLower()), nickname); - if (mode == "+o") { - buddy.setOp(true); - } - else { - buddy.setOp(false); - } - - m_np->handleParticipantChanged(m_user, nickname, TO_UTF8(m->target().toLower()) + m_suffix,(int) buddy.isOp(), pbnetwork::STATUS_ONLINE, ""); - LOG4CXX_INFO(logger, m_user << ": " << nickname << " changed mode to " << mode << " in " << TO_UTF8(m->target().toLower())); + sendUserToFrontend(user, pbnetwork::STATUS_ONLINE, "", TO_UTF8(m->newNick())); } void MyIrcSession::on_topicChanged(IrcMessage *message) { @@ -245,6 +335,23 @@ void MyIrcSession::on_whoisMessageReceived(IrcMessage *message) { m_whois.erase(nickname); } +void MyIrcSession::on_namesMessageReceived(IrcMessage *message) { + LOG4CXX_INFO(logger, m_user << ": NAMES received"); + IrcBuffer *buffer = dynamic_cast(QObject::sender()); + IrcUserModel *userModel = m_userModels.value(buffer); + if (!userModel) { + LOG4CXX_ERROR(logger, m_user << ": Cannot find UserModel for IrcBuffer " << TO_UTF8(buffer->name())); + return; + } + + foreach (IrcUser *user, userModel->users()) { + sendUserToFrontend(user, pbnetwork::STATUS_ONLINE); + } + + LOG4CXX_INFO(logger, m_user << "Asking /who for channel " << TO_UTF8(buffer->name())); + sendCommand(IrcCommand::createWho(buffer->name())); +} + void MyIrcSession::sendMessageToFrontend(const std::string &channel, const std::string &nick, const std::string &msg) { QString html = "";//msg; // CommuniBackport::toPlainText(msg); @@ -267,7 +374,7 @@ void MyIrcSession::sendMessageToFrontend(const std::string &channel, const std:: if (m_pms.find(nickname) != m_pms.end()) { std::string room = m_pms[nickname].substr(0, m_pms[nickname].find("/")); room = room.substr(0, room.find("@")); - if (hasIRCBuddy(room, nickname)) { + if (hasIrcUser(room, nickname)) { m_np->handleMessage(m_user, room + m_suffix, msg, nickname, TO_UTF8(html), "", false, true); return; } @@ -276,12 +383,20 @@ void MyIrcSession::sendMessageToFrontend(const std::string &channel, const std:: } } else { - for (AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) { - if (!hasIRCBuddy(it->second->getChannel(), nickname)) { + foreach (IrcBuffer *buffer, m_bufferModel->buffers()) { + std::string room = "#" + TO_UTF8(buffer->name()); + IrcUserModel *userModel = m_userModels.value(buffer); + if (!userModel) { + LOG4CXX_ERROR(logger, m_user << ": Cannot find UserModel for IrcBuffer " << TO_UTF8(buffer->name())); continue; } - addPM(nickname, it->second->getChannel()); - m_np->handleMessage(m_user, it->second->getChannel() + m_suffix, msg, nickname, TO_UTF8(html), "", false, true); + + if (!userModel->contains(FROM_UTF8(nickname))) { + continue; + } + + addPM(nickname, room + "/" + nickname); + m_np->handleMessage(m_user, room + m_suffix, msg, nickname, TO_UTF8(html), "", false, true); return; } @@ -320,15 +435,10 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) { 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: + case Irc::RPL_TOPIC: m_topicData = TO_UTF8(parameters[2]); break; - case 333: + case Irc::RPL_TOPICWHOTIME: nick = TO_UTF8(parameters[2]); if (nick.find("!") != std::string::npos) { nick = nick.substr(0, nick.find("!")); @@ -338,59 +448,24 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) { } m_np->handleSubject(m_user, TO_UTF8(parameters[1].toLower()) + m_suffix, m_topicData, nick); break; - case 352: { - 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); - m_np->handleParticipantChanged(m_user, nick, TO_UTF8(channel) + m_suffix, buddy.isOp(), pbnetwork::STATUS_AWAY); - } - } - else if (buddy.isAway()) { - buddy.setAway(false); - m_np->handleParticipantChanged(m_user, nick, TO_UTF8(channel) + m_suffix, buddy.isOp(), pbnetwork::STATUS_ONLINE); - } - break; - } - case 353: - channel = parameters[2].toLower(); - members = parameters[3].split(" "); - - LOG4CXX_INFO(logger, m_user << ": Received members for " << TO_UTF8(channel) << m_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); - m_np->handleParticipantChanged(m_user, nickname, TO_UTF8(channel) + m_suffix, buddy.isOp(), pbnetwork::STATUS_ONLINE); - } - - break; - case 366: - // ask /who to get away states - channel = parameters[1].toLower(); - LOG4CXX_INFO(logger, m_user << "Asking /who for channel " << TO_UTF8(channel)); - sendCommand(IrcCommand::createWho(channel)); - break; - case 401: - case 402: + case Irc::ERR_NOSUCHNICK: + case Irc::ERR_NOSUCHSERVER: nick = TO_UTF8(parameters[1]); if (m_whois.find(nick) != m_whois.end()) { sendMessageToFrontend(m_whois[nick], "whois", nick + ": No such client"); m_whois.erase(nick); } break; - case 432: + case Irc::ERR_ERRONEUSNICKNAME: m_np->handleDisconnected(m_user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Erroneous Nickname"); break; - case 433: - for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) { - m_np->handleRoomNicknameChanged(m_user, it->second->getChannel() + m_suffix, TO_UTF8(nickName() + "_")); - m_np->handleParticipantChanged(m_user, TO_UTF8(nickName()), it->second->getChannel() + m_suffix, 0, pbnetwork::STATUS_ONLINE, "", TO_UTF8(nickName() + "_")); + case Irc::ERR_NICKNAMEINUSE: + foreach (IrcBuffer *buffer, m_bufferModel->buffers()) { + if (!buffer->isChannel()) { + continue; + } + m_np->handleRoomNicknameChanged(m_user, TO_UTF8(buffer->title()) + m_suffix, TO_UTF8(nickName() + "_")); + m_np->handleParticipantChanged(m_user, TO_UTF8(nickName()), TO_UTF8(buffer->title()) + m_suffix, 0, pbnetwork::STATUS_ONLINE, "", TO_UTF8(nickName() + "_")); } setNickName(nickName() + "_"); open(); @@ -401,14 +476,21 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) { // m_np->handleDisconnected(m_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++) { - m_np->handleParticipantChanged(m_user, TO_UTF8(nickName()), it->second->getChannel() + m_suffix, pbnetwork::PARTICIPANT_FLAG_CONFLICT); + case Irc::ERR_NICKCOLLISION: + foreach (IrcBuffer *buffer, m_bufferModel->buffers()) { + if (!buffer->isChannel()) { + continue; + } + m_np->handleParticipantChanged(m_user, TO_UTF8(nickName()), TO_UTF8(buffer->title()) + m_suffix, pbnetwork::PARTICIPANT_FLAG_CONFLICT); } m_np->handleDisconnected(m_user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Nickname collision KILL"); - case 464: - for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) { - m_np->handleParticipantChanged(m_user, TO_UTF8(nickName()), it->second->getChannel() + m_suffix, pbnetwork::PARTICIPANT_FLAG_NOT_AUTHORIZED); + break; + case Irc::ERR_PASSWDMISMATCH: + foreach (IrcBuffer *buffer, m_bufferModel->buffers()) { + if (!buffer->isChannel()) { + continue; + } + m_np->handleParticipantChanged(m_user, TO_UTF8(nickName()), TO_UTF8(buffer->title()) + m_suffix, pbnetwork::PARTICIPANT_FLAG_NOT_AUTHORIZED); } if (m_suffix.empty()) { m_np->handleDisconnected(m_user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Password incorrect"); @@ -434,11 +516,25 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) { } 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()))); + foreach (IrcBuffer *buffer, m_bufferModel->buffers()) { + if (!buffer->isChannel()) { + continue; } + + QVariantMap userData = buffer->userData(); + int awayCycle = userData["awayCycle"].toInt(); + int awayTick = userData["awayTick"].toInt(); + + if (awayTick == awayCycle) { + LOG4CXX_INFO(logger, m_user << ": The time has come. Asking /who " << TO_UTF8(buffer->title()) << " again to get current away states."); + sendCommand(IrcCommand::createWho(buffer->title())); + awayTick = 0; + } + awayTick++; + + userData["awayCycle"] = awayCycle; + userData["awayTick"] = awayTick; + buffer->setUserData(userData); } } @@ -446,46 +542,14 @@ void MyIrcSession::on_noticeMessageReceived(IrcMessage *message) { IrcNoticeMessage *m = (IrcNoticeMessage *) message; LOG4CXX_INFO(logger, m_user << ": NOTICE " << TO_UTF8(m->content())); - QString msg = m->content(); - CommuniBackport::toPlainText(msg); - + std::string msg = TO_UTF8(m->content()); std::string target = TO_UTF8(m->target().toLower()); - if (target.find("#") == 0) { - std::string nickname = TO_UTF8(m->nick()); - correctNickname(nickname); - m_np->handleMessage(m_user, target + m_suffix, TO_UTF8(msg), nickname); - } - else { - std::string nickname = TO_UTF8(m->nick()); - correctNickname(nickname); - if (nickname.find(".") != std::string::npos) { - return; - } - if (m_pms.find(nickname) != m_pms.end()) { - std::string room = m_pms[nickname].substr(0, m_pms[nickname].find("/")); - room = room.substr(0, room.find("@")); - if (hasIRCBuddy(room, nickname)) { - m_np->handleMessage(m_user, room + m_suffix, TO_UTF8(msg), nickname, "", "", false, true); - return; - } - else { - nickname = nickname + m_suffix; - } - } - else { - nickname = nickname + m_suffix; - } - - LOG4CXX_INFO(logger, nickname); - m_np->handleMessage(m_user, nickname, TO_UTF8(msg), ""); - } + std::string nickname = TO_UTF8(m->nick()); + sendMessageToFrontend(target, nickname, msg); } void MyIrcSession::onMessageReceived(IrcMessage *message) { switch (message->type()) { - case IrcMessage::Join: - on_joined(message); - break; case IrcMessage::Part: on_parted(message); break; @@ -495,9 +559,6 @@ void MyIrcSession::onMessageReceived(IrcMessage *message) { case IrcMessage::Nick: on_nickChanged(message); break; - case IrcMessage::Mode: - on_modeChanged(message); - break; case IrcMessage::Topic: on_topicChanged(message); break; @@ -513,6 +574,9 @@ void MyIrcSession::onMessageReceived(IrcMessage *message) { case IrcMessage::Whois: on_whoisMessageReceived(message); break; + case IrcMessage::Names: + on_namesMessageReceived(message); + break; default:break; } } diff --git a/backends/libcommuni/session.h b/backends/libcommuni/session.h index b5fcf106..55a1ee86 100644 --- a/backends/libcommuni/session.h +++ b/backends/libcommuni/session.h @@ -23,6 +23,9 @@ #ifndef Q_MOC_RUN #include +#include +#include +#include #include #include "Swiften/Swiften.h" #include @@ -38,58 +41,10 @@ class MyIrcSession : public IrcConnection Q_OBJECT public: - class AutoJoinChannel { - public: - AutoJoinChannel(const std::string &channel = "", const std::string &password = "", int awayCycle = 12) : m_channel(channel), m_password(password), - m_awayCycle(awayCycle), m_currentAwayTick(0) {} - virtual ~AutoJoinChannel() {} - - const std::string &getChannel() { return m_channel; } - const std::string &getPassword() { return m_password; } - bool shouldAskWho() { - if (m_currentAwayTick == m_awayCycle) { - m_currentAwayTick = 0; - return true; - } - m_currentAwayTick++; - return false; - } - - private: - std::string m_channel; - std::string m_password; - int m_awayCycle; - int m_currentAwayTick; - }; - - class IRCBuddy { - public: - IRCBuddy(bool op = false, bool away = false) : m_op(op), m_away(away) {}; - - void setOp(bool op) { m_op = op; } - bool isOp() { return m_op; } - void setAway(bool away) { m_away = away; } - bool isAway() { return m_away; } - - private: - bool m_op; - bool m_away; - }; - - typedef std::map > AutoJoinMap; - typedef std::map > IRCBuddyMap; - MyIrcSession(const std::string &user, IRCNetworkPlugin *np, const std::string &suffix = "", QObject* parent = 0); virtual ~MyIrcSession(); - void addAutoJoinChannel(const std::string &channel, const std::string &password) { - m_autoJoin[channel] = boost::make_shared(channel, password, 12 + m_autoJoin.size()); - } - - void removeAutoJoinChannel(const std::string &channel) { - m_autoJoin.erase(channel); - removeIRCBuddies(channel); - } + void createBufferModel(); // We are sending PM message. On XMPP side, user is sending PM using the particular channel, // for example #room@irc.freenode.org/hanzz. On IRC side, we are forwarding this message @@ -105,37 +60,26 @@ public: return m_identify; } - bool hasIRCBuddy(const std::string &channel, const std::string &name) { - return m_buddies[channel].find(name) != m_buddies[channel].end(); - } + bool hasIrcUser(const std::string &channel, const std::string &name); - IRCBuddy &getIRCBuddy(const std::string &channel, const std::string &name) { - return m_buddies[channel][name]; - } - - void removeIRCBuddy(const std::string &channel, const std::string &name) { - m_buddies[channel].erase(name); - } - - void removeIRCBuddies(const std::string &channel) { - m_buddies.erase(channel); - } - - bool correctNickname(std::string &nickname); + void correctNickname(std::string &nick); + IrcUser *getIrcUser(IrcBuffer *buffer, IrcMessage *message); + IrcUser *getIrcUser(IrcBuffer *buffer, std::string &nick); void sendWhoisCommand(const std::string &channel, const std::string &to); void sendMessageToFrontend(const std::string &channel, const std::string &nickname, const std::string &msg); + void sendUserToFrontend(IrcUser *user, pbnetwork::StatusType statusType, const std::string &statusMessage = "", const std::string &newNick = ""); void on_joined(IrcMessage *message); void on_parted(IrcMessage *message); void on_quit(IrcMessage *message); void on_nickChanged(IrcMessage *message); - void on_modeChanged(IrcMessage *message); void on_topicChanged(IrcMessage *message); void on_messageReceived(IrcMessage *message); void on_numericMessageReceived(IrcMessage *message); void on_noticeMessageReceived(IrcMessage *message); void on_whoisMessageReceived(IrcMessage *message); + void on_namesMessageReceived(IrcMessage *message); int rooms; @@ -144,24 +88,32 @@ protected Q_SLOTS: void on_disconnected(); void on_socketError(QAbstractSocket::SocketError error); + void onBufferAdded(IrcBuffer* buffer); + void onBufferRemoved(IrcBuffer* buffer); + + void onIrcUserAdded(IrcUser *user); + void onIrcUserChanged(const QString &); + void onIrcUserChanged(bool); + void onIrcUserRemoved(IrcUser *user); + void onMessageReceived(IrcMessage* message); void awayTimeout(); protected: IRCNetworkPlugin *m_np; + IrcBufferModel *m_bufferModel; std::string m_user; std::string m_identify; - AutoJoinMap m_autoJoin; std::string m_topicData; bool m_connected; std::list m_rooms; std::list m_names; std::map m_pms; - IRCBuddyMap m_buddies; QTimer *m_awayTimer; std::string m_suffix; std::map m_whois; + QHash m_userModels; }; #endif // SESSION_H diff --git a/cmake_modules/CommuniConfig.cmake b/cmake_modules/CommuniConfig.cmake index 31add02b..49d732cc 100644 --- a/cmake_modules/CommuniConfig.cmake +++ b/cmake_modules/CommuniConfig.cmake @@ -6,9 +6,15 @@ FIND_PATH(IRC_INCLUDE_DIR NAMES "IrcCore/ircglobal.h" PATHS ${QT_INCLUDE_DIR} PA # message( STATUS ${IRC_LIBRARY}) if( IRC_LIBRARY AND IRC_INCLUDE_DIR ) - set(IRC_INCLUDE_DIR ${IRC_INCLUDE_DIR}/IrcCore ${IRC_INCLUDE_DIR}/IrcUtil ${IRC_INCLUDE_DIR}/IrcModel) - message( STATUS "Found libCommuni ${IRC_LIBRARY}, ${IRC_INCLUDE_DIR}") - set( IRC_FOUND 1 ) + FIND_LIBRARY(IRC_MODEL_LIBRARY NAMES IrcModel PATHS ${QT_LIBRARY_DIR}) + if (IRC_MODEL_LIBRARY) + set(IRC_LIBRARY ${IRC_LIBRARY} ${IRC_MODEL_LIBRARY}) + set(IRC_INCLUDE_DIR ${IRC_INCLUDE_DIR}/IrcCore ${IRC_INCLUDE_DIR}/IrcUtil ${IRC_INCLUDE_DIR}/IrcModel) + message( STATUS "Found libCommuni ${IRC_LIBRARY}, ${IRC_INCLUDE_DIR}") + set( IRC_FOUND 1 ) + else() + message( STATUS "Could NOT find libCommuni - IrcModel library" ) + endif() else() - message( STATUS "Could NOT find libCommuni" ) + message( STATUS "Could NOT find libCommuni - IrcCore library" ) endif() diff --git a/tests/libcommuni/irc_test.cfg b/tests/libcommuni/irc_test.cfg index 91b57448..4745ccac 100644 --- a/tests/libcommuni/irc_test.cfg +++ b/tests/libcommuni/irc_test.cfg @@ -23,6 +23,7 @@ protocol=prpl-jabber working_dir=./ portfile=./$jid.port irc_server=localhost +irc_away_timeout=2 [backend] #default_avatar=catmelonhead.jpg diff --git a/tests/libcommuni/irc_test2.cfg b/tests/libcommuni/irc_test2.cfg index cecf18b8..6d21fa10 100644 --- a/tests/libcommuni/irc_test2.cfg +++ b/tests/libcommuni/irc_test2.cfg @@ -22,6 +22,7 @@ protocol=prpl-jabber #protocol=prpl-icq working_dir=./ portfile=./$jid.port +irc_away_timeout=2 [backend] #default_avatar=catmelonhead.jpg diff --git a/tests/libcommuni/muc_away.py b/tests/libcommuni/muc_away.py new file mode 100644 index 00000000..aac93664 --- /dev/null +++ b/tests/libcommuni/muc_away.py @@ -0,0 +1,46 @@ +import optparse +import sys +import time +import subprocess +import os + +import sleekxmpp + + +class Responder(sleekxmpp.ClientXMPP): + def __init__(self, jid, password, room, room_password, nick): + sleekxmpp.ClientXMPP.__init__(self, jid, password) + self.room = room + self.room_password = room_password + self.nick = nick + self.finished = False + self.add_event_handler("session_start", self.start) + self.add_event_handler("muc::" + room + "::presence", self.muc_got_online) + + self.tests = {} + self.tests["online_received"] = ["libcommuni: Received away presence from 'client'", False] + + def muc_got_online(self, presence): + if presence['muc']['nick'] == "client" and presence['show'] == "away": + self.tests["online_received"][1] = True + self.finished = True + + def start(self, event): + self.plugin['xep_0045'].joinMUC(self.room, self.nick, password=self.room_password, wait=True) + +class Client(sleekxmpp.ClientXMPP): + def __init__(self, jid, password, room, nick): + sleekxmpp.ClientXMPP.__init__(self, jid, password) + self.room = room + self.nick = nick + self.add_event_handler("session_start", self.start) + self.finished = False + + self.tests = {} + + def start(self, event): + self.getRoster() + self.sendPresence() + self.plugin['xep_0045'].joinMUC(self.room, self.nick, wait=True) + self.sendPresence(ptype = "away") + diff --git a/tests/libcommuni/muc_join_leave.py b/tests/libcommuni/muc_join_leave.py index 1b7b0f71..6bbf73f7 100644 --- a/tests/libcommuni/muc_join_leave.py +++ b/tests/libcommuni/muc_join_leave.py @@ -19,8 +19,8 @@ class Responder(sleekxmpp.ClientXMPP): self.add_event_handler("muc::" + room + "::got_offline", self.muc_got_offline) self.tests = {} - self.tests["online_received"] = ["libcommuni: Received available presence", False] - self.tests["offline_received"] = ["libcommuni: Received unavailable presence", False] + self.tests["online_received"] = ["libcommuni: Received available presence from 'client'", False] + self.tests["offline_received"] = ["libcommuni: Received unavailable presence frin 'client'", False] def muc_got_online(self, presence): if presence['muc']['nick'] == "client": @@ -40,12 +40,18 @@ class Client(sleekxmpp.ClientXMPP): self.room = room self.nick = nick self.add_event_handler("session_start", self.start) + self.add_event_handler("muc::" + room + "::got_online", self.muc_got_online) self.finished = False self.tests = {} + self.tests["online_received"] = ["libcommuni: Received available presence from 'responder'", False] + + def muc_got_online(self, presence): + if presence['muc']['nick'] == "responder": + self.tests["online_received"][1] = True + self.plugin['xep_0045'].leaveMUC(self.room, self.nick) def start(self, event): self.getRoster() self.sendPresence() self.plugin['xep_0045'].joinMUC(self.room, self.nick, wait=True) - self.plugin['xep_0045'].leaveMUC(self.room, self.nick) diff --git a/tests/libcommuni/muc_join_nickname_used.py b/tests/libcommuni/muc_join_nickname_used.py new file mode 100644 index 00000000..037d98bc --- /dev/null +++ b/tests/libcommuni/muc_join_nickname_used.py @@ -0,0 +1,57 @@ +import optparse +import sys +import time +import subprocess +import os + +import sleekxmpp + + +class Responder(sleekxmpp.ClientXMPP): + def __init__(self, jid, password, room, room_password, nick): + sleekxmpp.ClientXMPP.__init__(self, jid, password) + self.room = room + self.room_password = room_password + self.nick = nick + self.finished = False + self.add_event_handler("session_start", self.start) + self.add_event_handler("muc::" + room + "::got_online", self.muc_got_online) + self.add_event_handler("muc::" + room + "::got_offline", self.muc_got_offline) + + self.tests = {} + self.tests["online_received"] = ["libcommuni: Received available presence", False] + self.tests["offline_received"] = ["libcommuni: Received unavailable presence", False] + + def muc_got_online(self, presence): + if presence['muc']['nick'] == "respond_": + self.tests["online_received"][1] = True + self.send_message(mto=self.room, + mbody="disconnect please :)", + mtype='groupchat') + + def muc_got_offline(self, presence): + if presence['muc']['nick'] == "respond_": + self.tests["offline_received"][1] = True + self.finished = True + + def start(self, event): + self.plugin['xep_0045'].joinMUC(self.room, "respond", password=self.room_password, wait=True) + +class Client(sleekxmpp.ClientXMPP): + def __init__(self, jid, password, room, nick): + sleekxmpp.ClientXMPP.__init__(self, jid, password) + self.room = room + self.nick = nick + self.add_event_handler("session_start", self.start) + self.add_event_handler("groupchat_message", self.muc_message) + self.finished = False + + self.tests = {} + + def muc_message(self, msg): + self.plugin['xep_0045'].leaveMUC(self.room, "respond_") + + def start(self, event): + self.getRoster() + self.sendPresence() + self.plugin['xep_0045'].joinMUC(self.room, "respond", wait=True) diff --git a/tests/start.py b/tests/start.py index a0ba8d73..e8c9b132 100644 --- a/tests/start.py +++ b/tests/start.py @@ -208,7 +208,7 @@ class TwitterServerModeConf(BaseTest): configurations = [] configurations.append(LibcommuniServerModeSingleServerConf()) configurations.append(LibcommuniServerModeConf()) -configurations.append(JabberServerModeConf()) +#configurations.append(JabberServerModeConf()) ##configurations.append(JabberSlackServerModeConf()) #configurations.append(TwitterServerModeConf())