diff --git a/src/application.cpp b/src/application.cpp index a940500b..615eadc1 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -1,1403 +1,1403 @@ /* 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. */ /* Copyright (C) 2002 Dario Abatianni Copyright (C) 2005 Ismail Donmez Copyright (C) 2005 Peter Simonsson Copyright (C) 2005 John Tapsell Copyright (C) 2005-2008 Eike Hein */ #include "application.h" #include "connectionmanager.h" #include "scriptlauncher.h" #include "transfermanager.h" #include "viewcontainer.h" #include "urlcatcher.h" #include "highlight.h" #include "server.h" #include "sound.h" #include "quickconnectdialog.h" #include "dbus.h" #include "servergroupsettings.h" #include "serversettings.h" #include "channel.h" #include "images.h" #include "notificationhandler.h" #include "awaymanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Konversation; Application::Application(int &argc, char **argv) : QApplication(argc, argv) { mainWindow = nullptr; m_restartScheduled = false; m_connectionManager = nullptr; m_awayManager = nullptr; m_scriptLauncher = nullptr; quickConnectDialog = nullptr; osd = nullptr; m_wallet = nullptr; m_images = nullptr; m_sound = nullptr; m_dccTransferManager = nullptr; m_notificationHandler = nullptr; m_urlModel = nullptr; dbusObject = nullptr; identDBus = nullptr; m_networkConfigurationManager = nullptr; } Application::~Application() { qDebug(); if (!m_images) return; // Nothing to do, newInstance() has never been called. stashQueueRates(); Preferences::self()->save(); // FIXME i can't figure out why this isn't in saveOptions --argonel saveOptions(false); // Delete m_dccTransferManager here as its destructor depends on the main loop being in tact which it // won't be if if we wait till Qt starts deleting parent pointers. delete m_dccTransferManager; delete m_images; delete m_sound; //delete dbusObject; //delete prefsDCOP; //delete identDBus; delete osd; osd = nullptr; closeWallet(); delete m_networkConfigurationManager; if (m_restartScheduled) implementRestart(); } void Application::implementRestart() { // Pop off the executable name. May not be the first argument in argv // everywhere, so verify first. if (QFileInfo(m_restartArguments.first()) == QFileInfo(QCoreApplication::applicationFilePath())) m_restartArguments.removeFirst(); // Don't round-trip --restart. m_restartArguments.removeAll(QStringLiteral("--restart")); // Avoid accumulating multiple --startupdelay arguments across multiple // uses of restart(). - if (m_restartArguments.contains(QStringLiteral("--startupdelay"))) + if (m_restartArguments.contains(QLatin1String("--startupdelay"))) { int index = m_restartArguments.lastIndexOf(QStringLiteral("--startupdelay")); if (index < m_restartArguments.count() - 1 && !m_restartArguments.at(index + 1).startsWith(QLatin1Char('-'))) { QString delayArgument = m_restartArguments.at(index + 1); bool ok; uint delay = delayArgument.toUInt(&ok, 10); // If the argument is invalid or too low, raise to at least 2000 msecs. if (!ok || delay < 2000) m_restartArguments.replace(index + 1, QStringLiteral("2000")); } } else m_restartArguments << QStringLiteral("--startupdelay") << QStringLiteral("2000"); KProcess::startDetached(QCoreApplication::applicationFilePath(), m_restartArguments); } void Application::newInstance(QCommandLineParser *args) { QString url; if (args->positionalArguments().count() > 1) url = args->positionalArguments().at(1); if (!mainWindow) { connect(this, &Application::aboutToQuit, this, &Application::prepareShutdown); m_connectionManager = new ConnectionManager(this); m_awayManager = new AwayManager(this); connect(m_connectionManager, &ConnectionManager::identityOnline, m_awayManager, &AwayManager::identityOnline); connect(m_connectionManager, &ConnectionManager::identityOffline, m_awayManager, &AwayManager::identityOffline); connect(m_connectionManager, &ConnectionManager::connectionChangedAwayState, m_awayManager, &AwayManager::updateGlobalAwayAction); m_networkConfigurationManager = new QNetworkConfigurationManager(); connect(m_networkConfigurationManager, SIGNAL(onlineStateChanged(bool)), m_connectionManager, SLOT(onOnlineStateChanged(bool))); m_scriptLauncher = new ScriptLauncher(this); // an instance of DccTransferManager needs to be created before GUI class instances' creation. m_dccTransferManager = new DCC::TransferManager(this); // make sure all vars are initialized properly quickConnectDialog = nullptr; // Sound object used to play sound is created when needed. m_sound = nullptr; // initialize OSD display here, so we can read the Preferences::properly osd = new OSDWidget( QStringLiteral("Konversation") ); Preferences::self(); readOptions(); // Images object providing LEDs, NickIcons m_images = new Images(); m_urlModel = new QStandardItemModel(0, 3, this); // Auto-alias scripts. This adds any missing aliases QStringList aliasList(Preferences::self()->aliasList()); const QStringList scripts(Preferences::defaultAliasList()); bool changed = false; for ( QStringList::ConstIterator it = scripts.constBegin(); it != scripts.constEnd(); ++it ) { if(!aliasList.contains(*it)) { changed = true; aliasList.append(*it); } } if(changed) Preferences::self()->setAliasList(aliasList); // open main window mainWindow = new MainWindow(); connect(mainWindow.data(), &MainWindow::showQuickConnectDialog, this, &Application::openQuickConnectDialog); connect(Preferences::self(), &Preferences::updateTrayIcon, mainWindow.data(), &MainWindow::updateTrayIcon); connect(mainWindow.data(), &MainWindow::endNotification, osd, &OSDWidget::hide); // take care of user style changes, setting back colors and stuff // apply GUI settings emit appearanceChanged(); if (Preferences::self()->showTrayIcon() && Preferences::self()->hideToTrayOnStartup()) mainWindow->hide(); else mainWindow->show(); bool openServerList = Preferences::self()->showServerList(); // handle autoconnect on startup Konversation::ServerGroupHash serverGroups = Preferences::serverGroupHash(); if (!args->isSet(QStringLiteral("noautoconnect")) && url.isEmpty() && !args->isSet(QStringLiteral("server"))) { QList serversToAutoconnect; QHashIterator it(serverGroups); while(it.hasNext()) { it.next(); if (it.value()->autoConnectEnabled()) { openServerList = false; serversToAutoconnect << it.value(); } } std::sort(serversToAutoconnect.begin(), serversToAutoconnect.end(), [] (const ServerGroupSettingsPtr &left, const ServerGroupSettingsPtr &right) { return left->sortIndex() < right->sortIndex(); }); for (QList::iterator it = serversToAutoconnect.begin(); it != serversToAutoconnect.end(); ++it) { m_connectionManager->connectTo(Konversation::CreateNewConnection, (*it)->id()); } } if (openServerList) mainWindow->openServerList(); connect(this, SIGNAL(serverGroupsChanged(Konversation::ServerGroupSettingsPtr)), this, SLOT(saveOptions())); // prepare dbus interface dbusObject = new Konversation::DBus(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/irc"), dbusObject, QDBusConnection::ExportNonScriptableSlots); identDBus = new Konversation::IdentDBus(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/identity"), identDBus, QDBusConnection::ExportNonScriptableSlots); if (dbusObject) { connect(dbusObject,&DBus::dbusMultiServerRaw, this,&Application::dbusMultiServerRaw ); connect(dbusObject,&DBus::dbusRaw, this,&Application::dbusRaw ); connect(dbusObject,&DBus::dbusSay, this,&Application::dbusSay ); connect(dbusObject,&DBus::dbusInfo, this,&Application::dbusInfo ); connect(dbusObject,&DBus::dbusInsertMarkerLine, mainWindow.data(),&MainWindow::insertMarkerLine); connect(dbusObject, SIGNAL(connectTo(Konversation::ConnectionFlag,QString,QString,QString,QString,QString,bool)), m_connectionManager, SLOT(connectTo(Konversation::ConnectionFlag,QString,QString,QString,QString,QString,bool))); } m_notificationHandler = new Konversation::NotificationHandler(this); connect(this, &Application::appearanceChanged, this, &Application::updateProxySettings); } else if (args->isSet(QStringLiteral("restart"))) { restart(); return; } if (!url.isEmpty()) getConnectionManager()->connectTo(Konversation::SilentlyReuseConnection, url); else if (args->isSet(QStringLiteral("server"))) { getConnectionManager()->connectTo(Konversation::SilentlyReuseConnection, args->value(QStringLiteral("server")), args->value(QStringLiteral("port")), args->value(QStringLiteral("password")), args->value(QStringLiteral("nick")), args->value(QStringLiteral("channel")), args->isSet(QStringLiteral("ssl"))); } return; } Application* Application::instance() { return qobject_cast(QApplication::instance()); } void Application::restart() { m_restartScheduled = true; mainWindow->quitProgram(); } void Application::prepareShutdown() { if (mainWindow) mainWindow->getViewContainer()->prepareShutdown(); if (m_awayManager) { m_awayManager->blockSignals(true); delete m_awayManager; m_awayManager = nullptr; } if (m_connectionManager) { m_connectionManager->quitServers(); m_connectionManager->blockSignals(true); delete m_connectionManager; m_connectionManager = nullptr; } } bool Application::event(QEvent* event) { if (event->type() == QEvent::ApplicationPaletteChange || event->type() == QEvent::ApplicationFontChange) { emit appearanceChanged(); } return QApplication::event(event); } void Application::showQueueTuner(bool p) { getMainWindow()->getViewContainer()->showQueueTuner(p); } void Application::dbusMultiServerRaw(const QString &command) { sendMultiServerCommand(command.section(QLatin1Char(' '), 0,0), command.section(QLatin1Char(' '), 1)); } void Application::dbusRaw(const QString& connection, const QString &command) { Server* server = getConnectionManager()->getServerByName(connection, ConnectionManager::MatchByIdThenName); if (server) server->dbusRaw(command); } void Application::dbusSay(const QString& connection, const QString& target, const QString& command) { Server* server = getConnectionManager()->getServerByName(connection, ConnectionManager::MatchByIdThenName); if (server) server->dbusSay(target, command); } void Application::dbusInfo(const QString& string) { mainWindow->getViewContainer()->appendToFrontmost(i18n("D-Bus"), string, nullptr); } void Application::readOptions() { // get standard config file // read nickname sorting order for channel nick lists KConfigGroup cgSortNicknames(KSharedConfig::openConfig()->group("Sort Nicknames")); QString sortOrder=cgSortNicknames.readEntry("SortOrder"); QStringList sortOrderList=sortOrder.split(QString()); sortOrderList.sort(); if (sortOrderList.join(QString())!=QStringLiteral("-hopqv")) { sortOrder=Preferences::defaultNicknameSortingOrder(); Preferences::self()->setSortOrder(sortOrder); } // Identity list QStringList identityList=KSharedConfig::openConfig()->groupList().filter(QRegExp(QStringLiteral("Identity [0-9]+"))); if (!identityList.isEmpty()) { Preferences::clearIdentityList(); for(int index=0;indexgroup(identityList[index])); newIdentity->setName(cgIdentity.readEntry("Name")); newIdentity->setIdent(cgIdentity.readEntry("Ident")); newIdentity->setRealName(cgIdentity.readEntry("Realname")); newIdentity->setNicknameList(cgIdentity.readEntry("Nicknames",QStringList())); newIdentity->setAuthType(cgIdentity.readEntry("AuthType", "nickserv")); newIdentity->setAuthPassword(cgIdentity.readEntry("Password")); newIdentity->setNickservNickname(cgIdentity.readEntry("Bot")); newIdentity->setNickservCommand(cgIdentity.readEntry("NickservCommand", "identify")); newIdentity->setSaslAccount(cgIdentity.readEntry("SaslAccount")); newIdentity->setPemClientCertFile(cgIdentity.readEntry("PemClientCertFile", QUrl())); newIdentity->setInsertRememberLineOnAway(cgIdentity.readEntry("InsertRememberLineOnAway", false)); newIdentity->setRunAwayCommands(cgIdentity.readEntry("ShowAwayMessage", false)); newIdentity->setAwayCommand(cgIdentity.readEntry("AwayMessage")); newIdentity->setReturnCommand(cgIdentity.readEntry("ReturnMessage")); newIdentity->setAutomaticAway(cgIdentity.readEntry("AutomaticAway", false)); newIdentity->setAwayInactivity(cgIdentity.readEntry("AwayInactivity", 10)); newIdentity->setAutomaticUnaway(cgIdentity.readEntry("AutomaticUnaway", false)); newIdentity->setQuitReason(cgIdentity.readEntry("QuitReason")); newIdentity->setPartReason(cgIdentity.readEntry("PartReason")); newIdentity->setKickReason(cgIdentity.readEntry("KickReason")); newIdentity->setShellCommand(cgIdentity.readEntry("PreShellCommand")); newIdentity->setCodecName(cgIdentity.readEntry("Codec")); newIdentity->setAwayMessage(cgIdentity.readEntry("AwayReason")); newIdentity->setAwayNickname(cgIdentity.readEntry("AwayNick")); Preferences::addIdentity(newIdentity); } } osd->setEnabled(Preferences::self()->useOSD()); //How to load the font from the text? osd->setFont(Preferences::self()->oSDFont()); osd->setDuration(Preferences::self()->oSDDuration()); osd->setScreen(Preferences::self()->oSDScreen()); osd->setShadow(Preferences::self()->oSDDrawShadow()); osd->setOffset(Preferences::self()->oSDOffsetX(), Preferences::self()->oSDOffsetY()); osd->setAlignment((OSDWidget::Alignment)Preferences::self()->oSDAlignment()); if(Preferences::self()->oSDUseCustomColors()) { osd->setTextColor(Preferences::self()->oSDTextColor()); QPalette p = osd->palette(); p.setColor(osd->backgroundRole(), Preferences::self()->oSDBackgroundColor()); osd->setPalette(p); } // Check if there is old server list config //TODO FIXME why are we doing this here? KConfigGroup cgServerList(KSharedConfig::openConfig()->group("Server List")); // Read the new server settings QStringList groups = KSharedConfig::openConfig()->groupList().filter(QRegExp(QStringLiteral("ServerGroup [0-9]+"))); QMap notifyList; QList sgKeys; if(!groups.isEmpty()) { Konversation::ServerGroupHash serverGroups; QStringList::iterator it; QStringList tmp1; QStringList::iterator it2; int index = 0; Konversation::ChannelList channelHistory; Konversation::ServerSettings server; Konversation::ChannelSettings channel; for (it = groups.begin(); it != groups.end(); ++it) { KConfigGroup cgServerGroup(KSharedConfig::openConfig()->group(*it)); Konversation::ServerGroupSettingsPtr serverGroup(new Konversation::ServerGroupSettings); serverGroup->setName(cgServerGroup.readEntry("Name")); serverGroup->setSortIndex(groups.at(index).section(' ', -1).toInt() ); serverGroup->setIdentityId(Preferences::identityByName(cgServerGroup.readEntry("Identity"))->id()); serverGroup->setConnectCommands(cgServerGroup.readEntry("ConnectCommands")); serverGroup->setAutoConnectEnabled(cgServerGroup.readEntry("AutoConnect", false)); serverGroup->setNotificationsEnabled(cgServerGroup.readEntry("EnableNotifications", true)); serverGroup->setExpanded(cgServerGroup.readEntry("Expanded", false)); notifyList.insert((*serverGroup).id(), cgServerGroup.readEntry("NotifyList", QString()).split(QLatin1Char(' '), QString::SkipEmptyParts)); tmp1 = cgServerGroup.readEntry("ServerList", QStringList()); for (it2 = tmp1.begin(); it2 != tmp1.end(); ++it2) { KConfigGroup cgServer(KSharedConfig::openConfig()->group(*it2)); server.setHost(cgServer.readEntry("Server")); server.setPort(cgServer.readEntry("Port", 0)); server.setPassword(cgServer.readEntry("Password")); server.setSSLEnabled(cgServer.readEntry("SSLEnabled", false)); server.setBypassProxy(cgServer.readEntry("BypassProxy", false)); serverGroup->addServer(server); } //config->setGroup((*it)); tmp1 = cgServerGroup.readEntry("AutoJoinChannels", QStringList()); for (it2 = tmp1.begin(); it2 != tmp1.end(); ++it2) { KConfigGroup cgJoin(KSharedConfig::openConfig()->group(*it2)); if (!cgJoin.readEntry("Name").isEmpty()) { channel.setName(cgJoin.readEntry("Name")); channel.setPassword(cgJoin.readEntry("Password")); serverGroup->addChannel(channel); } } //config->setGroup((*it)); tmp1 = cgServerGroup.readEntry("ChannelHistory", QStringList()); channelHistory.clear(); for (it2 = tmp1.begin(); it2 != tmp1.end(); ++it2) { KConfigGroup cgChanHistory(KSharedConfig::openConfig()->group(*it2)); if (!cgChanHistory.readEntry("Name").isEmpty()) { channel.setName(cgChanHistory.readEntry("Name")); channel.setPassword(cgChanHistory.readEntry("Password")); channel.setNotificationsEnabled(cgChanHistory.readEntry("EnableNotifications", true)); channelHistory.append(channel); } } serverGroup->setChannelHistory(channelHistory); serverGroups.insert(serverGroup->id(), serverGroup); sgKeys.append(serverGroup->id()); index++; } Preferences::setServerGroupHash(serverGroups); } // Notify Settings and lists. Must follow Server List. Preferences::setNotifyList(notifyList); Preferences::self()->setNotifyDelay(Preferences::self()->notifyDelay()); Preferences::self()->setUseNotify(Preferences::self()->useNotify()); // Quick Buttons List // if there are button definitions in the config file, remove default buttons if (KSharedConfig::openConfig()->hasGroup("Button List")) Preferences::clearQuickButtonList(); KConfigGroup cgQuickButtons(KSharedConfig::openConfig()->group("Button List")); // Read all default buttons QStringList buttonList(Preferences::quickButtonList()); // Read all quick buttons int index=0; while (cgQuickButtons.hasKey(QString(QStringLiteral("Button%1")).arg(index))) { buttonList.append(cgQuickButtons.readEntry(QString(QStringLiteral("Button%1")).arg(index++))); } // while // Put back the changed button list Preferences::setQuickButtonList(buttonList); // Autoreplace List // if there are autoreplace definitions in the config file, remove default entries if (KSharedConfig::openConfig()->hasGroup("Autoreplace List")) Preferences::clearAutoreplaceList(); KConfigGroup cgAutoreplace(KSharedConfig::openConfig()->group("Autoreplace List")); // Read all default entries QList autoreplaceList(Preferences::autoreplaceList()); // Read all entries index=0; // legacy code for old autoreplace format 4/6/09 QString autoReplaceString(QStringLiteral("Autoreplace")); while (cgAutoreplace.hasKey(autoReplaceString + QString::number(index))) { // read entry and get length of the string QString entry=cgAutoreplace.readEntry(autoReplaceString + QString::number(index++)); int length=entry.length()-1; // if there's a "#" in the end, strip it (used to preserve blanks at the end of the replacement text) // there should always be one, but older versions did not do it, so we check first if (entry.at(length)==QLatin1Char('#')) - entry=entry.left(length); + entry.truncate(length); QString regex = entry.section(QLatin1Char(','),0,0); QString direction = entry.section(QLatin1Char(','),1,1); QString pattern = entry.section(QLatin1Char(','),2,2); QString replace = entry.section(QLatin1Char(','),3); // add entry to internal list autoreplaceList.append(QStringList() << regex << direction << pattern << replace); } // while //end legacy code for old autoreplace format index=0; //new code for autoreplace config QString indexString(QString::number(index)); QString regexString(QStringLiteral("Regex")); QString directString(QStringLiteral("Direction")); QString patternString(QStringLiteral("Pattern")); QString replaceString(QStringLiteral("Replace")); while (cgAutoreplace.hasKey(patternString + indexString)) { QString pattern = cgAutoreplace.readEntry(patternString + indexString); QString regex = cgAutoreplace.readEntry(regexString + indexString, QStringLiteral("0")); QString direction = cgAutoreplace.readEntry(directString + indexString, QStringLiteral("o")); QString replace = cgAutoreplace.readEntry(replaceString + indexString, QString()); if (replace.length()>0) { int repLen=replace.length()-1; if (replace.at(repLen)==QLatin1Char('#')) - replace=replace.left(repLen); + replace.truncate(repLen); } if (pattern.length()>0) { int patLen=pattern.length()-1; if (pattern.at(patLen)==QLatin1Char('#')) - pattern=pattern.left(patLen); + pattern.truncate(patLen); } index++; indexString = QString::number(index); autoreplaceList.append(QStringList() << regex << direction << pattern << replace); } // Put back the changed autoreplace list Preferences::setAutoreplaceList(autoreplaceList); //TODO FIXME I assume this is in the group, but I have a hunch we just don't care about <1.0.1 // Highlight List KConfigGroup cgDefault(KSharedConfig::openConfig()->group("")); if (cgDefault.hasKey("Highlight")) // Stay compatible with versions < 0.14 { QString highlight=cgDefault.readEntry("Highlight"); QStringList hiList = highlight.split(QLatin1Char(' '), QString::SkipEmptyParts); for (int hiIndex=0; hiIndex < hiList.count(); hiIndex+=2) { Preferences::addHighlight(hiList[hiIndex], false, QString(QLatin1Char('#')+hiList[hiIndex+1]), QString(), QString(), QString(), true); } cgDefault.deleteEntry("Highlight"); } else { int i = 0; while (KSharedConfig::openConfig()->hasGroup(QString(QStringLiteral("Highlight%1")).arg(i))) { KConfigGroup cgHilight(KSharedConfig::openConfig()->group(QString(QStringLiteral("Highlight%1")).arg(i))); Preferences::addHighlight( cgHilight.readEntry("Pattern"), cgHilight.readEntry("RegExp", false), cgHilight.readEntry("Color", QColor(Qt::black)), cgHilight.readPathEntry("Sound", QString()), cgHilight.readEntry("AutoText"), cgHilight.readEntry("ChatWindows"), cgHilight.readEntry("Notify", true) ); i++; } } // Ignore List KConfigGroup cgIgnoreList(KSharedConfig::openConfig()->group("Ignore List")); // Remove all default entries if there is at least one Ignore in the Preferences::file if (cgIgnoreList.hasKey("Ignore0")) Preferences::clearIgnoreList(); // Read all ignores index=0; while (cgIgnoreList.hasKey(QString(QStringLiteral("Ignore%1")).arg(index))) { Preferences::addIgnore(cgIgnoreList.readEntry(QString(QStringLiteral("Ignore%1")).arg(index++))); } // Aliases KConfigGroup cgAliases(KSharedConfig::openConfig()->group("Aliases")); QStringList newList=cgAliases.readEntry("AliasList", QStringList()); if (!newList.isEmpty()) Preferences::self()->setAliasList(newList); // Channel Encodings //Legacy channel encodings read in Jun. 29, 2009 KConfigGroup cgChannelEncodings(KSharedConfig::openConfig()->group("Channel Encodings")); QMap channelEncodingEntries=cgChannelEncodings.entryMap(); QRegExp re(QStringLiteral("^(.+) ([^\\s]+)$")); QList channelEncodingEntryKeys=channelEncodingEntries.keys(); for(QList::const_iterator itStr=channelEncodingEntryKeys.constBegin(); itStr != channelEncodingEntryKeys.constEnd(); ++itStr) { if(re.indexIn(*itStr) > -1) { Preferences::setChannelEncoding(re.cap(1),re.cap(2),channelEncodingEntries[*itStr]); } } //End legacy channel encodings read in Jun 29, 2009 KConfigGroup cgEncodings(KSharedConfig::openConfig()->group("Encodings")); QMap encodingEntries=cgEncodings.entryMap(); QList encodingEntryKeys=encodingEntries.keys(); QRegExp reg(QStringLiteral("^([^\\s]+) ([^\\s]+)\\s?([^\\s]*)$")); for(QList::const_iterator itStr=encodingEntryKeys.constBegin(); itStr != encodingEntryKeys.constEnd(); ++itStr) { if(reg.indexIn(*itStr) > -1) { - if(reg.cap(1) == QStringLiteral("ServerGroup") && !reg.cap(3).isEmpty()) + if(reg.cap(1) == QLatin1String("ServerGroup") && !reg.cap(3).isEmpty()) Preferences::setChannelEncoding(sgKeys.at(reg.cap(2).toInt()), reg.cap(3), encodingEntries[*itStr]); else Preferences::setChannelEncoding(reg.cap(1), reg.cap(2), encodingEntries[*itStr]); } } // Spell Checking Languages KConfigGroup cgSpellCheckingLanguages(KSharedConfig::openConfig()->group("Spell Checking Languages")); QMap spellCheckingLanguageEntries=cgSpellCheckingLanguages.entryMap(); QList spellCheckingLanguageEntryKeys=spellCheckingLanguageEntries.keys(); for (QList::const_iterator itStr=spellCheckingLanguageEntryKeys.constBegin(); itStr != spellCheckingLanguageEntryKeys.constEnd(); ++itStr) { if (reg.indexIn(*itStr) > -1) { - if (reg.cap(1) == QStringLiteral("ServerGroup") && !reg.cap(3).isEmpty()) + if (reg.cap(1) == QLatin1String("ServerGroup") && !reg.cap(3).isEmpty()) { ServerGroupSettingsPtr serverGroup = Preferences::serverGroupById(sgKeys.at(reg.cap(2).toInt())); if (serverGroup) Preferences::setSpellCheckingLanguage(serverGroup, reg.cap(3), spellCheckingLanguageEntries[*itStr]); } else Preferences::setSpellCheckingLanguage(reg.cap(1), reg.cap(2), spellCheckingLanguageEntries[*itStr]); } } fetchQueueRates(); updateProxySettings(); } void Application::saveOptions(bool updateGUI) { // template: KConfigGroup (KSharedConfig::openConfig()->group( )); //KConfig* config=KSharedConfig::openConfig(); // Should be handled in NicklistBehaviorConfigController now // config->setGroup("Sort Nicknames"); // Clean up identity list QStringList identities=KSharedConfig::openConfig()->groupList().filter(QRegExp(QStringLiteral("Identity [0-9]+"))); if (identities.count()) { // remove old identity list from Preferences::file to keep numbering under control for (int index=0; index < identities.count(); index++) KSharedConfig::openConfig()->deleteGroup(identities[index]); } IdentityList identityList = Preferences::identityList(); int index = 0; for (IdentityList::ConstIterator it = identityList.constBegin(); it != identityList.constEnd(); ++it) { IdentityPtr identity = (*it); KConfigGroup cgIdentity(KSharedConfig::openConfig()->group(QString(QStringLiteral("Identity %1")).arg(index))); cgIdentity.writeEntry("Name",identity->getName()); cgIdentity.writeEntry("Ident",identity->getIdent()); cgIdentity.writeEntry("Realname",identity->getRealName()); cgIdentity.writeEntry("Nicknames",identity->getNicknameList()); cgIdentity.writeEntry("AuthType",identity->getAuthType()); cgIdentity.writeEntry("Password",identity->getAuthPassword()); cgIdentity.writeEntry("Bot",identity->getNickservNickname()); cgIdentity.writeEntry("NickservCommand",identity->getNickservCommand()); cgIdentity.writeEntry("SaslAccount",identity->getSaslAccount()); cgIdentity.writeEntry("PemClientCertFile", identity->getPemClientCertFile().toString()); cgIdentity.writeEntry("InsertRememberLineOnAway", identity->getInsertRememberLineOnAway()); cgIdentity.writeEntry("ShowAwayMessage",identity->getRunAwayCommands()); cgIdentity.writeEntry("AwayMessage",identity->getAwayCommand()); cgIdentity.writeEntry("ReturnMessage",identity->getReturnCommand()); cgIdentity.writeEntry("AutomaticAway", identity->getAutomaticAway()); cgIdentity.writeEntry("AwayInactivity", identity->getAwayInactivity()); cgIdentity.writeEntry("AutomaticUnaway", identity->getAutomaticUnaway()); cgIdentity.writeEntry("QuitReason",identity->getQuitReason()); cgIdentity.writeEntry("PartReason",identity->getPartReason()); cgIdentity.writeEntry("KickReason",identity->getKickReason()); cgIdentity.writeEntry("PreShellCommand",identity->getShellCommand()); cgIdentity.writeEntry("Codec",identity->getCodecName()); cgIdentity.writeEntry("AwayReason",identity->getAwayMessage()); cgIdentity.writeEntry("AwayNick", identity->getAwayNickname()); index++; } // endfor // Remove the old servergroups from the config QStringList groups = KSharedConfig::openConfig()->groupList().filter(QRegExp(QStringLiteral("ServerGroup [0-9]+"))); if (groups.count()) { QStringList::iterator it; for(it = groups.begin(); it != groups.end(); ++it) { KSharedConfig::openConfig()->deleteGroup((*it)); } } // Remove the old servers from the config groups = KSharedConfig::openConfig()->groupList().filter(QRegExp(QStringLiteral("Server [0-9]+"))); if (groups.count()) { QStringList::iterator it; for(it = groups.begin(); it != groups.end(); ++it) { KSharedConfig::openConfig()->deleteGroup((*it)); } } // Remove the old channels from the config groups = KSharedConfig::openConfig()->groupList().filter(QRegExp(QStringLiteral("Channel [0-9]+"))); if (groups.count()) { QStringList::iterator it; for(it = groups.begin(); it != groups.end(); ++it) { KSharedConfig::openConfig()->deleteGroup((*it)); } } // Add the new servergroups to the config Konversation::ServerGroupHash serverGroupHash = Preferences::serverGroupHash(); QHashIterator hashIt(serverGroupHash); QMap sortedServerGroupMap; // Make the indices in the group headers reflect the server list dialog sorting. while (hashIt.hasNext()) { hashIt.next(); sortedServerGroupMap.insert(hashIt.value()->sortIndex(), hashIt.value()); } QMapIterator it(sortedServerGroupMap); index = 0; int index2 = 0; int index3 = 0; int width = 0; QList keys = serverGroupHash.keys(); for(int i=0; i sgKeys; while(it.hasNext()) { it.next(); serverlist = (it.value())->serverList(); servers.clear(); sgKeys.append(it.value()->id()); for(it2 = serverlist.begin(); it2 != serverlist.end(); ++it2) { groupName = QString(QStringLiteral("Server %1")).arg(index2); servers.append(groupName); KConfigGroup cgServer(KSharedConfig::openConfig()->group(groupName)); cgServer.writeEntry("Server", (*it2).host()); cgServer.writeEntry("Port", (*it2).port()); cgServer.writeEntry("Password", (*it2).password()); cgServer.writeEntry("SSLEnabled", (*it2).SSLEnabled()); cgServer.writeEntry("BypassProxy", (*it2).bypassProxy()); index2++; } channelList = it.value()->channelList(); channels.clear(); for(it3 = channelList.begin(); it3 != channelList.end(); ++it3) { groupName = QString(QStringLiteral("Channel %1")).arg(index3); channels.append(groupName); KConfigGroup cgChannel(KSharedConfig::openConfig()->group(groupName)); cgChannel.writeEntry("Name", (*it3).name()); cgChannel.writeEntry("Password", (*it3).password()); index3++; } channelList = it.value()->channelHistory(); channelHistory.clear(); for(it3 = channelList.begin(); it3 != channelList.end(); ++it3) { // TODO FIXME: is it just me or is this broken? groupName = QString(QStringLiteral("Channel %1")).arg(index3); channelHistory.append(groupName); KConfigGroup cgChannelHistory(KSharedConfig::openConfig()->group(groupName)); cgChannelHistory.writeEntry("Name", (*it3).name()); cgChannelHistory.writeEntry("Password", (*it3).password()); cgChannelHistory.writeEntry("EnableNotifications", (*it3).enableNotifications()); index3++; } QString sgn = QString(QStringLiteral("ServerGroup %1")).arg(QString::number(index).rightJustified(width,QLatin1Char('0'))); KConfigGroup cgServerGroup(KSharedConfig::openConfig()->group(sgn)); cgServerGroup.writeEntry("Name", it.value()->name()); cgServerGroup.writeEntry("Identity", it.value()->identity()->getName()); cgServerGroup.writeEntry("ServerList", servers); cgServerGroup.writeEntry("AutoJoinChannels", channels); cgServerGroup.writeEntry("ConnectCommands", it.value()->connectCommands()); cgServerGroup.writeEntry("AutoConnect", it.value()->autoConnectEnabled()); cgServerGroup.writeEntry("ChannelHistory", channelHistory); cgServerGroup.writeEntry("EnableNotifications", it.value()->enableNotifications()); cgServerGroup.writeEntry("Expanded", it.value()->expanded()); cgServerGroup.writeEntry("NotifyList",Preferences::notifyStringByGroupId(it.value()->id())); index++; } KSharedConfig::openConfig()->deleteGroup("Server List"); // Ignore List KSharedConfig::openConfig()->deleteGroup("Ignore List"); KConfigGroup cgIgnoreList(KSharedConfig::openConfig()->group("Ignore List")); QList ignoreList=Preferences::ignoreList(); for (int i = 0; i < ignoreList.size(); ++i) { cgIgnoreList.writeEntry(QString(QStringLiteral("Ignore%1")).arg(i),QString(QStringLiteral("%1,%2")).arg(ignoreList.at(i)->getName()).arg(ignoreList.at(i)->getFlags())); } // Channel Encodings // remove all entries once KSharedConfig::openConfig()->deleteGroup("Channel Encodings"); // legacy Jun 29, 2009 KSharedConfig::openConfig()->deleteGroup("Encodings"); KConfigGroup cgEncoding(KSharedConfig::openConfig()->group("Encodings")); QList encServers=Preferences::channelEncodingsServerGroupIdList(); //i have no idea these would need to be sorted //encServers.sort(); QList::iterator encServer; for ( encServer = encServers.begin(); encServer != encServers.end(); ++encServer ) { Konversation::ServerGroupSettingsPtr sgsp = Preferences::serverGroupById(*encServer); if ( sgsp ) // sgsp == 0 when the entry is of QuickConnect or something? { QStringList encChannels=Preferences::channelEncodingsChannelList(*encServer); //ditto //encChannels.sort(); QStringList::iterator encChannel; for ( encChannel = encChannels.begin(); encChannel != encChannels.end(); ++encChannel ) { QString enc = Preferences::channelEncoding(*encServer, *encChannel); QString key = QLatin1Char(' ') + (*encChannel); if(sgKeys.contains(*encServer)) key.prepend(QStringLiteral("ServerGroup ")+QString::number(sgKeys.indexOf(*encServer))); else key.prepend(sgsp->name()); cgEncoding.writeEntry(key, enc); } } } // Spell Checking Languages KSharedConfig::openConfig()->deleteGroup("Spell Checking Languages"); KConfigGroup cgSpellCheckingLanguages(KSharedConfig::openConfig()->group("Spell Checking Languages")); QHashIterator > i(Preferences::serverGroupSpellCheckingLanguages()); while (i.hasNext()) { i.next(); QHashIterator i2(i.value()); while (i2.hasNext()) { i2.next(); int serverGroupIndex = sgKeys.indexOf(i.key()->id()); if (serverGroupIndex != -1) cgSpellCheckingLanguages.writeEntry(QStringLiteral("ServerGroup ") + QString::number(serverGroupIndex) + QLatin1Char(' ') + i2.key(), i2.value()); } } QHashIterator > i3(Preferences::serverSpellCheckingLanguages()); while (i3.hasNext()) { i3.next(); QHashIterator i4(i3.value()); while (i4.hasNext()) { i4.next(); cgSpellCheckingLanguages.writeEntry(i3.key() + QLatin1Char(' ') + i4.key(), i4.value()); } } KSharedConfig::openConfig()->sync(); if(updateGUI) emit appearanceChanged(); } void Application::fetchQueueRates() { //The following rate was found in the rc for all queues, which were deliberately bad numbers chosen for debugging. //Its possible that the static array was constructed or deconstructed at the wrong time, and so those values saved //in the rc. When reading the values out of the rc, we must check to see if they're this specific value, //and if so, reset to defaults. --argonel IRCQueue::EmptyingRate shit(6, 50000, IRCQueue::EmptyingRate::Lines); int bad = 0; for (int i=0; i <= countOfQueues(); i++) { QList r = Preferences::self()->queueRate(i); staticrates[i] = IRCQueue::EmptyingRate(r[0], r[1]*1000, IRCQueue::EmptyingRate::RateType(r[2])); if (staticrates[i] == shit) bad++; } if (bad == 3) resetQueueRates(); } void Application::stashQueueRates() { for (int i=0; i <= countOfQueues(); i++) { QList r; r.append(staticrates[i].m_rate); r.append(staticrates[i].m_interval / 1000); r.append(int(staticrates[i].m_type)); Preferences::self()->setQueueRate(i, r); } } void Application::resetQueueRates() { for (int i=0; i <= countOfQueues(); i++) { Preferences::self()->queueRateItem(i)->setDefault(); QList r=Preferences::self()->queueRate(i); staticrates[i]=IRCQueue::EmptyingRate(r[0], r[1]*1000, IRCQueue::EmptyingRate::RateType(r[2])); } } void Application::storeUrl(const QString& origin, const QString& newUrl, const QDateTime& dateTime) { QString url(newUrl); - url = url.replace(QStringLiteral("&"), QStringLiteral("&")); + url.replace(QStringLiteral("&"), QStringLiteral("&")); QList existing = m_urlModel->findItems(url, Qt::MatchExactly, 1); foreach(QStandardItem* item, existing) { if (m_urlModel->item(item->row(), 0)->data(Qt::DisplayRole).toString() == origin) m_urlModel->removeRow(item->row()); } m_urlModel->insertRow(0); m_urlModel->setData(m_urlModel->index(0, 0), origin, Qt::DisplayRole); m_urlModel->setData(m_urlModel->index(0, 1), url, Qt::DisplayRole); UrlDateItem* dateItem = new UrlDateItem(dateTime); m_urlModel->setItem(0, 2, dateItem); } void Application::openQuickConnectDialog() { quickConnectDialog = new QuickConnectDialog(mainWindow); connect(quickConnectDialog, SIGNAL(connectClicked(Konversation::ConnectionFlag,QString,QString,QString,QString,QString,bool)), m_connectionManager, SLOT(connectTo(Konversation::ConnectionFlag,QString,QString,QString,QString,QString,bool))); quickConnectDialog->show(); } void Application::sendMultiServerCommand(const QString& command, const QString& parameter) { const QList serverList = getConnectionManager()->getServerList(); foreach (Server* server, serverList) server->executeMultiServerCommand(command, parameter); } void Application::splitNick_Server(const QString& nick_server, QString &ircnick, QString &serverOrGroup) { //kaddresbook uses the utf separator 0xE120, so treat that as a separator as well QString nickServer = nick_server; nickServer.replace(QChar(0xE120), QStringLiteral("@")); ircnick = nickServer.section(QLatin1Char('@'),0,0); serverOrGroup = nickServer.section(QLatin1Char('@'),1); } NickInfoPtr Application::getNickInfo(const QString &ircnick, const QString &serverOrGroup) { const QList serverList = getConnectionManager()->getServerList(); NickInfoPtr nickInfo; QString lserverOrGroup = serverOrGroup.toLower(); foreach (Server* lookServer, serverList) { if (lserverOrGroup.isEmpty() || lookServer->getServerName().toLower()==lserverOrGroup || lookServer->getDisplayName().toLower()==lserverOrGroup) { nickInfo = lookServer->getNickInfo(ircnick); if (nickInfo) return nickInfo; //If we found one } } return (NickInfoPtr)nullptr; } // auto replace on input/output QPair Application::doAutoreplace(const QString& text, bool output, int cursorPos) { // get autoreplace list QList autoreplaceList=Preferences::autoreplaceList(); // working copy QString line=text; // loop through the list of replacement patterns for (int index=0;index -1 && cursorPos >= index) { if (cursorPos < index + captures[0].length()) cursorPos = newIndex; else { if (captures[0].length() > replaceWith.length()) cursorPos -= captures[0].length() - replaceWith.length(); else cursorPos += replaceWith.length() - captures[0].length(); } } index = newIndex; } } while (index >= 0 && index < line.length()); } else { QRegExp needleReg(pattern); needleReg.setPatternSyntax(QRegExp::FixedString); int index=line.indexOf(needleReg); while (index>=0) { int length,nextLength,patLen,repLen; patLen=pattern.length(); repLen=replacement.length(); length=index; length+=patLen; nextLength=length; //nextlength is used to account for the replacement taking up less space QChar before,after; if (index!=0) before = line.at(index-1); if (line.length() > length) after = line.at(length); if (index==0 || before.isSpace() || before.isPunct()) { if (line.length() == length || after.isSpace() || after.isPunct()) { // allow for var expansion in autoreplace replacement = Konversation::doVarExpansion(replacement); line.replace(index,patLen,replacement); nextLength = index+repLen; } } if (cursorPos > -1 && cursorPos >= index) { if (cursorPos < length) cursorPos = nextLength; else { if (patLen > repLen) cursorPos -= patLen - repLen; else cursorPos += repLen - patLen; } } index=line.indexOf(needleReg,nextLength); } } } } return QPair(line, cursorPos); } void Application::doInlineAutoreplace(KTextEdit* textEdit) { QTextCursor cursor(textEdit->document()); cursor.beginEditBlock(); const QPair& replace = Application::instance()->doAutoreplace(textEdit->toPlainText(), true, textEdit->textCursor().position()); cursor.select(QTextCursor::Document); cursor.insertText(replace.first); cursor.setPosition(replace.second); cursor.endEditBlock(); textEdit->setTextCursor(cursor); } void Application::openUrl(const QString& url) { if (!Preferences::self()->useCustomBrowser() || url.startsWith(QLatin1String("mailto:")) || url.startsWith(QLatin1String("amarok:"))) { if (url.startsWith(QLatin1String("irc://")) || url.startsWith(QLatin1String("ircs://"))) Application::instance()->getConnectionManager()->connectTo(Konversation::SilentlyReuseConnection, url); else if (url.startsWith(QLatin1String("mailto:"))) QDesktopServices::openUrl(QUrl(url)); else #ifndef Q_OS_WIN new KRun(QUrl(url), Application::instance()->getMainWindow()); #else QDesktopServices::openUrl(QUrl::fromUserInput(url)); #endif } else { QHash map; map.insert(QLatin1Char('u'), url); const QString cmd = KMacroExpander::expandMacrosShellQuote(Preferences::webBrowserCmd(), map); const QStringList args = KShell::splitArgs(cmd); if (!args.isEmpty()) { KProcess::startDetached(args); return; } } } Konversation::Sound* Application::sound() { if (!m_sound) m_sound = new Konversation::Sound; return m_sound; } void Application::updateProxySettings() { if (Preferences::self()->proxyEnabled()) { QNetworkProxy proxy; if (Preferences::self()->proxyType() == Preferences::Socksv5Proxy) { proxy.setType(QNetworkProxy::Socks5Proxy); } else { proxy.setType(QNetworkProxy::HttpProxy); } proxy.setHostName(Preferences::self()->proxyAddress()); proxy.setPort(Preferences::self()->proxyPort()); proxy.setUser(Preferences::self()->proxyUsername()); QString password; if(wallet()) { int ret = wallet()->readPassword(QStringLiteral("ProxyPassword"), password); if(ret != 0) { qCritical() << "Failed to read the proxy password from the wallet, error code:" << ret; } } proxy.setPassword(password); QNetworkProxy::setApplicationProxy(proxy); } else { QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); } } KWallet::Wallet* Application::wallet() { if(!m_wallet) { WId winid = 0; if(mainWindow) winid = mainWindow->winId(); m_wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), winid); if(!m_wallet) return nullptr; connect(m_wallet, &KWallet::Wallet::walletClosed, this, &Application::closeWallet); if(!m_wallet->hasFolder(QStringLiteral("Konversation"))) { if(!m_wallet->createFolder(QStringLiteral("Konversation"))) { qCritical() << "Failed to create folder Konversation in the network wallet."; closeWallet(); return nullptr; } } if(!m_wallet->setFolder(QStringLiteral("Konversation"))) { qCritical() << "Failed to set active folder to Konversation in the network wallet."; closeWallet(); return nullptr; } } return m_wallet; } void Application::closeWallet() { delete m_wallet; m_wallet = nullptr; } void Application::handleActivate(const QStringList& arguments) { m_commandLineParser->parse(QStringList(applicationFilePath()) << arguments); if(m_commandLineParser->isSet(QStringLiteral("restart"))) { m_restartArguments = arguments; } newInstance(m_commandLineParser); KStartupInfo::setNewStartupId(mainWindow, KStartupInfo::startupId()); mainWindow->show(); mainWindow->raise(); } // kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on; // vim: set et sw=4 ts=4 cino=l1,cs,U1: diff --git a/src/cipher.cpp b/src/cipher.cpp index aa092bca..fa8e6fe5 100644 --- a/src/cipher.cpp +++ b/src/cipher.cpp @@ -1,517 +1,517 @@ /* 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. */ /* Copyright (C) 1997 Robey Pointer Copyright (C) 2005 Ismail Donmez Copyright (C) 2009 Travis McHenry */ #include "cipher.h" #include "preferences.h" #include #include namespace Konversation { QString Cipher::m_runtimeError; Cipher::Cipher() { m_primeNum = QCA::BigInteger("12745216229761186769575009943944198619149164746831579719941140425076456621824834322853258804883232842877311723249782818608677050956745409379781245497526069657222703636504651898833151008222772087491045206203033063108075098874712912417029101508315117935752962862335062591404043092163187352352197487303798807791605274487594646923"); setType(QStringLiteral("blowfish")); if(Preferences::self()->encryptionType()) m_cbc = true; else m_cbc = false; } Cipher::Cipher(const QByteArray &key, const QString &cipherType) { m_primeNum = QCA::BigInteger("12745216229761186769575009943944198619149164746831579719941140425076456621824834322853258804883232842877311723249782818608677050956745409379781245497526069657222703636504651898833151008222772087491045206203033063108075098874712912417029101508315117935752962862335062591404043092163187352352197487303798807791605274487594646923"); setKey(key); setType(cipherType); } Cipher::~Cipher() { } bool Cipher::setKey(const QByteArray &key) { if(key.isEmpty()) return false; if(key.mid(0,4).toLower() == "ecb:") { m_cbc = false; m_key = key.mid(4); } //strip cbc: if included else if(key.mid(0,4).toLower() == "cbc:") { m_cbc = true; m_key = key.mid(4); } else { if(Preferences::self()->encryptionType()) m_cbc = true; else m_cbc = false; m_key = key; } return true; } bool Cipher::setType(const QString &type) { //TODO check QCA::isSupported() m_type = type; return true; } QByteArray Cipher::decrypt(QByteArray cipherText) { QByteArray pfx = "(e) "; bool error = false; // used to flag non cbc, seems like good practice not to parse w/o regard for set encryption type //if we get cbc if(cipherText.mid(0,5) == "+OK *") { //if we have cbc if(m_cbc) - cipherText = cipherText.mid(5); + cipherText.remove(0, 5); //if we don't else { - cipherText = cipherText.mid(5); + cipherText.remove(0, 5); pfx = "ERROR_NONECB: "; error = true; } } //if we get ecb else if(cipherText.mid(0,4) == "+OK " || cipherText.mid(0,5) == "mcps ") { //if we had cbc if(m_cbc) { cipherText = (cipherText.mid(0,4) == "+OK ") ? cipherText.mid(4) : cipherText.mid(5); pfx = "ERROR_NONCBC: "; error = true; } //if we don't else { if(cipherText.mid(0,4) == "+OK ") - cipherText = cipherText.mid(4); + cipherText.remove(0, 4); else - cipherText = cipherText.mid(5); + cipherText.remove(0, 5); } } //all other cases we fail else return cipherText; QByteArray temp; // (if cbc and no error we parse cbc) || (if ecb and error we parse cbc) if((m_cbc && !error) || (!m_cbc && error)) { temp = blowfishCBC(cipherText, false); if(temp == cipherText) { qDebug() << "Decryption from CBC Failed"; return "ERROR: "+cipherText+' '+'\n'; } else cipherText = temp; } else { temp = blowfishECB(cipherText, false); if(temp == cipherText) { qDebug() << "Decryption from ECB Failed"; return "ERROR: "+cipherText+' '+'\n'; } else cipherText = temp; } // If it's a CTCP we don't want to have the (e) interfering with the processing // TODO FIXME the proper fix for this is to show encryption differently e.g. [nick] instead of // don't hate me for the mircryption reference there. if (cipherText.at(0) == 1) pfx = "\x0"; cipherText = pfx+cipherText+' '+'\n'; // FIXME(??) why is there an added space here? return cipherText; } QByteArray Cipher::initKeyExchange() { QCA::Initializer init; m_tempKey = QCA::KeyGenerator().createDH(QCA::DLGroup(m_primeNum, QCA::BigInteger(2))).toDH(); if(m_tempKey.isNull()) return QByteArray(); QByteArray publicKey = m_tempKey.toPublicKey().toDH().y().toArray().toByteArray(); //remove leading 0 if(publicKey.length() > 135 && publicKey.at(0) == '\0') - publicKey = publicKey.mid(1); + publicKey.remove(0, 1); return publicKey.toBase64().append('A'); } QByteArray Cipher::parseInitKeyX(const QByteArray &key) { QCA::Initializer init; if(key.length() != 181) return QByteArray(); QCA::SecureArray remoteKey = QByteArray::fromBase64(key.left(180)); QCA::DLGroup group(m_primeNum, QCA::BigInteger(2)); QCA::DHPrivateKey privateKey = QCA::KeyGenerator().createDH(group).toDH(); if(privateKey.isNull()) return QByteArray(); QByteArray publicKey = privateKey.y().toArray().toByteArray(); //remove leading 0 if(publicKey.length() > 135 && publicKey.at(0) == '\0') - publicKey = publicKey.mid(1); + publicKey.remove(0, 1); QCA::DHPublicKey remotePub(group, remoteKey); if(remotePub.isNull()) return QByteArray(); QByteArray sharedKey = privateKey.deriveKey(remotePub).toByteArray(); sharedKey = QCA::Hash(QStringLiteral("sha256")).hash(sharedKey).toByteArray().toBase64(); //remove trailing = because mircryption and fish think it's a swell idea. while(sharedKey.endsWith('=')) sharedKey.chop(1); bool success = setKey(sharedKey); if(!success) return QByteArray(); return publicKey.toBase64().append('A'); } bool Cipher::parseFinishKeyX(const QByteArray &key) { QCA::Initializer init; if(key.length() != 181) return false; QCA::SecureArray remoteKey = QByteArray::fromBase64(key.left(180)); QCA::DLGroup group(m_primeNum, QCA::BigInteger(2)); QCA::DHPublicKey remotePub(group, remoteKey); if(remotePub.isNull()) return false; if(m_tempKey.isNull()) return false; QByteArray sharedKey = m_tempKey.deriveKey(remotePub).toByteArray(); sharedKey = QCA::Hash(QStringLiteral("sha256")).hash(sharedKey).toByteArray().toBase64(); //remove trailng = because mircryption and fish think it's a swell idea. while(sharedKey.endsWith('=')) sharedKey.chop(1); bool success = setKey(sharedKey); return success; } QByteArray Cipher::decryptTopic(QByteArray cipherText) { if(cipherText.mid(0,4) == "+OK ")// FiSH style topic - cipherText = cipherText.mid(4); + cipherText.remove(0, 4); else if(cipherText.left(5) == "«m«") cipherText = cipherText.mid(5,cipherText.length()-10); else return "ERROR: "+cipherText; QByteArray temp; //TODO currently no backwards sanity checks for topic, it seems to use different standards //if somebody figures them out they can enable it and add "ERROR_NONECB/CBC" warnings if(m_cbc) temp = blowfishCBC(cipherText.mid(1), false); else temp = blowfishECB(cipherText, false); if(temp == cipherText) { return "ERROR: "+cipherText; } else cipherText = temp; if(cipherText.mid(0,2) == "@@") - cipherText = cipherText.mid(2); + cipherText.remove(0, 2); cipherText = "(e) "+cipherText; return cipherText; } bool Cipher::encrypt(QByteArray& cipherText) { if (cipherText.left(3) == "+p ") //don't encode if...? - cipherText = cipherText.mid(3); + cipherText.remove(0, 3); else { if(m_cbc) //encode in ecb or cbc decide how to determine later { QByteArray temp = blowfishCBC(cipherText, true); if(temp == cipherText) { qDebug() << "CBC Encoding Failed"; return false; } cipherText = "+OK *" + temp; } else { QByteArray temp = blowfishECB(cipherText, true); if(temp == cipherText) { qDebug() << "ECB Encoding Failed"; return false; } cipherText = "+OK " + temp; } } return true; } //THE BELOW WORKS AKA DO NOT TOUCH UNLESS YOU KNOW WHAT YOU'RE DOING QByteArray Cipher::blowfishCBC(QByteArray cipherText, bool direction) { QCA::Initializer init; QByteArray temp = cipherText; if(direction) { // make sure cipherText is an interval of 8 bits. We do this before so that we // know there's at least 8 bytes to en/decryption this ensures QCA doesn't fail while((temp.length() % 8) != 0) temp.append('\0'); QCA::InitializationVector iv(8); temp.prepend(iv.toByteArray()); // prefix with 8bits of IV for mircryptions *CUSTOM* cbc implementation } else { temp = QByteArray::fromBase64(temp); //supposedly nescessary if we get a truncated message also allows for decryption of 'crazy' //en/decoding clients that use STANDARDIZED PADDING TECHNIQUES while((temp.length() % 8) != 0) temp.append('\0'); } QCA::Direction dir = (direction) ? QCA::Encode : QCA::Decode; QCA::Cipher cipher(m_type, QCA::Cipher::CBC, QCA::Cipher::NoPadding, dir, m_key, QCA::InitializationVector(QByteArray("0"))); QByteArray temp2 = cipher.update(QCA::MemoryRegion(temp)).toByteArray(); temp2 += cipher.final().toByteArray(); if(!cipher.ok()) return cipherText; if(direction) //send in base64 temp2 = temp2.toBase64(); else //cut off the 8bits of IV - temp2 = temp2.remove(0,8); + temp2.remove(0,8); return temp2; } QByteArray Cipher::blowfishECB(QByteArray cipherText, bool direction) { QCA::Initializer init; QByteArray temp = cipherText; //do padding ourselves if(direction) { while((temp.length() % 8) != 0) temp.append('\0'); } else { // ECB Blowfish encodes in blocks of 12 chars, so anything else is malformed input if ((temp.length() % 12) != 0) return cipherText; temp = b64ToByte(temp); while ((temp.length() % 8) != 0) temp.append('\0'); } QCA::Direction dir = (direction) ? QCA::Encode : QCA::Decode; QCA::Cipher cipher(m_type, QCA::Cipher::ECB, QCA::Cipher::NoPadding, dir, m_key); QByteArray temp2 = cipher.update(QCA::MemoryRegion(temp)).toByteArray(); temp2 += cipher.final().toByteArray(); if (!cipher.ok()) return cipherText; if (direction) { // Sanity check if ((temp2.length() % 8) != 0) return cipherText; temp2 = byteToB64(temp2); } return temp2; } //Custom non RFC 2045 compliant Base64 enc/dec code for mircryption / FiSH compatibility QByteArray Cipher::byteToB64(const QByteArray &text) { int left = 0; int right = 0; int k = -1; int v; QString base64 = QStringLiteral("./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); QByteArray encoded; while (k < (text.length() - 1)) { k++; v=text.at(k); if (v<0) v+=256; left = v << 24; k++; v=text.at(k); if (v<0) v+=256; left += v << 16; k++; v=text.at(k); if (v<0) v+=256; left += v << 8; k++; v=text.at(k); if (v<0) v+=256; left += v; k++; v=text.at(k); if (v<0) v+=256; right = v << 24; k++; v=text.at(k); if (v<0) v+=256; right += v << 16; k++; v=text.at(k); if (v<0) v+=256; right += v << 8; k++; v=text.at(k); if (v<0) v+=256; right += v; for (int i = 0; i < 6; i++) { encoded.append(base64.at(right & 0x3F).toLatin1()); right = right >> 6; } //TODO make sure the .tolatin1 doesn't break anything for (int i = 0; i < 6; i++) { encoded.append(base64.at(left & 0x3F).toLatin1()); left = left >> 6; } } return encoded; } QByteArray Cipher::b64ToByte(const QByteArray &text) { QString base64 = QStringLiteral("./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); QByteArray decoded; int k = -1; while (k < (text.length() - 1)) { int right = 0; int left = 0; int v = 0; int w = 0; int z = 0; for (int i = 0; i < 6; i++) { k++; v = base64.indexOf(text.at(k)); right |= v << (i * 6); } for (int i = 0; i < 6; i++) { k++; v = base64.indexOf(text.at(k)); left |= v << (i * 6); } for (int i = 0; i < 4; i++) { w = ((left & (0xFF << ((3 - i) * 8)))); z = w >> ((3 - i) * 8); if(z < 0) {z = z + 256;} decoded.append(z); } for (int i = 0; i < 4; i++) { w = ((right & (0xFF << ((3 - i) * 8)))); z = w >> ((3 - i) * 8); if(z < 0) {z = z + 256;} decoded.append(z); } } return decoded; } bool Cipher::isFeatureAvailable(CipherFeature feature) { QCA::Initializer init; if (feature == DH && !QCA::isSupported("dh")) { m_runtimeError = i18n("Diffie-Hellman key exchange is not supported by your installation of the " "Qt Cryptographic Architecture (QCA). You likely need to install an additional " "provider plugin. Diffie-Hellman key exchange support is usually provided " "by the qca-ossl plugin."); return false; } else if (feature == Blowfish) { if (Preferences::self()->encryptionType()) { if (!QCA::isSupported("blowfish-cbc")) { m_runtimeError = i18n("Blowfish in cipher-block chaining (CBC) mode is not supported by " "your installation of the Qt Cryptographic Architecture (QCA). You " "likely need to install an additional provider plugin for QCA. " "Blowfish support is usually provided by the qca-ossl plugin."); return false; } } else { if (!QCA::isSupported("blowfish-ecb")) { m_runtimeError = i18n("Blowfish in electronic codebook (ECB) mode is not supported by " "your installation of the Qt Cryptographic Architecture (QCA). You " "likely need to install an additional provider plugin for QCA. " "Blowfish support is usually provided by the qca-ossl plugin."); return false; } } } return true; } } diff --git a/src/config/autoreplace_config.cpp b/src/config/autoreplace_config.cpp index 62d0bfac..af87149f 100644 --- a/src/config/autoreplace_config.cpp +++ b/src/config/autoreplace_config.cpp @@ -1,346 +1,346 @@ /* 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. */ /* Copyright (C) 2006 Dario Abatianni Copyright (C) 2006 Eike Hein */ #include "autoreplace_config.h" #include "preferences.h" #include #define DIRECTION_OUTPUT 0 #define DIRECTION_INPUT 1 #define DIRECTION_BOTH 2 Autoreplace_Config::Autoreplace_Config(QWidget* parent, const char* name) : QWidget(parent) { setObjectName(QString::fromLatin1(name)); setupUi(this); // reset flag to defined state (used to block signals when just selecting a new item) m_newItemSelected=false; // populate combobox directionCombo->insertItem(DIRECTION_OUTPUT, i18n("Outgoing")); directionCombo->insertItem(DIRECTION_INPUT, i18n("Incoming")); directionCombo->insertItem(DIRECTION_BOTH, i18n("Both")); // populate listview loadSettings(); connect(patternListView, &QTreeWidget::currentItemChanged, this, &Autoreplace_Config::entrySelected); connect(directionCombo, static_cast(&KComboBox::activated), this, &Autoreplace_Config::directionChanged); connect(patternInput, &KLineEdit::textChanged, this, &Autoreplace_Config::patternChanged); connect(replacementInput, &KLineEdit::textChanged, this, &Autoreplace_Config::replacementChanged); connect(newButton, &QPushButton::clicked, this, &Autoreplace_Config::addEntry); connect(removeButton, &QPushButton::clicked, this, &Autoreplace_Config::removeEntry); } Autoreplace_Config::~Autoreplace_Config() { } void Autoreplace_Config::loadSettings() { setAutoreplaceListView(Preferences::autoreplaceList()); // remember autoreplace list for hasChanged() m_oldAutoreplaceList=Preferences::autoreplaceList(); } // fill listview with autoreplace definitions void Autoreplace_Config::setAutoreplaceListView(const QList &autoreplaceList) { // clear listView patternListView->clear(); // go through the list for (int index=0;indexsetFlags(newItem->flags() &~ Qt::ItemIsDropEnabled); newItem->setCheckState(0, Qt::Unchecked); // Regular expression? - if (definition.at(0)==QLatin1String("1")) newItem->setCheckState(0, Qt::Checked); + if (definition.at(0)== QLatin1Char('1')) newItem->setCheckState(0, Qt::Checked); // direction input/output/both - if (definition.at(1)==QLatin1String("i")) + if (definition.at(1)== QLatin1Char('i')) { newItem->setText(1,directionCombo->itemText(DIRECTION_INPUT)); } - else if (definition.at(1)==QLatin1String("o")) + else if (definition.at(1)== QLatin1Char('o')) { newItem->setText(1,directionCombo->itemText(DIRECTION_OUTPUT)); } else if (definition.at(1)==QLatin1String("io")) { newItem->setText(1,directionCombo->itemText(DIRECTION_BOTH)); } // pattern newItem->setText(2,definition.at(2)); // replacement newItem->setText(3,definition.at(3)); // hidden column, so we are independent of the i18n()ed display string newItem->setText(4,definition.at(1)); } // for patternListView->setCurrentItem(patternListView->topLevelItem(0)); } // save autoreplace entries to configuration void Autoreplace_Config::saveSettings() { // get configuration object KSharedConfigPtr config=KSharedConfig::openConfig(); // delete all patterns config->deleteGroup("Autoreplace List"); // create new empty autoreplace group KConfigGroup grp = config->group("Autoreplace List"); // create empty list QList newList=currentAutoreplaceList(); // check if there are any patterns in the list view if(newList.count()) { // go through all patterns and save them into the configuration QString regexString(QStringLiteral("Regex")); QString directString(QStringLiteral("Direction")); QString patternString(QStringLiteral("Pattern")); QString replaceString(QStringLiteral("Replace")); for(int index=0;index Autoreplace_Config::currentAutoreplaceList() { // get first item of the autoreplace listview QTreeWidgetItem* item=patternListView->topLevelItem(0); // create empty list QList newList; QChar regex; // go through all items and save them into the configuration while(item) { regex = (item->checkState(0) == Qt::Checked) ? '1' : '0'; // remember entry in internal list (col 4 is hidden for input/output) newList.append(QStringList() << regex << item->text(4) << item->text(2) << item->text(3)); // get next item in the listview item=patternListView->itemBelow(item); } // while // return list return newList; } bool Autoreplace_Config::hasChanged() { return(m_oldAutoreplaceList!=currentAutoreplaceList()); } // slots // what to do when the user selects an item void Autoreplace_Config::entrySelected(QTreeWidgetItem* autoreplaceEntry) { // play it safe, assume disabling all widgets first bool enabled=false; // check if there really was an item selected if(autoreplaceEntry) { // remember to enable the editing widgets enabled=true; // tell the editing widgets not to emit modified() on signals now m_newItemSelected=true; // update editing widget contents patternInput->setText(autoreplaceEntry->text(2)); replacementInput->setText(autoreplaceEntry->text(3)); // set combobox to selected item int itemIndex=0; QString direction=autoreplaceEntry->text(4); - if(direction==QLatin1String("i")) itemIndex=DIRECTION_INPUT; - else if(direction==QLatin1String("o")) itemIndex=DIRECTION_OUTPUT; + if(direction== QLatin1Char('i')) itemIndex=DIRECTION_INPUT; + else if(direction== QLatin1Char('o')) itemIndex=DIRECTION_OUTPUT; else if(direction==QLatin1String("io")) itemIndex=DIRECTION_BOTH; directionCombo->setCurrentIndex(itemIndex); // re-enable modified() signal on text changes in edit widgets m_newItemSelected=false; } // enable or disable editing widgets removeButton->setEnabled(enabled); directionLabel->setEnabled(enabled); directionCombo->setEnabled(enabled); patternLabel->setEnabled(enabled); patternInput->setEnabled(enabled); replacementLabel->setEnabled(enabled); replacementInput->setEnabled(enabled); // make checkboxes work emit modified(); } // what to do when the user changes the direction of an entry void Autoreplace_Config::directionChanged(int newDirection) { // get possible selected item QTreeWidgetItem* item=patternListView->currentItem(); // sanity check if(item) { // prepare hidden identifier string QString id; // find the direction strings to set up in the item if(newDirection==DIRECTION_INPUT) id='i'; else if(newDirection==DIRECTION_OUTPUT) id='o'; else if(newDirection==DIRECTION_BOTH) id=QStringLiteral("io"); // rename direction item->setText(1,directionCombo->itemText(newDirection)); item->setText(4,id); // tell the config system that something has changed if(!m_newItemSelected) emit modified(); } } // what to do when the user changes the pattern of an entry void Autoreplace_Config::patternChanged(const QString& newPattern) { // get possible selected item QTreeWidgetItem* item=patternListView->currentItem(); // sanity check if(item) { // rename pattern if (newPattern.length()>0) { item->setText(2,newPattern); } else { item->setText(2,i18nc("Fallback content for the \"Find:\" field of an auto-replace rule that gets set when the user leaves the field empty and e.g. switches to another rule in the list. It's identical to the default content of the field after adding a new auto-replace rule.", "New")); } // tell the config system that something has changed if(!m_newItemSelected) emit modified(); } } // what to do when the user changes the replacement of an entry void Autoreplace_Config::replacementChanged(const QString& newReplacement) { // get possible selected item QTreeWidgetItem* item=patternListView->currentItem(); // sanity check if(item) { // rename item item->setText(3,newReplacement); // tell the config system that something has changed if(!m_newItemSelected) emit modified(); } } // add button pressed void Autoreplace_Config::addEntry() { // add new item at the bottom of list view QTreeWidgetItem* newItem=new QTreeWidgetItem(patternListView); if (newItem) { newItem->setFlags(newItem->flags() &~ Qt::ItemIsDropEnabled); newItem->setCheckState(0, Qt::Unchecked); // set default direction newItem->setText(1,directionCombo->itemText(DIRECTION_OUTPUT)); // set default pattern name newItem->setText(2,i18nc("Default content of the \"Find:\" field of a newly-added auto-replace rule.", "New")); // set default direction newItem->setText(4,QStringLiteral("o")); // select new item and make it the current one patternListView->setCurrentItem(newItem); // set input focus on item pattern edit patternInput->setFocus(); // select all text to make overwriting easier patternInput->selectAll(); // tell the config system that something has changed emit modified(); } } // remove button pressed void Autoreplace_Config::removeEntry() { // get possible first selected item QTreeWidgetItem* item=patternListView->currentItem(); // sanity check if(item) { // get item below the current one QTreeWidgetItem* nextItem=patternListView->itemBelow(item); // if there was none, get the one above if(!nextItem) nextItem=patternListView->itemAbove(item); // remove the item from the list delete item; // check if we found the next item if(nextItem) { // select the item and make it the current item patternListView->setCurrentItem(nextItem); } else { // no next item found, this means the list is empty entrySelected(nullptr); } // tell the config system that somethig has changed emit modified(); } } diff --git a/src/config/warnings_config.cpp b/src/config/warnings_config.cpp index 3269e15c..1af2d8f7 100644 --- a/src/config/warnings_config.cpp +++ b/src/config/warnings_config.cpp @@ -1,269 +1,269 @@ /* 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. */ /* Copyright (C) 2006 Dario Abatianni Copyright (C) 2006 John Tapsell Copyright (C) 2006-2008 Eike Hein Copyright (C) 2010 Eli Mackenzie */ #include "warnings_config.h" #include #include #include static const int WarningNameRole = Qt::UserRole + 100; Warnings_Config::Warnings_Config(QWidget* parent, const char* name, Qt::WindowFlags fl) : QWidget(parent, fl) { setObjectName(QString::fromLatin1(name)); setupUi(this); dialogListView->header()->setSectionsClickable(false); dialogListView->header()->setSectionsMovable(false); loadSettings(); connect(dialogListView, &QTreeWidget::itemChanged, this, &Warnings_Config::modified); } Warnings_Config::~Warnings_Config() { } void Warnings_Config::restorePageToDefaults() { bool changed=false; for (int i = 0; i < dialogListView->topLevelItemCount(); ++i) { QTreeWidgetItem *item = dialogListView->topLevelItem(i); if (item->checkState(0) == Qt::Unchecked) { item->setCheckState(0, Qt::Checked); changed=true; } } if(changed) { emit modified(); } } void Warnings_Config::saveSettings() { KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup grp = config->group("Notification Messages"); // prepare list QString warningsChecked; for (int i = 0; i < dialogListView->topLevelItemCount(); ++i) { QTreeWidgetItem *item = dialogListView->topLevelItem(i); const bool checked = item->checkState(0) == Qt::Checked; const QString warningName = item->data(0, WarningNameRole).toString(); // save state of this item in hasChanged() list warningsChecked += checked ? "1" : "0"; if (warningName == QLatin1String("LargePaste")) { if (checked) { grp.deleteEntry(warningName); } else { // Let's keep the old state if we got one. QString state = grp.readEntry(warningName, QString()); if (!state.isEmpty()) grp.writeEntry(warningName, state); else grp.writeEntry(warningName, "true"); } } else if (warningName == QLatin1String("Invitation")) { // 0 == always ask // 1 == always join // 2 == always ignore if (checked) { grp.writeEntry(warningName, "0"); } else { // We have two cases here, new unchecked and old already unchecked // If we already ignore the joining, keep it "2" // else newly unchecked, always join "1" QString state = grp.readEntry(warningName, QString()); - if (state == QLatin1String("2")) + if (state == QLatin1Char('2')) grp.writeEntry(warningName, state); else grp.writeEntry(warningName, "1"); } } else { grp.writeEntry(warningName, checked ? "1" : "0"); } } // remember checkbox state for hasChanged() m_oldWarningsChecked=warningsChecked; } void Warnings_Config::loadSettings() { // This table is very wide, on purpose, so that the nauseatingly constant context string is // out of the way. The problem is this: // The context string in I18N_NOOP2_NOSTRIP must always be first, and now that it has // semantic markers it is always very long. So, if you want to understand a string // definition, you must repeatedly look at the context string. A macro replacement for it // is not possible as such recent and complicated inventions are not supported by the tools // used to generate the message files. static const struct DefinitionItem { const char *flagName; const char *context; const char *message; } warningDialogDefinitions[] = { { "Invitation", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... a channel invitation is received" )}, { "SaveLogfileNote", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... saving a log file would save only the visible portion" )}, { "ClearLogfileQuestion", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... a log file is about to be deleted" )}, { "CloseQueryAfterIgnore", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... an open query exists for a nickname that has just been marked as ignored" )}, { "ReconnectWithDifferentServer", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... switching a connection to a different server in the same network" )}, { "ReuseExistingConnection", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... creating a new connection to an already connected network" )}, { "QuitServerTab", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... closing a server tab" )}, { "QuitChannelTab", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... closing a channel tab" )}, { "QuitQueryTab", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... closing a query tab" )}, { "QuitDCCChatTab", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... closing a DCC Chat tab" )}, { "ChannelListNoServerSelected", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... a source for the channel list cannot be determined from the current tab" )}, { "HideMenuBarWarning", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... you have chosen to hide the menu bar" )}, { "ChannelListWarning", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... a channel listing may cause disconnection due to the download size" )}, { "LargePaste", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... pasting large portions of text" )}, { "systemtrayquitKonversation", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... quitting Konversation via the tray icon" )}, { "IgnoreNick", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... you have chosen to ignore a nickname" )}, { "UnignoreNick", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... you have chosen to stop ignoring a nickname" )}, { "QuitWithActiveDccTransfers", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... quitting Konversation while DCC file transfers are active" )}, { "WarnEncodingConflict", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... some characters in a message are incompatible with the active encoding" )}, { "HideOnCloseInfo", I18N_NOOP2_NOSTRIP("@item:inlistbox Checkbox item, determines whether warning dialog is shown; concludes sentence \"Show a warning dialog when...\"", "... closing the window will minimize to the system tray" )} }; static const int definitionsCount = sizeof(warningDialogDefinitions) / sizeof(warningDialogDefinitions[0]); dialogListView->clear(); KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup grp = config->group("Notification Messages"); for (int i = 0; i < definitionsCount; ++i) { const QLatin1String flagName(warningDialogDefinitions[i].flagName); const char * const message(warningDialogDefinitions[i].message); const char * const ctx(warningDialogDefinitions[i].context); QTreeWidgetItem *item = new QTreeWidgetItem(dialogListView); item->setText(0, i18nc(ctx, message)); item->setFlags(item->flags() | Qt::ItemIsUserCheckable); item->setData(0, WarningNameRole, flagName); if (flagName == QLatin1String("LargePaste")) { item->setCheckState(0, grp.readEntry(flagName, QString()).isEmpty() ? Qt::Checked : Qt::Unchecked); } else if (flagName == QLatin1String("Invitation")) { - item->setCheckState(0, grp.readEntry(flagName, QStringLiteral("0")) == QLatin1String("0") ? Qt::Checked : Qt::Unchecked); + item->setCheckState(0, grp.readEntry(flagName, QStringLiteral("0")) == QLatin1Char('0') ? Qt::Checked : Qt::Unchecked); } else { item->setCheckState(0, grp.readEntry(flagName, true) ? Qt::Checked : Qt::Unchecked); } } dialogListView->sortItems(0, Qt::AscendingOrder); // remember checkbox state for hasChanged() m_oldWarningsChecked=currentWarningsChecked(); } // get a list of checked/unchecked items for hasChanged() QString Warnings_Config::currentWarningsChecked() { // prepare list QString newList; // get first checklist item for (int i = 0; i < dialogListView->topLevelItemCount(); ++i) { newList += dialogListView->topLevelItem(i)->checkState(0) == Qt::Checked ? "1" : "0"; } // return list return newList; } bool Warnings_Config::hasChanged() { return(m_oldWarningsChecked!=currentWarningsChecked()); } // Sets the strings of the subwidgets using the current language. void Warnings_Config::languageChange() { loadSettings(); } diff --git a/src/dcc/transferpanel.cpp b/src/dcc/transferpanel.cpp index 5a6e2df1..73020e9e 100644 --- a/src/dcc/transferpanel.cpp +++ b/src/dcc/transferpanel.cpp @@ -1,533 +1,533 @@ /* 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. */ /* begin: Mit Aug 7 2002 copyright: (C) 2002 by Dario Abatianni email: eisfuchs@tigress.com */ /* Copyright (C) 2004-2008 Shintaro Matsuoka Copyright (C) 2009,2010 Bernd Buschinski */ #include "transferpanel.h" #include "application.h" #include "transferdetailedinfopanel.h" #include "transfermanager.h" #include "transfersend.h" #include "preferences.h" #include "transferview.h" #include "transferlistmodel.h" #include #include #include #include #include #include #include namespace Konversation { namespace DCC { TransferPanel::TransferPanel(QWidget *parent) : ChatWindow(parent) { setType(ChatWindow::DccTransferPanel); setName(i18n("DCC Status")); initGUI(); connect(Application::instance()->getDccTransferManager(), &TransferManager::newTransferAdded, this, &TransferPanel::slotNewTransferAdded); } TransferPanel::~TransferPanel() { KConfigGroup config(KSharedConfig::openConfig(), "DCC Settings"); const QByteArray state = m_splitter->saveState(); config.writeEntry(QStringLiteral("PanelSplitter"), state.toBase64()); } void TransferPanel::initGUI() { setSpacing(0); m_toolBar = new KToolBar(this, true, true); m_toolBar->setObjectName(QStringLiteral("dccstatus_toolbar")); m_splitter = new QSplitter(this); m_splitter->setOrientation(Qt::Vertical); m_transferView = new TransferView(m_splitter); connect(m_transferView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &TransferPanel::updateButton); connect(m_transferView, &TransferView::runSelectedTransfers, this, &TransferPanel::runDcc); // detailed info panel m_detailPanel = new TransferDetailedInfoPanel(m_splitter); m_splitter->setStretchFactor(0, QSizePolicy::Expanding); // popup menu m_popup = new QMenu(this); m_selectAll = m_popup->addAction(i18n("&Select All Items"), this, &TransferPanel::selectAll); m_selectAllCompleted = m_popup->addAction(i18n("S&elect All Completed Items"), this, &TransferPanel::selectAllCompleted); m_popup->addSeparator(); // ----- m_accept = m_popup->addAction(QIcon::fromTheme(QStringLiteral("media-playback-start")), i18n("&Accept"), this, &TransferPanel::acceptDcc); m_accept->setStatusTip(i18n("Start receiving")); m_abort = m_popup->addAction(QIcon::fromTheme(QStringLiteral("process-stop")),i18n("A&bort"), this, &TransferPanel::abortDcc); m_abort->setStatusTip(i18n("Abort the transfer(s)")); m_popup->addSeparator(); // ----- m_resend = m_popup->addAction(QIcon::fromTheme(QStringLiteral("edit-redo")),i18n("Resend"), this, &TransferPanel::resendFile); m_clear = m_popup->addAction(QIcon::fromTheme(QStringLiteral("edit-delete")),i18nc("clear selected dcctransfer","&Clear"), this, &TransferPanel::clearDcc); m_clear->setStatusTip(i18n("Clear all selected Items")); m_clearCompleted = m_popup->addAction(QIcon::fromTheme(QStringLiteral("edit-clear-list")),i18n("Clear Completed"), this, &TransferPanel::clearCompletedDcc); m_clearCompleted->setStatusTip(i18n("Clear Completed Items")); m_popup->addSeparator(); // ----- m_open = m_popup->addAction(QIcon::fromTheme(QStringLiteral("system-run")), i18n("&Open File"), this, &TransferPanel::runDcc); m_open->setStatusTip(i18n("Run the file")); m_openLocation = m_popup->addAction(QIcon::fromTheme(QStringLiteral("document-open-folder")), i18n("Open Location"), this, SLOT(openLocation())); m_openLocation->setStatusTip(i18n("Open the file location")); m_transferView->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_transferView, &TransferView::customContextMenuRequested, this, &TransferPanel::popupRequested); // misc. connect(m_transferView, &TransferView::doubleClicked, this, &TransferPanel::doubleClicked); connect(m_transferView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &TransferPanel::setDetailPanelItem); m_toolBar->addAction(m_accept); m_toolBar->addAction(m_abort); m_toolBar->addAction(m_clear); m_toolBar->addAction(m_clearCompleted); m_toolBar->addAction(m_open); m_toolBar->addAction(m_openLocation); KConfigGroup config(KSharedConfig::openConfig(), "DCC Settings"); QByteArray state; if (config.hasKey("PanelSplitter")) { state = config.readEntry("PanelSplitter", state); state = QByteArray::fromBase64(state); m_splitter->restoreState(state); } updateButton(); } void TransferPanel::slotNewTransferAdded(Transfer *transfer) { connect(transfer, &Transfer::statusChanged, this, &TransferPanel::slotTransferStatusChanged); m_transferView->addTransfer(transfer); if (m_transferView->itemCount() == 1) { m_transferView->selectAll(); m_detailPanel->setTransfer(transfer); updateButton(); } } void TransferPanel::slotTransferStatusChanged() { updateButton(); activateTabNotification(Konversation::tnfSystem); } void TransferPanel::updateButton() { bool accept = false, abort = false, clear = false, info = false, open = false, openLocation = false, resend = false, selectAll = false, selectAllCompleted = false; QItemSelectionModel *selectionModel = m_transferView->selectionModel(); foreach (const QModelIndex &index, m_transferView->rowIndexes()) { Transfer::Type type = (Transfer::Type)index.data(TransferListModel::TransferType).toInt(); Transfer::Status status = (Transfer::Status)index.data(TransferListModel::TransferStatus).toInt(); selectAll = true; selectAllCompleted |= (status >= Transfer::Done); if (selectionModel->isRowSelected(index.row(), QModelIndex())) { accept |= (status == Transfer::Queued); abort |= (status < Transfer::Done); clear |= (status >= Transfer::Done); info |= (type == Transfer::Send || status == Transfer::Done); open |= (type == Transfer::Send || status == Transfer::Done); openLocation = true; resend |= (type == Transfer::Send && status >= Transfer::Done); } } if (!KAuthorized::authorizeAction(QStringLiteral("allow_downloading"))) { accept = false; } m_selectAll->setEnabled(selectAll); m_selectAllCompleted->setEnabled(selectAllCompleted); m_accept->setEnabled(accept); m_abort->setEnabled(abort); m_clear->setEnabled(clear); m_clearCompleted->setEnabled(selectAllCompleted); m_open->setEnabled(open); m_openLocation->setEnabled(openLocation); m_resend->setEnabled(resend); } void TransferPanel::setDetailPanelItem (const QItemSelection &/*newindex*/, const QItemSelection &/*oldindex*/) { QModelIndex index; if (m_transferView->selectionModel()->selectedRows().contains(m_transferView->selectionModel()->currentIndex())) { index = m_transferView->selectionModel()->currentIndex(); } else if (!m_transferView->selectionModel()->selectedRows().isEmpty()) { index = m_transferView->selectionModel()->selectedRows().first(); } if (index.isValid()) { Transfer *transfer = qobject_cast(index.data(TransferListModel::TransferPointer).value()); if (transfer) { m_detailPanel->setTransfer(transfer); } } } void TransferPanel::acceptDcc() { foreach (const QModelIndex &index, m_transferView->selectedRows()) { if (index.data(TransferListModel::TransferType).toInt() == Transfer::Receive && index.data(TransferListModel::TransferStatus).toInt() == Transfer::Queued) { Transfer *transfer = qobject_cast(index.data(TransferListModel::TransferPointer).value()); if (transfer) { transfer->start(); } } } updateButton(); } void TransferPanel::abortDcc() { foreach (const QModelIndex &index, m_transferView->selectedRows()) { if (index.data(TransferListModel::TransferStatus).toInt() < Transfer::Done) { Transfer *transfer = qobject_cast(index.data(TransferListModel::TransferPointer).value()); if (transfer) { transfer->abort(); } } } updateButton(); } void TransferPanel::resendFile() { QList transferList; foreach (const QModelIndex &index, m_transferView->selectedRows()) { if (index.data(TransferListModel::TransferType).toInt() == Transfer::Send && index.data(TransferListModel::TransferStatus).toInt() >= Transfer::Done) { Transfer *transfer = qobject_cast(index.data(TransferListModel::TransferPointer).value()); if (!transfer) { continue; } transferList.append(transfer); } } foreach (Transfer* transfer, transferList) { TransferSend *newTransfer = Application::instance()->getDccTransferManager()->newUpload(); newTransfer->setConnectionId(transfer->getConnectionId()); newTransfer->setPartnerNick(transfer->getPartnerNick()); newTransfer->setFileURL(transfer->getFileURL()); newTransfer->setFileName(transfer->getFileName()); newTransfer->setReverse(transfer->isReverse()); if (newTransfer->queue()) { newTransfer->start(); } } } //sort QModelIndexList descending bool rowGreaterThan(const QModelIndex &index1, const QModelIndex &index2) { return index1.row() >= index2.row(); } void TransferPanel::clearDcc() { //selected item Transfer *transfer = m_detailPanel->transfer(); if (transfer && transfer->getStatus() >= Transfer::Done) { //item will be gone transfer = nullptr; } QModelIndexList indexes = m_transferView->selectedRows(); QModelIndexList indexesToRemove; foreach (const QModelIndex &index, indexes) { if (index.data(TransferListModel::TransferStatus).toInt() >= Transfer::Done) { indexesToRemove.append(index); } } //sort QModelIndexList descending //NOTE: selectedRows() returned an unsorted list std::sort(indexesToRemove.begin(), indexesToRemove.end(), rowGreaterThan); //remove from last to first item, to keep a valid row foreach (const QModelIndex &index, indexesToRemove) { m_transferView->model()->removeRow(index.row(), QModelIndex()); //needed, otherwise valid rows "can be treated" as invalid, //proxymodel does not keep up with changes m_transferView->updateModel(); } //remove all gone items foreach (const QModelIndex &index, indexesToRemove) { indexes.removeOne(index); } m_transferView->clearSelection(); QList toSelectList; //select everything that got not removed foreach (const QModelIndex &index, indexes) { int offset = 0; foreach (const QModelIndex &removedIndex, indexesToRemove) { if (removedIndex.row() < index.row()) { ++offset; } } toSelectList.append(index.row() - offset); } m_transferView->selectRows(toSelectList); if (transfer) { m_detailPanel->setTransfer(transfer); } - else if (!transfer || m_transferView->itemCount() == 0 || m_transferView->selectedIndexes().count() == 0) + else if (!transfer || m_transferView->itemCount() == 0 || m_transferView->selectedIndexes().isEmpty()) { m_detailPanel->clear(); } updateButton(); } void TransferPanel::clearCompletedDcc() { //save selected item Transfer *transfer = m_detailPanel->transfer(); if (transfer && transfer->getStatus() >= Transfer::Done) { //item will be gone transfer = nullptr; } QModelIndexList indexesToRemove; QModelIndexList selectedIndexes = m_transferView->selectedRows(); foreach (const QModelIndex &index, m_transferView->rowIndexes()) { if (index.data(TransferListModel::TransferStatus).toInt() >= Transfer::Done) { indexesToRemove.append(index); } } //sort QModelIndexList descending //NOTE: selectedRows() returned an unsorted list std::sort(indexesToRemove.begin(), indexesToRemove.end(), rowGreaterThan); //remove from last to first item, to keep a valid row foreach (const QModelIndex &index, indexesToRemove) { m_transferView->model()->removeRow(index.row(), QModelIndex()); //needed, otherwise valid rows "can be treated" as invalid, //proxymodel does not keep up with changes m_transferView->updateModel(); } //remove all gone items foreach (const QModelIndex &index, indexesToRemove) { selectedIndexes.removeOne(index); } m_transferView->clearSelection(); QList toSelectList; //select everything that got not removed foreach (const QModelIndex &index, selectedIndexes) { int offset = 0; foreach (const QModelIndex &removedIndex, indexesToRemove) { if (removedIndex.row() < index.row()) { ++offset; } } toSelectList.append(index.row() - offset); } m_transferView->selectRows(toSelectList); if (transfer) { m_detailPanel->setTransfer(transfer); } - else if (m_transferView->itemCount() == 0 || m_transferView->selectedIndexes().count() == 0 || !transfer) + else if (m_transferView->itemCount() == 0 || m_transferView->selectedIndexes().isEmpty() || !transfer) { m_detailPanel->clear(); } updateButton(); } void TransferPanel::runDcc() { const int selectedRows = m_transferView->selectedRows().count(); if (selectedRows > 3) { int ret = KMessageBox::questionYesNo(this, i18np("You have selected %1 file to execute, are you sure you want to continue?", "You have selected %1 files to execute, are you sure you want to continue?", selectedRows), i18np("Execute %1 file", "Execute %1 files", selectedRows) ); if (ret == KMessageBox::No) { return; } } foreach (const QModelIndex &index, m_transferView->selectedRows()) { if (index.data(TransferListModel::TransferType).toInt() == Transfer::Send || index.data(TransferListModel::TransferStatus).toInt() == Transfer::Done) { Transfer *transfer = qobject_cast(index.data(TransferListModel::TransferPointer).value()); if (transfer) { transfer->runFile(); } } } } void TransferPanel::openLocation() { foreach (const QModelIndex &index, m_transferView->selectedRows()) { Transfer *transfer = qobject_cast(index.data(TransferListModel::TransferPointer).value()); if (transfer) { openLocation(transfer); } } } void TransferPanel::selectAll() { m_transferView->selectAll(); updateButton(); } void TransferPanel::selectAllCompleted() { m_transferView->selectAllCompleted(); updateButton(); } void TransferPanel::popupRequested(const QPoint &pos) { updateButton(); m_popup->popup(QWidget::mapToGlobal(m_transferView->viewport()->mapTo(this, pos))); } void TransferPanel::doubleClicked(const QModelIndex &index) { Transfer *transfer = qobject_cast(index.data(TransferListModel::TransferPointer).value()); if (transfer) { transfer->runFile(); } } // virtual void TransferPanel::childAdjustFocus() { } TransferView *TransferPanel::getTransferView() { return m_transferView; } void TransferPanel::openLocation(Transfer *transfer) { QString urlString = transfer->getFileURL().toString(QUrl::PreferLocalFile|QUrl::RemoveFilename|QUrl::StripTrailingSlash); if (!urlString.isEmpty()) { QUrl url(QUrl::fromLocalFile(urlString)); new KRun(url, nullptr, true); } } } } diff --git a/src/dcc/whiteboard.cpp b/src/dcc/whiteboard.cpp index 776cb0f3..acb1aba5 100644 --- a/src/dcc/whiteboard.cpp +++ b/src/dcc/whiteboard.cpp @@ -1,582 +1,582 @@ /* 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. */ /* Copyright (C) 2009-2010 Bernd Buschinski */ #include "whiteboard.h" #include "whiteboardtoolbar.h" #include "whiteboardpaintarea.h" #include #include "qdebug.h" namespace Konversation { namespace DCC { WhiteBoard::WhiteBoard(QWidget* parent) : QWidget(parent) { QHBoxLayout *layout = new QHBoxLayout; m_toolbar = new WhiteBoardToolBar(this); layout->addWidget(m_toolbar); QScrollArea* area = new QScrollArea(this); m_paintArea = new WhiteBoardPaintArea(area); area->setWidgetResizable(true); area->setWidget(m_paintArea); layout->addWidget(area); setLayout(layout); connect(m_toolbar, &WhiteBoardToolBar::toolChanged, m_paintArea, &WhiteBoardPaintArea::setTool); connect(m_toolbar, &WhiteBoardToolBar::foregroundColorChanged, m_paintArea, &WhiteBoardPaintArea::setForegroundColor); connect(m_toolbar, &WhiteBoardToolBar::backgroundColorChanged, m_paintArea, &WhiteBoardPaintArea::setBackgroundColor); connect(m_toolbar, &WhiteBoardToolBar::colorsSwapped, m_paintArea, &WhiteBoardPaintArea::swapColors); connect(m_toolbar, &WhiteBoardToolBar::fontChanged, m_paintArea, &WhiteBoardPaintArea::setFont); connect(m_toolbar, &WhiteBoardToolBar::clear, this, &WhiteBoard::clear); connect(m_toolbar, &WhiteBoardToolBar::save, m_paintArea, &WhiteBoardPaintArea::save); connect(m_toolbar, &WhiteBoardToolBar::lineWidthChanged, m_paintArea, &WhiteBoardPaintArea::setPenWidth); connect(m_paintArea, &WhiteBoardPaintArea::drawedPencil, this, &WhiteBoard::drawedPencil); connect(m_paintArea, &WhiteBoardPaintArea::drawedLine, this, &WhiteBoard::drawedLine); connect(m_paintArea, &WhiteBoardPaintArea::drawedRectangle, this, &WhiteBoard::drawedRectangle); connect(m_paintArea, &WhiteBoardPaintArea::drawedFilledRectangle, this, &WhiteBoard::drawedFilledRectangle); connect(m_paintArea, &WhiteBoardPaintArea::drawedEllipse, this, &WhiteBoard::drawedEllipse); connect(m_paintArea, &WhiteBoardPaintArea::drawedFilledEllipse, this, &WhiteBoard::drawedFilledEllipse); connect(m_paintArea, &WhiteBoardPaintArea::drawedArrow, this, &WhiteBoard::drawedArrow); connect(m_paintArea, &WhiteBoardPaintArea::usedEraser, this, &WhiteBoard::usedEraser); connect(m_paintArea, &WhiteBoardPaintArea::usedFloodFill, this, &WhiteBoard::usedFloodFill); connect(m_paintArea, &WhiteBoardPaintArea::usedText, this, &WhiteBoard::usedText); connect(m_paintArea, &WhiteBoardPaintArea::usedTextExtended, this, &WhiteBoard::usedTextExtended); connect(m_paintArea, &WhiteBoardPaintArea::colorPicked, m_toolbar, &WhiteBoardToolBar::setForegroundColor); } WhiteBoard::~WhiteBoard() { } QStringList WhiteBoard::whiteboardCommands() { static const QStringList commands = WhiteBoardGlobals::wboardCommandHash().keys(); return commands; } void WhiteBoard::receivedWhiteBoardLine(const QString& line) { // qDebug() << line; if (line.isEmpty() || !line.contains(' ')) return; //fontname can have spaces, as well as text QStringList firstSplit; firstSplit.append(line.section(' ', 0 ,0)); firstSplit.append(line.section(' ', 1)); if (firstSplit.isEmpty()) return; const QString ctcpCommand(firstSplit.at(0)); // qDebug() << "ctcpCommand" << ctcpCommand; // qDebug() << "firstSize" << firstSplit.size(); if (ctcpCommand == QLatin1String("DR") && firstSplit.size() == 2) { // DR tooltype,width,pencolor,brushcolor,x1,y1,x2,y2 const QStringList drArgsList = firstSplit.at(1).split(',', QString::SkipEmptyParts); if (drArgsList.size() != 8) { qDebug() << "wrong size:" << drArgsList.size(); return; } bool ok = false; QString tmp = drArgsList.at(0); int toolType = tmp.toInt(&ok); if (!ok) { qDebug() << "unabled to parse tooltype:" << tmp; return; } tmp = drArgsList.at(1); int lineWidth = tmp.toInt(&ok); if (!ok) { qDebug() << "unabled to parse linewidth:" << tmp; return; } QColor penColor = parseColor(drArgsList.at(2), &ok); if (!ok) { qDebug() << "unabled to parse pencolor:" << drArgsList.at(2); return; } QColor brushColor = parseColor(drArgsList.at(3), &ok); if (!ok) { qDebug() << "unabled to parse brush:" << drArgsList.at(3); return; } tmp = drArgsList.at(4); int xFrom = tmp.toInt(&ok); if (!ok) { qDebug() << "unabled to parse xFrom:" << tmp; return; } tmp = drArgsList.at(5); int yFrom = tmp.toInt(&ok); if (!ok) { qDebug() << "unabled to parse yFrom:" << tmp; return; } tmp = drArgsList.at(6); int xTo = tmp.toInt(&ok); if (!ok) { qDebug() << "unabled to parse xTo:" << tmp; return; } tmp = drArgsList.at(7); int yTo = tmp.toInt(&ok); if (!ok) { qDebug() << "unabled to parse yTo:" << tmp; return; } // vIRC can spam us with invalid coords that vIRC itself just ignores if (xFrom < 0 && yFrom < 0 && xTo < 0 && yTo < 0) { return; } switch (toolType) { case WhiteBoardGlobals::Line: case WhiteBoardGlobals::Pencil: // qDebug() << "drawing a line" << lineWidth << xFrom << yFrom << xTo << yTo; m_paintArea->drawLine(lineWidth, penColor, brushColor, xFrom, yFrom, xTo, yTo); break; case WhiteBoardGlobals::Rectangle: // qDebug() << "drawing a rectangle" << xFrom << yFrom << xTo << yTo; m_paintArea->drawRectangle(lineWidth, penColor, xFrom, yFrom, xTo, yTo); break; case WhiteBoardGlobals::FilledRectangle: // qDebug() << "drawing a filledrectangle" << xFrom << yFrom << xTo << yTo; m_paintArea->drawFilledRectangle(lineWidth, penColor, brushColor, xFrom, yFrom, xTo, yTo); break; case WhiteBoardGlobals::Ellipse: // qDebug() << "drawing a rectangle" << xFrom << yFrom << xTo << yTo; m_paintArea->drawEllipse(lineWidth, penColor, xFrom, yFrom, xTo, yTo); break; case WhiteBoardGlobals::FilledEllipse: // qDebug() << "drawing a filledrectangle" << xFrom << yFrom << xTo << yTo; m_paintArea->drawFilledEllipse(lineWidth, penColor, brushColor, xFrom, yFrom, xTo, yTo); break; case WhiteBoardGlobals::Eraser: // qDebug() << "drawing a Eraser" << xFrom << yFrom << xTo << yTo; m_paintArea->useEraser(lineWidth, xFrom, yFrom, xTo, yTo); break; case WhiteBoardGlobals::FloodFill: // qDebug() << "drawing a FloodFill" << xFrom << yFrom; m_paintArea->useFloodFill(xFrom, yFrom, penColor); break; case WhiteBoardGlobals::Arrow: // qDebug() << "drawing an Arrow" << xFrom << yFrom; m_paintArea->drawArrow(lineWidth, penColor, xFrom, yFrom, xTo, yTo); break; } } else if (ctcpCommand == QLatin1String("TXT") && firstSplit.size() == 2) { QStringList txtArgsList = firstSplit.at(1).split(',', QString::KeepEmptyParts); if (txtArgsList.size() < 3) { qDebug() << "txt wrong size:" << txtArgsList.size(); return; } QString tmp = txtArgsList.at(0); bool ok; int x1 = tmp.toInt(&ok); if (!ok) { qDebug() << "txt unabled to parse x1:" << tmp; return; } tmp = txtArgsList.at(1); int y1 = tmp.toInt(&ok); if (!ok) { qDebug() << "txt unabled to parse y1:" << tmp; return; } txtArgsList.removeFirst(); txtArgsList.removeFirst(); - QString text(txtArgsList.join(QStringLiteral(","))); + QString text(txtArgsList.join(QLatin1Char(','))); m_paintArea->useText(x1,y1,text); } else if (ctcpCommand == QLatin1String("TXTEX") && firstSplit.size() == 2) { QStringList txtArgsList = firstSplit.at(1).split(',', QString::KeepEmptyParts); if (txtArgsList.size() < 8) { qDebug() << "txtex wrong size:" << txtArgsList.size(); return; } QString tmp = txtArgsList.at(0); bool ok; int x1 = tmp.toInt(&ok); if (!ok) { qDebug() << "txtex unabled to parse x1:" << tmp; return; } tmp = txtArgsList.at(1); int y1 = tmp.toInt(&ok); if (!ok) { qDebug() << "txtex unabled to parse y1:" << tmp; return; } QString fontName = txtArgsList.at(2); tmp = txtArgsList.at(3); int fontSize = tmp.toInt(&ok); if (!ok) { qDebug() << "txtex unabled to parse fontsize:" << tmp; return; } tmp = txtArgsList.at(4); int fontStyle = tmp.toInt(&ok); if (!ok) { qDebug() << "txtex unabled to parse fontstyle:" << tmp; return; } QColor penColor = parseColor(txtArgsList.at(5), &ok); if (!ok) { qDebug() << "txtex unabled to parse pencolor:" << txtArgsList.at(5); return; } QColor brushColor = parseColor(txtArgsList.at(6), &ok); if (!ok) { qDebug() << "txtex unabled to parse brush:" << txtArgsList.at(6); return; } QFont tFont(fontName, fontSize); if (fontStyle & WhiteBoardGlobals::Underline) tFont.setUnderline(true); if (fontStyle & WhiteBoardGlobals::Strikeout) tFont.setStrikeOut(true); if (fontStyle & WhiteBoardGlobals::Italic) tFont.setItalic(true); if (fontStyle & WhiteBoardGlobals::Bold) tFont.setBold(true); txtArgsList.removeFirst(); // x1 txtArgsList.removeFirst(); // y1 txtArgsList.removeFirst(); // fontname txtArgsList.removeFirst(); // fontsize txtArgsList.removeFirst(); // fontstyle txtArgsList.removeFirst(); // textcolor txtArgsList.removeFirst(); // bgcolor - QString text(txtArgsList.join(QStringLiteral(","))); + QString text(txtArgsList.join(QLatin1Char(','))); // qDebug() << "TXTEX" << text << fontSize << fontName; m_paintArea->useTextExtended(x1,y1,tFont,penColor,brushColor,text); } else if (ctcpCommand == QLatin1String("CLS") && firstSplit.size() == 1) { //CLS m_paintArea->clear(); } else if (ctcpCommand == QLatin1String("CAN") && firstSplit.size() == 2) { //TODO implement me const QString& can = firstSplit.at(1).toLower(); if (can == QLatin1String("use-wb2")) { //no we currently can't, I lied emitDo(QStringLiteral("use-wb2")); return; } else if (can == QLatin1String("txtex")) { emitDo(QStringLiteral("TXTEX")); m_toolbar->setSupportedTextType(WhiteBoardToolBar::ExtentedText); return; } qDebug() << "unhandled CAN" << firstSplit.at(1); } else if (ctcpCommand == QLatin1String("CANT") && firstSplit.size() == 2) { //TODO implement me const QString& cannot = firstSplit.at(1).toLower(); if (cannot == QLatin1String("txtex")) { m_toolbar->setSupportedTextType(WhiteBoardToolBar::SimpleText); return; } qDebug() << "unhandled CANT" << firstSplit.at(1); } else if (ctcpCommand == QLatin1String("DO") && firstSplit.size() == 2) { //TODO implement me const QString& doString = firstSplit.at(1).toLower(); if (doString == QLatin1String("txtex")) { m_toolbar->setSupportedTextType(WhiteBoardToolBar::ExtentedText); return; } } else if (ctcpCommand == QLatin1String("BLT") && firstSplit.size() == 2) { // BLT x1src,y1src,x2src,y2src,xdest,ydest const QStringList drArgsList = firstSplit.at(1).split(',', QString::SkipEmptyParts); if (drArgsList.size() != 6) { qDebug() << "blt wrong size:" << drArgsList.size(); return; } bool ok = false; bool finalOk = true; int x1src = drArgsList.at(0).toInt(&ok); finalOk &= ok; int y1src = drArgsList.at(1).toInt(&ok); finalOk &= ok; int x2src = drArgsList.at(2).toInt(&ok); finalOk &= ok; int y2src = drArgsList.at(3).toInt(&ok); finalOk &= ok; int xdest = drArgsList.at(4).toInt(&ok); finalOk &= ok; int ydest = drArgsList.at(5).toInt(&ok); finalOk &= ok; if (!finalOk) { qDebug() << "blt unabled to parse coords:" << firstSplit.at(1); return; } if (x2src <= x1src || y2src <= y1src) { qDebug() << "blt coords invalid:" << firstSplit.at(1); return; } m_paintArea->useBlt(x1src, y1src, x2src, y2src, xdest, ydest); } } void WhiteBoard::clear() { qDebug(); m_paintArea->clear(); static const QString cls = QString("\x01""CLS\x01"); emit rawWhiteBoardCommand(cls); } void WhiteBoard::drawedPencil(int lineWidth, const QColor& penColor, const QColor& brushColor, int xFrom, int yFrom, int xTo, int yTo) { emitDRCommand(WhiteBoardGlobals::Pencil, lineWidth, penColor, brushColor, xFrom, yFrom, xTo, yTo); } void WhiteBoard::drawedLine(int lineWidth, const QColor& penColor, const QColor& brushColor, int xFrom, int yFrom, int xTo, int yTo) { emitDRCommand(WhiteBoardGlobals::Line, lineWidth, penColor, brushColor, xFrom, yFrom, xTo, yTo); } void WhiteBoard::drawedRectangle(int lineWidth, const QColor& penColor, int xFrom, int yFrom, int xTo, int yTo) { emitDRCommand(WhiteBoardGlobals::Rectangle, lineWidth, penColor, QColor(0,0,0), xFrom, yFrom, xTo, yTo); } void WhiteBoard::drawedFilledRectangle(int lineWidth, const QColor& penColor, const QColor& brushColor, int xFrom, int yFrom, int xTo, int yTo) { emitDRCommand(WhiteBoardGlobals::FilledRectangle, lineWidth, penColor, brushColor, xFrom, yFrom, xTo, yTo); } void WhiteBoard::drawedEllipse(int lineWidth, const QColor& penColor, int xFrom, int yFrom, int xTo, int yTo) { emitDRCommand(WhiteBoardGlobals::Ellipse, lineWidth, penColor, QColor(0,0,0), xFrom, yFrom, xTo, yTo); } void WhiteBoard::drawedFilledEllipse(int lineWidth, const QColor& penColor, const QColor& brushColor, int xFrom, int yFrom, int xTo, int yTo) { emitDRCommand(WhiteBoardGlobals::FilledEllipse, lineWidth, penColor, brushColor, xFrom, yFrom, xTo, yTo); } void WhiteBoard::drawedArrow(int lineWidth, const QColor& penColor, int xFrom, int yFrom, int xTo, int yTo) { emitDRCommand(WhiteBoardGlobals::Arrow, lineWidth, penColor, QColor(0,0,0), xFrom, yFrom, xTo, yTo); } void WhiteBoard::usedEraser(int lineWidth, int xFrom, int yFrom, int xTo, int yTo) { emitDRCommand(WhiteBoardGlobals::Eraser, lineWidth, QColor(0,0,0), QColor(0,0,0), xFrom, yFrom, xTo, yTo); } void WhiteBoard::usedFloodFill(int x, int y, const QColor& color) { emitDRCommand(WhiteBoardGlobals::FloodFill, 0, color, QColor(0,0,0), x, y, 0, 0); } void WhiteBoard::usedText(int x, int y, const QString& text) { //TXT x,y,text static const QString txtLineCommand("\x01""TXT %1,%2,%3\x01"); emit rawWhiteBoardCommand(txtLineCommand.arg(x).arg(y).arg(text)); } void WhiteBoard::usedTextExtended(int x, int y, const QFont& font, const QColor& textColor, const QColor& background, const QString& text) { //TXTEX x,y,fontname,ptsize,style,textcolor,bgcolor,text static const QString txtexLineCommand("\x01""TXTEX %1,%2,%3,%4,%5,%6,%7,%8\x01"); QString fontname = font.family(); QString fontSize = QString::number(font.pointSize()); QString fontStyle = QString::number(fontToStyle(font)); emit rawWhiteBoardCommand(txtexLineCommand.arg(x).arg(y).arg( fontname).arg(fontSize).arg(fontStyle).arg(colorToString(textColor)).arg( colorToString(background)).arg(text)); } QColor WhiteBoard::parseColor(const QString& colorString, bool* ok) { bool tOk = false; int colorInt = colorString.toInt(&tOk); if (!tOk || colorInt > 0xFFFFFF) { if (ok) { *ok = false; } return QColor(); } int r = colorInt & 0x0000FF; int g = (colorInt & 0x00FF00) >> 8; int b = (colorInt & 0xFF0000) >> 16; if (ok) { *ok = true; } return QColor(r,g,b); } QString WhiteBoard::colorToString(const QColor& color) { return QString::number((color.blue()<<16) | (color.green()<<8) | (color.red())); } int WhiteBoard::fontToStyle(const QFont& font) { int style = 0; if (font.underline()) style |= WhiteBoardGlobals::Underline; if (font.bold()) style |= WhiteBoardGlobals::Bold; if (font.strikeOut()) style |= WhiteBoardGlobals::Strikeout; if (font.italic()) style |= WhiteBoardGlobals::Italic; return style; } void WhiteBoard::connected() { emitCan(QStringLiteral("use-wb2")); emitCan(QStringLiteral("TXTEX")); } void WhiteBoard::emitDRCommand(WhiteBoardGlobals::WhiteBoardTool tool, int lineWidth, const QColor& penColor, const QColor& brushColor, int xFrom, int yFrom, int xTo, int yTo) { if (xFrom < 0 && yFrom < 0 && xTo < 0 && yTo < 0) { return; } QString drLineCommand("\x01""DR %2,%3,%4,%5,%6,%7,%8,%9\x01"); drLineCommand = drLineCommand.arg(tool).arg( lineWidth).arg(colorToString(penColor)).arg(colorToString(brushColor)).arg( xFrom).arg(yFrom).arg(xTo).arg(yTo); // qDebug() << drLineCommand; emit rawWhiteBoardCommand(drLineCommand); } void WhiteBoard::emitCan(const QString& canString) { static const QString can = QString("\x01""CAN %1\x01"); emit rawWhiteBoardCommand(can.arg(canString)); } void WhiteBoard::emitCant(const QString& cantString) { static const QString cant = QString("\x01""CANT %1\x01"); emit rawWhiteBoardCommand(cant.arg(cantString)); } void WhiteBoard::emitDo(const QString& doString) { static const QString doCommand = QString("\x01""DO %1\x01"); emit rawWhiteBoardCommand(doCommand.arg(doString)); } void WhiteBoard::emitDont(const QString& doNotString) { static const QString doNot = QString("\x01""DONT %1\x01"); //krazy:exclude=spelling emit rawWhiteBoardCommand(doNot.arg(doNotString)); } } } diff --git a/src/identitydialog.cpp b/src/identitydialog.cpp index 1b2f7420..bbe33d70 100644 --- a/src/identitydialog.cpp +++ b/src/identitydialog.cpp @@ -1,428 +1,428 @@ /* 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. */ /* copyright: (C) 2004, 2009 by Peter Simonsson email: peter.simonsson@gmail.com Copyright (C) 2012 Eike Hein */ #include "identitydialog.h" #include "application.h" #include "awaymanager.h" #include "irccharsets.h" #include #include #include #include #include #include #include #include #include #include namespace Konversation { IdentityDialog::IdentityDialog(QWidget *parent) : QDialog(parent) { setWindowTitle( i18n("Identities") ); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &IdentityDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &IdentityDialog::reject); okButton->setDefault(true); // Initialize the dialog widget QWidget* w = new QWidget(this); setupUi(w); mainLayout->addWidget(w); mainLayout->addWidget(buttonBox); QGroupBox* nickGroupBox = new QGroupBox(i18n("Nickname")); verticalLayout->insertWidget(1, nickGroupBox); QVBoxLayout* nickGroupBoxLayout = new QVBoxLayout(nickGroupBox); nickGroupBoxLayout->setContentsMargins(0, 0, 0, 0); m_nicknameLBox = new KEditListWidget(nickGroupBox); nickGroupBoxLayout->addWidget(m_nicknameLBox); m_nicknameLBox->setWhatsThis(i18n("This is your list of nicknames. A nickname is the name that other users will " "know you by. You may use any name you desire. The first character must be a letter.\n\n" "Since nicknames must be unique across an entire IRC network, your desired name may be " "rejected by the server because someone else is already using that nickname. Enter " "alternate nicknames for yourself. If your first choice is rejected by the server, " "Konversation will try the alternate nicknames.")); newBtn->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); connect(newBtn, &QPushButton::clicked, this, &IdentityDialog::newIdentity); copyBtn->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy"))); connect(copyBtn, &QPushButton::clicked, this, &IdentityDialog::copyIdentity); m_editBtn->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); connect(m_editBtn, &QPushButton::clicked, this, &IdentityDialog::renameIdentity); m_delBtn->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); connect(m_delBtn, &QPushButton::clicked, this, &IdentityDialog::deleteIdentity); foreach(const IdentityPtr &id, Preferences::identityList()) { m_identityCBox->addItem(id->getName()); m_identityList.append( IdentityPtr( id ) ); } m_additionalAuthInfo = new KMessageWidget(generalWidget); m_additionalAuthInfo->setWordWrap(true); m_additionalAuthInfo->setCloseButtonVisible(false); m_additionalAuthInfo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); connect(m_authTypeCombo, static_cast(&KComboBox::currentIndexChanged), this, &IdentityDialog::authTypeChanged); m_authTypeCombo->addItem(i18n("Standard NickServ"), QStringLiteral("nickserv")); m_authTypeCombo->addItem(i18n("Server Password"), QStringLiteral("serverpw")); m_authTypeCombo->addItem(i18n("SASL PLAIN"), QStringLiteral("saslplain")); m_authTypeCombo->addItem(i18nc("Cert = Certificate", "SASL EXTERNAL (Cert)"), QStringLiteral("saslexternal")); m_authTypeCombo->addItem(i18n("SSL Client Certificate"), QStringLiteral("pemclientcert")); // add encodings to combo box m_codecCBox->addItems(Konversation::IRCCharsets::self()->availableEncodingDescriptiveNames()); // set the suffix for the inactivity time spinbox m_awayInactivitySpin->setSuffix(ki18np(" minute", " minutes")); // set values for the widgets updateIdentity(0); // Set up signals / slots for identity page //connect(m_identityCBox, SIGNAL(activated(int)), this, SLOT(updateIdentity(int))); okButton->setToolTip(i18n("Change identity information")); buttonBox->button(QDialogButtonBox::Cancel)->setToolTip(i18n("Discards all changes made")); AwayManager* awayManager = Application::instance()->getAwayManager(); connect(m_identityCBox, static_cast(&KComboBox::currentIndexChanged), this, &IdentityDialog::updateIdentity); connect(this, &IdentityDialog::identitiesChanged, awayManager, &AwayManager::identitiesChanged); } void IdentityDialog::updateIdentity(int index) { if (m_currentIdentity && !checkCurrentIdentity()) { return; } refreshCurrentIdentity(); m_currentIdentity = m_identityList[index]; m_realNameEdit->setText(m_currentIdentity->getRealName()); m_nicknameLBox->clear(); m_nicknameLBox->insertStringList(m_currentIdentity->getNicknameList()); m_authTypeCombo->setCurrentIndex(m_authTypeCombo->findData(m_currentIdentity->getAuthType())); m_authPasswordEdit->setText(m_currentIdentity->getAuthPassword()); m_nickservNicknameEdit->setText(m_currentIdentity->getNickservNickname()); m_nickservCommandEdit->setText(m_currentIdentity->getNickservCommand()); m_saslAccountEdit->setText(m_currentIdentity->getSaslAccount()); m_pemClientCertFile->setUrl(m_currentIdentity->getPemClientCertFile()); m_insertRememberLineOnAwayChBox->setChecked(m_currentIdentity->getInsertRememberLineOnAway()); m_awayMessageEdit->setText(m_currentIdentity->getAwayMessage()); m_awayNickEdit->setText(m_currentIdentity->getAwayNickname()); awayCommandsGroup->setChecked(m_currentIdentity->getRunAwayCommands()); m_awayEdit->setText(m_currentIdentity->getAwayCommand()); m_unAwayEdit->setText(m_currentIdentity->getReturnCommand()); automaticAwayGroup->setChecked(m_currentIdentity->getAutomaticAway()); m_awayInactivitySpin->setValue(m_currentIdentity->getAwayInactivity()); m_automaticUnawayChBox->setChecked(m_currentIdentity->getAutomaticUnaway()); m_sCommandEdit->setText(m_currentIdentity->getShellCommand()); m_codecCBox->setCurrentIndex(Konversation::IRCCharsets::self()->shortNameToIndex(m_currentIdentity->getCodecName())); m_loginEdit->setText(m_currentIdentity->getIdent()); m_quitEdit->setText(m_currentIdentity->getQuitReason()); m_partEdit->setText(m_currentIdentity->getPartReason()); m_kickEdit->setText(m_currentIdentity->getKickReason()); if(index == 0) { m_editBtn->setEnabled(false); m_delBtn->setEnabled(false); } else { m_editBtn->setEnabled(true); m_delBtn->setEnabled(true); } } void IdentityDialog::refreshCurrentIdentity() { if(!m_currentIdentity) { return; } m_currentIdentity->setRealName(m_realNameEdit->text()); const QStringList nicks = m_nicknameLBox->items(); m_currentIdentity->setNicknameList(nicks); m_currentIdentity->setAuthType(m_authTypeCombo->itemData(m_authTypeCombo->currentIndex()).toString()); m_currentIdentity->setAuthPassword(m_authPasswordEdit->text()); m_currentIdentity->setNickservNickname(m_nickservNicknameEdit->text()); m_currentIdentity->setNickservCommand(m_nickservCommandEdit->text()); m_currentIdentity->setSaslAccount(m_saslAccountEdit->text()); m_currentIdentity->setPemClientCertFile(m_pemClientCertFile->url()); m_currentIdentity->setInsertRememberLineOnAway(m_insertRememberLineOnAwayChBox->isChecked()); m_currentIdentity->setAwayMessage(m_awayMessageEdit->text()); m_currentIdentity->setAwayNickname(m_awayNickEdit->text()); m_currentIdentity->setRunAwayCommands(awayCommandsGroup->isChecked()); m_currentIdentity->setAwayCommand(m_awayEdit->text()); m_currentIdentity->setReturnCommand(m_unAwayEdit->text()); m_currentIdentity->setAutomaticAway(automaticAwayGroup->isChecked()); m_currentIdentity->setAwayInactivity(m_awayInactivitySpin->value()); m_currentIdentity->setAutomaticUnaway(m_automaticUnawayChBox->isChecked()); m_currentIdentity->setShellCommand(m_sCommandEdit->text()); if(m_codecCBox->currentIndex() >= 0 && m_codecCBox->currentIndex() < Konversation::IRCCharsets::self()->availableEncodingShortNames().count()) m_currentIdentity->setCodecName(Konversation::IRCCharsets::self()->availableEncodingShortNames()[m_codecCBox->currentIndex()]); m_currentIdentity->setIdent(m_loginEdit->text()); m_currentIdentity->setQuitReason(m_quitEdit->text()); m_currentIdentity->setPartReason(m_partEdit->text()); m_currentIdentity->setKickReason(m_kickEdit->text()); } void IdentityDialog::accept() { if (!checkCurrentIdentity()) { return; } refreshCurrentIdentity(); Preferences::setIdentityList(m_identityList); Application::instance()->saveOptions(true); emit identitiesChanged(); QDialog::accept(); } void IdentityDialog::newIdentity() { bool ok = false; QString txt = QInputDialog::getText(this, i18n("Add Identity"), i18n("Identity name:"), QLineEdit::Normal, QString(), &ok); if(ok && !txt.isEmpty()) { KUser user(KUser::UseRealUserID); IdentityPtr identity=IdentityPtr(new Identity); identity->setName(txt); identity->setIdent(user.loginName()); m_identityList.append(identity); m_identityCBox->addItem(txt); m_identityCBox->setCurrentIndex(m_identityCBox->count() - 1); } else if(ok && txt.isEmpty()) { KMessageBox::error(this, i18n("You need to give the identity a name.")); newIdentity(); } } void IdentityDialog::renameIdentity() { bool ok = false; QString currentTxt = m_identityCBox->currentText(); QString txt = QInputDialog::getText(this, i18n("Rename Identity"), i18n("Identity name:"), QLineEdit::Normal, currentTxt, &ok); if(ok && !txt.isEmpty()) { m_currentIdentity->setName(txt); m_identityCBox->setItemText(m_identityCBox->currentIndex(), txt); } else if(ok && txt.isEmpty()) { KMessageBox::error(this, i18n("You need to give the identity a name.")); renameIdentity(); } } void IdentityDialog::deleteIdentity() { int current = m_identityCBox->currentIndex(); if(current <= 0) { return; } ServerGroupHash serverGroups = Preferences::serverGroupHash(); QHashIterator it(serverGroups); bool found = false; while (it.hasNext() && !found) if (it.next().value()->identityId() == m_currentIdentity->id()) found = true; QString warningTxt; if(found) { warningTxt = i18n("This identity is in use, if you remove it the network settings using it will" " fall back to the default identity. Should it be deleted anyway?"); } else { warningTxt = i18n("Are you sure you want to delete all information for this identity?"); } if(KMessageBox::warningContinueCancel(this, warningTxt, i18n("Delete Identity"), KStandardGuiItem::del()) == KMessageBox::Continue) { m_identityList.removeOne(m_currentIdentity); m_currentIdentity = nullptr; m_identityCBox->removeItem(current); } } void IdentityDialog::copyIdentity() { bool ok = false; QString currentTxt = m_identityCBox->currentText(); QString txt = QInputDialog::getText(this, i18n("Duplicate Identity"), i18n("Identity name:"), QLineEdit::Normal, currentTxt, &ok); if(ok && !txt.isEmpty()) { IdentityPtr identity(new Identity); identity->copy(*m_currentIdentity); identity->setName(txt); m_identityList.append(identity); m_identityCBox->addItem(txt); m_identityCBox->setCurrentIndex(m_identityCBox->count() - 1); } else if(ok && txt.isEmpty()) { KMessageBox::error(this, i18n("You need to give the identity a name.")); renameIdentity(); } } void IdentityDialog::setCurrentIdentity(int index) { if (index >= m_identityCBox->count()) index = 0; m_identityCBox->setCurrentIndex(index); } IdentityPtr IdentityDialog::setCurrentIdentity(const IdentityPtr &identity) { int index = Preferences::identityList().indexOf(identity); setCurrentIdentity(index); return m_currentIdentity; } IdentityPtr IdentityDialog::currentIdentity() const { return m_currentIdentity; } bool IdentityDialog::checkCurrentIdentity() { if(m_nicknameLBox->count() == 0) { KMessageBox::error(this, i18n("You must add at least one nick to the identity.")); bool block = m_identityCBox->blockSignals(true); m_identityCBox->setCurrentIndex(m_identityCBox->findText(m_currentIdentity->getName())); m_identityCBox->blockSignals(block); tabWidget->setCurrentIndex(0); m_nicknameLBox->lineEdit()->setFocus(); return false; } if(m_realNameEdit->text().isEmpty()) { KMessageBox::error(this, i18n("Please enter a real name.")); bool block = m_identityCBox->blockSignals(true); m_identityCBox->setCurrentIndex(m_identityCBox->findText(m_currentIdentity->getName())); m_identityCBox->blockSignals(block); tabWidget->setCurrentIndex(0); m_realNameEdit->setFocus(); return false; } return true; } void IdentityDialog::authTypeChanged(int index) { QString authType = m_authTypeCombo->itemData(index).toString(); - bool isNickServ = (authType == QStringLiteral("nickserv")); - bool isSaslPlain = (authType == QStringLiteral("saslplain")); - bool isSaslExernal = (authType == QStringLiteral("saslexternal")); - bool isServerPw = (authType == QStringLiteral("serverpw")); - bool isPemClientCert = (isSaslExernal || (authType == QStringLiteral("pemclientcert"))); + bool isNickServ = (authType == QLatin1String("nickserv")); + bool isSaslPlain = (authType == QLatin1String("saslplain")); + bool isSaslExernal = (authType == QLatin1String("saslexternal")); + bool isServerPw = (authType == QLatin1String("serverpw")); + bool isPemClientCert = (isSaslExernal || (authType == QLatin1String("pemclientcert"))); nickservNicknameLabel->setVisible(isNickServ); m_nickservNicknameEdit->setVisible(isNickServ); nickservCommandLabel->setVisible(isNickServ); m_nickservCommandEdit->setVisible(isNickServ); saslAccountLabel->setVisible(isSaslPlain || isSaslExernal); m_saslAccountEdit->setVisible(isSaslPlain || isSaslExernal); authPasswordLabel->setVisible(!isPemClientCert); m_authPasswordEdit->setVisible(!isPemClientCert); pemClientCertFileLabel->setVisible(isPemClientCert); m_pemClientCertFile->setVisible(isPemClientCert); m_additionalAuthInfo->setVisible(isServerPw || isPemClientCert); // Clear. m_saslAccountEdit->setPlaceholderText(QString()); for (int i = 0; i < autoIdentifyLayout->count(); ++i) autoIdentifyLayout->removeItem(autoIdentifyLayout->itemAt(0)); autoIdentifyLayout->addRow(authTypeLabel, m_authTypeCombo); if (isNickServ) { autoIdentifyLayout->addRow(nickservNicknameLabel, m_nickservNicknameEdit); autoIdentifyLayout->addRow(nickservCommandLabel, m_nickservCommandEdit); autoIdentifyLayout->addRow(authPasswordLabel, m_authPasswordEdit); } else if (isServerPw) { autoIdentifyLayout->addRow(authPasswordLabel, m_authPasswordEdit); m_additionalAuthInfo->setText(i18n("The password entered here will override the one set in the server settings, if any.")); autoIdentifyLayout->addRow(nullptr, m_additionalAuthInfo); } else if (isSaslPlain) { autoIdentifyLayout->addRow(saslAccountLabel, m_saslAccountEdit); autoIdentifyLayout->addRow(authPasswordLabel, m_authPasswordEdit); } else if (isSaslExernal) { autoIdentifyLayout->addRow(saslAccountLabel, m_saslAccountEdit); m_saslAccountEdit->setPlaceholderText(i18nc("Shown in unfilled line edit", "(optional)")); } if (isPemClientCert) { autoIdentifyLayout->addRow(pemClientCertFileLabel, m_pemClientCertFile); m_additionalAuthInfo->setText(i18n("Certificate-based authentication forces SSL to be enabled for a connection, overriding any server settings.")); autoIdentifyLayout->addRow(nullptr, m_additionalAuthInfo); } } } diff --git a/src/irc/channel.cpp b/src/irc/channel.cpp index f593236c..058d1d7c 100644 --- a/src/irc/channel.cpp +++ b/src/irc/channel.cpp @@ -1,2906 +1,2906 @@ /* 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. */ /* Copyright (C) 2002 Dario Abatianni Copyright (C) 2004-2016 Peter Simonsson Copyright (C) 2006-2008 Eike Hein */ #include "channel.h" #include "channeloptionsdialog.h" #include "application.h" #include "server.h" #include "nick.h" #include "nicklistview.h" #include "quickbutton.h" #include "modebutton.h" #include "ircinput.h" #include "ircviewbox.h" #include "ircview.h" #include "awaylabel.h" #include "topiclabel.h" #include "topichistorymodel.h" #include "notificationhandler.h" #include "viewcontainer.h" #include #include #include #include #include #include #include #include #include #include #include #define DELAYED_SORT_TRIGGER 10 using namespace Konversation; bool nickTimestampLessThan(const Nick* nick1, const Nick* nick2) { int returnValue = nick2->getChannelNick()->timeStamp() - nick1->getChannelNick()->timeStamp(); if( returnValue == 0) { returnValue = QString::compare(nick1->getChannelNick()->loweredNickname(), nick2->getChannelNick()->loweredNickname()); } return (returnValue < 0); } bool nickLessThan(const Nick* nick1, const Nick* nick2) { return nick1->getChannelNick()->loweredNickname() < nick2->getChannelNick()->loweredNickname(); } using Konversation::ChannelOptionsDialog; Channel::Channel(QWidget* parent, const QString& _name) : ChatWindow(parent) { // init variables //HACK I needed the channel name at time of setServer, but setName needs m_server.. // This effectively assigns the name twice, but none of the other logic has been moved or updated. name=_name; m_ownChannelNick = nullptr; m_optionsDialog = nullptr; m_delayedSortTimer = nullptr; m_delayedSortTrigger = 0; m_processedNicksCount = 0; m_processedOpsCount = 0; m_initialNamesReceived = false; nicks = 0; ops = 0; completionPosition = 0; m_nicknameListViewTextChanged = 0; m_joined = false; quickButtonsChanged = false; quickButtonsState = false; modeButtonsChanged = false; modeButtonsState = false; awayChanged = false; awayState = false; splittersInitialized = false; topicSplitterHidden = false; channelSplitterHidden = false; setType(ChatWindow::Channel); m_isTopLevelView = false; setChannelEncodingSupported(true); // Build some size policies for the widgets QSizePolicy hfixed = QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); QSizePolicy hmodest = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); QSizePolicy vfixed = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); QSizePolicy greedy = QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); m_vertSplitter = new QSplitter(Qt::Vertical, this); QWidget* topicWidget = new QWidget(m_vertSplitter); m_vertSplitter->setStretchFactor(m_vertSplitter->indexOf(topicWidget), 0); QGridLayout* topicLayout = new QGridLayout(topicWidget); topicLayout->setContentsMargins(0, 0, 0, 0); topicLayout->setSpacing(0); m_topicButton = new QToolButton(topicWidget); m_topicButton->setIcon(QIcon::fromTheme(QStringLiteral("document-edit"))); m_topicButton->setToolTip(i18n("Edit Channel Settings")); m_topicButton->setAutoRaise(true); connect(m_topicButton, &QAbstractButton::clicked, this, &Channel::showOptionsDialog); topicLine = new Konversation::TopicLabel(topicWidget); topicLine->setContextMenuOptions(IrcContextMenus::ShowChannelActions | IrcContextMenus::ShowLogAction, true); topicLine->setChannelName(getName()); topicLine->setWordWrap(true); topicLine->setWhatsThis(i18n("

Every channel on IRC has a topic associated with it. This is simply a message that everybody can see.

If you are an operator, or the channel mode 'T' has not been set, then you can change the topic by clicking the Edit Channel Properties button to the left of the topic. You can also view the history of topics there.

")); connect(topicLine, &TopicLabel::setStatusBarTempText, this, &ChatWindow::setStatusBarTempText); connect(topicLine, &TopicLabel::clearStatusBarTempText, this, &ChatWindow::clearStatusBarTempText); m_topicHistory = new TopicHistoryModel(this); connect(m_topicHistory, SIGNAL(currentTopicChanged(QString)), topicLine, SLOT(setText(QString))); topicLayout->addWidget(m_topicButton, 0, 0); topicLayout->addWidget(topicLine, 0, 1, -1, 1); // The box holding the channel modes modeBox = new QFrame(topicWidget); QHBoxLayout* modeBoxLayout = new QHBoxLayout(modeBox); modeBoxLayout->setContentsMargins(0, 0, 0, 0); modeBox->hide(); modeBox->setSizePolicy(hfixed); modeT = new ModeButton(QStringLiteral("T"),modeBox,0); modeBoxLayout->addWidget(modeT); modeN = new ModeButton(QStringLiteral("N"),modeBox,1); modeBoxLayout->addWidget(modeN); modeS = new ModeButton(QStringLiteral("S"),modeBox,2); modeBoxLayout->addWidget(modeS); modeI = new ModeButton(QStringLiteral("I"),modeBox,3); modeBoxLayout->addWidget(modeI); modeP = new ModeButton(QStringLiteral("P"),modeBox,4); modeBoxLayout->addWidget(modeP); modeM = new ModeButton(QStringLiteral("M"),modeBox,5); modeBoxLayout->addWidget(modeM); modeK = new ModeButton(QStringLiteral("K"),modeBox,6); modeBoxLayout->addWidget(modeK); modeL = new ModeButton(QStringLiteral("L"),modeBox,7); modeBoxLayout->addWidget(modeL); modeT->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('T')); modeN->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('N')); modeS->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('S')); modeI->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('I')); modeP->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('P')); modeM->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('M')); modeK->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('K')); modeL->setWhatsThis(ChannelOptionsDialog::whatsThisForMode('L')); connect(modeT, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); connect(modeN, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); connect(modeS, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); connect(modeI, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); connect(modeP, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); connect(modeM, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); connect(modeK, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); connect(modeL, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); connect(modeT, &ModeButton::modeClicked, this, &Channel::modeButtonClicked); limit=new KLineEdit(modeBox); modeBoxLayout->addWidget(limit); limit->setToolTip(i18n("Maximum users allowed in channel")); limit->setWhatsThis(i18n("

This is the channel user limit - the maximum number of users that can be in the channel at a time. If you are an operator, you can set this. The channel mode Topic (button to left) will automatically be set if set this.

")); connect(limit,SIGNAL (returnPressed()),this,SLOT (channelLimitChanged()) ); connect(limit,&QLineEdit::editingFinished, this, &Channel::channelLimitChanged ); topicLayout->addWidget(modeBox, 0, 2); topicLayout->setRowStretch(1, 10); topicLayout->setColumnStretch(1, 10); showTopic(Preferences::self()->showTopic()); showModeButtons(Preferences::self()->showModeButtons()); // (this) The main Box, holding the channel view/topic and the input line m_horizSplitter = new QSplitter(m_vertSplitter); m_vertSplitter->setStretchFactor(m_vertSplitter->indexOf(m_horizSplitter), 1); // Server will be set later in setServer() IRCViewBox* ircViewBox = new IRCViewBox(m_horizSplitter); m_horizSplitter->setStretchFactor(m_horizSplitter->indexOf(ircViewBox), 1); setTextView(ircViewBox->ircView()); ircViewBox->ircView()->setContextMenuOptions(IrcContextMenus::ShowChannelActions, true); // The box that holds the Nick List and the quick action buttons nickListButtons = new QFrame(m_horizSplitter); m_horizSplitter->setStretchFactor(m_horizSplitter->indexOf(nickListButtons), 0); QVBoxLayout* nickListButtonsLayout = new QVBoxLayout(nickListButtons); nickListButtonsLayout->setSpacing(0); nickListButtonsLayout->setContentsMargins(0, 0, 0, 0); nicknameListView=new NickListView(nickListButtons, this); nickListButtons->layout()->addWidget(nicknameListView); nicknameListView->installEventFilter(this); // initialize buttons grid, will be set up in updateQuickButtons m_buttonsGrid = nullptr; // The box holding the Nickname button and Channel input commandLineBox = new QFrame(this); QHBoxLayout* commandLineLayout = new QHBoxLayout(commandLineBox); commandLineBox->setLayout(commandLineLayout); commandLineLayout->setContentsMargins(0, 0, 0, 0); commandLineLayout->setSpacing(spacing()); nicknameCombobox = new KComboBox(commandLineBox); nicknameCombobox->setEditable(true); nicknameCombobox->setSizeAdjustPolicy(KComboBox::AdjustToContents); KLineEdit* nicknameComboboxLineEdit = qobject_cast(nicknameCombobox->lineEdit()); if (nicknameComboboxLineEdit) nicknameComboboxLineEdit->setClearButtonEnabled(false); nicknameCombobox->setWhatsThis(i18n("

This shows your current nick, and any alternatives you have set up. If you select or type in a different nickname, then a request will be sent to the IRC server to change your nick. If the server allows it, the new nickname will be selected. If you type in a new nickname, you need to press 'Enter' at the end.

You can edit the alternative nicknames from the Identities option in the Settings menu.

")); awayLabel = new AwayLabel(commandLineBox); awayLabel->hide(); cipherLabel = new QLabel(commandLineBox); cipherLabel->hide(); cipherLabel->setPixmap(KIconLoader::global()->loadIcon(QStringLiteral("document-encrypt"), KIconLoader::Toolbar)); m_inputBar = new IRCInput(commandLineBox); commandLineLayout->addWidget(nicknameCombobox); commandLineLayout->addWidget(awayLabel); commandLineLayout->addWidget(cipherLabel); commandLineLayout->addWidget(m_inputBar); getTextView()->installEventFilter(m_inputBar); topicLine->installEventFilter(m_inputBar); m_inputBar->installEventFilter(this); // Set the widgets size policies m_topicButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); topicLine->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum)); commandLineBox->setSizePolicy(vfixed); limit->setMaximumSize(40,100); limit->setSizePolicy(hfixed); modeT->setMaximumSize(20,100); modeT->setSizePolicy(hfixed); modeN->setMaximumSize(20,100); modeN->setSizePolicy(hfixed); modeS->setMaximumSize(20,100); modeS->setSizePolicy(hfixed); modeI->setMaximumSize(20,100); modeI->setSizePolicy(hfixed); modeP->setMaximumSize(20,100); modeP->setSizePolicy(hfixed); modeM->setMaximumSize(20,100); modeM->setSizePolicy(hfixed); modeK->setMaximumSize(20,100); modeK->setSizePolicy(hfixed); modeL->setMaximumSize(20,100); modeL->setSizePolicy(hfixed); getTextView()->setSizePolicy(greedy); nicknameListView->setSizePolicy(hmodest); connect(m_inputBar,&IRCInput::submit,this,&Channel::channelTextEntered ); connect(m_inputBar,&IRCInput::envelopeCommand,this,&Channel::channelPassthroughCommand ); connect(m_inputBar,&IRCInput::nickCompletion,this,&Channel::completeNick ); connect(m_inputBar,&IRCInput::endCompletion,this,&Channel::endCompleteNick ); connect(m_inputBar,&IRCInput::textPasted,this,&Channel::textPasted ); connect(getTextView(), SIGNAL(textPasted(bool)), m_inputBar, SLOT(paste(bool))); connect(getTextView(),SIGNAL (gotFocus()),m_inputBar,SLOT (setFocus()) ); connect(getTextView(),&IRCView::sendFile,this,&Channel::sendFileMenu ); connect(getTextView(),SIGNAL (autoText(QString)),this,SLOT (sendText(QString)) ); connect(nicknameListView,&QTreeWidget::itemDoubleClicked,this,&Channel::doubleClickCommand ); connect(nicknameCombobox,SIGNAL (activated(int)),this,SLOT(nicknameComboboxChanged())); if(nicknameCombobox->lineEdit()) connect(nicknameCombobox->lineEdit(), &QLineEdit::returnPressed,this,&Channel::nicknameComboboxChanged); connect(&userhostTimer,&QTimer::timeout,this,&Channel::autoUserhost); m_whoTimer.setSingleShot(true); connect(&m_whoTimer, &QTimer::timeout, this, &Channel::autoWho); connect(Application::instance(), &Application::appearanceChanged, this, &Channel::updateAutoWho); // every 5 minutes decrease everyone's activity by 1 unit m_fadeActivityTimer.start(5*60*1000); connect(&m_fadeActivityTimer, &QTimer::timeout, this, &Channel::fadeActivity); updateAppearance(); #ifdef HAVE_QCA2 m_cipher = nullptr; #endif // Setup delayed sort timer m_delayedSortTimer = new QTimer(this); m_delayedSortTimer->setSingleShot(true); connect(m_delayedSortTimer, &QTimer::timeout, this, &Channel::delayedSortNickList); } //FIXME there is some logic in setLogfileName that needs to be split out and called here if the server display name gets changed void Channel::setServer(Server* server) { if (m_server != server) { connect(server, &Server::connectionStateChanged, this, &Channel::connectionStateChanged); connect(server, SIGNAL(nickInfoChanged()), this, SLOT(updateNickInfos())); connect(server, &Server::channelNickChanged, this, &Channel::updateChannelNicks); } ChatWindow::setServer(server); if (!server->getKeyForRecipient(getName()).isEmpty()) cipherLabel->show(); topicLine->setServer(server); refreshModeButtons(); nicknameCombobox->setModel(m_server->nickListModel()); connect(awayLabel, &AwayLabel::unaway, m_server, &Server::requestUnaway); connect(awayLabel, &AwayLabel::awayMessageChanged, m_server, &Server::requestAway); } void Channel::connectionStateChanged(Server* server, Konversation::ConnectionState state) { if (server == m_server) { if (state != Konversation::SSConnected) { m_joined = false; ViewContainer* viewContainer = Application::instance()->getMainWindow()->getViewContainer(); //HACK the way the notification priorities work sucks, this forces the tab text color to gray right now. if (viewContainer->getFrontView() == this || m_currentTabNotify == Konversation::tnfNone || (!Preferences::self()->tabNotificationsEvents() && m_currentTabNotify == Konversation::tnfControl)) { viewContainer->unsetViewNotification(this); } } } } void Channel::setEncryptedOutput(bool e) { #ifdef HAVE_QCA2 if (e) { cipherLabel->show(); if (!getCipher()->setKey(m_server->getKeyForRecipient(getName()))) return; m_topicHistory->setCipher(getCipher()); topicLine->setText(m_topicHistory->currentTopic()); } else { cipherLabel->hide(); m_topicHistory->clearCipher(); topicLine->setText(m_topicHistory->currentTopic()); } #else Q_UNUSED(e) #endif } Channel::~Channel() { qDebug() << "(" << getName() << ")"; // Purge nickname list purgeNicks(); qDebug() << "Nicks purged."; // Unlink this channel from channel list m_server->removeChannel(this); qDebug() << "Channel removed."; if (m_recreationScheduled) { QMetaObject::invokeMethod(m_server, "sendJoinCommand", Qt::QueuedConnection, Q_ARG(QString, getName()), Q_ARG(QString, getPassword())); } } bool Channel::rejoinable() { if (getServer() && getServer()->isConnected()) return !m_joined; return false; } void Channel::rejoin() { if (rejoinable()) m_server->sendJoinCommand(getName(), getPassword()); } bool Channel::log() { return ChatWindow::log() && !Preferences::self()->privateOnly(); } ChannelNickPtr Channel::getOwnChannelNick() const { return m_ownChannelNick; } ChannelNickPtr Channel::getChannelNick(const QString &ircnick) const { return m_server->getChannelNick(getName(), ircnick); } void Channel::purgeNicks() { m_ownChannelNick = nullptr; // Purge nickname list qDeleteAll(nicknameList); nicknameList.clear(); m_nicknameNickHash.clear(); // Execute this otherwise it may crash trying to access // deleted nicks nicknameListView->executeDelayedItemsLayout(); // clear stats counter nicks=0; ops=0; } void Channel::showOptionsDialog() { if (!m_optionsDialog) m_optionsDialog = new Konversation::ChannelOptionsDialog(this); m_optionsDialog->show(); } void Channel::textPasted(const QString& text) { if(m_server) { QStringList multiline = text.split(QLatin1Char('\n'), QString::SkipEmptyParts); for(int index=0;indexcommandChar()); // make sure that lines starting with command char get escaped if(line.startsWith(cChar)) line=cChar+line; sendText(line); } } } // Will be connected to NickListView::doubleClicked() void Channel::doubleClickCommand(QTreeWidgetItem *item, int column) { Q_UNUSED(column) if(item) { nicknameListView->clearSelection(); item->setSelected(true); // TODO: put the quick button code in another function to make reusal more legitimate quickButtonClicked(Preferences::self()->channelDoubleClickAction()); } } void Channel::completeNick() { int pos, oldPos; QTextCursor cursor = m_inputBar->textCursor(); pos = cursor.position(); oldPos = m_inputBar->getOldCursorPosition(); QString line=m_inputBar->toPlainText(); QString newLine; // Check if completion position is out of range if(completionPosition >= nicknameList.count()) completionPosition = 0; // Check, which completion mode is active char mode = m_inputBar->getCompletionMode(); if(mode == 'c') { line.remove(oldPos, pos - oldPos); pos = oldPos; } // If the cursor is at beginning of line, insert last completion if the nick is still around if(pos == 0 && !m_inputBar->lastCompletion().isEmpty() && nicknameList.containsNick(m_inputBar->lastCompletion())) { QString addStart(Preferences::self()->nickCompleteSuffixStart()); newLine = m_inputBar->lastCompletion() + addStart; // New cursor position is behind nickname pos = newLine.length(); // Add rest of the line newLine += line; } else { // remember old cursor position in input field m_inputBar->setOldCursorPosition(pos); // remember old cursor position locally oldPos = pos; // step back to []{}-_^`\| or start of line QString regexpStr(QStringLiteral("[^A-Z0-9a-z\\_\\[\\]\\{\\}\\-\\^\\`\\\\\\|")); if(!Preferences::self()->prefixCharacter().isEmpty()) regexpStr += "\\" + Preferences::self()->prefixCharacter(); regexpStr += QLatin1Char(']'); QRegExp tmp(regexpStr); pos = tmp.lastIndexIn(line, pos - 1); if (pos < 0) pos = 0; else pos++; // copy search pattern (lowercase) QString pattern = line.mid(pos, oldPos - pos); // copy line to newLine-buffer newLine = line; // did we find any pattern? if(!pattern.isEmpty()) { bool complete = false; QString foundNick; // try to find matching nickname in list of names if(Preferences::self()->nickCompletionMode() == 1 || Preferences::self()->nickCompletionMode() == 2) { // Shell like completion QStringList found; foundNick = nicknameList.completeNick(pattern, complete, found, (Preferences::self()->nickCompletionMode() == 2), Preferences::self()->nickCompletionCaseSensitive()); if(!complete && !found.isEmpty()) { if(Preferences::self()->nickCompletionMode() == 1) { - QString nicksFound = found.join(QStringLiteral(" ")); + QString nicksFound = found.join(QLatin1Char(' ')); appendServerMessage(i18n("Completion"), i18n("Possible completions: %1.", nicksFound)); } else { m_inputBar->showCompletionList(found); } } } // Cycle completion else if(Preferences::self()->nickCompletionMode() == 0 && !nicknameList.isEmpty()) { if(mode == '\0') { uint timeStamp = 0; int listPosition = 0; foreach (Nick* nick, nicknameList) { if(nick->getChannelNick()->getNickname().startsWith(pattern, Preferences::self()->nickCompletionCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive) && (nick->getChannelNick()->timeStamp() > timeStamp)) { timeStamp = nick->getChannelNick()->timeStamp(); completionPosition = listPosition; } ++listPosition; } } // remember old nick completion position int oldCompletionPosition = completionPosition; complete = true; QString prefixCharacter = Preferences::self()->prefixCharacter(); do { QString lookNick = nicknameList.at(completionPosition)->getChannelNick()->getNickname(); if(!prefixCharacter.isEmpty() && lookNick.contains(prefixCharacter)) { lookNick = lookNick.section( prefixCharacter,1 ); } if(lookNick.startsWith(pattern, Preferences::self()->nickCompletionCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive)) { foundNick = lookNick; } // increment search position completionPosition++; // wrap around if(completionPosition == nicknameList.count()) { completionPosition = 0; } // the search ends when we either find a suitable nick or we end up at the // first search position } while((completionPosition != oldCompletionPosition) && foundNick.isEmpty()); } // did we find a suitable nick? if(!foundNick.isEmpty()) { // set channel nicks completion mode m_inputBar->setCompletionMode('c'); // remove pattern from line newLine.remove(pos, pattern.length()); // did we find the nick in the middle of the line? if(pos && complete) { m_inputBar->setLastCompletion(foundNick); QString addMiddle = Preferences::self()->nickCompleteSuffixMiddle(); newLine.insert(pos, foundNick + addMiddle); pos = pos + foundNick.length() + addMiddle.length(); } // no, it was at the beginning else if(complete) { m_inputBar->setLastCompletion(foundNick); QString addStart = Preferences::self()->nickCompleteSuffixStart(); newLine.insert(pos, foundNick + addStart); pos = pos + foundNick.length() + addStart.length(); } // the nick wasn't complete else { newLine.insert(pos, foundNick); pos = pos + foundNick.length(); } } // no pattern found, so restore old cursor position else pos = oldPos; } } // Set new text and cursor position m_inputBar->setText(newLine); cursor.setPosition(pos); m_inputBar->setTextCursor(cursor); } // make sure to step back one position when completion ends so the user starts // with the last complete they made void Channel::endCompleteNick() { if(completionPosition) completionPosition--; else completionPosition=nicknameList.count()-1; } void Channel::setName(const QString& newName) { ChatWindow::setName(newName); setLogfileName(newName.toLower()); } bool Channel::autoJoin() { if (!m_server->getServerGroup()) return false; Konversation::ChannelList channelList = m_server->getServerGroup()->channelList(); return channelList.contains(channelSettings()); } void Channel::setAutoJoin(bool autojoin) { if (autojoin && !(autoJoin())) { Konversation::ChannelSettings before; QList channelList = m_server->getChannelList(); if (channelList.count() > 1) { QMap channelMap; int index = -1; int ownIndex = m_server->getViewContainer()->getViewIndex(this); foreach (Channel* channel, channelList) { index = m_server->getViewContainer()->getViewIndex(channel); if (index && index > ownIndex) channelMap.insert(index, channel); } if (channelMap.count()) { QMap::Iterator it2; Channel* channel; for (it2 = channelMap.begin(); it2 != channelMap.end(); ++it2) { channel = it2.value(); if (channel->autoJoin()) { before = channel->channelSettings(); break; } } } } if (m_server->getServerGroup()) m_server->getServerGroup()->addChannel(channelSettings(), before); } else { if (m_server->getServerGroup()) m_server->getServerGroup()->removeChannel(channelSettings()); } } QString Channel::getPassword() { QString password; for (QStringList::const_iterator it = m_modeList.constBegin(); it != m_modeList.constEnd(); ++it) { if ((*it)[0] == QLatin1Char('k')) password = (*it).mid(1); } if (password.isEmpty() && m_server->getServerGroup()) { Konversation::ChannelList channelSettingsList = m_server->getServerGroup()->channelList(); Konversation::ChannelSettings channelSettings(getName()); int index = channelSettingsList.indexOf(channelSettings); if(index >= 0) password = channelSettingsList.at(index).password(); } return password; } const Konversation::ChannelSettings Channel::channelSettings() { Konversation::ChannelSettings channel; channel.setName(getName()); channel.setPassword(getPassword()); channel.setNotificationsEnabled(notificationsEnabled()); return channel; } void Channel::sendFileMenu() { emit sendFile(); } void Channel::channelTextEntered() { QString line = m_inputBar->toPlainText(); m_inputBar->clear(); if (!line.isEmpty()) sendText(sterilizeUnicode(line)); } void Channel::channelPassthroughCommand() { QString commandChar = Preferences::self()->commandChar(); QString line = m_inputBar->toPlainText(); m_inputBar->clear(); if(!line.isEmpty()) { // Prepend commandChar on Ctrl+Enter to bypass outputfilter command recognition if (line.startsWith(commandChar)) { line = commandChar + line; } sendText(sterilizeUnicode(line)); } } void Channel::sendText(const QString& sendLine) { // create a work copy QString outputAll(sendLine); // replace aliases and wildcards OutputFilter::replaceAliases(outputAll, this); // Send all strings, one after another QStringList outList = outputAll.split(QRegExp(QStringLiteral("[\r\n]+")), QString::SkipEmptyParts); for(int index=0;indexgetOutputFilter()->parse(m_server->getNickname(), output, getName(), this); // Is there something we need to display for ourselves? if(!result.output.isEmpty()) { if(result.type == Konversation::Action) appendAction(m_server->getNickname(), result.output); else if(result.type == Konversation::Command) appendCommandMessage(result.typeString, result.output); else if(result.type == Konversation::Program) appendServerMessage(result.typeString, result.output); else if(result.type == Konversation::PrivateMessage) msgHelper(result.typeString, result.output); else append(m_server->getNickname(), result.output); } else if (result.outputList.count()) { if (result.type == Konversation::Message) { QStringListIterator it(result.outputList); while (it.hasNext()) append(m_server->getNickname(), it.next()); } else if (result.type == Konversation::Action) { for (int i = 0; i < result.outputList.count(); ++i) { if (i == 0) appendAction(m_server->getNickname(), result.outputList.at(i)); else append(m_server->getNickname(), result.outputList.at(i)); } } } // Send anything else to the server if (!result.toServerList.empty()) m_server->queueList(result.toServerList); else m_server->queue(result.toServer); } } void Channel::setNickname(const QString& newNickname) { nicknameCombobox->setCurrentIndex(nicknameCombobox->findText(newNickname)); } QStringList Channel::getSelectedNickList() { QStringList selectedNicks; foreach (Nick* nick, nicknameList) { if (nick->isSelected()) selectedNicks << nick->getChannelNick()->getNickname(); } return selectedNicks; } void Channel::channelLimitChanged() { unsigned int lim=limit->text().toUInt(); modeButtonClicked(7,lim>0); } void Channel::modeButtonClicked(int id, bool on) { char mode[]={'t','n','s','i','p','m','k','l'}; QString command(QStringLiteral("MODE %1 %2%3 %4")); QString args = getPassword(); if (mode[id] == 'k') { if (args.isEmpty()) { QPointer dlg = new KPasswordDialog(this); dlg->setPrompt(i18n("Channel Password")); if (dlg->exec() && !dlg->password().isEmpty()) { args = dlg->password(); } delete dlg; } } else if(mode[id]=='l') { if(limit->text().isEmpty() && on) { bool ok=false; // ask user how many nicks should be the limit args=QInputDialog::getText(this, i18n("Channel User Limit"), i18n("Enter the new user limit for the channel:"), QLineEdit::Normal, limit->text(), // will be always "" but what the hell ;) &ok); // leave this function if user cancels if(!ok) return; } else if(on) args=limit->text(); } // put together the mode command and send it to the server queue m_server->queue(command.arg(getName()).arg((on) ? QStringLiteral("+") : QStringLiteral("-")).arg(mode[id]).arg(args)); } void Channel::quickButtonClicked(const QString &buttonText) { // parse wildcards (toParse,nickname,channelName,nickList,queryName,parameter) QString out=m_server->parseWildcards(buttonText,m_server->getNickname(),getName(),getPassword(),getSelectedNickList(), m_inputBar->toPlainText()); // are there any newlines in the definition? if (out.contains(QLatin1Char('\n'))) sendText(out); // single line without newline needs to be copied into input line else m_inputBar->setText(out, true); } void Channel::addNickname(ChannelNickPtr channelnick) { QString nickname = channelnick->loweredNickname(); Nick* nick=nullptr; foreach (Nick* lookNick, nicknameList) { if(lookNick->getChannelNick()->loweredNickname() == nickname) { nick = lookNick; break; } } if (nick == nullptr) { fastAddNickname(channelnick); if(channelnick->isAnyTypeOfOp()) { adjustOps(1); } adjustNicks(1); requestNickListSort(); } // TODO (re)-investigate why it was thought unusual to add an already added nick // -- see bug 333969 } // Use with caution! Does not check for duplicates or may not // sort if delayed sorting is in effect. void Channel::fastAddNickname(ChannelNickPtr channelnick, Nick *nick) { Q_ASSERT(channelnick); if(!channelnick) return; if (!nick || !nick->treeWidget()) { // Deal with nicknameListView now (creating nick if necessary) NickListView::NoSorting noSorting(nicknameListView); int index = nicknameListView->topLevelItemCount(); // Append nick to the lists if (nick) { nicknameListView->addTopLevelItem(nick); } else { nick = new Nick(nicknameListView, this, channelnick); m_nicknameListViewTextChanged |= 0xFF; // new nick, text changed. } if (!m_delayedSortTimer->isActive()) { // Find its right place and insert where it belongs int newindex = nicknameListView->findLowerBound(*nick); if (newindex != index) { if (newindex >= index) newindex--; nicknameListView->takeTopLevelItem(index); nicknameListView->insertTopLevelItem(newindex, nick); } } // Otherwise it will be sorted by delayed sort. } // Now deal with nicknameList if (m_delayedSortTimer->isActive()) { // nicks get sorted later nicknameList.append(nick); } else { NickList::iterator it = qLowerBound(nicknameList.begin(), nicknameList.end(), nick, nickLessThan); nicknameList.insert(it, nick); } m_nicknameNickHash.insert (channelnick->loweredNickname(), nick); } /* Determines whether Nick/Part/Join event should be shown or skipped based on user settings. */ bool Channel::shouldShowEvent(ChannelNickPtr channelNick) { if (Preferences::self()->hideUnimportantEvents()) { if (channelNick && Preferences::self()->hideUnimportantEventsExcludeActive()) { uint activityThreshold = 3600; if (Preferences::self()->hideUnimportantEventsExcludeActiveThreshold() == 0) // last 10 minutes activityThreshold = 600; if (Preferences::self()->hideUnimportantEventsExcludeActiveThreshold() == 1) // last hour activityThreshold = 3600; else if (Preferences::self()->hideUnimportantEventsExcludeActiveThreshold() == 2) // last day activityThreshold = 86400; else if (Preferences::self()->hideUnimportantEventsExcludeActiveThreshold() == 3) // last week activityThreshold = 604800; if (m_server->isWatchedNick(channelNick->getNickname())) return true; // nick is on our watched list, so we probably want to see the event else if (channelNick->timeStamp()+activityThreshold > QDateTime::currentDateTime().toTime_t()) return true; // the nick has spoken within activity threshold else return false; } else return false; // if hideUnimportantEventsExcludeActive is off, we hide all events } else return true; // if hideUnimportantEvents is off we don't care and just show the event } void Channel::nickRenamed(const QString &oldNick, const NickInfo& nickInfo, const QHash &messageTags) { QString newNick = nickInfo.getNickname(); Nick *nick = getNickByName(oldNick); bool displayCommandMessage; if (Preferences::self()->hideUnimportantEventsExcludeActive() && m_server->isWatchedNick(oldNick)) displayCommandMessage = true; // this is for displaying watched people NICK events both ways (watched->unwatched and unwatched->watched) else if (nick) displayCommandMessage = shouldShowEvent(nick->getChannelNick()); else displayCommandMessage = shouldShowEvent(ChannelNickPtr()); // passing null pointer /* Did we change our nick name? */ if(newNick == m_server->getNickname()) /* Check newNick because m_server->getNickname() is already updated to new nick */ { setNickname(newNick); if (displayCommandMessage) appendCommandMessage(i18n("Nick"),i18n("You are now known as %1.", newNick), messageTags, true, true); } else if (displayCommandMessage) { /* No, must've been someone else */ appendCommandMessage(i18n("Nick"),i18n("%1 is now known as %2.", oldNick, newNick), messageTags); } if (nick) { m_nicknameNickHash.remove(oldNick.toLower()); m_nicknameNickHash.insert(newNick.toLower(), nick); repositionNick(nick); } } void Channel::joinNickname(ChannelNickPtr channelNick, const QHash &messageTags) { bool displayCommandMessage = shouldShowEvent(channelNick); if(channelNick->getNickname() == m_server->getNickname()) { m_joined = true; emit joined(this); if (displayCommandMessage) appendCommandMessage(i18nc("Message type", "Join"), i18nc("%1 = our hostmask, %2 = channel", "You (%1) have joined the channel %2.", channelNick->getHostmask(), getName()), messageTags, false, true); // Prepare for impending NAMES. purgeNicks(); nicknameListView->setUpdatesEnabled(false); m_ownChannelNick = channelNick; refreshModeButtons(); setActive(true); ViewContainer* viewContainer = Application::instance()->getMainWindow()->getViewContainer(); //HACK the way the notification priorities work sucks, this forces the tab text color to ungray right now. if (viewContainer->getFrontView() == this || m_currentTabNotify == Konversation::tnfNone || (!Preferences::self()->tabNotificationsEvents() && m_currentTabNotify == Konversation::tnfControl)) { Application::instance()->getMainWindow()->getViewContainer()->unsetViewNotification(this); } Application::instance()->notificationHandler()->channelJoin(this,getName()); } else { QString nick = channelNick->getNickname(); QString hostname = channelNick->getHostmask(); if (displayCommandMessage) appendCommandMessage(i18nc("Message type", "Join"), i18nc("%1 is the nick joining and %2 the hostmask of that nick", "%1 (%2) has joined this channel.", nick, hostname), messageTags, false); addNickname(channelNick); } } void Channel::removeNick(ChannelNickPtr channelNick, const QString &reason, bool quit, const QHash &messageTags) { bool displayCommandMessage = shouldShowEvent(channelNick); QString displayReason = reason; if(!displayReason.isEmpty()) { // if the reason contains text markup characters, play it safe and reset all if (hasIRCMarkups(displayReason)) displayReason += QStringLiteral("\017"); } if(channelNick->getNickname() == m_server->getNickname()) { if (displayCommandMessage) { //If in the future we can leave a channel, but not close the window, refreshModeButtons() has to be called. if (quit) { if (displayReason.isEmpty()) appendCommandMessage(i18nc("Message type", "Quit"), i18n("You (%1) have left this server.", channelNick->getHostmask()), messageTags); else appendCommandMessage(i18nc("Message type", "Quit"), i18nc("%1 = our hostmask, %2 = reason", "You (%1) have left this server (%2).", channelNick->getHostmask(), displayReason), messageTags, false); } else { if (displayReason.isEmpty()) appendCommandMessage(i18nc("Message type", "Part"), i18n("You have left channel %1.", getName()), messageTags); else appendCommandMessage(i18nc("Message type", "Part"), i18nc("%1 = our hostmask, %2 = channel, %3 = reason", "You (%1) have left channel %2 (%3).", channelNick->getHostmask(), getName(), displayReason), messageTags, false); } } delete this; } else { if (displayCommandMessage) { if (quit) { if (displayReason.isEmpty()) appendCommandMessage(i18nc("Message type", "Quit"), i18n("%1 (%2) has left this server.", channelNick->getNickname(), channelNick->getHostmask()), messageTags, false); else appendCommandMessage(i18nc("Message type", "Quit"), i18nc("%1 = nick, %2 = hostname, %3 = reason", "%1 (%2) has left this server (%3).", channelNick->getNickname(), channelNick->getHostmask(), displayReason), messageTags, false); } else { if (displayReason.isEmpty()) appendCommandMessage(i18nc("Message type", "Part"), i18n("%1 (%2) has left this channel.", channelNick->getNickname(), channelNick->getHostmask()), messageTags, false); else appendCommandMessage(i18nc("Message type", "Part"), i18nc("%1 = nick, %2 = hostmask, %3 = reason", "%1 (%2) has left this channel (%3).", channelNick->getNickname(), channelNick->getHostmask(), displayReason), messageTags, false); } } if(channelNick->isAnyTypeOfOp()) { adjustOps(-1); } adjustNicks(-1); Nick* nick = getNickByName(channelNick->loweredNickname()); if(nick) { nicknameList.removeOne(nick); m_nicknameNickHash.remove(channelNick->loweredNickname()); delete nick; // Execute this otherwise it may crash trying to access deleted nick nicknameListView->executeDelayedItemsLayout(); } else { qWarning() << "Nickname " << channelNick->getNickname() << " not found!"<< endl; } } } void Channel::flushNickQueue() { processQueuedNicks(true); } void Channel::kickNick(ChannelNickPtr channelNick, const QString &kicker, const QString &reason, const QHash &messageTags) { QString displayReason = reason; if(!displayReason.isEmpty()) { // if the reason contains text markup characters, play it safe and reset all if (hasIRCMarkups(displayReason)) displayReason += QStringLiteral("\017"); } if(channelNick->getNickname() == m_server->getNickname()) { if(kicker == m_server->getNickname()) { if (displayReason.isEmpty()) appendCommandMessage(i18n("Kick"), i18n("You have kicked yourself from channel %1.", getName()), messageTags); else appendCommandMessage(i18n("Kick"), i18nc("%1 adds the channel and %2 the reason", "You have kicked yourself from channel %1 (%2).", getName(), displayReason), messageTags); } else { if (displayReason.isEmpty()) { appendCommandMessage(i18n("Kick"), i18nc("%1 adds the channel, %2 adds the kicker", "You have been kicked from channel %1 by %2.", getName(), kicker), messageTags); } else { appendCommandMessage(i18n("Kick"), i18nc("%1 adds the channel, %2 the kicker and %3 the reason", "You have been kicked from channel %1 by %2 (%3).", getName(), kicker, displayReason), messageTags); } Application::instance()->notificationHandler()->kick(this,getName(), kicker); } m_joined=false; setActive(false); //HACK the way the notification priorities work sucks, this forces the tab text color to gray right now. if (m_currentTabNotify == Konversation::tnfNone || (!Preferences::self()->tabNotificationsEvents() && m_currentTabNotify == Konversation::tnfControl)) Application::instance()->getMainWindow()->getViewContainer()->unsetViewNotification(this); return; } else { if(kicker == m_server->getNickname()) { if (displayReason.isEmpty()) appendCommandMessage(i18n("Kick"), i18n("You have kicked %1 from the channel.", channelNick->getNickname()), messageTags); else appendCommandMessage(i18n("Kick"), i18nc("%1 adds the kicked nick and %2 the reason", "You have kicked %1 from the channel (%2).", channelNick->getNickname(), displayReason), messageTags); } else { if (displayReason.isEmpty()) { appendCommandMessage(i18n("Kick"), i18nc("%1 adds the kicked nick, %2 adds the kicker", "%1 has been kicked from the channel by %2.", channelNick->getNickname(), kicker), messageTags); } else { appendCommandMessage(i18n("Kick"), i18nc("%1 adds the kicked nick, %2 the kicker and %3 the reason", "%1 has been kicked from the channel by %2 (%3).", channelNick->getNickname(), kicker, displayReason), messageTags); } } if(channelNick->isAnyTypeOfOp()) adjustOps(-1); adjustNicks(-1); Nick* nick = getNickByName(channelNick->loweredNickname()); if(nick == nullptr) { qWarning() << "Nickname " << channelNick->getNickname() << " not found!"<< endl; } else { nicknameList.removeOne(nick); m_nicknameNickHash.remove(channelNick->loweredNickname()); delete nick; } } } Nick* Channel::getNickByName(const QString &lookname) const { QString lcLookname(lookname.toLower()); return m_nicknameNickHash.value(lcLookname); } void Channel::adjustNicks(int value) { if((nicks == 0) && (value <= 0)) { return; } nicks += value; if(nicks < 0) { nicks = 0; } emitUpdateInfo(); } void Channel::adjustOps(int value) { if((ops == 0) && (value <= 0)) { return; } ops += value; if(ops < 0) { ops = 0; } emitUpdateInfo(); } void Channel::emitUpdateInfo() { QString info = getName() + QStringLiteral(" - "); info += i18np("%1 nick", "%1 nicks", numberOfNicks()); info += i18np(" (%1 op)", " (%1 ops)", numberOfOps()); emit updateInfo(info); } QString Channel::getTopic() { return m_topicHistory->currentTopic(); } void Channel::setTopic(const QString& text, const QHash &messageTags) { QString cleanTopic = text; // If the reason contains text markup characters, play it safe and reset all. if (!cleanTopic.isEmpty() && hasIRCMarkups(cleanTopic)) cleanTopic += QStringLiteral("\017"); appendCommandMessage(i18n("Topic"), i18n("The channel topic is \"%1\".", cleanTopic), messageTags); m_topicHistory->appendTopic(replaceIRCMarkups(Konversation::removeIrcMarkup(text))); } void Channel::setTopic(const QString& nickname, const QString& text, const QHash &messageTags) { QString cleanTopic = text; // If the reason contains text markup characters, play it safe and reset all. if (!cleanTopic.isEmpty() && hasIRCMarkups(cleanTopic)) cleanTopic += QStringLiteral("\017"); if (nickname == m_server->getNickname()) appendCommandMessage(i18n("Topic"), i18n("You set the channel topic to \"%1\".", cleanTopic), messageTags); else appendCommandMessage(i18n("Topic"), i18n("%1 sets the channel topic to \"%2\".", nickname, cleanTopic), messageTags); m_topicHistory->appendTopic(replaceIRCMarkups(Konversation::removeIrcMarkup(text)), nickname); } void Channel::setTopicAuthor(const QString& author, QDateTime time) { if (time.isNull() || !time.isValid()) time = QDateTime::currentDateTime(); m_topicHistory->setCurrentTopicMetadata(author, time); } void Channel::updateMode(const QString& sourceNick, char mode, bool plus, const QString ¶meter, const QHash &messageTags) { // Note for future expansion: // m_server->getChannelNick(getName(), sourceNick); // may not return a valid channelNickPtr if the mode is updated by // the server. // --johnflux, 9 September 2004 // Note: nick repositioning in the nicknameListView should be // triggered by nickinfo / channelnick signals QString message; ChannelNickPtr parameterChannelNick = m_server->getChannelNick(getName(), parameter); bool fromMe = false; bool toMe = false; // HACK right now Server only keeps type A modes bool banTypeThang = m_server->banAddressListModes().contains(QLatin1Char(mode)); // remember if this nick had any type of op. bool wasAnyOp = false; if (parameterChannelNick) { addNickname(parameterChannelNick); wasAnyOp = parameterChannelNick->isAnyTypeOfOp(); } if (sourceNick.toLower() == m_server->loweredNickname()) fromMe = true; if (parameter.toLower() == m_server->loweredNickname()) toMe = true; switch (mode) { case 'q': if (banTypeThang) { if (plus) { if (fromMe) message = i18n("You set a quiet on %1.", parameter); else message = i18n("%1 sets a quiet on %2.", sourceNick, parameter); } else { if (fromMe) message = i18n("You remove the quiet on %1.", parameter); else message = i18n("%1 removes the quiet on %2.", sourceNick, parameter); } } else { if (plus) { if (fromMe) { if (toMe) message = i18n("You give channel owner privileges to yourself."); else message = i18n("You give channel owner privileges to %1.", parameter); } else { if (toMe) message = i18n("%1 gives channel owner privileges to you.", sourceNick); else message = i18n("%1 gives channel owner privileges to %2.", sourceNick, parameter); } } else { if (fromMe) { if (toMe) message = i18n("You take channel owner privileges from yourself."); else message = i18n("You take channel owner privileges from %1.", parameter); } else { if (toMe) message = i18n("%1 takes channel owner privileges from you.", sourceNick); else message = i18n("%1 takes channel owner privileges from %2.", sourceNick, parameter); } } if (parameterChannelNick) { parameterChannelNick->setOwner(plus); emitUpdateInfo(); } } break; case 'a': if (plus) { if (fromMe) { if (toMe) message = i18n("You give channel admin privileges to yourself."); else message = i18n("You give channel admin privileges to %1.", parameter); } else { if (toMe) message = i18n("%1 gives channel admin privileges to you.", sourceNick); else message = i18n("%1 gives channel admin privileges to %2.", sourceNick, parameter); } } else { if (fromMe) { if (toMe) message = i18n("You take channel admin privileges from yourself."); else message = i18n("You take channel admin privileges from %1.", parameter); } else { if (toMe) message = i18n("%1 takes channel admin privileges from you.", sourceNick); else message = i18n("%1 takes channel admin privileges from %2.", sourceNick, parameter); } } if (parameterChannelNick) { parameterChannelNick->setAdmin(plus); emitUpdateInfo(); } break; case 'o': if (plus) { if (fromMe) { if (toMe) message = i18n("You give channel operator privileges to yourself."); else message = i18n("You give channel operator privileges to %1.", parameter); } else { if (toMe) message = i18n("%1 gives channel operator privileges to you.", sourceNick); else message = i18n("%1 gives channel operator privileges to %2.", sourceNick, parameter); } } else { if (fromMe) { if (toMe) message = i18n("You take channel operator privileges from yourself."); else message = i18n("You take channel operator privileges from %1.", parameter); } else { if (toMe) message = i18n("%1 takes channel operator privileges from you.", sourceNick); else message = i18n("%1 takes channel operator privileges from %2.", sourceNick, parameter); } } if (parameterChannelNick) { parameterChannelNick->setOp(plus); emitUpdateInfo(); } break; case 'h': if (plus) { if (fromMe) { if (toMe) message = i18n("You give channel halfop privileges to yourself."); else message = i18n("You give channel halfop privileges to %1.", parameter); } else { if (toMe) message = i18n("%1 gives channel halfop privileges to you.", sourceNick); else message = i18n("%1 gives channel halfop privileges to %2.", sourceNick, parameter); } } else { if (fromMe) { if (toMe) message = i18n("You take channel halfop privileges from yourself."); else message = i18n("You take channel halfop privileges from %1.", parameter); } else { if (toMe) message = i18n("%1 takes channel halfop privileges from you.", sourceNick); else message = i18n("%1 takes channel halfop privileges from %2.", sourceNick, parameter); } } if (parameterChannelNick) { parameterChannelNick->setHalfOp(plus); emitUpdateInfo(); } break; //case 'O': break; case 'v': if (plus) { if (fromMe) { if (toMe) message = i18n("You give yourself permission to talk."); else message = i18n("You give %1 permission to talk.", parameter); } else { if (toMe) message = i18n("%1 gives you permission to talk.", sourceNick); else message = i18n("%1 gives %2 permission to talk.", sourceNick, parameter); } } else { if (fromMe) { if (toMe) message = i18n("You take the permission to talk from yourself."); else message = i18n("You take the permission to talk from %1.", parameter); } else { if (toMe) message = i18n("%1 takes the permission to talk from you.", sourceNick); else message = i18n("%1 takes the permission to talk from %2.", sourceNick, parameter); } } if (parameterChannelNick) { parameterChannelNick->setVoice(plus); } break; case 'c': if (plus) { if (fromMe) message = i18n("You set the channel mode to 'no colors allowed'."); else message = i18n("%1 sets the channel mode to 'no colors allowed'.", sourceNick); } else { if (fromMe) message = i18n("You set the channel mode to 'allow color codes'."); else message = i18n("%1 sets the channel mode to 'allow color codes'.", sourceNick); } break; case 'i': if (plus) { if (fromMe) message = i18n("You set the channel mode to 'invite only'."); else message = i18n("%1 sets the channel mode to 'invite only'.", sourceNick); } else { if (fromMe) message = i18n("You remove the 'invite only' mode from the channel."); else message = i18n("%1 removes the 'invite only' mode from the channel.", sourceNick); } modeI->setDown(plus); break; case 'm': if (plus) { if (fromMe) message = i18n("You set the channel mode to 'moderated'."); else message = i18n("%1 sets the channel mode to 'moderated'.", sourceNick); } else { if (fromMe) message = i18n("You set the channel mode to 'unmoderated'."); else message = i18n("%1 sets the channel mode to 'unmoderated'.", sourceNick); } modeM->setDown(plus); break; case 'n': if (plus) { if (fromMe) message = i18n("You set the channel mode to 'no messages from outside'."); else message = i18n("%1 sets the channel mode to 'no messages from outside'.", sourceNick); } else { if (fromMe) message = i18n("You set the channel mode to 'allow messages from outside'."); else message = i18n("%1 sets the channel mode to 'allow messages from outside'.", sourceNick); } modeN->setDown(plus); break; case 'p': if (plus) { if (fromMe) message = i18n("You set the channel mode to 'private'."); else message = i18n("%1 sets the channel mode to 'private'.", sourceNick); } else { if (fromMe) message = i18n("You set the channel mode to 'public'."); else message = i18n("%1 sets the channel mode to 'public'.", sourceNick); } modeP->setDown(plus); if (plus) modeS->setDown(false); break; case 's': if (plus) { if (fromMe) message = i18n("You set the channel mode to 'secret'."); else message = i18n("%1 sets the channel mode to 'secret'.", sourceNick); } else { if (fromMe) message = i18n("You set the channel mode to 'visible'."); else message = i18n("%1 sets the channel mode to 'visible'.", sourceNick); } modeS->setDown(plus); if (plus) modeP->setDown(false); break; //case 'r': break; case 't': if (plus) { if (fromMe) message = i18n("You switch on 'topic protection'."); else message = i18n("%1 switches on 'topic protection'.", sourceNick); } else { if (fromMe) message = i18n("You switch off 'topic protection'."); else message = i18n("%1 switches off 'topic protection'.", sourceNick); } modeT->setDown(plus); break; case 'k': if (plus) { if (fromMe) message = i18n("You set the channel key to '%1'.", parameter); else message = i18n("%1 sets the channel key to '%2'.", sourceNick, parameter); } else { if (fromMe) message = i18n("You remove the channel key."); else message = i18n("%1 removes the channel key.", sourceNick); } modeK->setDown(plus); break; case 'l': if (plus) { if (fromMe) message = i18np("You set the channel limit to 1 nick.", "You set the channel limit to %1 nicks.", parameter); else message = i18np("%2 sets the channel limit to 1 nick.", "%2 sets the channel limit to %1 nicks.", parameter, sourceNick); } else { if (fromMe) message = i18n("You remove the channel limit."); else message = i18n("%1 removes the channel limit.", sourceNick); } modeL->setDown(plus); if (plus) limit->setText(parameter); else limit->clear(); break; case 'b': if (plus) { if (fromMe) message = i18n("You set a ban on %1.", parameter); else message = i18n("%1 sets a ban on %2.", sourceNick, parameter); } else { if (fromMe) message = i18n("You remove the ban on %1.", parameter); else message = i18n("%1 removes the ban on %2.", sourceNick, parameter); } break; case 'e': if (plus) { if (fromMe) message = i18n("You set a ban exception on %1.", parameter); else message = i18n("%1 sets a ban exception on %2.", sourceNick, parameter); } else { if (fromMe) message = i18n("You remove the ban exception on %1.", parameter); else message = i18n("%1 removes the ban exception on %2.", sourceNick, parameter); } break; case 'I': if (plus) { if (fromMe) message = i18n("You set invitation mask %1.", parameter); else message = i18n("%1 sets invitation mask %2.", sourceNick, parameter); } else { if (fromMe) message = i18n("You remove the invitation mask %1.", parameter); else message = i18n("%1 removes the invitation mask %2.", sourceNick, parameter); } break; default: if (plus) { if (Konversation::getChannelModesHash().contains(QLatin1Char(mode))) { if (fromMe) message = i18n("You set the channel mode '%1'.", Konversation::getChannelModesHash().value(QLatin1Char(mode))); else message= i18n("%1 sets the channel mode '%2'.", sourceNick, Konversation::getChannelModesHash().value(QLatin1Char(mode))); } else { if (fromMe) message = i18n("You set channel mode +%1", QLatin1Char(mode)); else message = i18n("%1 sets channel mode +%2", sourceNick, QLatin1Char(mode)); } } else { if (Konversation::getChannelModesHash().contains(QLatin1Char(mode))) { if (fromMe) message = i18n("You remove the channel mode '%1'.", Konversation::getChannelModesHash().value(QLatin1Char(mode))); else message= i18n("%1 removes the channel mode '%2'.", sourceNick, Konversation::getChannelModesHash().value(QLatin1Char(mode))); } else { if (fromMe) message = i18n("You set channel mode -%1", QLatin1Char(mode)); else message = i18n("%1 sets channel mode -%2", sourceNick, QLatin1Char(mode)); } } } // check if this nick's anyOp-status has changed and adjust ops accordingly if (parameterChannelNick) { if (wasAnyOp && (!parameterChannelNick->isAnyTypeOfOp())) adjustOps(-1); else if (!wasAnyOp && parameterChannelNick->isAnyTypeOfOp()) adjustOps(1); } if (!message.isEmpty() && !Preferences::self()->useLiteralModes()) { appendCommandMessage(i18n("Mode"), message, messageTags); } updateModeWidgets(mode, plus, parameter); } void Channel::clearModeList() { QString k; // Keep channel password in the backing store, for rejoins. for (QStringList::const_iterator it = m_modeList.constBegin(); it != m_modeList.constEnd(); ++it) { if ((*it)[0] == QLatin1Char('k')) k = (*it); } m_modeList.clear(); if (!k.isEmpty()) m_modeList << k; modeT->setOn(0); modeT->setDown(0); modeN->setOn(0); modeN->setDown(0); modeS->setOn(0); modeS->setDown(0); modeI->setOn(0); modeI->setDown(0); modeP->setOn(0); modeP->setDown(0); modeM->setOn(0); modeM->setDown(0); modeK->setOn(0); modeK->setDown(0); modeL->setOn(0); modeL->setDown(0); limit->clear(); emit modesChanged(); } void Channel::updateModeWidgets(char mode, bool plus, const QString ¶meter) { ModeButton* widget=nullptr; if(mode=='t') widget=modeT; else if(mode=='n') widget=modeN; else if(mode=='s') widget=modeS; else if(mode=='i') widget=modeI; else if(mode=='p') widget=modeP; else if(mode=='m') widget=modeM; else if(mode=='k') widget=modeK; else if(mode=='l') { widget=modeL; if(plus) limit->setText(parameter); else limit->clear(); } if(widget) widget->setOn(plus); if(plus) { m_modeList.append(QString(QLatin1Char(mode) + parameter)); } else { QStringList removable = m_modeList.filter(QRegExp(QString(QStringLiteral("^%1.*")).arg(mode))); foreach(const QString &mode, removable) { m_modeList.removeOne(mode); } } emit modesChanged(); } void Channel::updateQuickButtons() { delete m_buttonsGrid; m_buttonsGrid = nullptr; // the grid that holds the quick action buttons m_buttonsGrid = new QWidget (nickListButtons); //Q3Grid(2, nickListButtons); nickListButtons->layout()->addWidget(m_buttonsGrid); m_buttonsGrid->hide(); QGridLayout* layout = new QGridLayout (m_buttonsGrid); layout->setContentsMargins(0, 0, 0, 0); int col = 0; int row = 0; const QStringList &newButtonList = Preferences::quickButtonList(); // add new quick buttons for(int index=0;indexaddWidget (quickButton, row, col); row += col; connect(quickButton, SIGNAL(clicked(QString)), this, SLOT(quickButtonClicked(QString))); // Get the button definition QString buttonText=newButtonList[index]; // Extract button label QString buttonLabel=buttonText.section(QLatin1Char(','),0,0); // Extract button definition buttonText=buttonText.section(QLatin1Char(','),1); quickButton->setText(buttonLabel); quickButton->setDefinition(buttonText); // Add tool tips QString toolTip=buttonText.replace(QLatin1Char('&'),QStringLiteral("&")). replace(QLatin1Char('<'),QStringLiteral("<")). replace(QLatin1Char('>'),QStringLiteral(">")); quickButton->setToolTip(toolTip); quickButton->show(); } // for // set hide() or show() on grid showQuickButtons(Preferences::self()->showQuickButtons()); } void Channel::showQuickButtons(bool show) { // Qt does not redraw the buttons properly when they are not on screen // while getting hidden, so we remember the "soon to be" state here. if(isHidden() || !m_buttonsGrid) { quickButtonsChanged=true; quickButtonsState=show; } else { if(show) m_buttonsGrid->show(); else m_buttonsGrid->hide(); } } void Channel::showModeButtons(bool show) { // Qt does not redraw the buttons properly when they are not on screen // while getting hidden, so we remember the "soon to be" state here. if(isHidden()) { modeButtonsChanged=true; modeButtonsState=show; } else { if(show) { topicSplitterHidden = false; modeBox->show(); modeBox->parentWidget()->show(); } else { modeBox->hide(); if(topicLine->isHidden()) { topicSplitterHidden = true; modeBox->parentWidget()->hide(); } } } } void Channel::indicateAway(bool show) { // Qt does not redraw the label properly when they are not on screen // while getting hidden, so we remember the "soon to be" state here. if(isHidden()) { awayChanged=true; awayState=show; } else { if(show) awayLabel->show(); else awayLabel->hide(); } } void Channel::showEvent(QShowEvent*) { // If the show quick/mode button settings have changed, apply the changes now if(quickButtonsChanged) { quickButtonsChanged=false; showQuickButtons(quickButtonsState); } if(modeButtonsChanged) { modeButtonsChanged=false; showModeButtons(modeButtonsState); } if(awayChanged) { awayChanged=false; indicateAway(awayState); } syncSplitters(); } void Channel::syncSplitters() { QList vertSizes = Preferences::self()->topicSplitterSizes(); QList horizSizes = Preferences::self()->channelSplitterSizes(); if (vertSizes.isEmpty()) { vertSizes << m_topicButton->height() << (height() - m_topicButton->height()); Preferences::self()->setTopicSplitterSizes(vertSizes); } if (horizSizes.isEmpty()) { // An approximation of a common NICKLEN plus the width of the icon, // tested with 8pt and 10pt DejaVu Sans and Droid Sans. int listWidth = fontMetrics().averageCharWidth() * 17 + 20; horizSizes << (width() - listWidth) << listWidth; Preferences::self()->setChannelSplitterSizes(horizSizes); } m_vertSplitter->setSizes(vertSizes); m_horizSplitter->setSizes(horizSizes); splittersInitialized = true; } void Channel::updateAppearance() { QPalette palette; if (Preferences::self()->inputFieldsBackgroundColor()) { palette.setColor(QPalette::Text, Preferences::self()->color(Preferences::ChannelMessage)); palette.setColor(QPalette::Base, Preferences::self()->color(Preferences::TextViewBackground)); palette.setColor(QPalette::AlternateBase, Preferences::self()->color(Preferences::AlternateBackground)); } limit->setPalette(palette); topicLine->setPalette(QPalette()); if (Preferences::self()->customTextFont()) { topicLine->setFont(Preferences::self()->textFont()); m_inputBar->setFont(Preferences::self()->textFont()); nicknameCombobox->setFont(Preferences::self()->textFont()); limit->setFont(Preferences::self()->textFont()); } else { topicLine->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); m_inputBar->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); nicknameCombobox->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); limit->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); } nicknameListView->resort(); nicknameListView->setPalette(palette); nicknameListView->setAlternatingRowColors(Preferences::self()->inputFieldsBackgroundColor()); if (Preferences::self()->customListFont()) nicknameListView->setFont(Preferences::self()->listFont()); else nicknameListView->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); nicknameListView->refresh(); showModeButtons(Preferences::self()->showModeButtons()); showNicknameList(Preferences::self()->showNickList()); showNicknameBox(Preferences::self()->showNicknameBox()); showTopic(Preferences::self()->showTopic()); setAutoUserhost(Preferences::self()->autoUserhost()); QMetaObject::invokeMethod(this, "updateQuickButtons", Qt::QueuedConnection); // Nick sorting settings might have changed. Trigger timer if (m_delayedSortTimer) { m_delayedSortTrigger = DELAYED_SORT_TRIGGER + 1; m_delayedSortTimer->start(500 + qrand()/2000); } ChatWindow::updateAppearance(); } void Channel::nicknameComboboxChanged() { QString newNick=nicknameCombobox->currentText(); oldNick=m_server->getNickname(); if (oldNick != newNick) { nicknameCombobox->setCurrentIndex(nicknameCombobox->findText(oldNick)); changeNickname(newNick); // return focus to input line m_inputBar->setFocus(); } } void Channel::changeNickname(const QString& newNickname) { if (!newNickname.isEmpty()) m_server->queue(QStringLiteral("NICK ")+newNickname); } void Channel::queueNicks(const QStringList& nicknameList) { if (nicknameList.isEmpty()) return; m_nickQueue.append(nicknameList); processQueuedNicks(); } void Channel::endOfNames() { if (!m_initialNamesReceived) { m_initialNamesReceived = true; if (m_server->capabilities() & Server::AwayNotify && !Preferences::self()->autoWhoContinuousEnabled()) { // Do one who request to get the initial away state for the channel QMetaObject::invokeMethod(m_server, "requestWho", Qt::QueuedConnection, Q_ARG(QString, getName())); } scheduleAutoWho(); } } void Channel::childAdjustFocus() { m_inputBar->setFocus(); refreshModeButtons(); } void Channel::refreshModeButtons() { bool enable = true; if(getOwnChannelNick()) { enable=getOwnChannelNick()->isAnyTypeOfOp(); } // if not channel nick, then enable is true - fall back to assuming they are op //don't disable the mode buttons since you can't then tell if they are enabled or not. //needs to be fixed somehow /* modeT->setEnabled(enable); modeN->setEnabled(enable); modeS->setEnabled(enable); modeI->setEnabled(enable); modeP->setEnabled(enable); modeM->setEnabled(enable); modeK->setEnabled(enable); modeL->setEnabled(enable);*/ limit->setEnabled(enable); // Tooltips for the ModeButtons QString opOnly; if(!enable) opOnly = i18n("You have to be an operator to change this."); modeT->setToolTip(i18n("Topic can be changed by channel operator only. %1", opOnly)); modeN->setToolTip(i18n("No messages to channel from clients on the outside. %1", opOnly)); modeS->setToolTip(i18n("Secret channel. %1", opOnly)); modeI->setToolTip(i18n("Invite only channel. %1", opOnly)); modeP->setToolTip(i18n("Private channel. %1", opOnly)); modeM->setToolTip(i18n("Moderated channel. %1", opOnly)); modeK->setToolTip(i18n("Protect channel with a password.")); modeL->setToolTip(i18n("Set user limit to channel.")); } void Channel::nicknameListViewTextChanged(int textChangedFlags) { m_nicknameListViewTextChanged |= textChangedFlags; } void Channel::autoUserhost() { if(Preferences::self()->autoUserhost() && !Preferences::self()->autoWhoContinuousEnabled()) { int limit = 5; QString nickString; foreach (Nick* nick, getNickList()) { if(nick->getChannelNick()->getHostmask().isEmpty()) { if(limit--) nickString = nickString + nick->getChannelNick()->getNickname() + QLatin1Char(' '); else break; } } if(!nickString.isEmpty()) m_server->requestUserhost(nickString); } if(!nicknameList.isEmpty()) { resizeNicknameListViewColumns(); } } void Channel::setAutoUserhost(bool state) { nicknameListView->setColumnHidden(Nick::HostmaskColumn, !state); if (state) { nicknameListView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); // Cannot use QHeaderView::ResizeToContents here because it is slow // and it gets triggered by setSortingEnabled(). Using timed resize // instead, see Channel::autoUserhost() above. nicknameListView->header()->setSectionResizeMode(Nick::NicknameColumn, QHeaderView::Fixed); nicknameListView->header()->setSectionResizeMode(Nick::HostmaskColumn, QHeaderView::Fixed); userhostTimer.start(10000); m_nicknameListViewTextChanged |= 0xFF; // ResizeColumnsToContents QTimer::singleShot(0, this, &Channel::autoUserhost); // resize columns ASAP } else { nicknameListView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); nicknameListView->header()->setSectionResizeMode(Nick::NicknameColumn, QHeaderView::Stretch); userhostTimer.stop(); } } void Channel::scheduleAutoWho(int msec) { // The first auto-who is scheduled by ENDOFNAMES in InputFilter, which means // the first auto-who occurs one interval after it. This has two desirable // consequences specifically related to the startup phase: auto-who dispatch // doesn't occur at the same time for all channels that are auto-joined, and // it gives some breathing room to process the NAMES replies for all channels // first before getting started on WHO. // Subsequent auto-whos are scheduled by ENDOFWHO in InputFilter. However, // autoWho() might refuse to actually do the request if the number of nicks // in the channel exceeds the threshold, and will instead schedule another // attempt later. Thus scheduling an auto-who does not guarantee it will be // performed. // If this is called mid-interval (e.g. due to the ENDOFWHO from a manual WHO) // it will reset the interval to avoid cutting it short. if (m_whoTimer.isActive()) m_whoTimer.stop(); if (Preferences::self()->autoWhoContinuousEnabled()) { if (msec > 0) m_whoTimer.start(msec); else m_whoTimer.start(Preferences::self()->autoWhoContinuousInterval() * 1000); } } void Channel::autoWho() { // Try again later if there are too many nicks or we're already processing a WHO request. if ((nicks > Preferences::self()->autoWhoNicksLimit()) || m_server->getInputFilter()->isWhoRequestUnderProcess(getName())) { scheduleAutoWho(); return; } m_server->requestWho(getName()); } void Channel::updateAutoWho() { if (!Preferences::self()->autoWhoContinuousEnabled()) m_whoTimer.stop(); else if (Preferences::self()->autoWhoContinuousEnabled() && !m_whoTimer.isActive()) autoWho(); else if (m_whoTimer.isActive()) { // The below tries to meet user expectations on an interval settings change, // making two assumptions: // - If the new interval is lower than the old one, the user may be impatient // and desires an information update. // - If the new interval is longer than the old one, the user may be trying to // avoid Konversation producing too much traffic in a given timeframe, and // wants it to stop doing so sooner rather than later. // Both require rescheduling the next auto-who request. int interval = Preferences::self()->autoWhoContinuousInterval() * 1000; if (interval != m_whoTimer.interval()) { if (m_whoTimerStarted.elapsed() >= interval) { // If the time since the last auto-who request is longer than (or // equal to) the new interval setting, it follows that the new // setting is lower than the old setting. In this case issue a new // request immediately, which is the closest we can come to acting // as if the new setting had been active all along, short of tra- // velling back in time to change history. This handles the impa- // tient user. // FIXME: Adjust algorithm when time machine becomes available. m_whoTimer.stop(); autoWho(); } else { // If on the other hand the elapsed time is shorter than the new // interval setting, the new setting could be either shorter or // _longer_ than the old setting. Happily, this time we can actually // behave as if the new setting had been active all along, by sched- // uling the next request to happen in the new interval time minus // the already elapsed time, meeting user expecations for both cases // originally laid out. scheduleAutoWho(interval - m_whoTimerStarted.elapsed()); } } } } void Channel::fadeActivity() { foreach (Nick *nick, nicknameList) { nick->getChannelNick()->lessActive(); } } bool Channel::canBeFrontView() { return true; } bool Channel::searchView() { return true; } bool Channel::closeYourself(bool confirm) { int result=KMessageBox::Continue; if (confirm) result = KMessageBox::warningContinueCancel(this, i18n("Do you want to leave %1?", getName()), i18n("Leave Channel"), KGuiItem(i18n("Leave")), KStandardGuiItem::cancel(), QStringLiteral("QuitChannelTab")); if (result==KMessageBox::Continue) { m_server->closeChannel(getName()); m_server->removeChannel(this); deleteLater(); return true; } else m_recreationScheduled = false; return false; } void Channel::serverOnline(bool online) { setActive(online); } //Used to disable functions when not connected, does not necessarily mean the server is offline void Channel::setActive(bool active) { if (active) nicknameCombobox->setEnabled(true); else { m_initialNamesReceived = false; purgeNicks(); nicknameCombobox->setEnabled(false); topicLine->clear(); clearModeList(); clearBanList(); m_whoTimer.stop(); } } void Channel::showTopic(bool show) { if(show) { topicSplitterHidden = false; topicLine->show(); m_topicButton->show(); topicLine->parentWidget()->show(); } else { topicLine->hide(); m_topicButton->hide(); if(modeBox->isHidden()) { topicSplitterHidden = true; topicLine->parentWidget()->hide(); } } } void Channel::processQueuedNicks(bool flush) { // This pops nicks from the front of a queue added to by incoming NAMES // messages and adds them to the channel nicklist, calling itself via // the event loop until the last invocation finds the queue empty and // adjusts the nicks/ops counters and requests a nicklist sort, but only // if previous invocations actually processed any nicks. The latter is // an optimization for the common case of processing being kicked off by // flushNickQueue(), which is done e.g. before a nick rename or part to // make sure the channel is up to date and will usually find an empty // queue. This is also the use case for the 'flush' parameter, which if // true causes the recursion to block in a tight loop instead of queueing // via the event loop. if (m_nickQueue.isEmpty()) { if (m_processedNicksCount) { adjustNicks(m_processedNicksCount); adjustOps(m_processedOpsCount); m_processedNicksCount = 0; m_processedOpsCount = 0; sortNickList(); nicknameListView->setUpdatesEnabled(true); if (Preferences::self()->autoUserhost()) resizeNicknameListViewColumns(); } } else { QString nickname; while (nickname.isEmpty() && !m_nickQueue.isEmpty()) nickname = m_nickQueue.takeFirst(); QString userHost; if(m_server->capabilities() & Server::UserHostInNames) { int index = nickname.indexOf(QLatin1Char('!')); if(index >= 0) { userHost = nickname.mid(index + 1); - nickname = nickname.left(index); + nickname.truncate(index); } } bool admin = false; bool owner = false; bool op = false; bool halfop = false; bool voice = false; // Remove possible mode characters from nickname and store the resulting mode. m_server->mangleNicknameWithModes(nickname, admin, owner, op, halfop, voice); // TODO: Make these an enumeration in KApplication or somewhere, we can use them as well. unsigned int mode = (admin ? 16 : 0) + (owner ? 8 : 0) + (op ? 4 : 0) + (halfop ? 2 : 0) + (voice ? 1 : 0); // Check if nick is already in the nicklist. if (!nickname.isEmpty() && !getNickByName(nickname)) { ChannelNickPtr nick = m_server->addNickToJoinedChannelsList(getName(), nickname); Q_ASSERT(nick); nick->setMode(mode); if(!userHost.isEmpty()) { nick->getNickInfo()->setHostmask(userHost); } fastAddNickname(nick); ++m_processedNicksCount; if (nick->isAdmin() || nick->isOwner() || nick->isOp() || nick->isHalfOp()) ++m_processedOpsCount; } QMetaObject::invokeMethod(this, "processQueuedNicks", flush ? Qt::DirectConnection : Qt::QueuedConnection, Q_ARG(bool, flush)); } } void Channel::setChannelEncoding(const QString& encoding) // virtual { if(m_server->getServerGroup()) Preferences::setChannelEncoding(m_server->getServerGroup()->id(), getName(), encoding); else Preferences::setChannelEncoding(m_server->getDisplayName(), getName(), encoding); } QString Channel::getChannelEncoding() // virtual { if(m_server->getServerGroup()) return Preferences::channelEncoding(m_server->getServerGroup()->id(), getName()); return Preferences::channelEncoding(m_server->getDisplayName(), getName()); } QString Channel::getChannelEncodingDefaultDesc() // virtual { return i18n("Identity Default ( %1 )", getServer()->getIdentity()->getCodecName()); } void Channel::showNicknameBox(bool show) { if(show) { nicknameCombobox->show(); } else { nicknameCombobox->hide(); } } void Channel::showNicknameList(bool show) { if (show) { channelSplitterHidden = false; nickListButtons->show(); } else { channelSplitterHidden = true; nickListButtons->hide(); } } void Channel::requestNickListSort() { m_delayedSortTrigger++; if (m_delayedSortTrigger == DELAYED_SORT_TRIGGER && !m_delayedSortTimer->isActive()) { nicknameListView->fastSetSortingEnabled(false); m_delayedSortTimer->start(1000); } } void Channel::delayedSortNickList() { sortNickList(true); } void Channel::sortNickList(bool delayed) { if (!delayed || m_delayedSortTrigger > DELAYED_SORT_TRIGGER) { std::sort(nicknameList.begin(), nicknameList.end(), nickLessThan); nicknameListView->resort(); } if (!nicknameListView->isSortingEnabled()) nicknameListView->fastSetSortingEnabled(true); m_delayedSortTrigger = 0; m_delayedSortTimer->stop(); } void Channel::repositionNick(Nick *nick) { int index = nicknameList.indexOf(nick); if (index > -1) { // Trigger nick reposition in the nicklist including // field updates nick->refresh(); // Readd nick to the nicknameList nicknameList.removeAt(index); fastAddNickname(nick->getChannelNick(), nick); } else { qWarning() << "Nickname " << nick->getChannelNick()->getNickname() << " not found!"<< endl; } } bool Channel::eventFilter(QObject* watched, QEvent* e) { if((watched == nicknameListView) && (e->type() == QEvent::Resize) && splittersInitialized && isVisible()) { if (!topicSplitterHidden && !channelSplitterHidden) { Preferences::self()->setChannelSplitterSizes(m_horizSplitter->sizes()); Preferences::self()->setTopicSplitterSizes(m_vertSplitter->sizes()); } if (!topicSplitterHidden && channelSplitterHidden) { Preferences::self()->setTopicSplitterSizes(m_vertSplitter->sizes()); } if (!channelSplitterHidden && topicSplitterHidden) { Preferences::self()->setChannelSplitterSizes(m_horizSplitter->sizes()); } } return ChatWindow::eventFilter(watched, e); } void Channel::addBan(const QString& ban) { for ( QStringList::iterator it = m_BanList.begin(); it != m_BanList.end(); ++it ) { if ((*it).section(QLatin1Char(' '), 0, 0) == ban.section(QLatin1Char(' '), 0, 0)) { // Ban is already in list. it = m_BanList.erase(it); emit banRemoved(ban.section(QLatin1Char(' '), 0, 0)); if (it == m_BanList.end()) break; } } m_BanList.prepend(ban); emit banAdded(ban); } void Channel::removeBan(const QString& ban) { foreach(const QString &string, m_BanList) { if (string.section(QLatin1Char(' '), 0, 0) == ban) { m_BanList.removeOne(string); emit banRemoved(ban); } } } void Channel::clearBanList() { m_BanList.clear(); emit banListCleared(); } void Channel::append(const QString& nickname, const QString& message, const QHash &messageTags, const QString& label) { if(nickname != getServer()->getNickname()) { Nick* nick = getNickByName(nickname); if(nick) { nick->getChannelNick()->setTimeStamp(QDateTime::currentDateTime().toTime_t()); } } ChatWindow::append(nickname, message, messageTags, label); nickActive(nickname); } void Channel::appendAction(const QString& nickname, const QString& message, const QHash &messageTags) { if(nickname != getServer()->getNickname()) { Nick* nick = getNickByName(nickname); if(nick) { nick->getChannelNick()->setTimeStamp(QDateTime::currentDateTime().toTime_t()); } } ChatWindow::appendAction(nickname, message, messageTags); nickActive(nickname); } void Channel::nickActive(const QString& nickname) //FIXME reported to crash, can't reproduce { ChannelNickPtr channelnick=getChannelNick(nickname); //XXX Would be nice to know why it can be null here... if (channelnick) { channelnick->moreActive(); if (Preferences::self()->sortByActivity()) { Nick* nick = getNickByName(nickname); if (nick) { nick->repositionMe(); } } } } #ifdef HAVE_QCA2 Konversation::Cipher* Channel::getCipher() { if(!m_cipher) m_cipher = new Konversation::Cipher(); return m_cipher; } #endif void Channel::updateNickInfos() { foreach(Nick* nick, nicknameList) { if(nick->getChannelNick()->getNickInfo()->isChanged()) { nick->refresh(); } } } void Channel::updateChannelNicks(const QString& channel) { if(channel != name.toLower()) return; foreach(Nick* nick, nicknameList) { if(nick->getChannelNick()->isChanged()) { nick->refresh(); if(nick->getChannelNick() == m_ownChannelNick) { refreshModeButtons(); } } } } void Channel::resizeNicknameListViewColumns() { // Resize columns if needed (on regular basis) if (m_nicknameListViewTextChanged & (1 << Nick::NicknameColumn)) nicknameListView->resizeColumnToContents(Nick::NicknameColumn); if (m_nicknameListViewTextChanged & (1 << Nick::HostmaskColumn)) nicknameListView->resizeColumnToContents(Nick::HostmaskColumn); m_nicknameListViewTextChanged = 0; } // // NickList // NickList::NickList() : QList() { } QString NickList::completeNick(const QString& pattern, bool& complete, QStringList& found, bool skipNonAlfaNum, bool caseSensitive) { found.clear(); QString prefix(QLatin1Char('^')); QString newNick; QString prefixCharacter = Preferences::self()->prefixCharacter(); NickList foundNicks; if((pattern.contains(QRegExp(QStringLiteral("^(\\d|\\w)")))) && skipNonAlfaNum) { prefix = QStringLiteral("^([^\\d\\w]|[\\_]){0,}"); } QRegExp regexp(prefix + QRegExp::escape(pattern)); regexp.setCaseSensitivity(caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); foreach (Nick* nick, *this) { newNick = nick->getChannelNick()->getNickname(); if(!prefix.isEmpty() && newNick.contains(prefixCharacter)) { newNick = newNick.section( prefixCharacter,1 ); } if(newNick.contains(regexp)) { foundNicks.append(nick); } } std::sort(foundNicks.begin(), foundNicks.end(), nickTimestampLessThan); foreach (Nick *nick, foundNicks) { found.append(nick->getChannelNick()->getNickname()); } if(found.count() > 1) { bool ok = true; int patternLength = pattern.length(); QString firstNick = found[0]; int firstNickLength = firstNick.length(); int foundCount = found.count(); while(ok && ((patternLength) < firstNickLength)) { ++patternLength; QStringList tmp = found.filter(firstNick.left(patternLength), caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); if(tmp.count() != foundCount) { ok = false; --patternLength; } } complete = false; return firstNick.left(patternLength); } else if(found.count() == 1) { complete = true; return found[0]; } return QString(); } bool NickList::containsNick(const QString& nickname) { foreach (Nick* nick, *this) { if (nick->getChannelNick()->getNickname()==nickname) return true; } return false; } // kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on; // vim: set et sw=4 ts=4 cino=l1,cs,U1: diff --git a/src/irc/channellistpanel.cpp b/src/irc/channellistpanel.cpp index a436e514..c528e471 100644 --- a/src/irc/channellistpanel.cpp +++ b/src/irc/channellistpanel.cpp @@ -1,540 +1,540 @@ /* 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. */ /* Shows the list of channels Copyright (C) 2003 Dario Abatianni Copyright (C) 2009 Travis McHenry */ #include "channellistpanel.h" #include "channel.h" #include "preferences.h" #include "server.h" #include "common.h" #include "application.h" #include #include #include #include #include #include ChannelListModel::ChannelListModel(QObject* parent) : QAbstractListModel(parent) { } void ChannelListModel::append(const ChannelItem& item) { m_channelList.append(item); beginResetModel(); endResetModel(); } int ChannelListModel::columnCount(const QModelIndex& /*parent*/) const { return 3; } int ChannelListModel::rowCount(const QModelIndex& /*parent*/) const { return m_channelList.count(); } QVariant ChannelListModel::data(const QModelIndex& index, int role) const { if(!index.isValid() || index.row() >= m_channelList.count ()) return QVariant(); const ChannelItem& item = m_channelList[index.row()]; if(role == Qt::DisplayRole) { switch(index.column()) { case 0: return item.name; case 1: return item.users; case 2: return item.topic; default: return QVariant(); } } else if(role == Qt::ToolTipRole) { return QString(QLatin1String("") + item.topic.toHtmlEscaped() + QLatin1String("")); } return QVariant(); } QVariant ChannelListModel::headerData (int section, Qt::Orientation orientation, int role) const { if(orientation == Qt::Vertical || role != Qt::DisplayRole) return QVariant(); switch(section) { case 0: return i18n("Channel Name"); case 1: return i18n("Users"); case 2: return i18n("Channel Topic"); default: return QVariant(); } } ChannelListProxyModel::ChannelListProxyModel(QObject* parent) : QSortFilterProxyModel(parent) { m_minUsers = 0; m_maxUsers = 0; m_filterChannel = true; m_filterTopic = false; } bool ChannelListProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent); QModelIndex index1 = sourceModel()->index(sourceRow, 1, sourceParent); QModelIndex index2 = sourceModel()->index(sourceRow, 2, sourceParent); return (((m_filterChannel && sourceModel()->data(index0).toString().contains(filterRegExp())) || (m_filterTopic && sourceModel()->data(index2).toString().contains(filterRegExp())) || (!m_filterChannel && !m_filterTopic)) && usersInRange(sourceModel()->data(index1).toInt())); } bool ChannelListProxyModel::usersInRange(int users) const { return (!m_minUsers || users >= m_minUsers) && (!m_maxUsers || users <= m_maxUsers); } void ChannelListProxyModel::setFilterMinimumUsers(int users) { m_minUsers = users; } void ChannelListProxyModel::setFilterMaximumUsers(int users) { m_maxUsers = users; } void ChannelListProxyModel::setFilterTopic(bool filter) { m_filterTopic = filter; } void ChannelListProxyModel::setFilterChannel(bool filter) { m_filterChannel = filter; } ChannelListPanel::ChannelListPanel(QWidget* parent) : ChatWindow(parent) { setType(ChatWindow::ChannelList); m_isTopLevelView = false; setName(i18n("Channel List")); m_firstRun = true; m_regexState = false; m_numUsers = 0; m_numChannels = 0; m_visibleUsers = 0; m_visibleChannels = 0; m_progressTimer = new QTimer(this); m_filterTimer = new QTimer(this); m_filterTimer->setSingleShot(true); m_tempTimer = new QTimer(this); m_tempTimer->setSingleShot(true); setSpacing(0); m_toolBar = new KToolBar(this, true, true); m_toolBar->setObjectName(QStringLiteral("channellistpanel_toolbar")); m_saveList = m_toolBar->addAction(QIcon::fromTheme(QStringLiteral("document-save")), i18nc("save list", "Save &List..."), this, SLOT(saveList())); m_saveList->setWhatsThis(i18n("Click here to save the channel list.")); m_refreshList = m_toolBar->addAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18nc("refresh list", "&Refresh List"), this, SLOT(refreshList())); m_refreshList->setWhatsThis(i18n("Click here to refresh the channel list.")); m_toolBar->addSeparator(); m_joinChannel = m_toolBar->addAction(QIcon::fromTheme(QStringLiteral("irc-join-channel")), i18nc("join channel", "&Join Channel"), this, SLOT(joinChannelClicked())); m_joinChannel->setWhatsThis(i18n("Click here to join the channel. A new tab is created for the channel.")); //UI Setup setupUi(this); m_channelListModel = new ChannelListModel(this); m_proxyModel = new ChannelListProxyModel(this); m_proxyModel->setSourceModel(m_channelListModel); m_channelListView->setModel(m_proxyModel); m_channelListView->header()->resizeSection(1,75); // resize users section to be smaller Preferences::restoreColumnState(m_channelListView, QStringLiteral("ChannelList ViewSettings")); // double click on channel entry joins the channel connect(m_channelListView, &QTreeView::doubleClicked, this, &ChannelListPanel::joinChannelClicked); connect(m_channelListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ChannelListPanel::currentChanged); connect(m_channelListView, &QTreeView::customContextMenuRequested, this, &ChannelListPanel::contextMenu); connect(m_regexBox, &QCheckBox::stateChanged, this, &ChannelListPanel::filterChanged); connect(m_topicBox, &QCheckBox::stateChanged, this, &ChannelListPanel::filterChanged); connect(m_channelBox, &QCheckBox::stateChanged, this, &ChannelListPanel::filterChanged); connect(m_minUser, static_cast(&QSpinBox::valueChanged), this, &ChannelListPanel::filterChanged); connect(m_maxUser, static_cast(&QSpinBox::valueChanged), this, &ChannelListPanel::filterChanged); connect(m_filterLine, &KLineEdit::returnPressed, this, &ChannelListPanel::applyFilterClicked); connect(m_filterLine, &KLineEdit::textChanged, this, &ChannelListPanel::filterChanged); connect(m_filterTimer, &QTimer::timeout, this, &ChannelListPanel::updateFilter); connect(m_progressTimer, &QTimer::timeout, this, &ChannelListPanel::setProgress); connect(m_tempTimer, &QTimer::timeout, this, &ChannelListPanel::endOfChannelList); updateUsersChannels(); } ChannelListPanel::~ChannelListPanel() { Preferences::saveColumnState(m_channelListView, QStringLiteral("ChannelList ViewSettings")); } void ChannelListPanel::refreshList() { if (!m_refreshList->isEnabled()) return; m_numUsers = 0; m_numChannels = 0; m_visibleUsers = 0; m_visibleChannels = 0; //hide temporarily to prevent multiple refreshes, but renable if it doesn't start in //3 seconds in case we got a 'server busy' error. It doesn't matter if it's slower than //that because addToChannelList's first run handles this anyway m_refreshList->setEnabled(false); m_tempTimer->start(3000); emit refreshChannelList(); } void ChannelListPanel::addToChannelList(const QString& channel,int users,const QString& topic) { if (m_firstRun) { if(m_tempTimer->isActive()) m_tempTimer->stop(); m_refreshList->setEnabled(false); m_statsLabel->setText(i18n("Refreshing.")); m_progressTimer->start(500); m_firstRun = false; m_channelListModel = new ChannelListModel(this); } ChannelItem item; item.name = channel; item.users = users; item.topic = Konversation::removeIrcMarkup(topic); m_channelListModel->append(item); ++m_numChannels; m_numUsers += users; } void ChannelListPanel::endOfChannelList() { m_progressTimer->stop(); m_proxyModel->setSourceModel(m_channelListModel); m_proxyModel->invalidate(); m_refreshList->setEnabled(true); m_firstRun = true; updateUsersChannels(); } void ChannelListPanel::filterChanged() { m_filterTimer->start(300); } void ChannelListPanel::updateFilter() { QString text = m_filterLine->text(); int max = m_maxUser->value(); int min = m_minUser->value(); bool topic = m_topicBox->isChecked(); bool channel = m_channelBox->isChecked(); bool regex = m_regexBox->isChecked(); bool regexChanged = (regex != m_regexState); if (regexChanged) m_regexState = regex; bool change = false; if (m_proxyModel->filterRegExp().pattern() != text || regexChanged) { change = true; if(m_regexState) m_proxyModel->setFilterRegExp(text); else m_proxyModel->setFilterWildcard(text); } if (m_proxyModel->filterMinimumUsers() != min) { change = true; m_proxyModel->setFilterMinimumUsers(min); } if (m_proxyModel->filterMaximumUsers() != max) { change = true; m_proxyModel->setFilterMaximumUsers(max); } if (m_proxyModel->filterTopic() != topic) { change = true; m_proxyModel->setFilterTopic(topic); } if (m_proxyModel->filterChannel() != channel) { change = true; m_proxyModel->setFilterChannel(channel); } if (change) m_proxyModel->invalidate(); updateUsersChannels(); } void ChannelListPanel::currentChanged(const QModelIndex ¤t,const QModelIndex &previous) { Q_UNUSED(previous); m_joinChannel->setEnabled(m_online && current.isValid()); } void ChannelListPanel::setProgress() { QString text = m_statsLabel->text(); if(text.length() < 13) m_statsLabel->setText(text + QLatin1Char('.')); else m_statsLabel->setText(i18n("Refreshing.")); } void ChannelListPanel::countUsers(const QModelIndex& index, int pos) { m_visibleUsers += index.data().toInt(); ++pos; if (pos < m_proxyModel->rowCount()) countUsers(index.sibling(pos,1), pos); } void ChannelListPanel::updateUsersChannels() { m_visibleUsers = 0; countUsers(m_proxyModel->index(0,1,QModelIndex()),0); m_visibleChannels = m_proxyModel->rowCount(); m_statsLabel->setText(i18n("Channels: %1 (%2 shown)", m_numChannels, m_visibleChannels) + i18n(" Non-unique users: %1 (%2 shown)", m_numUsers, m_visibleUsers)); } void ChannelListPanel::saveList() { // Ask user for file name QString fileName = QFileDialog::getSaveFileName( this, i18n("Save Channel List")); if (!fileName.isEmpty()) { // first find the longest channel name and nick number for clean table layouting int maxChannelWidth=0; int maxUsersWidth=0; int rows = m_proxyModel->rowCount(); QModelIndex index = m_proxyModel->index(0,0,QModelIndex()); for (int r = 0; r < rows; r++) { QString channel = index.sibling(r,0).data().toString(); QString users = index.sibling(r,1).data().toString(); if (channel.length()>maxChannelWidth) { maxChannelWidth = channel.length(); } if (users.length()>maxUsersWidth) { maxUsersWidth = users.length(); } } // now save the list to disk QFile listFile(fileName); listFile.open(QIODevice::WriteOnly); // wrap the file into a stream QTextStream stream(&listFile); QString header(i18n("Konversation Channel List: %1 - %2\n\n", m_server->getServerName(), QDateTime::currentDateTime().toString())); // send header to stream stream << header; for (int r = 0; r < rows; r++) { QString channel = index.sibling(r,0).data().toString(); QString users = index.sibling(r,1).data().toString(); QString topic = index.sibling(r,2).data().toString(); QString channelName; channelName.fill(QLatin1Char(' '), maxChannelWidth); channelName.replace(0, channel.length(), channel); QString usersPad; usersPad.fill(QLatin1Char(' '),maxUsersWidth); QString usersNum(usersPad+users); usersNum = usersNum.right(maxUsersWidth); QString line(channelName+QLatin1Char(' ')+usersNum+QLatin1Char(' ')+topic+QLatin1Char('\n')); stream << line; } listFile.close(); } } void ChannelListPanel::joinChannelClicked() { QModelIndex item = m_channelListView->currentIndex(); if(item.isValid()) { if(item.column() != 0) item = item.sibling(item.row(),0); emit joinChannel(item.data().toString()); } } void ChannelListPanel::applyFilterClicked() { if (!m_numChannels) { refreshList(); return; } } void ChannelListPanel::contextMenu(const QPoint& p) { QModelIndex item = m_channelListView->indexAt(p); if (!item.isValid()) return; if (item.column() != 2) item = item.sibling(item.row(),2); QString filteredLine = item.data().toString(); QMenu* menu = new QMenu(this); // Join Channel Action QAction *joinAction = new QAction(menu); joinAction->setText(i18n("Join Channel")); joinAction->setIcon(QIcon::fromTheme(QStringLiteral("irc-join-channel"))); menu->addAction(joinAction); connect(joinAction, &QAction::triggered, this, &ChannelListPanel::joinChannelClicked); // Adds a separator between the Join action and the URL(s) submenu menu->addSeparator(); // open URL submenu QMenu* showURLmenu = new QMenu(i18n("Open URL"), menu); QList > urlRanges = Konversation::getUrlRanges(filteredLine); QPair urlRange; QListIterator > i(urlRanges); while (i.hasNext()) { urlRange = i.next(); QString url = filteredLine.mid(urlRange.first, urlRange.second); QAction* action = new QAction(showURLmenu); action->setText(url); action->setData(url); showURLmenu->addAction(action); connect(action, &QAction::triggered, this, &ChannelListPanel::openURL); } - if (showURLmenu->actions().count()==0) + if (showURLmenu->actions().isEmpty()) showURLmenu->setEnabled(false); menu->addMenu(showURLmenu); menu->exec(QCursor::pos()); delete menu; } void ChannelListPanel::openURL() { const QAction* action = qobject_cast(sender()); if (action) { Application::openUrl(action->data().toString()); } } bool ChannelListPanel::closeYourself() { // make the server delete us so server can reset the pointer to us m_server->closeChannelListPanel(); return true; } void ChannelListPanel::appendInputText(const QString& text, bool fromCursor) { Q_UNUSED(fromCursor); m_filterLine->setText(m_filterLine->text() + text); } //Used to disable functions when not connected void ChannelListPanel::serverOnline(bool online) { m_online = online; m_refreshList->setEnabled(m_online); m_joinChannel->setEnabled(m_online && m_channelListView->currentIndex().isValid()); } void ChannelListPanel::emitUpdateInfo() { QString info; info = i18n("Channel List for %1", m_server->getDisplayName()); emit updateInfo(info); } void ChannelListPanel::setFilter(const QString& filter) { m_filterLine->setText(filter); } diff --git a/src/irc/channelnick.cpp b/src/irc/channelnick.cpp index 45777bea..6e3f30b5 100644 --- a/src/irc/channelnick.cpp +++ b/src/irc/channelnick.cpp @@ -1,248 +1,248 @@ /* 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. */ /* begin: Wed Aug 04 2004 copyright: (C) 2002,2003,2004 by Dario Abatianni email: eisfuchs@tigress.com */ /* An instance of ChannelNick is made for each nick in each channel. So for a person in multiple channels, they will have one NickInfo, and multiple ChannelNicks. It contains a pointer to the NickInfo, and the mode of that person in the channel. */ #include "channelnick.h" #include "channel.h" #include "server.h" ChannelNick::ChannelNick(const NickInfoPtr& nickInfo, const QString& channel) : QSharedData() { m_nickInfo = nickInfo; m_isop = false; m_isadmin = false; m_isowner = false; m_ishalfop = false; m_hasvoice = false; m_timeStamp = 0; m_recentActivity = 0; m_channel = channel; m_isChanged = false; } ChannelNick::~ChannelNick() { } bool ChannelNick::isOp() const { return m_isop; } bool ChannelNick::isAdmin() const { return m_isadmin; } bool ChannelNick::isOwner() const { return m_isowner; } bool ChannelNick::isHalfOp() const { return m_ishalfop; } bool ChannelNick::hasVoice() const { return m_hasvoice; } bool ChannelNick::isAnyTypeOfOp() const { return m_isop || m_isadmin || m_isowner || m_ishalfop; } NickInfoPtr ChannelNick::getNickInfo() const { return m_nickInfo; } /** @param mode 'v' to set voice, 'a' to set admin, 'h' to set halfop, 'o' to set op. * @param state what to set the mode to. */ bool ChannelNick::setMode(char mode, bool state) { switch (mode) { case 'q': return setOwner(state); case 'a': return setAdmin(state); case 'o': return setOp(state); case 'h': return setHalfOp(state); case 'v': return setVoice(state); default: qDebug() << "Mode '" << mode << "' not recognised in setModeForChannelNick"; return false; } } /** Used still for passing modes from inputfilter to Server. Should be removed. */ bool ChannelNick::setMode(int mode) { bool voice = mode%2; mode >>= 1; bool halfop = mode %2; mode >>= 1; bool op = mode %2; mode >>= 1; bool owner = mode %2; mode >>= 1; bool admin = mode %2; return setMode(admin, owner, op, halfop, voice); } bool ChannelNick::setMode(bool admin,bool owner,bool op,bool halfop,bool voice) { if(m_isadmin==admin && m_isowner==owner && m_isop==op && m_ishalfop==halfop && m_hasvoice==voice) return false; m_isadmin=admin; m_isowner=owner; m_isop=op; m_ishalfop=halfop; m_hasvoice=voice; markAsChanged(); return true; } /** set the voice for the nick, and update * @returns Whether it needed to be changed. False for no change. */ bool ChannelNick::setVoice(bool state) { if(m_hasvoice==state) return false; m_hasvoice=state; markAsChanged(); return true; } bool ChannelNick::setOwner(bool state) { if(m_isowner==state) return false; m_isowner=state; markAsChanged(); return true; } bool ChannelNick::setAdmin(bool state) { if(m_isadmin==state) return false; m_isadmin=state; markAsChanged(); return true; } bool ChannelNick::setHalfOp(bool state) { if(m_ishalfop==state) return false; m_ishalfop=state; markAsChanged(); return true; } bool ChannelNick::setOp(bool state) { if(m_isop==state) return false; m_isop=state; markAsChanged(); return true; } //Purely provided for convience because they are used so often. //Just calls nickInfo->getNickname() etc QString ChannelNick::getNickname() const { return m_nickInfo->getNickname(); } QString ChannelNick::getHostmask() const { return m_nickInfo->getHostmask(); } QString ChannelNick::tooltip() const { QString strTooltip; QTextStream tooltip( &strTooltip, QIODevice::WriteOnly ); tooltip << ""; tooltip << ""; m_nickInfo->tooltipTableData(tooltip); QStringList modes; if(isOp()) modes << i18n("Operator"); if(isAdmin()) modes << i18n("Admin"); if(isOwner()) modes << i18n("Owner"); if(isHalfOp()) modes << i18n("Half-operator"); if(hasVoice()) modes << i18n("Has voice"); //Don't show anything if the user is just a normal user //if(modes.empty()) modes << i18n("A normal user"); if(!modes.empty()) { - tooltip << ""; + tooltip << ""; } tooltip << "
" << i18n("Mode") << ":" << modes.join(QStringLiteral(", ")) << "
" << i18n("Mode") << ":" << modes.join(QLatin1String(", ")) << "
"; //qDebug() << strTooltip ; //if(!dirty) return QString(); return strTooltip; } QString ChannelNick::loweredNickname() const { return m_nickInfo->loweredNickname(); } uint ChannelNick::timeStamp() const { return m_timeStamp; } uint ChannelNick::recentActivity() const { return m_recentActivity; } void ChannelNick::moreActive() { m_recentActivity++; } void ChannelNick::lessActive() { m_recentActivity--; } void ChannelNick::setTimeStamp(uint stamp) { m_timeStamp = stamp; } void ChannelNick::markAsChanged() { setChanged(true); m_nickInfo->getServer()->startChannelNickChangedTimer(m_channel); } diff --git a/src/irc/inputfilter.cpp b/src/irc/inputfilter.cpp index 6e9e2628..914d5682 100644 --- a/src/irc/inputfilter.cpp +++ b/src/irc/inputfilter.cpp @@ -1,2539 +1,2539 @@ /* 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. */ /* Copyright (C) 2002 Dario Abatianni Copyright (C) 2004, 2016 Peter Simonsson Copyright (C) 2006-2008 Eike Hein */ #include "inputfilter.h" #include "server.h" #include "replycodes.h" #include "application.h" #include "version.h" #include "commit.h" #include "query.h" #include "channel.h" #include "statuspanel.h" #include "common.h" #include "notificationhandler.h" #include #include #include #include #include InputFilter::InputFilter() : m_server(nullptr), m_lagMeasuring(false) { m_connecting = false; } InputFilter::~InputFilter() { } void InputFilter::setServer(Server* newServer) { m_server = newServer; } template int posOrLen(T chr, const QString& str, int from=0) { int p=str.indexOf(QLatin1Char(chr), from); if (p<0) return str.size(); return p; } /// "[22:08] >> :thiago!n=thiago@kde/thiago QUIT :Read error: 110 (Connection timed out)" /// "[21:47] >> :Zarin!n=x365@kde/developer/lmurray PRIVMSG #plasma :If the decoration doesn't have paint( QPixmap ) it falls back to the old one" /// "[21:49] >> :niven.freenode.net 352 argonel #kde-forum i=beezle konversation/developer/argonel irc.freenode.net argonel H :0 Konversation User " void InputFilter::parseLine(const QString& line) { int start=0; QHash messageTags; if (line[0] == QLatin1Char('@')) { messageTags = parseMessageTags(line, &start); } QString prefix; int end(posOrLen(' ', line, start)); if (line[start]==QLatin1Char(':')) { prefix = line.mid(start + 1, end - start - 1); //skips the colon and does not include the trailing space start = end + 1; end = posOrLen(' ', line, start); } //even though the standard is UPPER CASE, someone when through a great deal of trouble to make this lower case... QString command = QString(line.mid(start, end-start)).toLower(); start=end+1; - int trailing=line.indexOf(QStringLiteral(" :"), end); + int trailing=line.indexOf(QLatin1String(" :"), end); if (trailing >= 0) end=trailing; else end=line.size(); QStringList parameterList; while (start < end) { if (line[start]==QLatin1Char(' ')) start++; else { int p=line.indexOf(QLatin1Char(' '), start); //easier to have Qt loop for me :) if (p<0) p=end; parameterList << line.mid(start, p-start); start=p+1; } }; /* Quote: "The final colon is specified as a "last argument" designator, and * is always valid before the final argument." * Quote: "The last parameter may be an empty string." * Quote: "After extracting the parameter list, all parameters are equal * whether matched by or . is just a * syntactic trick to allow SPACE within the parameter." */ if (trailing >= 0 ) //<-- trailing == ":" - per above we need the empty string parameterList << line.mid( qMin(trailing+2, line.size()) ); Q_ASSERT(m_server); //how could we have gotten a line without a server? // Server command, if no "!" was found in prefix if ((!prefix.contains(QLatin1Char('!'))) && (prefix != m_server->getNickname())) { parseServerCommand(prefix, command, parameterList, messageTags); } else { parseClientCommand(prefix, command, parameterList, messageTags); } } #define trailing (parameterList.isEmpty() ? QString() : parameterList.last()) #define plHas(x) _plHas(parameterList.count(), (x)) bool _plHad=false; int _plWanted = 0; bool _plHas(int count, int x) { _plHad=(count >= x); _plWanted = x; if (!_plHad) qDebug() << "plhad" << count << "wanted" << x; return _plHad; } void InputFilter::parseClientCommand(const QString &prefix, const QString &command, QStringList ¶meterList, const QHash &messageTags) { Application* konv_app = Application::instance(); Q_ASSERT(konv_app); Q_ASSERT(m_server); // Extract nickname from prefix int pos = prefix.indexOf(QLatin1Char('!')); QString sourceNick = prefix.left(pos); QString sourceHostmask = prefix.mid(pos + 1); // remember hostmask for this nick, it could have changed m_server->addHostmaskToNick(sourceNick, sourceHostmask); bool isNumeric = false; int numeric = command.toInt(&isNumeric); if(isNumeric) { parseNumeric(prefix, numeric, parameterList, messageTags); } //PRIVMSG #channel :message - else if (command == QStringLiteral("privmsg") && plHas(2)) + else if (command == QLatin1String("privmsg") && plHas(2)) { bool isChan = isAChannel(parameterList.value(0)); // CTCP message? if (m_server->identifyMsg() && (trailing.length() > 1 && (trailing.at(0) == QLatin1Char('+') || trailing.at(0) == QLatin1Char('-')))) { - trailing = trailing.mid(1); + trailing.remove(0, 1); } if (!trailing.isEmpty() && trailing.at(0)==QChar(0x01)) { // cut out the CTCP command QString ctcp = trailing.mid(1,trailing.indexOf(QChar(0x01),1)-1); //QString::left(-1) returns the entire string QString ctcpCommand = ctcp.left(ctcp.indexOf(QLatin1Char(' '))).toLower(); bool hasArg = ctcp.indexOf(QLatin1Char(' ')) > 0; //QString::mid(-1)+1 = 0, which returns the entire string if there is no space, resulting in command==arg QString ctcpArgument = hasArg ? ctcp.mid(ctcp.indexOf(QLatin1Char(' '))+1) : QString(); hasArg = !ctcpArgument.isEmpty(); if (hasArg) ctcpArgument = konv_app->doAutoreplace(ctcpArgument, false).first; // If it was a ctcp action, build an action string - if (ctcpCommand == QStringLiteral("action") && isChan) + if (ctcpCommand == QLatin1String("action") && isChan) { if (!isIgnore(prefix, Ignore::Channel)) { Channel* channel = m_server->getChannelByName( parameterList.value(0) ); if (!channel) { qCritical() << "Didn't find the channel " << parameterList.value(0) << endl; return; } channel->appendAction(sourceNick, ctcpArgument, messageTags); if (sourceNick != m_server->getNickname()) { if (hasArg && ctcpArgument.toLower().contains(QRegExp(QStringLiteral("(^|[^\\d\\w])") + QRegExp::escape(m_server->loweredNickname()) + QStringLiteral("([^\\d\\w]|$)")))) { konv_app->notificationHandler()->nick(channel, sourceNick, ctcpArgument); } else { konv_app->notificationHandler()->message(channel, sourceNick, ctcpArgument); } } } } // If it was a ctcp action, build an action string - else if (ctcpCommand == QStringLiteral("action") && !isChan) + else if (ctcpCommand == QLatin1String("action") && !isChan) { // Check if we ignore queries from this nick if (!isIgnore(prefix, Ignore::Query)) { NickInfoPtr nickinfo = m_server->obtainNickInfo(sourceNick); nickinfo->setHostmask(sourceHostmask); // create new query (server will check for dupes) Query* query = m_server->addQuery(nickinfo, false /* we didn't initiate this*/ ); // send action to query query->appendAction(sourceNick, ctcpArgument, messageTags); if (sourceNick != m_server->getNickname() && query) konv_app->notificationHandler()->queryMessage(query, sourceNick, ctcpArgument); } } // Answer ping requests - else if (ctcpCommand == QStringLiteral("ping") && hasArg) + else if (ctcpCommand == QLatin1String("ping") && hasArg) { if (!isIgnore(prefix,Ignore::CTCP)) { if (isChan) { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-PING request from %1 to channel %2, sending answer.", sourceNick, parameterList.value(0)), messageTags ); } else { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-%1 request from %2, sending answer.", QStringLiteral("PING"), sourceNick), messageTags ); } m_server->ctcpReply(sourceNick, QString(QStringLiteral("PING %1")).arg(ctcpArgument)); } } // Maybe it was a version request, so act appropriately - else if (ctcpCommand == QStringLiteral("version")) + else if (ctcpCommand == QLatin1String("version")) { if(!isIgnore(prefix,Ignore::CTCP)) { if (isChan) { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received Version request from %1 to channel %2.", sourceNick, parameterList.value(0)), messageTags ); } else { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received Version request from %1.", sourceNick), messageTags ); } QString reply; if (Preferences::self()->customVersionReplyEnabled()) { reply = Preferences::self()->customVersionReply().trimmed(); } else { // Do not internationalize the below version string reply = QString(QStringLiteral("Konversation %1 Build %2 (C) 2002-2018 by the Konversation team")) .arg(QStringLiteral(KONVI_VERSION)) .arg(QString::number(COMMIT)); } if (!reply.isEmpty()) m_server->ctcpReply(sourceNick,QStringLiteral("VERSION ")+reply); } } // DCC request? else if (ctcpCommand==QStringLiteral("dcc") && !isChan && hasArg) { if (!isIgnore(prefix,Ignore::DCC)) { // Extract DCC type and argument list QString dccType=ctcpArgument.toLower().section(QLatin1Char(' '),0,0); // Support file names with spaces QString dccArguments = ctcpArgument.mid(ctcpArgument.indexOf(QLatin1Char(' '))+1); QStringList dccArgumentList; if ((dccArguments.count(QLatin1Char('\"')) >= 2) && (dccArguments.startsWith(QLatin1Char('\"')))) { int lastQuotePos = dccArguments.lastIndexOf(QLatin1Char('\"')); if (dccArguments[lastQuotePos+1] == QLatin1Char(' ')) { QString fileName = dccArguments.mid(1, lastQuotePos-1); dccArguments = dccArguments.mid(lastQuotePos+2); dccArgumentList.append(fileName); } } dccArgumentList += dccArguments.split(QLatin1Char(' '), QString::SkipEmptyParts); if (dccType==QStringLiteral("send")) { if (dccArgumentList.count()==4) { // incoming file konv_app->notificationHandler()->dccIncoming(m_server->getStatusView(), sourceNick); emit addDccGet(sourceNick,dccArgumentList); } else if (dccArgumentList.count() >= 5) { - if (dccArgumentList[dccArgumentList.size() - 3] == QStringLiteral("0")) + if (dccArgumentList[dccArgumentList.size() - 3] == QLatin1Char('0')) { // incoming file (Reverse DCC) konv_app->notificationHandler()->dccIncoming(m_server->getStatusView(), sourceNick); emit addDccGet(sourceNick,dccArgumentList); } else { // the receiver accepted the offer for Reverse DCC emit startReverseDccSendTransfer(sourceNick,dccArgumentList); } } else { m_server->appendMessageToFrontmost(i18n("DCC"), i18n("Received invalid DCC SEND request from %1.", sourceNick), messageTags ); } } else if (dccType==QStringLiteral("accept")) { // resume request was accepted if (dccArgumentList.count() >= 3) { emit resumeDccGetTransfer(sourceNick,dccArgumentList); } else { m_server->appendMessageToFrontmost(i18n("DCC"), i18n("Received invalid DCC ACCEPT request from %1.", sourceNick), messageTags ); } } // Remote client wants our sent file resumed else if (dccType==QStringLiteral("resume")) { if (dccArgumentList.count() >= 3) { emit resumeDccSendTransfer(sourceNick,dccArgumentList); } else { m_server->appendMessageToFrontmost(i18n("DCC"), i18n("Received invalid DCC RESUME request from %1.", sourceNick), messageTags ); } } else if (dccType==QStringLiteral("chat")) { if (dccArgumentList.count() == 3) { // incoming chat emit addDccChat(sourceNick,dccArgumentList); } else if (dccArgumentList.count() == 4) { - if (dccArgumentList[dccArgumentList.size() - 2] == QStringLiteral("0")) + if (dccArgumentList[dccArgumentList.size() - 2] == QLatin1Char('0')) { // incoming chat (Reverse DCC) emit addDccChat(sourceNick,dccArgumentList); } else { // the receiver accepted the offer for Reverse DCC chat emit startReverseDccChat(sourceNick,dccArgumentList); } } else { m_server->appendMessageToFrontmost(i18n("DCC"), i18n("Received invalid DCC CHAT request from %1.", sourceNick), messageTags ); } } else { m_server->appendMessageToFrontmost(i18n("DCC"), i18n("Unknown DCC command %1 received from %2.", ctcpArgument, sourceNick), messageTags ); } } } else if (ctcpCommand==QStringLiteral("clientinfo") && !isChan) { if (!isIgnore(prefix, Ignore::CTCP)) { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-%1 request from %2, sending answer.", QStringLiteral("CLIENTINFO"), sourceNick), messageTags ); m_server->ctcpReply(sourceNick, QStringLiteral("CLIENTINFO ACTION CLIENTINFO DCC PING TIME VERSION")); } } else if (ctcpCommand==QStringLiteral("time") && !isChan) { if (!isIgnore(prefix, Ignore::CTCP)) { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-%1 request from %2, sending answer.", QStringLiteral("TIME"), sourceNick), messageTags ); m_server->ctcpReply(sourceNick, QStringLiteral("TIME ")+QDateTime::currentDateTime().toString()); } } // No known CTCP request, give a general message else { if (!isIgnore(prefix,Ignore::CTCP)) { if (isChan) m_server->appendServerMessageToChannel( parameterList.value(0), QStringLiteral("CTCP"), i18n("Received unknown CTCP-%1 request from %2 to Channel %3.", ctcp, sourceNick, parameterList.value(0)), messageTags ); else m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received unknown CTCP-%1 request from %2.", ctcp, sourceNick), messageTags ); } } } // No CTCP, so it's an ordinary channel or query message else { parsePrivMsg(prefix, parameterList, messageTags); } } else if (command==QStringLiteral("notice") && plHas(2)) { if (!isIgnore(prefix,Ignore::Notice)) { // Channel notice? if(isAChannel(parameterList.value(0))) { if (m_server->identifyMsg() && (trailing.length() > 1 && (trailing.at(0) == QLatin1Char('+') || trailing.at(0) == QLatin1Char('-')))) { - trailing = trailing.mid(1); + trailing.remove(0, 1); } m_server->appendServerMessageToChannel(parameterList.value(0), i18n("Notice"), i18n("-%1 to %2- %3", sourceNick, parameterList.value(0), trailing), messageTags ); } // Private notice else { // Was this a CTCP reply? if (!trailing.isEmpty() && trailing.at(0) == QChar(0x01)) { // cut 0x01 bytes from trailing string QString ctcp(trailing.mid(1,trailing.length()-2)); QString replyReason(ctcp.section(QLatin1Char(' '),0,0)); QString reply(ctcp.section(QLatin1Char(' '),1)); // pong reply, calculate turnaround time if (replyReason.toLower()==QStringLiteral("ping")) { int dateArrived=QDateTime::currentDateTime().toTime_t(); int dateSent=reply.toInt(); int time = dateArrived-dateSent; QString unit = i18np("second", "seconds", time); m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-PING reply from %1: %2 %3.", sourceNick, time, unit), messageTags ); } - else if (replyReason.toLower() == QStringLiteral("dcc")) + else if (replyReason.toLower() == QLatin1String("dcc")) { qDebug() << reply; QStringList dccList = reply.split(QLatin1Char(' ')); //all dcc notices we receive are rejects - if (dccList.count() >= 2 && dccList.first().toLower() == QStringLiteral("reject")) + if (dccList.count() >= 2 && dccList.first().toLower() == QLatin1String("reject")) { dccList.removeFirst(); - if (dccList.count() >= 2 && dccList.first().toLower() == QStringLiteral("send")) + if (dccList.count() >= 2 && dccList.first().toLower() == QLatin1String("send")) { dccList.removeFirst(); emit rejectDccSendTransfer(sourceNick,dccList); } - else if (dccList.first().toLower() == QStringLiteral("chat")) + else if (dccList.first().toLower() == QLatin1String("chat")) { emit rejectDccChat(sourceNick); } } } // all other ctcp replies get a general message else { m_server->appendMessageToFrontmost(i18n("CTCP"), i18n("Received CTCP-%1 reply from %2: %3.", replyReason, sourceNick, reply), messageTags ); } } // No, so it was a normal notice else { #ifdef HAVE_QCA2 //Key exchange if (trailing.startsWith(QLatin1String("DH1080_INIT "))) { m_server->appendMessageToFrontmost(i18n("Notice"), i18n("Received DH1080_INIT from %1", sourceNick), messageTags); m_server->parseInitKeyX(sourceNick, trailing.mid(12)); } else if (trailing.startsWith(QLatin1String("DH1080_FINISH "))) { m_server->appendMessageToFrontmost(i18n("Notice"), i18n("Received DH1080_FINISH from %1", sourceNick), messageTags); m_server->parseFinishKeyX(sourceNick, trailing.mid(14)); } else { #endif m_server->appendMessageToFrontmost(i18n("Notice"), i18n("-%1- %2", sourceNick, m_server->identifyMsg() ? trailing.mid(1) : trailing), messageTags); #ifdef HAVE_QCA2 } #endif } } } } else if (command==QStringLiteral("join") && plHas(1)) { QString channelName; QString account; QString realName; channelName = parameterList[0]; if (m_server->capabilities() & Server::ExtendedJoin && plHas(3)) { if (parameterList[1] != QLatin1String("*")) account = parameterList[1]; realName = parameterList[2]; } // Did we join the channel, or was it someone else? if (m_server->isNickname(sourceNick)) { /* QString key; // TODO: Try to remember channel keys for autojoins and manual joins, so // we can get %k to work if(channelName.contains(' ')) { key=channelName.section(' ',1,1); channelName=channelName.section(' ',0,0); } */ // Join the channel Channel* channel = m_server->joinChannel(channelName, sourceHostmask, messageTags); // Upon JOIN we're going to receive some NAMES input from the server which // we need to be able to tell apart from manual invocations of /names setAutomaticRequest(QStringLiteral("NAMES"),channelName,true); channel->clearModeList(); // Request modes for the channel m_server->queue(QStringLiteral("MODE ")+channelName, Server::LowPriority); // Initiate channel ban list channel->clearBanList(); setAutomaticRequest(QStringLiteral("BANLIST"),channelName,true); m_server->queue(QStringLiteral("MODE ")+channelName+QStringLiteral(" +b"), Server::LowPriority); } else { Channel* channel = m_server->nickJoinsChannel(channelName, sourceNick, sourceHostmask, account, realName, messageTags); konv_app->notificationHandler()->join(channel, sourceNick); } } else if (command==QStringLiteral("kick") && plHas(2)) { m_server->nickWasKickedFromChannel(parameterList.value(0), parameterList.value(1), sourceNick, trailing, messageTags); } else if (command==QStringLiteral("part") && plHas(1)) { // A version of the PART line encountered on ircu: ":Nick!user@host PART :#channel" QString channel(parameterList.value(0)); QString reason(parameterList.value(1)); Channel* channelPtr = m_server->removeNickFromChannel(channel, sourceNick, reason, messageTags); if (sourceNick != m_server->getNickname()) { konv_app->notificationHandler()->part(channelPtr, sourceNick); } } else if (command==QStringLiteral("quit") && plHas(1)) { m_server->removeNickFromServer(sourceNick, trailing, messageTags); if (sourceNick != m_server->getNickname()) { konv_app->notificationHandler()->quit(m_server->getStatusView(), sourceNick); } } else if (command==QStringLiteral("nick") && plHas(1)) { QString newNick(parameterList.value(0)); // Message may not include ":" in front of the new nickname m_server->renameNick(sourceNick, newNick, messageTags); if (sourceNick != m_server->getNickname()) { konv_app->notificationHandler()->nickChange(m_server->getStatusView(), sourceNick, newNick); } } else if (command==QStringLiteral("topic") && plHas(2)) { m_server->setChannelTopic(sourceNick, parameterList.value(0), trailing, messageTags); } else if (command==QStringLiteral("mode") && plHas(2)) // mode #channel -/+ mmm params { parseModes(sourceNick, parameterList, messageTags); Channel* channel = m_server->getChannelByName(parameterList.value(0)); konv_app->notificationHandler()->mode(channel, sourceNick, parameterList.value(0), - QStringList(parameterList.mid(1)).join (QStringLiteral(" "))); + QStringList(parameterList.mid(1)).join(QLatin1Char(' '))); } else if (command==QStringLiteral("invite") && plHas(2)) //:ejm!i=beezle@bas5-oshawa95-1176455927.dsl.bell.ca INVITE argnl :#sug4 { if (!isIgnore(prefix, Ignore::Invite)) { QString channel(trailing); m_server->appendMessageToFrontmost(i18n("Invite"), i18n("%1 invited you to channel %2.", sourceNick, channel), messageTags ); emit invitation(sourceNick, channel); } } - else if (command == QStringLiteral("away")) + else if (command == QLatin1String("away")) { NickInfoPtr nickInfo = m_server->getNickInfo(sourceNick); if (nickInfo) { if (!parameterList.isEmpty()) { nickInfo->setAway(true); nickInfo->setAwayMessage(parameterList.first()); } else { nickInfo->setAway(false); nickInfo->setAwayMessage(QString()); } } else { qDebug() << "Received away message for unknown nick," << sourceNick; } } - else if (command == QStringLiteral("account") && plHas(1)) + else if (command == QLatin1String("account") && plHas(1)) { NickInfoPtr nickInfo = m_server->getNickInfo(sourceNick); QString account = parameterList.first(); if (account == QLatin1String("*")) { nickInfo->setAccount(QString()); } else { nickInfo->setAccount(account); } } else { - qDebug() << "unknown client command" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QStringLiteral(" ")); - m_server->appendMessageToFrontmost(command, parameterList.join(QStringLiteral(" ")), messageTags); + qDebug() << "unknown client command" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QLatin1Char(' ')); + m_server->appendMessageToFrontmost(command, parameterList.join(QLatin1Char(' ')), messageTags); } } void InputFilter::parseServerCommand(const QString &prefix, const QString &command, QStringList ¶meterList, const QHash &messageTags) { bool isNumeric; int numeric = command.toInt(&isNumeric); Q_ASSERT(m_server); if (!m_server) return; if (!isNumeric) { - if (command == QStringLiteral("ping")) + if (command == QLatin1String("ping")) { QString text; - text = (!trailing.isEmpty()) ? trailing : parameterList.join(QStringLiteral(" ")); + text = (!trailing.isEmpty()) ? trailing : parameterList.join(QLatin1Char(' ')); if (!trailing.isEmpty()) { - text = prefix + QStringLiteral(" :") + text; + text = prefix + QLatin1String(" :") + text; } if (!text.startsWith(QLatin1Char(' '))) { text.prepend(QLatin1Char(' ')); } // queue the reply to send it as soon as possible m_server->queue(QStringLiteral("PONG")+text, Server::HighPriority); } - else if (command == QStringLiteral("error :closing link:")) + else if (command == QLatin1String("error :closing link:")) { qDebug() << "link closed"; } - else if (command == QStringLiteral("pong")) + else if (command == QLatin1String("pong")) { // double check if we are in lag measuring mode since some servers fail to send // the LAG cookie back in PONG if (trailing.startsWith(QLatin1String("LAG")) || getLagMeasuring()) { m_server->pongReceived(); } } - else if (command == QStringLiteral("mode")) + else if (command == QLatin1String("mode")) { parseModes(prefix, parameterList, messageTags); } - else if (command == QStringLiteral("notice")) + else if (command == QLatin1String("notice")) { m_server->appendStatusMessage(i18n("Notice"), i18n("-%1- %2", prefix, trailing), messageTags); } - else if (command == QStringLiteral("kick") && plHas(3)) + else if (command == QLatin1String("kick") && plHas(3)) { m_server->nickWasKickedFromChannel(parameterList.value(1), parameterList.value(2), prefix, trailing, messageTags); } - else if (command == QStringLiteral("privmsg")) + else if (command == QLatin1String("privmsg")) { parsePrivMsg(prefix, parameterList, messageTags); } else if (command==QStringLiteral("cap") && plHas(3)) { QString command = parameterList.value(1).toLower(); - if (command == QStringLiteral("ack") || command == QStringLiteral("nak")) + if (command == QLatin1String("ack") || command == QLatin1String("nak")) { m_server->capReply(); QStringList capabilities = parameterList.value(2).split(QLatin1Char(' '), QString::SkipEmptyParts); foreach(const QString& capability, capabilities) { int nameStart = capability.indexOf(QRegExp(QStringLiteral("[a-z0-9]"), Qt::CaseInsensitive)); QString modifierString = capability.left(nameStart); QString name = capability.mid(nameStart); Server::CapModifiers modifiers = Server::NoModifiers; if (modifierString.contains(QLatin1Char('-'))) { modifiers = modifiers | Server::DisMod; modifiers = modifiers ^ Server::NoModifiers; } if (modifierString.contains(QLatin1Char('='))) { modifiers = modifiers | Server::StickyMod; modifiers = modifiers ^ Server::NoModifiers; } if (modifierString.contains(QLatin1Char('~'))) { modifiers = modifiers | Server::AckMod; modifiers = modifiers ^ Server::NoModifiers; } - if (command == QStringLiteral("ack")) + if (command == QLatin1String("ack")) m_server->capAcknowledged(name, modifiers); else m_server->capDenied(name); } if(!m_server->capEndDelayed()) { m_server->capEndNegotiation(); } } else if (command == QLatin1String("ls") || command == QLatin1String("list")) { m_server->appendStatusMessage(i18n("Capabilities"), trailing, messageTags); if (getAutomaticRequest(QStringLiteral("CAP LS"), QString()) != 0) { if (parameterList.count() == 3) setAutomaticRequest (QStringLiteral("CAP LS"), QString (), false); m_server->capInitiateNegotiation (trailing); } } } - else if (command == QStringLiteral("authenticate") && plHas(1)) + else if (command == QLatin1String("authenticate") && plHas(1)) { - if ((m_server->getLastAuthenticateCommand() == QStringLiteral("PLAIN") - || m_server->getLastAuthenticateCommand() == QStringLiteral("EXTERNAL")) - && parameterList.value(0) == QStringLiteral("+")) + if ((m_server->getLastAuthenticateCommand() == QLatin1String("PLAIN") + || m_server->getLastAuthenticateCommand() == QLatin1String("EXTERNAL")) + && parameterList.value(0) == QLatin1String("+")) m_server->registerWithServices(); } // All yet unknown messages go into the frontmost window unaltered else { qDebug() << "unknown server command" << command; - m_server->appendMessageToFrontmost(command, parameterList.join(QStringLiteral(" ")), messageTags); + m_server->appendMessageToFrontmost(command, parameterList.join(QLatin1Char(' ')), messageTags); } } else if (plHas(2)) //[0]==ourNick, [1] needs to be *something* { parseNumeric(prefix, numeric, parameterList, messageTags); } // end of numeric elseif else { - qDebug() << "unknown message format" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QStringLiteral(" ")); + qDebug() << "unknown message format" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QLatin1Char(' ')); } } // end of server void InputFilter::parseModes(const QString &sourceNick, const QStringList ¶meterList, const QHash &messageTags) { const QString modestring=parameterList.value(1); if (!isAChannel(parameterList.value(0))) { QString message; if (parameterList.value(0) == m_server->getNickname()) { if (sourceNick == m_server->getNickname()) { //XXX someone might care about the potentially unnecessary plural here message = i18n("You have set personal modes: %1", modestring); } else { //XXX someone might care about the potentially unnecessary plural here message = i18n("%1 has changed your personal modes: %2", sourceNick, modestring); } } if (!message.isEmpty()) m_server->appendStatusMessage(i18n("Mode"), message, messageTags); return; } bool plus=false; int parameterIndex=0; // List of modes that need a parameter (note exception with -k and -l) // Mode q is quiet on freenode and acts like b... if this is a channel mode on other // networks then more logic is needed here. --MrGrim //FIXME: The assumptions being made here about which modes have parameters and // which don't strike me as very wrong, as some of these mode chars are used // both as user and channel modes (e.g. A and O) and whether they use a para- // meter can vary between those cases. This would seem to lead to trouble with // the list indices down there. --hein, Thu Aug 6 19:48:02 CEST 2009 / r1008015 QString parameterModes = QStringLiteral("aAoOvhkbleIq"); QString message = i18n("%1 sets mode: %2", sourceNick, modestring); for (int index=0;indexupdateChannelMode(sourceNick, parameterList.value(0), mode, plus, parameter, messageTags); } } // endfor if (Preferences::self()->useLiteralModes()) { m_server->appendCommandMessageToChannel(parameterList.value(0), i18n("Mode"), message, messageTags); } } // # & + and ! are *often*, but not necessarily, Channel identifiers. + and ! are non-RFC, // so if a server doesn't offer 005 and supports + and ! channels, I think thats broken behaviour // on their part - not ours. --Argonel bool InputFilter::isAChannel(const QString &check) { if (check.isEmpty()) return false; Q_ASSERT(m_server); // if we ever see the assert, we need the ternary return m_server? m_server->isAChannel(check) : bool(QString(QStringLiteral("#&")).contains(check.at(0))); } bool InputFilter::isIgnore(const QString &sender, Ignore::Type type) { bool doIgnore = false; foreach (Ignore* item, Preferences::ignoreList()) { QRegExp ignoreItem(QRegExp::escape(item->getName()).replace(QStringLiteral("\\*"), QStringLiteral("(.*)")), Qt::CaseInsensitive); if (ignoreItem.exactMatch(sender) && (item->getFlags() & type)) doIgnore = true; if (ignoreItem.exactMatch(sender) && (item->getFlags() & Ignore::Exception)) return false; } return doIgnore; } void InputFilter::reset() { m_automaticRequest.clear(); m_whoRequestList.clear(); } void InputFilter::setAutomaticRequest(const QString& command, const QString& name, bool yes) { m_automaticRequest[command][name.toLower()] += (yes) ? 1 : -1; if(m_automaticRequest[command][name.toLower()]<0) { qDebug() << "( " << command << ", " << name << " ) was negative! Resetting!"; m_automaticRequest[command][name.toLower()]=0; } } int InputFilter::getAutomaticRequest(const QString& command, const QString& name) { return m_automaticRequest[command][name.toLower()]; } void InputFilter::addWhoRequest(const QString& name) { m_whoRequestList << name.toLower(); } bool InputFilter::isWhoRequestUnderProcess(const QString& name) { return (m_whoRequestList.contains(name.toLower())>0); } void InputFilter::setLagMeasuring(bool state) { m_lagMeasuring=state; } bool InputFilter::getLagMeasuring() { return m_lagMeasuring; } void InputFilter::parsePrivMsg(const QString& prefix, QStringList& parameterList, const QHash &messageTags) { int pos = prefix.indexOf(QLatin1Char('!')); QString source; QString sourceHostmask; QString message(trailing); if(pos > 0) { source = prefix.left(pos); sourceHostmask = prefix.mid(pos + 1); } else { source = prefix; } Application* konv_app = Application::instance(); message = konv_app->doAutoreplace(message, false).first; if(isAChannel(parameterList.value(0))) { if(!isIgnore(prefix, Ignore::Channel)) { Channel* channel = m_server->getChannelByName(parameterList.value(0)); if(channel) { QString label; if (m_server->getServerNickPrefixes().contains(parameterList.value(0).at(0))) { label = parameterList.value(0); } channel->append(source, message, messageTags, label); if(source != m_server->getNickname()) { QRegExp regexp(QStringLiteral("(^|[^\\d\\w])") + QRegExp::escape(m_server->loweredNickname()) + QStringLiteral("([^\\d\\w]|$)")); regexp.setCaseSensitivity(Qt::CaseInsensitive); if(message.contains(regexp)) { konv_app->notificationHandler()->nick(channel, source, message); } else { konv_app->notificationHandler()->message(channel, source, message); } } } } } else { if(!isIgnore(prefix,Ignore::Query)) { QString queryName = source; // Handle znc.in/self-message correctly if (source == m_server->getNickname()) queryName = parameterList[0]; NickInfoPtr nickinfo = m_server->obtainNickInfo(queryName); if (queryName == source) nickinfo->setHostmask(sourceHostmask); // Create a new query (server will check for dupes) Query* query = m_server->addQuery(nickinfo, false /*we didn't initiate this*/ ); // send action to query query->appendQuery(source, message, messageTags); if(source != m_server->getNickname() && query) { QRegExp regexp(QStringLiteral("(^|[^\\d\\w])") + QRegExp::escape(m_server->loweredNickname()) + QStringLiteral("([^\\d\\w]|$)")); regexp.setCaseSensitivity(Qt::CaseInsensitive); if(message.contains(regexp)) { konv_app->notificationHandler()->nick(query, source, message); } else { konv_app->notificationHandler()->queryMessage(query, source, message); } } } } } QHash InputFilter::parseMessageTags(const QString &line, int *startOfMessage) { int index = line.indexOf(QLatin1Char(' ')); *startOfMessage = index + 1; QStringList tags = line.mid(1, index - 1).split(QLatin1Char(';')); QHash tagHash; foreach(const QString &tag, tags) { QStringList tagList = tag.split(QLatin1Char('=')); tagHash.insert(tagList.first(), tagList.last()); } return tagHash; } void InputFilter::parseNumeric(const QString &prefix, int command, QStringList ¶meterList, const QHash &messageTags) { //:niven.freenode.net 353 argnl @ #konversation :@argonel psn @argnl bugbot pinotree CIA-13 //QString m_serverAssignedNick(parameterList.takeFirst()); QString m_serverAssignedNick(parameterList.first()); switch (command) { case RPL_WELCOME: case RPL_YOURHOST: case RPL_CREATED: { if (plHas(0)) //make the script happy { if (command == RPL_WELCOME) { QString host; - if (trailing.contains(QStringLiteral("@"))) + if (trailing.contains(QLatin1Char('@'))) host = trailing.section(QLatin1Char('@'), 1); // re-set nickname, since the server may have truncated it if (m_serverAssignedNick != m_server->getNickname()) { m_server->renameNick(m_server->getNickname(), m_serverAssignedNick, messageTags); } // Send the welcome signal, so the server class knows we are connected properly emit welcome(host); m_connecting = true; } m_server->appendStatusMessage(i18n("Welcome"), trailing, messageTags); } break; } case RPL_MYINFO: { if (plHas(5)) { m_server->appendStatusMessage(i18n("Welcome"), i18n("Server %1 (Version %2), User modes: %3, Channel modes: %4", parameterList.value(1), parameterList.value(2), parameterList.value(3), parameterList.value(4)), messageTags ); QString allowed = m_server->allowedChannelModes(); QString newModes = parameterList.value(4); if(!allowed.isEmpty()) //attempt to merge the two { for(int i=0; i < allowed.length(); i++) { if(!newModes.contains(allowed.at(i))) newModes.append(allowed.at(i)); } } m_server->setAllowedChannelModes(newModes); } break; } //case RPL_BOUNCE: // RFC 1459 name, now seems to be obsoleted by ... case RPL_ISUPPORT: // ... DALnet RPL_ISUPPORT { if (plHas(0)) //make the script happy { - m_server->appendStatusMessage(i18n("Support"), parameterList.join(QStringLiteral(" ")), messageTags); + m_server->appendStatusMessage(i18n("Support"), parameterList.join(QLatin1Char(' ')), messageTags); // The following behaviour is neither documented in RFC 1459 nor in 2810-2813 // Nowadays, most ircds send server capabilities out via 005 (BOUNCE). // refer to http://www.irc.org/tech_docs/005.html for a kind of documentation. // More on http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt QStringList::const_iterator it = parameterList.constBegin(); // don't want the user name ++it; for (; it != parameterList.constEnd(); ++it ) { QString property, value; int pos; if ((pos=(*it).indexOf( QLatin1Char('=') )) !=-1) { property = (*it).left(pos); value = (*it).mid(pos+1); } else { property = *it; } if (property==QStringLiteral("PREFIX")) { pos = value.indexOf(QLatin1Char(')'),1); if(pos==-1) { m_server->setPrefixes(QString(), value); // XXX if ) isn't in the string, NOTHING should be there. anyone got a server if (value.length() || property.length()) m_server->appendStatusMessage(QString(), QStringLiteral("XXX Server sent bad PREFIX in RPL_ISUPPORT, please report."), messageTags); } else { m_server->setPrefixes(value.mid(1, pos-1), value.mid(pos+1)); } } else if (property==QStringLiteral("CHANTYPES")) { m_server->setChannelTypes(value); } else if (property==QStringLiteral("MODES")) { if (!value.isEmpty()) { bool ok = false; // If a value is given, it must be numeric. int modesCount = value.toInt(&ok, 10); if(ok) m_server->setModesCount(modesCount); } } - else if (property == QStringLiteral("CAPAB")) + else if (property == QLatin1String("CAPAB")) { // Disable as we don't use this for anything yet //server->queue("CAPAB IDENTIFY-MSG"); } - else if (property == QStringLiteral("CHANMODES")) + else if (property == QLatin1String("CHANMODES")) { if(!value.isEmpty()) { m_server->setChanModes(value); QString allowed = m_server->allowedChannelModes(); QString newModes = value.remove(QLatin1Char(',')); if(!allowed.isEmpty()) //attempt to merge the two { for(int i=0; i < allowed.length(); i++) { if(!newModes.contains(allowed.at(i))) newModes.append(allowed.at(i)); } } m_server->setAllowedChannelModes(newModes); } } - else if (property == QStringLiteral("TOPICLEN")) + else if (property == QLatin1String("TOPICLEN")) { if (!value.isEmpty()) { bool ok = false; int topicLength = value.toInt(&ok); if (ok) m_server->setTopicLength(topicLength); } } - else if (property == QStringLiteral("WHOX")) + else if (property == QLatin1String("WHOX")) { m_server->setHasWHOX(true); } else { //qDebug() << "Ignored server-capability: " << property << " with value '" << value << "'"; } } // endfor } break; } case RPL_UMODEIS: { if (plHas(0)) { // TODO check this one... I amputated + ' '+trailing QString message=QString(QStringLiteral("%1 %2")).arg(i18n("Your personal modes are:")).arg(parameterList.join(QLatin1Char(' ')).section(QLatin1Char(' '),1)); m_server->appendMessageToFrontmost(QStringLiteral("Info"), message, messageTags); } break; } case RPL_CHANNELMODEIS: { if (plHas(2)) { const QString modeString=parameterList.value(2); // TEST this one was a 2 // This is the string the user will see QString modesAre; QString message = i18n("Channel modes: ") + modeString; int parameterCount=3; QHash channelModesHash = Konversation::getChannelModesHash(); for (int index=0;indexupdateChannelModeWidgets(parameterList.value(1), mode, parameter); } } // endfor if (!modesAre.isEmpty() && Preferences::self()->useLiteralModes()) { m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("Mode"), message, messageTags); } else { m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("Mode"), i18n("Channel modes: ") + modesAre, messageTags); } } break; } case RPL_CHANNELURLIS: {// :niven.freenode.net 328 argonel #channel :http://www.buggeroff.com/ if (plHas(3)) { m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("URL"), i18n("Channel URL: %1", trailing), messageTags); } break; } case RPL_CHANNELCREATED: { if (plHas(3)) { QDateTime when; when.setTime_t(parameterList.value(2).toUInt()); m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("Created"), i18n("This channel was created on %1.", QLocale().toString(when, QLocale::ShortFormat)), messageTags ); } break; } case RPL_WHOISACCOUNT: { if (plHas(2)) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); if (nickInfo) { nickInfo->setIdentified(true); } // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is logged in as %2.", parameterList.value(1), parameterList.value(2)), messageTags); } } break; } //:niven.freenode.net 353 argnl @ #konversation :@argonel psn @argnl bugbot pinotree CIA-13 case RPL_NAMREPLY: { if (plHas(4)) { QStringList nickList; if (!trailing.isEmpty()) { nickList = trailing.split(QLatin1Char(' '), QString::SkipEmptyParts); } else if (parameterList.count() > 3) { for(int i = 3; i < parameterList.count(); i++) { nickList.append(parameterList.value(i)); } } else { qDebug() << "Hmm seems something is broken... can't get to the names!"; } // send list to channel m_server->queueNicks(parameterList.value(2), nickList); // TEST this was a 2 // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("NAMES"), parameterList.value(2)) == 0) { m_server->appendMessageToFrontmost(i18n("Names"), trailing, messageTags); } } break; } case RPL_ENDOFNAMES: { if (plHas(2)) { if (getAutomaticRequest(QStringLiteral("NAMES"),parameterList.value(1)) != 0) { // This code path was taken for the automatic NAMES input on JOIN, upcoming // NAMES input for this channel will be manual invocations of /names setAutomaticRequest(QStringLiteral("NAMES"), parameterList.value(1), false); } else { m_server->appendMessageToFrontmost(i18n("Names"), i18n("End of NAMES list."), messageTags); } emit endOfNames(parameterList.value(1)); } break; } // Topic set messages case RPL_NOTOPIC: { if (plHas(2)) { //this really has 3, but [2] is "No topic has been set" m_server->appendMessageToFrontmost(i18n("TOPIC"), i18n("The channel %1 has no topic set.", parameterList.value(1)), messageTags); } break; } case RPL_TOPIC: { if (plHas(3)) { QString topic(trailing); // FIXME: This is an abuse of the automaticRequest system: We're // using it in an inverted manner, i.e. the automaticRequest is // set to true by a manual invocation of /topic. Bad bad bad - // needs rethinking of automaticRequest. if (getAutomaticRequest(QStringLiteral("TOPIC"), parameterList.value(1)) == 0) { // Update channel window m_server->setChannelTopic(parameterList.value(1), topic, messageTags); } else { m_server->appendMessageToFrontmost(i18n("Topic"), i18n("The channel topic for %1 is: \"%2\"", parameterList.value(1), topic), messageTags); } } break; } case RPL_TOPICSETBY: { if (plHas(4)) { // Inform user who set the topic and when QDateTime when; when.setTime_t(parameterList.value(3).toUInt()); // See FIXME in RPL_TOPIC if (getAutomaticRequest(QStringLiteral("TOPIC"), parameterList.value(1)) == 0) { m_server->appendCommandMessageToChannel(parameterList.value(1), i18n("Topic"), i18n("The topic was set by %1 on %2.", parameterList.value(2), QLocale().toString(when, QLocale::ShortFormat)), messageTags, false, false); } else { m_server->appendMessageToFrontmost(i18n("Topic"), i18n("The topic for %1 was set by %2 on %3.", parameterList.value(1), parameterList.value(2), QLocale().toString(when, QLocale::ShortFormat)), messageTags, false); setAutomaticRequest(QStringLiteral("TOPIC"),parameterList.value(1), false); } emit topicAuthor(parameterList.value(1), parameterList.value(2), when); } break; } case RPL_WHOISACTUALLY: { if (plHas(3)) { // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"),parameterList.value(1)) == 0) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is actually using the host %2.", parameterList.value(1), parameterList.value(2)), messageTags); } } break; } case ERR_NOSUCHNICK: { if (plHas(2)) { // Display slightly different error message in case we performed a WHOIS for // IP resolve purposes, and clear it from the automaticRequest list if (getAutomaticRequest(QStringLiteral("DNS"), parameterList.value(1)) == 0) { m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1: No such nick/channel.", parameterList.value(1)), messageTags); } else if(getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) //Display message only if this was not an automatic request. { m_server->appendMessageToFrontmost(i18n("Error"), i18n("No such nick: %1.", parameterList.value(1)), messageTags); setAutomaticRequest(QStringLiteral("DNS"), parameterList.value(1), false); } } break; } case ERR_NOSUCHCHANNEL: { if (plHas(2)) { // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1: No such channel.", parameterList.value(1)), messageTags); } } break; } // Nick already on the server, so try another one case ERR_NICKNAMEINUSE: { if (plHas(1)) { // if we are already connected, don't try tro find another nick ourselves if (m_server->isConnected()) // Show message { m_server->appendMessageToFrontmost(i18n("Nick"), i18n("Nickname already in use, try a different one."), messageTags); } else // not connected yet, so try to find a nick that's not in use { // Get the next nick from the list or ask for a new one QString newNick = m_server->getNextNickname(); // The user chose to disconnect... if (newNick.isNull()) { if (m_server->isConnecting()) // ... or did they? m_server->disconnectServer(); else // No they didn't! m_server->appendMessageToFrontmost(i18n("Info"), i18n("The nickname %1 was already in use, but the connection failed before you responded.", m_server->getNickname()), messageTags); } else { // Update Server window m_server->obtainNickInfo(m_server->getNickname()) ; m_server->renameNick(m_server->getNickname(), newNick, messageTags); // Show message m_server->appendMessageToFrontmost(i18n("Nick"), i18n("Nickname already in use. Trying %1.", newNick), messageTags); // Send nickchange request to the server m_server->queue(QStringLiteral("NICK ")+newNick); } } } break; } case ERR_ERRONEUSNICKNAME: { if (plHas(1)) { if (m_server->isConnected()) { // We are already connected. Just print the error message m_server->appendMessageToFrontmost(i18n("Nick"), trailing, messageTags); } else // Find a new nick as in ERR_NICKNAMEINUSE { QString newNick = m_server->getNextNickname(); // The user chose to disconnect if (newNick.isNull()) { m_server->disconnectServer(); } else { m_server->obtainNickInfo(m_server->getNickname()) ; m_server->renameNick(m_server->getNickname(), newNick, messageTags); m_server->appendMessageToFrontmost(i18n("Nick"), i18n("Erroneous nickname. Changing nick to %1.", newNick), messageTags); m_server->queue(QStringLiteral("NICK ")+newNick); } } } break; } case ERR_NOTONCHANNEL: { if (plHas(2)) { m_server->appendMessageToFrontmost(i18n("Error"), i18n("You are not on %1.", parameterList.value(1)), messageTags); setAutomaticRequest(QStringLiteral("TOPIC"),parameterList.value(1), false); } break; } case RPL_MOTDSTART: { if (plHas(1)) { if (!m_connecting || !Preferences::self()->skipMOTD()) m_server->appendStatusMessage(i18n("MOTD"), i18n("Message of the day:"), messageTags); } break; } case RPL_MOTD: { if (plHas(2)) { if (!m_connecting || !Preferences::self()->skipMOTD()) m_server->appendStatusMessage(i18n("MOTD"), trailing, messageTags); } break; } case RPL_ENDOFMOTD: { if (plHas(1)) { if (!m_connecting || !Preferences::self()->skipMOTD()) m_server->appendStatusMessage(i18n("MOTD"), i18n("End of message of the day"), messageTags); if (m_connecting) m_server->autoCommandsAndChannels(); m_connecting = false; } break; } case ERR_NOMOTD: { if (plHas(1)) { if (m_connecting) m_server->autoCommandsAndChannels(); m_connecting = false; } break; } case ERR_CHANOPRIVSNEEDED: { if (plHas(2)) { m_server->appendMessageToFrontmost(i18n("Error"), i18n("You need to be a channel operator in %1 to do that.", parameterList.value(1)), messageTags); } break; } case RPL_YOUREOPER: { if (plHas(1)) { m_server->appendMessageToFrontmost(i18n("Notice"), i18n("You are now an IRC operator on this server."), messageTags); } break; } case RPL_HOSTHIDDEN: { if (plHas(2)) { m_server->appendStatusMessage(i18n("Info"), i18n("'%1' is now your hidden host (set by services).", parameterList.value(1)), messageTags); } break; } case RPL_GLOBALUSERS: // Current global users: 589 Max: 845 { if (plHas(2)) { QString current(trailing.section(QLatin1Char(' '),3)); //QString max(trailing.section(' ',5,5)); m_server->appendStatusMessage(i18n("Users"), i18n("Current users on the network: %1", current), messageTags); } break; } case RPL_LOCALUSERS: // Current local users: 589 Max: 845 { if (plHas(2)) { QString current(trailing.section(QLatin1Char(' '), 3)); //QString max(trailing.section(' ',5,5)); m_server->appendStatusMessage(i18n("Users"), i18n("Current users on %1: %2.", prefix, current), messageTags); } break; } case RPL_ISON: { if (plHas(2)) { // Tell server to start the next notify timer round emit notifyResponse(trailing); } break; } case RPL_AWAY: { if (plHas(3)) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); if (nickInfo) { nickInfo->setAway(true); if (nickInfo->getAwayMessage() != trailing) // FIXME i think this check should be in the setAwayMessage method { nickInfo->setAwayMessage(trailing); // TEST this used to skip the automatic request handler below } } if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { m_server->appendMessageToFrontmost(i18n("Away"), i18n("%1 is away: %2", parameterList.value(1), trailing), messageTags ); } } break; } case RPL_INVITING: { if (plHas(3)) { m_server->appendMessageToFrontmost(i18n("Invite"), i18n("You invited %1 to channel %2.", parameterList.value(1), parameterList.value(2)), messageTags ); } break; } //Sample WHOIS response //"/WHOIS psn" //[19:11] :zahn.freenode.net 311 PhantomsDad psn ~psn h106n2fls23o1068.bredband.comhem.se * :Peter Simonsson //[19:11] :zahn.freenode.net 319 PhantomsDad psn :#kde-devel #koffice //[19:11] :zahn.freenode.net 312 PhantomsDad psn irc.freenode.net :http://freenode.net/ //[19:11] :zahn.freenode.net 301 PhantomsDad psn :away //[19:11] :zahn.freenode.net 320 PhantomsDad psn :is an identified user //[19:11] :zahn.freenode.net 317 PhantomsDad psn 4921 1074973024 :seconds idle, signon time //[19:11] :zahn.freenode.net 318 PhantomsDad psn :End of /WHOIS list. case RPL_WHOISUSER: { if (plHas(4)) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); if (nickInfo) { nickInfo->setHostmask(i18n("%1@%2", parameterList.value(2), parameterList.value(3))); nickInfo->setRealName(trailing); } // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { // escape html tags QString escapedRealName(trailing); m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is %2@%3 (%4)", parameterList.value(1), parameterList.value(2), parameterList.value(3), escapedRealName), messageTags, false); // Don't parse any urls } else { // This WHOIS was requested by Server for DNS resolve purposes; try to resolve the host if (getAutomaticRequest(QStringLiteral("DNS"), parameterList.value(1)) != 0) { QHostInfo resolved = QHostInfo::fromName(parameterList.value(3)); if (resolved.error() == QHostInfo::NoError && !resolved.addresses().isEmpty()) { QString ip = resolved.addresses().first().toString(); m_server->appendMessageToFrontmost(i18n("DNS"), i18n("Resolved %1 (%2) to address: %3", parameterList.value(1), parameterList.value(3), ip), messageTags ); } else { m_server->appendMessageToFrontmost(i18n("Error"), i18n("Unable to resolve address for %1 (%2)", parameterList.value(1), parameterList.value(3)), messageTags ); } // Clear this from the automaticRequest list so it works repeatedly setAutomaticRequest(QStringLiteral("DNS"), parameterList.value(1), false); } } } break; } // From a WHOIS. //[19:11] :zahn.freenode.net 320 PhantomsDad psn :is an identified user case RPL_WHOISIDENTIFY: case RPL_IDENTIFIED: { if (plHas(2)) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); if (nickInfo) { nickInfo->setIdentified(true); } if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { // Prints "psn is an identified user" //server->appendStatusMessage(i18n("Whois"),parameterList.join(" ").section(' ',1)+' '+trailing); // The above line works fine, but can't be i18n'ised. So use the below instead.. I hope this is okay. m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is an identified user.", parameterList.value(1)), messageTags); } } break; } case RPL_WHOISSECURE: { if (plHas(2)) { if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is using a secure connection.", parameterList.value(1)), messageTags); } break; } // Sample WHO response //"/WHO #lounge" //[21:39] [352] #lounge jasmine bots.worldforge.org irc.worldforge.org jasmine H 0 jasmine //[21:39] [352] #lounge ~Nottingha worldforge.org irc.worldforge.org SherwoodSpirit H 0 Arboreal Entity case RPL_WHOSPCRPL: case RPL_WHOREPLY: { if (plHas(6)) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(5)); // G=away G@=away,op G+=away,voice bool bAway = parameterList.value(6).toUpper().startsWith(QLatin1Char('G')); QString realName = trailing; if (realName.indexOf (QRegExp(QStringLiteral("\\d\\s"))) == 0) realName = realName.mid (2); if (nickInfo) { nickInfo->setHostmask(i18n("%1@%2", parameterList.value(2), parameterList.value(3))); nickInfo->setRealName(realName); nickInfo->setAway(bAway); if(!bAway) { nickInfo->setAwayMessage(QString()); } if(m_server->capabilities() & Server::WHOX && m_server->capabilities() & Server::ExtendedJoin) { nickInfo->setAccount(parameterList.value(8)); } } // Display message only if this was not an automatic request. if (!m_whoRequestList.isEmpty()) // for safe { if (getAutomaticRequest(QStringLiteral("WHO"),m_whoRequestList.front())==0) { m_server->appendMessageToFrontmost(i18n("Who"), i18n("%1 is %2@%3 (%4)%5", parameterList.value(5), parameterList.value(2), parameterList.value(3), realName, bAway?i18n(" (Away)"):QString()), messageTags, false); // Don't parse as url } } } break; } case RPL_ENDOFWHO: { if (plHas(2)) { if (!m_whoRequestList.isEmpty()) { const QString param = parameterList.value(1).toLower(); // for safety const int idx = m_whoRequestList.indexOf(param); if (idx > -1) { if (getAutomaticRequest(QStringLiteral("WHO"), param) == 0) { m_server->appendMessageToFrontmost(i18n("Who"), i18n("End of /WHO list for %1", parameterList.value(1)), messageTags); } else { setAutomaticRequest(QStringLiteral("WHO"), param, false); } m_whoRequestList.removeAt(idx); } else { // whoRequestList seems to be broken. qDebug() << "RPL_ENDOFWHO: malformed ENDOFWHO. retrieved: " << parameterList.value(1) << " expected: " << m_whoRequestList.front(); m_whoRequestList.clear(); } } else { qDebug() << "RPL_ENDOFWHO: unexpected ENDOFWHO. retrieved: " << parameterList.value(1); } emit endOfWho(parameterList.value(1)); } break; } case RPL_WHOISCHANNELS: { if (plHas(3)) { QStringList userChannels,voiceChannels,opChannels,halfopChannels,ownerChannels,adminChannels; // get a list of all channels the user is in QStringList channelList=trailing.split(QLatin1Char(' '), QString::SkipEmptyParts); channelList.sort(); // split up the list in channels where they are operator / user / voice for (int index=0; index < channelList.count(); index++) { QString lookChannel=channelList[index]; if (lookChannel.startsWith(QLatin1Char('*')) || lookChannel.startsWith(QLatin1Char('&'))) { adminChannels.append(lookChannel.mid(1)); m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 16); } // See bug #97354 part 2 else if((lookChannel.startsWith(QLatin1Char('!')) || lookChannel.startsWith(QLatin1Char('~'))) && m_server->isAChannel(lookChannel.mid(1))) { ownerChannels.append(lookChannel.mid(1)); m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 8); } // See bug #97354 part 1 - else if (lookChannel.startsWith(QStringLiteral("@+"))) + else if (lookChannel.startsWith(QLatin1String("@+"))) { opChannels.append(lookChannel.mid(2)); m_server->setChannelNick(lookChannel.mid(2), parameterList.value(1), 4); } else if (lookChannel.startsWith(QLatin1Char('@'))) { opChannels.append(lookChannel.mid(1)); m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 4); } else if (lookChannel.startsWith(QLatin1Char('%'))) { halfopChannels.append(lookChannel.mid(1)); m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 2); } else if (lookChannel.startsWith(QLatin1Char('+'))) { voiceChannels.append(lookChannel.mid(1)); m_server->setChannelNick(lookChannel.mid(1), parameterList.value(1), 1); } else { userChannels.append(lookChannel); m_server->setChannelNick(lookChannel, parameterList.value(1), 0); } } // endfor // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { if (userChannels.count()) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is a user on channels: %2", parameterList.value(1), - userChannels.join(QStringLiteral(" "))), messageTags + userChannels.join(QLatin1Char(' '))), messageTags ); } if (voiceChannels.count()) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 has voice on channels: %2", - parameterList.value(1), voiceChannels.join(QStringLiteral(" "))), messageTags + parameterList.value(1), voiceChannels.join(QLatin1Char(' '))), messageTags ); } if (halfopChannels.count()) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is a halfop on channels: %2", - parameterList.value(1), halfopChannels.join(QStringLiteral(" "))), messageTags + parameterList.value(1), halfopChannels.join(QLatin1Char(' '))), messageTags ); } if (opChannels.count()) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is an operator on channels: %2", - parameterList.value(1), opChannels.join(QStringLiteral(" "))), messageTags + parameterList.value(1), opChannels.join(QLatin1Char(' '))), messageTags ); } if (ownerChannels.count()) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is owner of channels: %2", - parameterList.value(1), ownerChannels.join(QStringLiteral(" "))), messageTags + parameterList.value(1), ownerChannels.join(QLatin1Char(' '))), messageTags ); } if (adminChannels.count()) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is admin on channels: %2", - parameterList.value(1), adminChannels.join(QStringLiteral(" "))), messageTags + parameterList.value(1), adminChannels.join(QLatin1Char(' '))), messageTags ); } } } break; } case RPL_WHOISSERVER: { if (plHas(4)) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); if (nickInfo) { nickInfo->setNetServer(parameterList.value(2)); nickInfo->setNetServerInfo(trailing); // Clear the away state on assumption that if nick is away, this message will be followed // by a 301 RPL_AWAY message. Not necessary a invalid assumption, but what can we do? nickInfo->setAway(false); nickInfo->setAwayMessage(QString()); } // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is online via %2 (%3).", parameterList.value(1), parameterList.value(2), trailing), messageTags ); } } break; } case RPL_WHOISHELPER: { if (plHas(2)) { // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is available for help.", parameterList.value(1)), messageTags ); } } break; } case RPL_WHOISOPERATOR: { if (plHas(2)) { // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { if (trailing.toLower().simplified().startsWith(QLatin1String("is an irc operator"))) m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 is an IRC Operator.", parameterList.value(1)), messageTags); else m_server->appendMessageToFrontmost(i18n("Whois"), QString(QStringLiteral("%1 %2")).arg(parameterList.value(1)).arg(trailing), messageTags); } } break; } case RPL_WHOISIDLE: { if (plHas(3)) { // get idle time in seconds bool ok = false; long seconds = parameterList.value(2).toLong(&ok); if (!ok) break; long minutes = seconds/60; long hours = minutes/60; long days = hours/24; QDateTime signonTime; uint signonTimestamp = parameterList.value(3).toUInt(&ok); if (ok && parameterList.count() == 5) signonTime.setTime_t(signonTimestamp); if (!signonTime.isNull()) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); if (nickInfo) nickInfo->setOnlineSince(signonTime); } // if idle time is longer than a day // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { if (days) { const QString daysString = i18np("1 day", "%1 days", days); const QString hoursString = i18np("1 hour", "%1 hours", (hours % 24)); const QString minutesString = i18np("1 minute", "%1 minutes", (minutes % 60)); const QString secondsString = i18np("1 second", "%1 seconds", (seconds % 60)); m_server->appendMessageToFrontmost(i18n("Whois"), i18nc("%1 = name of person, %2 = (x days), %3 = (x hours), %4 = (x minutes), %5 = (x seconds)", "%1 has been idle for %2, %3, %4, and %5.", parameterList.value(1), daysString, hoursString, minutesString, secondsString), messageTags); // or longer than an hour } else if (hours) { const QString hoursString = i18np("1 hour", "%1 hours", hours); const QString minutesString = i18np("1 minute", "%1 minutes", (minutes % 60)); const QString secondsString = i18np("1 second", "%1 seconds", (seconds % 60)); m_server->appendMessageToFrontmost(i18n("Whois"), i18nc("%1 = name of person, %2 = (x hours), %3 = (x minutes), %4 = (x seconds)", "%1 has been idle for %2, %3, and %4.", parameterList.value(1), hoursString, minutesString, secondsString), messageTags); // or longer than a minute } else if (minutes) { const QString minutesString = i18np("1 minute", "%1 minutes", minutes); const QString secondsString = i18np("1 second", "%1 seconds", (seconds % 60)); m_server->appendMessageToFrontmost(i18n("Whois"), i18nc("%1 = name of person, %2 = (x minutes), %3 = (x seconds)", "%1 has been idle for %2 and %3.", parameterList.value(1), minutesString, secondsString), messageTags); // or just some seconds } else { m_server->appendMessageToFrontmost(i18n("Whois"), i18np("%2 has been idle for 1 second.", "%2 has been idle for %1 seconds.", seconds, parameterList.value(1)), messageTags); } if (!signonTime.isNull()) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("%1 has been online since %2.", parameterList.value(1), QLocale().toString(signonTime, QLocale::ShortFormat)), messageTags); } } } break; } case RPL_ENDOFWHOIS: { if (plHas(2)) { /*FIXME why is the nickinfo line below commented out? //NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(1)); */ // Display message only if this was not an automatic request. if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) == 0) { m_server->appendMessageToFrontmost(i18n("Whois"), i18n("End of WHOIS list."), messageTags); } // was this an automatic request? if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) != 0) { setAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1), false); } } break; } case RPL_USERHOST: { if (plHas(2)) { // iterate over all nick/masks in reply QStringList uhosts=trailing.split(QLatin1Char(' '), QString::SkipEmptyParts); for (int index=0;indexappendMessageToFrontmost(i18n("Userhost"), i18nc("%1 = nick, %2 = shows if nick is op, %3 = hostmask, %4 = shows away", "%1%2 is %3%4.", nick, (ircOp) ? i18n(" (IRC Operator)") : QString() ,mask, (away) ? i18n(" (away)") : QString()), messageTags); } // was this an automatic request? if (getAutomaticRequest(QStringLiteral("USERHOST"),nick)!=0) { setAutomaticRequest(QStringLiteral("USERHOST"),nick,false); } } // for } break; } case RPL_LISTSTART: //FIXME This reply is obsolete!!! { if (plHas(0)) { if (getAutomaticRequest(QStringLiteral("LIST"),QString())==0) { m_server->appendMessageToFrontmost(i18n("List"), i18n("List of channels:"), messageTags); } } break; } case RPL_LIST: { if (plHas(3)) { if (getAutomaticRequest(QStringLiteral("LIST"),QString())==0) { QString message; message=i18np("%2 (%1 user): %3", "%2 (%1 users): %3", parameterList.value(2).toInt(), parameterList.value(1), trailing); m_server->appendMessageToFrontmost(i18n("List"), message, messageTags); } else // send them to /LIST window { emit addToChannelList(parameterList.value(1), parameterList.value(2).toInt(), trailing); } } break; } case RPL_LISTEND: { if (plHas(0)) { // was this an automatic request? if (getAutomaticRequest(QStringLiteral("LIST"),QString())==0) { m_server->appendMessageToFrontmost(i18n("List"), i18n("End of channel list."), messageTags); } else { emit endOfChannelList(); setAutomaticRequest(QStringLiteral("LIST"),QString(),false); } } break; } case RPL_NOWAWAY: { if (plHas(1)) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(0)); if (nickInfo) { nickInfo->setAway(true); } m_server->setAway(true, messageTags); } break; } case RPL_UNAWAY: { if (plHas(1)) { NickInfoPtr nickInfo = m_server->getNickInfo(parameterList.value(0)); if (nickInfo) { nickInfo->setAway(false); nickInfo->setAwayMessage(QString()); } m_server->setAway(false, messageTags); } break; } case RPL_BANLIST: { //:calvino.freenode.net 367 argonel #konversation fooish!~a@example.com argonel!argkde4@konversation/developer/argonel 1269464382 if (plHas(3)) { if (getAutomaticRequest(QStringLiteral("BANLIST"), parameterList.value(1))) { - m_server->addBan(parameterList.value(1), parameterList.join(QStringLiteral(" ")).section(QLatin1Char(' '), 2, 4)); //<-- QString::Section handles out of bounds end parameter + m_server->addBan(parameterList.value(1), parameterList.join(QLatin1Char(' ')).section(QLatin1Char(' '), 2, 4)); //<-- QString::Section handles out of bounds end parameter } else { QDateTime when; if (plHas(5)) when.setTime_t(parameterList.value(4).toUInt()); else when = QDateTime::currentDateTime(); //use todays date instead of Jan 1 1970 QString setter(parameterList.value(3, i18nc("The server didn't respond with the identity of the ban creator, so we say unknown (in brackets to avoid confusion with a real nickname)", "(unknown)")).section(QLatin1Char('!'), 0, 0)); m_server->appendMessageToFrontmost(i18n("BanList:%1", parameterList.value(1)), i18nc("BanList message: e.g. *!*@aol.com set by MrGrim on ", "%1 set by %2 on %3", parameterList.value(2), setter, QLocale().toString(when, QLocale::ShortFormat)), messageTags ); } } break; } case RPL_ENDOFBANLIST: { if (plHas(2)) { if (getAutomaticRequest(QStringLiteral("BANLIST"), parameterList.value(1))) { setAutomaticRequest(QStringLiteral("BANLIST"), parameterList.value(1), false); } else { m_server->appendMessageToFrontmost(i18n("BanList:%1", parameterList.value(1)), i18n("End of Ban List."), messageTags); } } break; } case ERR_NOCHANMODES: { if (plHas(3)) { ChatWindow *chatwindow = m_server->getChannelByName(parameterList.value(1)); if (chatwindow) { chatwindow->appendServerMessage(i18n("Channel"), trailing, messageTags); } else // We couldn't join the channel , so print the error. with [#channel] : { m_server->appendMessageToFrontmost(i18n("Channel"), trailing, messageTags); } } break; } case ERR_NOSUCHSERVER: { if (plHas(2)) { //Some servers don't know their name, so they return an error instead of the PING data if (getLagMeasuring() && trailing.startsWith(prefix)) { m_server->pongReceived(); } else if (getAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1)) != 0) //Inhibit message if this was an automatic request { setAutomaticRequest(QStringLiteral("WHOIS"), parameterList.value(1), false); } else { m_server->appendMessageToFrontmost(i18n("Error"), i18n("No such server: %1.", parameterList.value(1)), messageTags); } } break; } case ERR_UNAVAILRESOURCE: { if (plHas(2)) { if (m_server->isConnected()) m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1 is currently unavailable.", parameterList.value(1)), messageTags); else { QString newNick = m_server->getNextNickname(); // The user chose to disconnect if (newNick.isNull()) m_server->disconnectServer(); else { m_server->obtainNickInfo(m_server->getNickname()) ; m_server->renameNick(m_server->getNickname(), newNick, messageTags); m_server->appendMessageToFrontmost(i18n("Nick"), i18n("Nickname %1 is unavailable. Trying %2.", parameterList.value(1), newNick), messageTags); m_server->queue(QStringLiteral("NICK ")+newNick); } } } break; } case RPL_HIGHCONNECTCOUNT: case RPL_LUSERCLIENT: case RPL_LUSEROP: case RPL_LUSERUNKNOWN: case RPL_LUSERCHANNELS: case RPL_LUSERME: { // TODO make sure this works, i amputated the "+ ' '+trailing" if (plHas(0)) { - m_server->appendStatusMessage(i18n("Users"), parameterList.join(QStringLiteral(" ")).section(QLatin1Char(' '),1), messageTags); + m_server->appendStatusMessage(i18n("Users"), parameterList.join(QLatin1Char(' ')).section(QLatin1Char(' '),1), messageTags); } break; } case ERR_UNKNOWNCOMMAND: { if (plHas(2)) { m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1: Unknown command.", parameterList.value(1)), messageTags); } break; } case ERR_NOTREGISTERED: { if (plHas(0)) { m_server->appendMessageToFrontmost(i18n("Error"), i18n("Not registered."), messageTags); } break; } case ERR_NEEDMOREPARAMS: { if (plHas(2)) { m_server->appendMessageToFrontmost(i18n("Error"), i18n("%1: This command requires more parameters.", parameterList.value(1)), messageTags); } break; } case RPL_CAPAB: // Special freenode reply afaik { if (plHas(2)) { // Disable as we don't use this for anything yet - if (trailing.contains(QStringLiteral("IDENTIFY-MSG"))) + if (trailing.contains(QLatin1String("IDENTIFY-MSG"))) { m_server->enableIdentifyMsg(true); } else // TEST is this right? split the logic up in prep for slotization { - m_server->appendMessageToFrontmost(QString::number(command), parameterList.join(QStringLiteral(" ")).section(QLatin1Char(' '),1) + QLatin1Char(' ')+trailing, messageTags); + m_server->appendMessageToFrontmost(QString::number(command), parameterList.join(QLatin1Char(' ')).section(QLatin1Char(' '),1) + QLatin1Char(' ')+trailing, messageTags); } } break; } case ERR_BADCHANNELKEY: { if (plHas(2)) { m_server->appendMessageToFrontmost(i18n("Error"), i18n("Cannot join %1: The channel is password-protected and either a wrong or no password was given.", parameterList.value(1)), messageTags); } break; } case RPL_LOGGEDIN: { if (plHas(3)) m_server->appendStatusMessage(i18n("Info"), i18n("You are now logged in as %1.", parameterList.value(2)), messageTags); break; } case RPL_SASLSUCCESS: { if (plHas(2)) { m_server->appendStatusMessage(i18n("Info"), i18n("SASL authentication successful."), messageTags); m_server->capEndNegotiation(); NickInfoPtr nickInfo = m_server->getNickInfo(m_server->getNickname()); if (nickInfo) nickInfo->setIdentified(true); } break; } case ERR_SASLFAIL: { if (plHas(2)) { m_server->appendStatusMessage(i18n("Error"), i18n("SASL authentication attempt failed."), messageTags); m_server->capEndNegotiation(); } break; } case ERR_SASLABORTED: { if (plHas(2)) m_server->appendStatusMessage(i18n("Info"), i18n("SASL authentication aborted."), messageTags); break; } default: { // All yet unknown messages go into the frontmost window without the // preceding nickname - qDebug() << "unknown numeric" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QStringLiteral(" ")); - m_server->appendMessageToFrontmost(QString::number(command), parameterList.join(QStringLiteral(" ")), messageTags); + qDebug() << "unknown numeric" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QLatin1Char(' ')); + m_server->appendMessageToFrontmost(QString::number(command), parameterList.join(QLatin1Char(' ')), messageTags); } } // end of numeric switch if (!_plHad) - qDebug() << "numeric format error" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QStringLiteral(" ")); + qDebug() << "numeric format error" << parameterList.count() << _plHad << _plWanted << command << parameterList.join(QLatin1Char(' ')); } // kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on; // vim: set et sw=4 ts=4 cino=l1,cs,U1: diff --git a/src/irc/invitedialog.cpp b/src/irc/invitedialog.cpp index 1457a9b6..dbfb0108 100644 --- a/src/irc/invitedialog.cpp +++ b/src/irc/invitedialog.cpp @@ -1,222 +1,222 @@ // Copyright 2009 Peter Simonsson // // 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) version 3 or any later version // accepted by the membership of KDE e.V. (or its successor approved // by the membership of KDE e.V.), which shall act as a proxy // defined in Section 14 of version 3 of the license. // // 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, see . #include "invitedialog.h" #include #include #include #include #include #include #include #include InviteDialog::InviteDialog(QWidget* parent) : QDialog(parent), Ui::InviteDialog() { setAttribute(Qt::WA_DeleteOnClose); setWindowTitle(i18n("Channel Invites")); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &InviteDialog::slotOk); connect(buttonBox, &QDialogButtonBox::rejected, this, &InviteDialog::reject); QWidget* mainWidget = new QWidget(this); mainLayout->addWidget(mainWidget); mainLayout->addWidget(buttonBox); setupUi(mainWidget); m_iconLabel->setPixmap(QIcon::fromTheme(QStringLiteral("irc-join-channel")).pixmap(48)); m_channelModel = new InviteChannelListModel(m_channelView); m_channelView->setModel(m_channelModel); m_channelView->setRootIsDecorated(false); m_channelView->setUniformRowHeights(true); } void InviteDialog::addInvite(const QString& nickname, const QString& channel) { m_channelModel->addInvite(nickname, channel); m_channelView->resizeColumnToContents(0); } void InviteDialog::slotOk() { QString channels = m_channelModel->selectedChannels(); if(!channels.isEmpty()) emit joinChannelsRequested(channels); KConfigGroup::WriteConfigFlags flags = KConfig::Persistent; KConfigGroup cg(KSharedConfig::openConfig().data(), "Notification Messages"); cg.writeEntry("Invitation", m_joinPreferences->currentIndex(), flags); cg.sync(); accept(); } bool InviteDialog::shouldBeShown(QDialogButtonBox::StandardButton& buttonCode) { KConfigGroup cg(KSharedConfig::openConfig().data(), "Notification Messages"); cg.sync(); const QString dontAsk = cg.readEntry("Invitation", QString()).toLower(); - if (dontAsk == QStringLiteral("1")) + if (dontAsk == QLatin1Char('1')) { buttonCode = QDialogButtonBox::Ok; return false; } - else if (dontAsk == QStringLiteral("2")) + else if (dontAsk == QLatin1Char('2')) { buttonCode = QDialogButtonBox::Cancel; return false; } return true; } // // InviteChannelListModel // InviteChannelListModel::InviteChannelListModel(QObject* parent) : QAbstractListModel(parent) { } void InviteChannelListModel::addInvite(const QString& nickname, const QString& channel) { if(m_channelMap.contains(channel)) { if(!m_channelMap[channel].nicknames.contains(nickname)) { - m_channelMap[channel].nicknames += QStringLiteral(", ") + nickname; + m_channelMap[channel].nicknames += QLatin1String(", ") + nickname; } } else { ChannelItem item; item.channel = channel; item.nicknames = nickname; item.checkState = Qt::Unchecked; m_channelMap.insert(channel, item); } beginResetModel(); endResetModel(); } int InviteChannelListModel::rowCount(const QModelIndex&) const { return m_channelMap.count(); } int InviteChannelListModel::columnCount(const QModelIndex&) const { return 3; } QVariant InviteChannelListModel::data(const QModelIndex& index, int role) const { if(!index.isValid()) return QVariant(); if(role == Qt::DisplayRole) { switch(index.column()) { case 1: return m_channelMap.values().at(index.row()).channel; case 2: return m_channelMap.values().at(index.row()).nicknames; default: return QVariant(); } } if(role == Qt::SizeHintRole) { return QSize(0, qMax(qApp->style()->sizeFromContents(QStyle::CT_CheckBox, nullptr, QSize(0, 0), nullptr).height(), qApp->fontMetrics().height())); } else if((role == Qt::CheckStateRole) && (index.column() == 0)) { return m_channelMap.values().at(index.row()).checkState; } return QVariant(); } QVariant InviteChannelListModel::headerData(int section, Qt::Orientation orientation, int role) const { if(role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); switch(section) { case 0: return i18n("Join"); case 1: return i18n("Channel"); case 2: return i18n("Nickname"); default: return QVariant(); } } Qt::ItemFlags InviteChannelListModel::flags(const QModelIndex& index) const { if(!index.isValid() || index.column() > 0) return Qt::ItemIsEnabled; else return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled; } QString InviteChannelListModel::selectedChannels() const { QStringList channels; foreach(const ChannelItem& item, m_channelMap) { if(item.checkState == Qt::Checked) channels.append(item.channel); } - return channels.join(QStringLiteral(",")); + return channels.join(QLatin1Char(',')); } bool InviteChannelListModel::setData(const QModelIndex& index, const QVariant& value, int role) { if(!index.isValid() || !value.isValid() || index.column() > 0 || role != Qt::CheckStateRole) return false; QString channel = m_channelMap.values().at(index.row()).channel; m_channelMap[channel].checkState = (value.toBool() ? Qt::Checked : Qt::Unchecked); emit dataChanged(index, index); return true; } diff --git a/src/irc/irccharsets.cpp b/src/irc/irccharsets.cpp index b6bd1316..bc9f9c87 100644 --- a/src/irc/irccharsets.cpp +++ b/src/irc/irccharsets.cpp @@ -1,162 +1,162 @@ // A wrapper for KCharsets // Copyright (C) 2004, 2006 Shintaro Matsuoka /* 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. */ #include "irccharsets.h" #include #include #include namespace Konversation { struct IRCCharsetsSingleton { IRCCharsets charsets; }; } Q_GLOBAL_STATIC(Konversation::IRCCharsetsSingleton, s_charsets) namespace Konversation { IRCCharsets *IRCCharsets::self() { return &s_charsets->charsets; } QStringList IRCCharsets::availableEncodingShortNames() const { return m_shortNames; } QStringList IRCCharsets::availableEncodingDescriptiveNames() const { return m_descriptiveNames; } int IRCCharsets::availableEncodingsCount() { return m_shortNames.count(); } QString IRCCharsets::shortNameToDescriptiveName( const QString& shortName ) { return m_descriptiveNames[ shortNameToIndex( shortName ) ]; } QString descriptiveNameToShortName( const QString& descriptiveName ) { return KCharsets::charsets()->encodingForName( descriptiveName ); } QString IRCCharsets::ambiguousNameToShortName( const QString& ambiguousName ) { // simplify ambiguousName QString simplifiedAmbiguousName( ambiguousName.toLower() ); simplifiedAmbiguousName.remove( QRegExp( QStringLiteral("[^a-z0-9]") )); // search m_simplifiedShortNames if(m_simplifiedShortNames.contains(simplifiedAmbiguousName)) return m_simplifiedShortNames[simplifiedAmbiguousName]; // failed return QString(); } int IRCCharsets::shortNameToIndex( const QString& shortName ) { int index = 0; for ( QStringList::iterator it = m_shortNames.begin() ; it != m_shortNames.end() ; ++it ) { if ( (*it) == shortName ) return index; ++index; } return -1; } bool IRCCharsets::isValidEncoding( const QString& shortName ) { return ( m_shortNames.contains( shortName ) > 0 ); } QString IRCCharsets::encodingForLocale() { // Special cases // don't add conditions for the languages for which QTextCodec::codecForLocale() returns a correct codec. // Japanese networks prefer jis7 over Utf-8. - if (QLocale::system().name() == QStringLiteral("ja_JP")) + if (QLocale::system().name() == QLatin1String("ja_JP")) return QStringLiteral("ISO-2022-JP"); // it's a little hacky.. for (QStringList::iterator it = m_shortNames.begin() ; it != m_shortNames.end() ; ++it) if (QTextCodec::codecForName( (*it).toLatin1() ) == QTextCodec::codecForLocale()) return *it; return QStringLiteral("UTF-8"); } QTextCodec* IRCCharsets::codecForName( const QString& shortName ) { // Qt 5 / KCharsets seem to no longer support jis7 in common builds, but we have // to assume existing user config. - if (shortName == QStringLiteral("jis7")) + if (shortName == QLatin1String("jis7")) return KCharsets::charsets()->codecForName(QStringLiteral("ISO-2022-JP")); else return KCharsets::charsets()->codecForName(shortName.toLatin1()); } IRCCharsets::IRCCharsets() { // Add some aliases // use only [a-z0-9] for keys! m_simplifiedShortNames[QStringLiteral("unicode")] = QStringLiteral("UTF-8"); m_simplifiedShortNames[QStringLiteral("latin1")] = QStringLiteral("ISO 8859-1"); // setup m_shortNames, m_descriptiveNames, m_simplifiedShortNames QRegExp reSimplify( QStringLiteral("[^a-zA-Z0-9]") ); m_descriptiveNames = KCharsets::charsets()->descriptiveEncodingNames(); QStringList::Iterator it = m_descriptiveNames.begin(); while ( it != m_descriptiveNames.end() ) { QString encodingName = KCharsets::charsets()->encodingForName( *it ); // exclude encodings which are not supported on IRC // 10646-UCS-2 & ucs2 are both UTF-16 - if ( encodingName == QStringLiteral("ISO 10646-UCS-2") || - encodingName == QStringLiteral("ucs2") || - encodingName == QStringLiteral("UTF-16") || - encodingName == QStringLiteral("utf7") ) + if ( encodingName == QLatin1String("ISO 10646-UCS-2") || + encodingName == QLatin1String("ucs2") || + encodingName == QLatin1String("UTF-16") || + encodingName == QLatin1String("utf7") ) { it = m_descriptiveNames.erase( it ); } else { m_shortNames.append( encodingName ); m_simplifiedShortNames.insert( encodingName.remove( reSimplify ).toLower(), m_shortNames.last() ); - if(encodingName == QStringLiteral("jis7")) // Add iso-2022-jp which is same as jis7 but not in Qt + if(encodingName == QLatin1String("jis7")) // Add iso-2022-jp which is same as jis7 but not in Qt { it = m_descriptiveNames.insert(it, QStringLiteral("Japanese ( ISO 2022-JP )")); m_shortNames.append( QStringLiteral("ISO 2022-JP") ); m_simplifiedShortNames.insert( QStringLiteral("iso2022jp"), QStringLiteral("ISO 2022-JP") ); ++it; } ++it; } } } } diff --git a/src/irc/nick.cpp b/src/irc/nick.cpp index a3ab707c..d9685b96 100644 --- a/src/irc/nick.cpp +++ b/src/irc/nick.cpp @@ -1,263 +1,263 @@ /* 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. */ /* begin: Fri Jan 25 2002 copyright: (C) 2002 by Dario Abatianni email: eisfuchs@tigress.com */ #include "nick.h" #include "application.h" #include "images.h" #include "preferences.h" #include "nicklistview.h" #include Nick::Nick(NickListView *listView, Channel* channel, const ChannelNickPtr& channelnick) : QTreeWidgetItem (listView) { m_channelnickptr = channelnick; m_channel = channel; Q_ASSERT(channelnick); Q_ASSERT(m_channel); m_flags = 0; refresh(); setFlags((flags() & ~Qt::ItemIsDragEnabled) | Qt::ItemIsDropEnabled); } Nick::~Nick() { } ChannelNickPtr Nick::getChannelNick() const { Q_ASSERT(m_channelnickptr); return m_channelnickptr; } void Nick::refresh() { int flags = 0; NickInfoPtr nickInfo(getChannelNick()->getNickInfo()); bool away = false; int textChangedFlags = 0; { // NOTE: this scoping is for NoSorting below. Do not remove it // Disable auto-sorting while updating data NickListView::NoSorting noSorting(qobject_cast(treeWidget())); if ( nickInfo ) away = nickInfo->isAway(); if (away) { // Brush of the first column will be used for all columns setForeground(NicknameColumn, qApp->palette(treeWidget()).brush(QPalette::Disabled, QPalette::Text)); flags = 1; } else { // Brush of the first column will be used for all columns setForeground(NicknameColumn, treeWidget()->palette().brush(QPalette::Normal, QPalette::Text)); } Images* images = Application::instance()->images(); QPixmap icon; if ( getChannelNick()->isOwner() ) { flags += 64; icon = images->getNickIcon( Images::Owner, away ); } else if ( getChannelNick()->isAdmin() ) { flags += 128; icon = images->getNickIcon( Images::Admin, away ); } else if ( getChannelNick()->isOp() ) { flags += 32; icon = images->getNickIcon( Images::Op, away ); } else if ( getChannelNick()->isHalfOp() ) { flags += 16; icon = images->getNickIcon( Images::HalfOp, away ); } else if ( getChannelNick()->hasVoice() ) { flags += 8; icon = images->getNickIcon( Images::Voice, away ); } else { flags += 4; icon = images->getNickIcon( Images::Normal, away ); } setIcon( NicknameColumn, icon ); QString newtext = calculateLabel1(); if(newtext != text(NicknameColumn)) { setText(NicknameColumn, newtext); textChangedFlags |= 1 << NicknameColumn; } newtext = calculateLabel2(); if(newtext != text(HostmaskColumn)) { setText(HostmaskColumn, newtext); textChangedFlags |= 1 << HostmaskColumn; } } if(m_flags != flags || textChangedFlags) { m_flags = flags; // Announce about nick update (and reposition the nick in the nick list as needed). emitDataChanged(); m_channel->nicknameListViewTextChanged(textChangedFlags); treeWidget()->repaint(); } } // Triggers reposition of this nick (QTreeWidgetItem) in the nick list void Nick::repositionMe() { if (treeWidget()->isSortingEnabled()) emitDataChanged(); } QString Nick::calculateLabel1() const { NickInfoPtr nickinfo = getChannelNick()->getNickInfo(); QString retString = nickinfo->getNickname(); if(Preferences::self()->showRealNames() && !nickinfo->getRealName().isEmpty()) { - retString += QStringLiteral(" (") + Konversation::removeIrcMarkup(nickinfo->getRealName()) + QLatin1Char(')'); + retString += QLatin1String(" (") + Konversation::removeIrcMarkup(nickinfo->getRealName()) + QLatin1Char(')'); } return retString; } QString Nick::calculateLabel2() const { return getChannelNick()->getNickInfo()->getHostmask(); } bool Nick::operator<(const QTreeWidgetItem& other) const { const Nick& otherNick = dynamic_cast(other); if(Preferences::self()->sortByActivity()) { uint thisRecentActivity = getChannelNick()->recentActivity(); uint otherRecentActivity = otherNick.getChannelNick()->recentActivity(); if(thisRecentActivity > otherRecentActivity) { return true; } if(thisRecentActivity < otherRecentActivity) { return false; } uint thisTimestamp = getChannelNick()->timeStamp(); uint otherTimestamp = otherNick.getChannelNick()->timeStamp(); if(thisTimestamp > otherTimestamp) { return true; } if(thisTimestamp < otherTimestamp) { return false; } } if(Preferences::self()->sortByStatus()) { int thisFlags = getSortingValue(); int otherFlags = otherNick.getSortingValue(); if(thisFlags > otherFlags) { return false; } if(thisFlags < otherFlags) { return true; } } QString thisKey; QString otherKey; int col = treeWidget()->sortColumn(); if(col == NicknameColumn) { if(Preferences::self()->sortCaseInsensitive()) { thisKey = getChannelNick()->loweredNickname(); otherKey = otherNick.getChannelNick()->loweredNickname(); } else { thisKey = text(col); otherKey = otherNick.text(col); } } else if (col > 0) //the reason we need this: enabling hostnames adds another column { if(Preferences::self()->sortCaseInsensitive()) { thisKey = text(col).toLower(); otherKey = otherNick.text(col).toLower(); } else { thisKey = text(col); otherKey = otherNick.text(col); } } return thisKey < otherKey; } QVariant Nick::data(int column, int role) const { if (role == Qt::ForegroundRole && column > 0) { // Use brush of the first column for all columns return data(NicknameColumn, role); } return QTreeWidgetItem::data(column, role); } int Nick::getSortingValue() const { int flags; QString sortingOrder = Preferences::self()->sortOrder(); if(getChannelNick()->isOwner()) flags=sortingOrder.indexOf(QLatin1Char('q')); else if(getChannelNick()->isAdmin()) flags=sortingOrder.indexOf(QLatin1Char('p')); else if(getChannelNick()->isOp() ) flags=sortingOrder.indexOf(QLatin1Char('o')); else if(getChannelNick()->isHalfOp()) flags=sortingOrder.indexOf(QLatin1Char('h')); else if(getChannelNick()->hasVoice()) flags=sortingOrder.indexOf(QLatin1Char('v')); else flags=sortingOrder.indexOf(QLatin1Char('-')); return flags; } diff --git a/src/irc/outputfilter.cpp b/src/irc/outputfilter.cpp index 3a99dd38..09e667a8 100644 --- a/src/irc/outputfilter.cpp +++ b/src/irc/outputfilter.cpp @@ -1,2079 +1,2079 @@ /* 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. */ /* Copyright (C) 2002 Dario Abatianni Copyright (C) 2005 Ismail Donmez Copyright (C) 2005 Peter Simonsson Copyright (C) 2005 John Tapsell Copyright (C) 2005-2009 Eike Hein */ #include "outputfilter.h" #include "application.h" #include "mainwindow.h" #include "channel.h" #include "awaymanager.h" #include "ignore.h" #include "server.h" #include "scriptlauncher.h" #include "irccharsets.h" #include "query.h" #include "viewcontainer.h" #include "outputfilterresolvejob.h" #include #ifdef HAVE_QCA2 #include "cipher.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "ircview.h" QDebug operator<<(QDebug d, QTextDocument* document); namespace Konversation { QSet OutputFilter::m_commands; void OutputFilter::fillCommandList() { if (m_commands.size() > 0) return; QString methodSignature; for (int i = OutputFilter::staticMetaObject.methodOffset(); i < OutputFilter::staticMetaObject.methodCount(); ++i) { methodSignature = QString::fromLatin1(OutputFilter::staticMetaObject.method(i).methodSignature()); if (methodSignature.startsWith(QLatin1String("command_"))) m_commands << methodSignature.mid(8).section(QLatin1Char('('), 0, 0).toLower(); } } OutputFilter::OutputFilter(Server* server) : QObject(server) { m_server = server; fillCommandList(); } OutputFilter::~OutputFilter() { } // replace all aliases in the string and return true if anything got replaced at all bool OutputFilter::replaceAliases(QString& line, ChatWindow* context) { QStringList aliasList = Preferences::self()->aliasList(); for(int index = 0; indexcommandChar() + aliasPattern) { QString aliasReplace = aliasList[index].section(QLatin1Char(' '),1); if (context) aliasReplace = context->getServer()->parseWildcards(aliasReplace, context); - if (!aliasList[index].contains(QStringLiteral("%p"))) + if (!aliasList[index].contains(QLatin1String("%p"))) aliasReplace.append(QLatin1Char(' ') + line.section(QLatin1Char(' '), 1)); // protect "%%" aliasReplace.replace(QStringLiteral("%%"),QStringLiteral("%\x01")); // replace %p placeholder with rest of line aliasReplace.replace(QStringLiteral("%p"), line.section(QLatin1Char(' '), 1)); // restore "%<1>" as "%%" aliasReplace.replace(QStringLiteral("%\x01"),QStringLiteral("%%")); // modify line line=aliasReplace; // return "replaced" return true; } } return false; } QStringList OutputFilter::splitForEncoding(const QString& destination, const QString& inputLine, int max, int segments) { int sublen = 0; //The encoded length since the last split int charLength = 0; //the length of this char int lastBreakPoint = 0; //FIXME should we run this through the encoder first, checking with "canEncode"? QString text = inputLine; // the text we'll send, currently in Unicode QStringList finals; // The strings we're going to output QString channelCodecName = Preferences::channelEncoding(m_server->getDisplayName(), destination); //Get the codec we're supposed to use. This must not fail. (not verified) QTextCodec* codec; // I copied this bit straight out of Server::send if (channelCodecName.isEmpty()) { codec = m_server->getIdentity()->getCodec(); } else { codec = Konversation::IRCCharsets::self()->codecForName(channelCodecName); } Q_ASSERT(codec); int index = 0; while (index < text.length() && (segments == -1 || finals.size() < segments-1)) { // The most important bit - turn the current char into a QCString so we can measure it QByteArray ch = codec->fromUnicode(QString(text[index])); charLength = ch.length(); // If adding this char puts us over the limit: if (charLength + sublen > max) { if (lastBreakPoint != 0) { finals.append(text.left(lastBreakPoint + 1)); text = text.mid(lastBreakPoint + 1); } else { finals.append(text.left(index)); text = text.mid(index); } lastBreakPoint = 0; sublen = 0; index = 0; } else if (text[index].isSpace() || text[index].isPunct()) { lastBreakPoint = index; } ++index; sublen += charLength; } if (!text.isEmpty()) { finals.append(text); } return finals; } bool OutputFilter::checkForEncodingConflict(QString *line, const QString& target) { QTextCodec* codec = nullptr; QString encoding; QString oldLine(*line); if(m_server->getServerGroup()) encoding = Preferences::channelEncoding(m_server->getServerGroup()->id(), target); else encoding = Preferences::channelEncoding(m_server->getDisplayName(), target); if(encoding.isEmpty()) codec = m_server->getIdentity()->getCodec(); else codec = Konversation::IRCCharsets::self()->codecForName(encoding); QTextCodec::ConverterState state; QString newLine; if(codec) newLine = codec->fromUnicode(oldLine.constData(),oldLine.length(),&state); if(!newLine.isEmpty() && state.invalidChars) { int ret = KMessageBox::Continue; ret = KMessageBox::warningContinueCancel(m_server->getViewContainer()->getWindow(), i18n("The message you are sending includes characters " "that do not exist in your current encoding. If " "you choose to continue anyway those characters " "will be replaced by a '?'."), i18n("Encoding Conflict Warning"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QStringLiteral("WarnEncodingConflict")); *line = newLine; //set so we show it as it's sent if (ret != KMessageBox::Continue) return true; } return false; } OutputFilterResult OutputFilter::parse(const QString& myNick, const QString& originalLine, const QString& destination, ChatWindow* inputContext) { OutputFilterInput input; input.myNick = myNick; input.destination = destination; input.context = inputContext; OutputFilterResult result; QString inputLine(originalLine); - if (inputLine.isEmpty() || inputLine == QLatin1String("\n") || checkForEncodingConflict(&inputLine, destination)) + if (inputLine.isEmpty() || inputLine == QLatin1Char('\n') || checkForEncodingConflict(&inputLine, destination)) return result; //Protect against nickserv auth being sent as a message on the off chance // someone didn't notice leading spaces { QString testNickServ(inputLine.trimmed()); if(testNickServ.startsWith(Preferences::self()->commandChar() + "nickserv", Qt::CaseInsensitive) || testNickServ.startsWith(Preferences::self()->commandChar() + "ns", Qt::CaseInsensitive)) { inputLine = testNickServ; } } //perform variable expansion according to prefs inputLine = Konversation::doVarExpansion(inputLine); QString line = inputLine.toLower(); // Convert double command chars at the beginning to single ones if(line.startsWith(Preferences::self()->commandChar() + Preferences::self()->commandChar()) && !destination.isEmpty()) { - inputLine = inputLine.mid(1); + inputLine.remove(0, 1); goto BYPASS_COMMAND_PARSING; } // Server command? else if (line.startsWith(Preferences::self()->commandChar())) { QString command = inputLine.section(' ', 0, 0).mid(1).toLower(); input.parameter = inputLine.section(' ', 1); if (command != QLatin1String("topic")) input.parameter = input.parameter.trimmed(); if (supportedCommands().contains(command)) { QString methodSignature("command_" + command); QMetaObject::invokeMethod(this, methodSignature.toLatin1().constData(), Qt::DirectConnection, Q_RETURN_ARG(OutputFilterResult, result), Q_ARG(OutputFilterInput, input)); } // Forward unknown commands to server else { result.toServer = inputLine.mid(1); result.type = Message; } } // Ordinary message to channel/query? else if (!destination.isEmpty()) { BYPASS_COMMAND_PARSING: QStringList outputList = splitForEncoding(destination, inputLine, m_server->getPreLength(QStringLiteral("PRIVMSG"), destination)); if (outputList.count() > 1) { result.output.clear(); result.outputList = outputList; for (QStringList::ConstIterator it = outputList.constBegin(); it != outputList.constEnd(); ++it) { result.toServerList += "PRIVMSG " + destination + " :" + *it; } } else { result.output = inputLine; result.toServer = "PRIVMSG " + destination + " :" + inputLine; } result.type = Message; } // Eveything else goes to the server unchanged else { result.toServer = inputLine; result.output = inputLine; result.typeString = i18n("Raw"); result.type = Program; } return result; } OutputFilterResult OutputFilter::command_op(const OutputFilterInput& input) { return changeMode(input.parameter, input.destination, 'o', '+'); } OutputFilterResult OutputFilter::command_deop(const OutputFilterInput& input) { return changeMode(addNickToEmptyNickList(input.myNick, input.parameter), input.destination, 'o', '-'); } OutputFilterResult OutputFilter::command_hop(const OutputFilterInput& input) { return changeMode(input.parameter, input.destination, 'h', '+'); } OutputFilterResult OutputFilter::command_dehop(const OutputFilterInput& input) { return changeMode(addNickToEmptyNickList(input.myNick, input.parameter), input.destination, 'h', '-'); } OutputFilterResult OutputFilter::command_voice(const OutputFilterInput& input) { return changeMode(input.parameter, input.destination, 'v', '+'); } OutputFilterResult OutputFilter::command_unvoice(const OutputFilterInput& input) { return changeMode(addNickToEmptyNickList(input.myNick, input.parameter), input.destination, 'v', '-'); } OutputFilterResult OutputFilter::command_devoice(const OutputFilterInput& input) { return command_unvoice(input); } OutputFilterResult OutputFilter::command_join(const OutputFilterInput& _input) { OutputFilterInput input(_input); OutputFilterResult result; - if (input.parameter.contains(QLatin1String(","))) // Protect against #foo,0 tricks - input.parameter = input.parameter.remove(QStringLiteral(",0")); + if (input.parameter.contains(QLatin1Char(','))) // Protect against #foo,0 tricks + input.parameter.remove(QStringLiteral(",0")); //else if(channelName == "0") // FIXME IRC RFC 2812 section 3.2.1 if (input.parameter.isEmpty()) { if (input.destination.isEmpty() || !isAChannel(input.destination)) return usage(i18n("Usage: %1JOIN [password]", Preferences::self()->commandChar())); input.parameter = input.destination; } else if (!isAChannel(input.parameter)) input.parameter = '#' + input.parameter.trimmed(); Channel* channel = m_server->getChannelByName(input.parameter); if (channel) { // Note that this relies on the channels-flush-nicklists-on-disconnect behavior. if (!channel->numberOfNicks()) result.toServer = "JOIN " + input.parameter; if (channel->joined()) emit showView(channel); } else result.toServer = "JOIN " + input.parameter; return result; } OutputFilterResult OutputFilter::command_j(const OutputFilterInput& input) { return command_join(input); } OutputFilterResult OutputFilter::command_kick(const OutputFilterInput& input) { OutputFilterResult result; if (isAChannel(input.destination)) { // get nick to kick QString victim = input.parameter.left(input.parameter.indexOf(' ')); if (victim.isEmpty()) result = usage(i18n("Usage: %1KICK [reason]", Preferences::self()->commandChar())); else { // get kick reason (if any) QString reason = input.parameter.mid(victim.length() + 1); // if no reason given, take default reason if (reason.isEmpty()) reason = m_server->getIdentity()->getKickReason(); result.toServer = "KICK " + input.destination + ' ' + victim + " :" + reason; } } else result = error(i18n("%1KICK only works from within channels.", Preferences::self()->commandChar())); return result; } OutputFilterResult OutputFilter::command_part(const OutputFilterInput& input) { OutputFilterResult result; // No parameter, try default part message if (input.parameter.isEmpty()) { // But only if we actually are in a channel if (isAChannel(input.destination)) result.toServer = "PART " + input.destination + " :" + m_server->getIdentity()->getPartReason(); else if (m_server->getQueryByName(input.destination)) m_server->closeQuery(input.destination); else result = error(i18n("%1PART and %1LEAVE without parameters only work from within a " "channel or a query.", Preferences::self()->commandChar())); } else { // part a given channel if (isAChannel(input.parameter)) { // get channel name QString channel = input.parameter.left(input.parameter.indexOf(' ')); // get part reason (if any) QString reason = input.parameter.mid(channel.length() + 1); // if no reason given, take default reason if (reason.isEmpty()) reason = m_server->getIdentity()->getPartReason(); result.toServer = "PART " + channel + " :" + reason; } // part this channel with a given reason else { if (isAChannel(input.destination)) result.toServer = "PART " + input.destination + " :" + input.parameter; else result = error(i18n("%1PART without channel name only works from within a channel.", Preferences::self()->commandChar())); } } return result; } OutputFilterResult OutputFilter::command_leave(const OutputFilterInput& input) { return command_part(input); } OutputFilterResult OutputFilter::command_topic(const OutputFilterInput& input) { OutputFilterResult result; // No parameter, try to get current topic if (input.parameter.isEmpty()) { // But only if we actually are in a channel if (isAChannel(input.destination)) result.toServer = "TOPIC " + input.destination; else result = error(i18n("%1TOPIC without parameters only works from within a channel.", Preferences::self()->commandChar())); } else { // retrieve or set topic of a given channel if (isAChannel(input.parameter)) { // get channel name QString channel = input.parameter.left(input.parameter.indexOf(' ')); // get topic (if any) QString topic = input.parameter.mid(channel.length()+1); // if no topic given, retrieve topic if (topic.isEmpty()) m_server->requestTopic(channel); // otherwise set topic there else { result.toServer = "TOPIC " + channel + " :"; //If we get passed a ^A as a topic its a sign we should clear the topic. //Used to be a \n, but those get smashed by QStringList::split and readded later //Now is not the time to fight with that. FIXME //If anyone out there *can* actually set the topic to a single ^A, now they have to //specify it twice to get one. if (topic =="\x01\x01") result.toServer += '\x01'; else if (topic!="\x01") result.toServer += topic; } } // set this channel's topic else { if (isAChannel(input.destination)) result.toServer = "TOPIC " + input.destination + " :" + input.parameter; else result = error(i18n("%1TOPIC without channel name only works from within a channel.", Preferences::self()->commandChar())); } } return result; } OutputFilterResult OutputFilter::command_away(const OutputFilterInput& input) { if (input.parameter.isEmpty() && m_server->isAway()) m_server->requestUnaway(); else m_server->requestAway(input.parameter); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_unaway(const OutputFilterInput& /* input */) { m_server->requestUnaway(); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_back(const OutputFilterInput& input) { return command_unaway(input); } OutputFilterResult OutputFilter::command_aaway(const OutputFilterInput& input) { Application::instance()->getAwayManager()->requestAllAway(input.parameter); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_aunaway(const OutputFilterInput& /* input */) { Application::instance()->getAwayManager()->requestAllUnaway(); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_aback(const OutputFilterInput& input) { return command_aunaway(input); } OutputFilterResult OutputFilter::command_names(const OutputFilterInput& input) { OutputFilterResult result; result.toServer = QStringLiteral("NAMES "); if (input.parameter.isNull()) result = error(i18n("%1NAMES with no target may disconnect you from the server. Specify " "'*' if you really want this.", Preferences::self()->commandChar())); else if (input.parameter != QChar('*')) result.toServer.append(input.parameter); return result; } OutputFilterResult OutputFilter::command_close(const OutputFilterInput& _input) { OutputFilterInput input(_input); OutputFilterResult result; if (input.parameter.isEmpty()) input.parameter = input.destination; if (isAChannel(input.parameter) && m_server->getChannelByName(input.parameter)) m_server->getChannelByName(input.parameter)->closeYourself(false); else if (m_server->getQueryByName(input.parameter)) m_server->getQueryByName(input.parameter)->closeYourself(false); else if (input.parameter.isEmpty()) // this can only mean one thing.. we're in the Server tab m_server->closeYourself(false); else result = usage(i18n("Usage: %1close [window] closes the named channel or query tab, " "or the current tab if none specified.", Preferences::self()->commandChar())); return result; } OutputFilterResult OutputFilter::command_reconnect(const OutputFilterInput& input) { emit reconnectServer(input.parameter); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_disconnect(const OutputFilterInput& input) { emit disconnectServer(input.parameter); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_quit(const OutputFilterInput& input) { emit disconnectServer(input.parameter); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_notice(const OutputFilterInput& input) { OutputFilterResult result; QString recipient = input.parameter.left(input.parameter.indexOf(' ')); QString message = input.parameter.mid(recipient.length()+1); if (input.parameter.isEmpty() || message.isEmpty()) result = usage(i18n("Usage: %1NOTICE ", Preferences::self()->commandChar())); else { result.typeString = i18n("Notice"); result.toServer = "NOTICE " + recipient + " :" + message; result.output=i18nc("%1 is the message, %2 the recipient nickname", "Sending notice \"%1\" to %2.", message, recipient); result.type = Program; } return result; } OutputFilterResult OutputFilter::command_me(const OutputFilterInput& input) { OutputFilterResult result; if (input.destination.isEmpty()) return error(i18n("%1ME only works from a channel, query or DCC chat tab.", Preferences::self()->commandChar())); else if (input.parameter.isEmpty()) return usage(i18n("Usage: %1ME text", Preferences::self()->commandChar())); QString command("PRIVMSGACTION \x01\x01"); QStringList outputList = splitForEncoding(input.destination, input.parameter, m_server->getPreLength(command, input.destination), 2); if (outputList.count() > 1) { command = QStringLiteral("PRIVMSG"); outputList += splitForEncoding(input.destination, outputList.at(1), m_server->getPreLength(command, input.destination)); outputList.removeAt(1); result.output.clear(); result.outputList = outputList; for (int i = 0; i < outputList.count(); ++i) { if (i == 0) result.toServerList += "PRIVMSG " + input.destination + " :" + '\x01' + "ACTION " + outputList.at(i) + '\x01'; else result.toServerList += "PRIVMSG " + input.destination + " :" + outputList.at(i); } } else { result.output = input.parameter; result.toServer = "PRIVMSG " + input.destination + " :" + '\x01' + "ACTION " + input.parameter + '\x01'; } result.type = Action; return result; } OutputFilterResult OutputFilter::command_msg(const OutputFilterInput& input) { return handleMsg(input.parameter, false); } OutputFilterResult OutputFilter::command_m(const OutputFilterInput& input) { return command_msg(input); } OutputFilterResult OutputFilter::command_query(const OutputFilterInput& input) { return handleMsg(input.parameter, true); } OutputFilterResult OutputFilter::handleMsg(const QString& parameter, bool commandIsQuery) { OutputFilterResult result; QString recipient = parameter.section(' ', 0, 0, QString::SectionSkipEmpty); QString message = parameter.section(' ', 1); QString output; bool recipientIsAChannel = false; if (recipient.isEmpty()) return error(i18n("You need to specify a recipient.")); else recipientIsAChannel = m_server->isAChannel(recipient); if (commandIsQuery && recipientIsAChannel) return error(i18n("You cannot open queries to channels.")); if (message.trimmed().isEmpty()) { // Empty result - we don't want to send any message to the server. if (!commandIsQuery) return error(i18n("You need to specify a message.")); } else { output = message; if (message.startsWith(Preferences::self()->commandChar() + "me")) result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "ACTION " + message.mid(4) + '\x01'; else result.toServer = "PRIVMSG " + recipient + " :" + message; } // If this is a /query, always open a query window. // Treat "/msg nick" as "/query nick". if (commandIsQuery || output.isEmpty()) { // Note we have to be a bit careful here. // We only want to create ('obtain') a new nickinfo if we have done /query // or "/msg nick". Not "/msg nick message". NickInfoPtr nickInfo = m_server->obtainNickInfo(recipient); ::Query* query = m_server->addQuery(nickInfo, true /*we initiated*/); // Force focus if the user did not specify any message. if (output.isEmpty() && Preferences::self()->focusNewQueries()) emit showView(query); } // Result should be completely empty; if (output.isEmpty()) return result; //FIXME: Don't do below line if query is focused. result.output = output; result.typeString = recipient; result.type = PrivateMessage; return result; } OutputFilterResult OutputFilter::command_smsg(const OutputFilterInput& input) { OutputFilterResult result; QString recipient = input.parameter.left(input.parameter.indexOf(' ')); QString message = input.parameter.mid(recipient.length() + 1); if (message.startsWith(Preferences::self()->commandChar() + "me")) result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "ACTION " + message.mid(4) + '\x01'; else result.toServer = "PRIVMSG " + recipient + " :" + message; return result; } OutputFilterResult OutputFilter::command_ping(const OutputFilterInput& input) { return handleCtcp(input.parameter.section(' ', 0, 0) + " ping"); } OutputFilterResult OutputFilter::command_ctcp(const OutputFilterInput& input) { return handleCtcp(input.parameter); } OutputFilterResult OutputFilter::handleCtcp(const QString& parameter) { OutputFilterResult result; // who is the recipient? QString recipient = parameter.section(' ', 0, 0); // what is the first word of the ctcp? QString request = parameter.section(' ', 1, 1, QString::SectionSkipEmpty).toUpper(); // what is the complete ctcp command? QString message = parameter.section(' ', 2, 0xffffff, QString::SectionSkipEmpty); QString out = request; if (!message.isEmpty()) out+= ' ' + message; if (request == QLatin1String("PING")) { unsigned int timeT = QDateTime::currentDateTime().toTime_t(); result.toServer = QString("PRIVMSG %1 :\x01PING %2\x01").arg(recipient).arg(timeT); result.output = i18n("Sending CTCP-%1 request to %2.", QStringLiteral("PING"), recipient); } else { result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + out + '\x01'; result.output = i18n("Sending CTCP-%1 request to %2.", out, recipient); } result.typeString = i18n("CTCP"); result.type = Program; return result; } OutputFilterResult OutputFilter::command_ame(const OutputFilterInput& input) { if (input.parameter.isEmpty()) return usage(i18n("Usage: %1AME [-LOCAL] text", Preferences::self()->commandChar())); if (isParameter(QStringLiteral("local"), input.parameter.section(' ', 0, 0))) m_server->sendToAllChannelsAndQueries(Preferences::self()->commandChar() + "me " + input.parameter.section(' ', 1)); else emit multiServerCommand(QStringLiteral("me"), input.parameter); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_amsg(const OutputFilterInput& input) { if (input.parameter.isEmpty()) return usage(i18n("Usage: %1AMSG [-LOCAL] text", Preferences::self()->commandChar())); if (isParameter(QStringLiteral("local"), input.parameter.section(' ', 0, 0))) m_server->sendToAllChannelsAndQueries(input.parameter.section(' ', 1)); else emit multiServerCommand(QStringLiteral("msg"), input.parameter); return OutputFilterResult(); } OutputFilterResult OutputFilter::command_omsg(const OutputFilterInput& input) { if (input.parameter.isEmpty()) return usage(i18n("Usage: %1OMSG text", Preferences::self()->commandChar())); OutputFilterResult result; result.toServer = "PRIVMSG @" + input.destination + " :" + input.parameter; return result; } OutputFilterResult OutputFilter::command_onotice(const OutputFilterInput& input) { OutputFilterResult result; if (!input.parameter.isEmpty()) { result.toServer = "NOTICE @" + input.destination + " :" + input.parameter; result.typeString = i18n("Notice"); result.type = Program; result.output = i18n("Sending notice \"%1\" to %2.", input.parameter, input.destination); } else result = usage(i18n("Usage: %1ONOTICE text", Preferences::self()->commandChar())); return result; } OutputFilterResult OutputFilter::command_quote(const OutputFilterInput& input) { OutputFilterResult result; if (input.parameter.isEmpty()) result = usage(i18n("Usage: %1QUOTE command list", Preferences::self()->commandChar())); else result.toServer = input.parameter; return result; } OutputFilterResult OutputFilter::command_say(const OutputFilterInput& input) { OutputFilterResult result; if (input.parameter.isEmpty()) result = usage(i18n("Usage: %1SAY text", Preferences::self()->commandChar())); else { result.toServer = "PRIVMSG " + input.destination + " :" + input.parameter; result.output = input.parameter; } return result; } OutputFilterResult OutputFilter::command_dcc(const OutputFilterInput& _input) { OutputFilterInput input(_input); OutputFilterResult result; qDebug() << input.parameter; // No parameter, just open DCC panel if (input.parameter.isEmpty()) { emit addDccPanel(); } else { QStringList parameterList = input.parameter.replace(QLatin1String("\\ "), QLatin1String("%20")).split(' '); QString dccType = parameterList[0].toLower(); //TODO close should not just refer to the gui-panel, let it close connections if (dccType == QLatin1String("close")) { emit closeDccPanel(); } else if (dccType == QLatin1String("send")) { if (parameterList.count() == 1) // DCC SEND { emit requestDccSend(); } else if (parameterList.count() == 2) // DCC SEND { emit requestDccSend(parameterList[1]); } else if (parameterList.count() > 2) // DCC SEND [file] ... { // TODO: make sure this will work: //output=i18n("Usage: %1DCC SEND nickname [filename] [filename] ...").arg(commandChar); QUrl fileURL(parameterList[2]); //We could easily check if the remote file exists, but then we might //end up asking for creditionals twice, so settle for only checking locally if (!fileURL.isLocalFile() || QFile::exists(fileURL.path())) { emit openDccSend(parameterList[1],fileURL); } else { result = error(i18n("File \"%1\" does not exist.", parameterList[2])); } } else // Don't know how this should happen, but ... result = usage(i18n("Usage: %1DCC [SEND [nickname [filename]]]", Preferences::self()->commandChar())); } else if (dccType == QLatin1String("get")) { //dcc get [nick [file]] switch (parameterList.count()) { case 1: emit acceptDccGet(QString(),QString()); break; case 2: emit acceptDccGet(parameterList.at(1),QString()); break; case 3: emit acceptDccGet(parameterList.at(1),parameterList.at(2)); break; default: result = usage(i18n("Usage: %1DCC [GET [nickname [filename]]]", Preferences::self()->commandChar())); } } else if (dccType == QLatin1String("chat")) { //dcc chat nick switch (parameterList.count()) { case 1: emit openDccChat(QString()); break; case 2: emit openDccChat(parameterList[1]); break; default: result = usage(i18n("Usage: %1DCC [CHAT [nickname]]", Preferences::self()->commandChar())); } } else if (dccType == QLatin1String("whiteboard")) { //dcc whiteboard nick switch (parameterList.count()) { case 1: emit openDccWBoard(QString()); break; case 2: emit openDccWBoard(parameterList[1]); break; default: result = usage(i18n("Usage: %1DCC [WHITEBOARD [nickname]]", Preferences::self()->commandChar())); } } else result = error(i18n("Unrecognized command %1DCC %2. Possible commands are SEND, " "CHAT, CLOSE, GET, WHITEBOARD.", Preferences::self()->commandChar(), parameterList[0])); } return result; } OutputFilterResult OutputFilter::sendRequest(const QString &recipient, const QString &fileName, const QString &address, quint16 port, quint64 size) { OutputFilterResult result; result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC SEND " + fileName + ' ' + address + ' ' + QString::number(port) + ' ' + QString::number(size) + '\x01'; return result; } OutputFilterResult OutputFilter::passiveSendRequest(const QString& recipient, const QString &fileName, const QString &address, quint64 size, const QString &token) { OutputFilterResult result; result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC SEND " + fileName + ' ' + address + " 0 " + QString::number(size) + ' ' + token + '\x01'; return result; } // Accepting Resume Request OutputFilterResult OutputFilter::acceptResumeRequest(const QString &recipient, const QString &fileName, quint16 port, quint64 startAt) { OutputFilterResult result; result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC ACCEPT " + fileName + ' ' + QString::number(port) + ' ' + QString::number(startAt) + '\x01'; return result; } // Accepting Passive Resume Request OutputFilterResult OutputFilter::acceptPassiveResumeRequest(const QString &recipient, const QString &fileName, quint16 port, quint64 startAt, const QString &token) { OutputFilterResult result; result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC ACCEPT " + fileName + ' ' + QString::number(port) + ' ' + QString::number(startAt) + ' ' + token +'\x01'; return result; } // Requesting Resume Request OutputFilterResult OutputFilter::resumeRequest(const QString &sender, const QString &fileName, quint16 port, KIO::filesize_t startAt) { OutputFilterResult result; result.toServer = "PRIVMSG " + sender + " :" + '\x01' + "DCC RESUME " + fileName + ' ' + QString::number(port) + ' ' + QString::number(startAt) + '\x01'; return result; } // Requesting Passive Resume Request OutputFilterResult OutputFilter::resumePassiveRequest(const QString &sender, const QString &fileName, quint16 port, KIO::filesize_t startAt, const QString &token) { OutputFilterResult result; result.toServer = "PRIVMSG " + sender + " :" + '\x01' + "DCC RESUME " + fileName + ' ' + QString::number(port) + ' ' + QString::number(startAt) + ' ' + token + '\x01'; return result; } // Accept Passive Send Request, there active doesn't need that OutputFilterResult OutputFilter::acceptPassiveSendRequest(const QString& recipient, const QString &fileName, const QString &address, quint16 port, quint64 size, const QString &token) { OutputFilterResult result; result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC SEND " + fileName + ' ' + address + ' ' + QString::number(port) + ' ' + QString::number(size) + ' ' + token + '\x01'; return result; } // Rejecting quened dcc receive, is not send when abort an active send OutputFilterResult OutputFilter::rejectDccSend(const QString& partnerNick, const QString & fileName) { OutputFilterResult result; result.toServer = "NOTICE " + partnerNick + " :" + '\x01' + "DCC REJECT SEND " + fileName + '\x01'; return result; } OutputFilterResult OutputFilter::rejectDccChat(const QString & partnerNick, const QString& extension) { OutputFilterResult result; result.toServer = "NOTICE " + partnerNick + " :" + '\x01' + "DCC REJECT CHAT " + extension.toUpper() + '\x01'; return result; } OutputFilterResult OutputFilter::requestDccChat(const QString& partnerNick, const QString& extension, const QString& numericalOwnIp, quint16 ownPort) { OutputFilterResult result; result.toServer = "PRIVMSG " + partnerNick + " :" + '\x01' + "DCC CHAT " + extension.toUpper() + ' ' + numericalOwnIp + ' ' + QString::number(ownPort) + '\x01'; return result; } OutputFilterResult OutputFilter::passiveChatRequest(const QString& recipient, const QString& extension, const QString& address, const QString& token) { OutputFilterResult result; result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC CHAT " + extension.toUpper() + ' ' + address + " 0 " + token + '\x01'; return result; } OutputFilterResult OutputFilter::acceptPassiveChatRequest(const QString& recipient, const QString& extension, const QString& numericalOwnIp, quint16 ownPort, const QString& token) { OutputFilterResult result; result.toServer = "PRIVMSG " + recipient + " :" + '\x01' + "DCC CHAT " + extension.toUpper() + ' ' + numericalOwnIp + ' ' + QString::number(ownPort) + ' ' + token + '\x01'; return result; } OutputFilterResult OutputFilter::command_invite(const OutputFilterInput& input) { OutputFilterResult result; if (input.parameter.isEmpty()) result = usage(i18n("Usage: %1INVITE [channel]", Preferences::self()->commandChar())); else { QString nick = input.parameter.section(' ', 0, 0, QString::SectionSkipEmpty); QString channel = input.parameter.section(' ', 1, 1, QString::SectionSkipEmpty); if (channel.isEmpty()) { if (isAChannel(input.destination)) channel = input.destination; else result = error(i18n("%1INVITE without channel name works only from within channels.", Preferences::self()->commandChar())); } if (!channel.isEmpty()) { if (isAChannel(channel)) result.toServer = "INVITE " + nick + ' ' + channel; else result = error(i18n("%1 is not a channel.", channel)); } } return result; } OutputFilterResult OutputFilter::command_exec(const OutputFilterInput& input) { OutputFilterResult result; if (input.parameter.isEmpty()) result = usage(i18n("Usage: %1EXEC [-SHOWPATH]