Compare commits
16 Commits
Author | SHA1 | Date |
---|---|---|
|
31a9c6efdf | 2 years ago |
|
0fc9373fc5 | 2 years ago |
|
2ae8e41479 | 2 years ago |
|
fa3fa74d02 | 2 years ago |
|
11f120d7ca | 2 years ago |
|
0c7ece7d1d | 2 years ago |
|
4ff6717820 | 2 years ago |
|
1a8d3b12a3 | 2 years ago |
|
7e5ea6fa9a | 2 years ago |
|
bbd086cd83 | 2 years ago |
|
1c874fc468 | 2 years ago |
|
13d569894e | 2 years ago |
|
6b13c67ed3 | 2 years ago |
|
49d9f8bd1d | 2 years ago |
|
087c4ff8e9 | 2 years ago |
|
4429931e50 | 2 years ago |
@ -0,0 +1,116 @@ |
||||
/*
|
||||
* IdealIRC - Internet Relay Chat client |
||||
* Copyright (c) 2023 Tom-Andre Barstad. |
||||
* This software is licensed under the Software Attribution License. |
||||
* See LICENSE for more information. |
||||
*/ |
||||
|
||||
#include "ICommand.h" |
||||
|
||||
#include "Commands.h" |
||||
#include "IRCClient/Commands.h" |
||||
|
||||
using namespace Command::Internal; |
||||
using namespace Command::IRC; |
||||
|
||||
int ICommand::AutoComplete(int index, QString& pattern) |
||||
{ |
||||
static QStringList commandList = { |
||||
/*
|
||||
* Internal commands |
||||
*/ |
||||
ACTION, |
||||
CTCP, |
||||
DCC, |
||||
CTCPREPLY, |
||||
ME, |
||||
ECHO, |
||||
QUERY, |
||||
MUTERESP, |
||||
UNMUTERESP, |
||||
CLEAR, |
||||
MSG, |
||||
QUOTE, |
||||
RAW, |
||||
SERVER, |
||||
|
||||
/*
|
||||
* IRC commands |
||||
*/ |
||||
PASS, |
||||
NICK, |
||||
USER, |
||||
OPER, |
||||
MODE, |
||||
QUIT, |
||||
SQUIT, |
||||
JOIN, |
||||
PART, |
||||
TOPIC, |
||||
NAMES, |
||||
LIST, |
||||
INVITE, |
||||
KICK, |
||||
PRIVMSG, |
||||
NOTICE, |
||||
MOTD, |
||||
LUSERS, |
||||
VERSION, |
||||
STATS, |
||||
LINKS, |
||||
TIME, |
||||
CONNECT, |
||||
TRACE, |
||||
ADMIN, |
||||
INFO, |
||||
SERVLIST, |
||||
SQUERY, |
||||
WHOWAS, |
||||
WHOIS, |
||||
WHO, |
||||
KILL, |
||||
PING, |
||||
PONG, |
||||
AWAY, |
||||
REHASH, |
||||
DIE, |
||||
RESTART, |
||||
SUMMON, |
||||
USERS, |
||||
WALLOPS, |
||||
USERHOST, |
||||
ISON |
||||
}; |
||||
|
||||
if (pattern.isEmpty()) |
||||
return 0; |
||||
|
||||
int searchIdx = 0; |
||||
bool changed = false; |
||||
|
||||
Retry: |
||||
for (const auto& command : commandList) |
||||
{ |
||||
if (pattern.length() > command.length()) |
||||
continue; |
||||
|
||||
if (command.left(pattern.length() - 1).compare(pattern.mid(1), Qt::CaseInsensitive) == 0) |
||||
{ |
||||
if (searchIdx == index) { |
||||
pattern = command; |
||||
pattern.prepend('/'); |
||||
changed = true; |
||||
} |
||||
++searchIdx; |
||||
} |
||||
} |
||||
|
||||
if (!changed && searchIdx > 0) { |
||||
changed = false; |
||||
searchIdx = 0; |
||||
index = 0; |
||||
goto Retry; |
||||
} |
||||
|
||||
return searchIdx; |
||||
} |
@ -1,135 +0,0 @@ |
||||
/*
|
||||
* IdealIRC Core - Internet Relay Chat API |
||||
* Copyright (C) 2021 Tom-Andre Barstad. |
||||
* This software is licensed under the Software Attribution License. |
||||
* See LICENSE for more information. |
||||
*/ |
||||
|
||||
#include "../IRCBase.h" |
||||
#include "../IRCBasePriv.h" |
||||
#include "../Commands.h" |
||||
|
||||
void IRCBasePriv::handleNumeric(const IRCMessage& ircmessage) { |
||||
using namespace Numeric; |
||||
|
||||
if (!isOnline && ircmessage == RPL_ISUPPORT) { |
||||
// Note: skip the first item (argument), it is always our nickname
|
||||
for (auto it = ircmessage.getArgs().cbegin() + 1; it != ircmessage.getArgs().cend(); ++it) { |
||||
const auto& arg = *it; |
||||
std::string key, val; |
||||
auto delim = std::find(arg.begin(), arg.end(), '='); |
||||
if (delim != arg.end()) { |
||||
key = std::string(arg.begin(), delim); |
||||
val = std::string(delim + 1, arg.end()); |
||||
} |
||||
else |
||||
key = arg; |
||||
isupport.insert_or_assign(key, val); |
||||
} |
||||
|
||||
const std::string& prefix = isupport.find("PREFIX")->second; |
||||
|
||||
validPrivilegeModes = std::string(std::find(prefix.begin(), prefix.end(), '(') + 1, |
||||
std::find(prefix.begin(), prefix.end(), ')')); |
||||
|
||||
validPrivilegeSymbols = std::string(std::find(prefix.begin(), prefix.end(), ')') + 1, |
||||
prefix.end()); |
||||
} |
||||
|
||||
else if (!isOnline && (ircmessage == RPL_ENDOFMOTD || ircmessage == ERR_NOMOTD)) { |
||||
nickname = ircmessage[0]; |
||||
isOnline = true; |
||||
if (keepaliveFreq > std::chrono::seconds(0)) |
||||
startKeepaliveTimer(); |
||||
|
||||
// Emplace ourself in the all-members list.
|
||||
allMembers.emplace_back(std::make_shared<IRCMember>(IRCPrefix::fromNickname(nickname))); |
||||
|
||||
super.onRegistered(); |
||||
} |
||||
|
||||
else if (ircmessage == RPL_NAMREPLY) { |
||||
auto chan = super.getChannel(ircmessage[2]); |
||||
if (chan && chan->isPopulating()) { |
||||
std::istringstream ss(ircmessage.getMessage()); |
||||
|
||||
/*
|
||||
* Note these things: |
||||
* A member may contain a usermode. |
||||
* IRCv3 may enable multiple usermodes (multi-prefix). |
||||
* IRCv3 may enable each member entry to be a full hostmask (userhost-in-names) and not only a nickname. |
||||
*/ |
||||
auto members = std::vector<std::string>(std::istream_iterator<std::string>{ ss }, |
||||
std::istream_iterator<std::string>()); |
||||
|
||||
for (std::string& namem : members) { |
||||
/* Extract mode symbols */ |
||||
auto memBegin = std::find_if_not(namem.begin(), namem.end(), |
||||
[this](char ms) { |
||||
return isMemberPrivilegeSymbol(ms); |
||||
}); |
||||
std::string modesymbols = std::string(namem.begin(), memBegin); |
||||
namem.erase(namem.begin(), memBegin); |
||||
|
||||
std::optional<IRCPrefix> memprefix; |
||||
/* IRC standard reply */ |
||||
if (namem.find('!') == std::string::npos) |
||||
memprefix.emplace(IRCPrefix::fromNickname(namem)); |
||||
/* IRCv3 reply */ |
||||
else |
||||
memprefix.emplace(namem); |
||||
|
||||
auto mem = super.getMember(memprefix->nickname()); |
||||
if (!mem) { |
||||
mem = std::make_shared<IRCMember>(*memprefix); |
||||
allMembers.push_back(mem); |
||||
} |
||||
if (mem->prefix().host().empty() && !memprefix->host().empty()) |
||||
mem->setPrefix(*memprefix); |
||||
|
||||
mem->addChannel(chan); |
||||
auto& entry = chan->addMember(mem); |
||||
for (char s : modesymbols) { |
||||
char letter = memberPrivilegeSymbolToMode(s); |
||||
if (letter != '\0') |
||||
entry.addMode(letter); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
else if (ircmessage == RPL_ENDOFNAMES) { |
||||
auto chan = super.getChannel(ircmessage[1]); |
||||
if (chan && chan->isPopulating()) |
||||
chan->donePopulating(); |
||||
} |
||||
|
||||
else if (ircmessage == RPL_TOPIC) { |
||||
auto chan = super.getChannel(ircmessage[1]); |
||||
if (chan) |
||||
chan->setTopic(ircmessage.getMessage()); |
||||
} |
||||
|
||||
else if (ircmessage == RPL_CHANNELMODEIS) { |
||||
auto chan = super.getChannel(ircmessage[1]); |
||||
const std::string& modes = ircmessage[2]; |
||||
|
||||
auto args = ircmessage.getArgs(); |
||||
args.erase(args.begin(), args.begin() + 3); |
||||
|
||||
if (chan) |
||||
parseChannelModeMessage(chan, modes, args); |
||||
} |
||||
|
||||
else if (ircmessage == NumericV3::RPL_SASLSUCCESS && saslInProgress) { |
||||
saslInProgress = false; |
||||
writeNoMsg(Command::IRCv3::CAP, { Command::IRCv3::END }); |
||||
} |
||||
|
||||
else if (ircmessage == NumericV3::ERR_SASLFAIL && saslInProgress) { |
||||
saslInProgress = false; |
||||
super.disconnectFromServer(); |
||||
} |
||||
|
||||
super.onMsgNumeric(ircmessage.getSender(), ircmessage.getCommand(), ircmessage.getArgs(), ircmessage.getMessage()); |
||||
} |
@ -0,0 +1,22 @@ |
||||
/*
|
||||
* IdealIRC - Internet Relay Chat client |
||||
* Copyright (c) 2023 Tom-Andre Barstad. |
||||
* This software is licensed under the Software Attribution License. |
||||
* See LICENSE for more information. |
||||
*/ |
||||
|
||||
#include "../IRCBase.h" |
||||
#include "../IRCBasePriv.h" |
||||
|
||||
#include <iostream> |
||||
|
||||
void IRCBasePriv::numErrSaslFail(const IRCMessage& ircmessage) |
||||
{ |
||||
if (!saslInProgress) |
||||
return; |
||||
|
||||
saslInProgress = false; |
||||
|
||||
if (super.disconnectFromServer() != IRCError::NoError) |
||||
std::cout << "Disconnected from an already closed connection." << std::endl; |
||||
} |
@ -0,0 +1,21 @@ |
||||
/*
|
||||
* IdealIRC - Internet Relay Chat client |
||||
* Copyright (c) 2023 Tom-Andre Barstad. |
||||
* This software is licensed under the Software Attribution License. |
||||
* See LICENSE for more information. |
||||
*/ |
||||
|
||||
#include "../IRCBase.h" |
||||
#include "../IRCBasePriv.h" |
||||
|
||||
void IRCBasePriv::numRplChannelModeIs(const IRCMessage& ircmessage) |
||||
{ |
||||
auto chan = super.getChannel(ircmessage[1]); |
||||
const std::string& modes = ircmessage[2]; |
||||
|
||||
auto args = ircmessage.getArgs(); |
||||
args.erase(args.begin(), args.begin() + 3); |
||||
|
||||
if (chan) |
||||
parseChannelModeMessage(chan, modes, args); |
||||
} |
@ -0,0 +1,25 @@ |
||||
/*
|
||||
* IdealIRC - Internet Relay Chat client |
||||
* Copyright (c) 2023 Tom-Andre Barstad. |
||||
* This software is licensed under the Software Attribution License. |
||||
* See LICENSE for more information. |
||||
*/ |
||||
|
||||
#include "../IRCBase.h" |
||||
#include "../IRCBasePriv.h" |
||||
|
||||
void IRCBasePriv::numRplEndOfMotd(const IRCMessage& ircmessage) |
||||
{ |
||||
if (isOnline) |
||||
return; |
||||
|
||||
nickname = ircmessage[0]; |
||||
isOnline = true; |
||||
if (keepaliveFreq > std::chrono::seconds(0)) |
||||
startKeepaliveTimer(); |
||||
|
||||
// Emplace ourselves in the all-members list.
|
||||
allMembers.emplace_back(std::make_shared<IRCMember>(IRCPrefix::fromNickname(nickname))); |
||||
|
||||
super.onRegistered(); |
||||
} |
@ -0,0 +1,16 @@ |
||||
/*
|
||||
* IdealIRC - Internet Relay Chat client |
||||
* Copyright (c) 2023 Tom-Andre Barstad. |
||||
* This software is licensed under the Software Attribution License. |
||||
* See LICENSE for more information. |
||||
*/ |
||||
|
||||
#include "../IRCBase.h" |
||||
#include "../IRCBasePriv.h" |
||||
|
||||
void IRCBasePriv::numRplEndOfNames(const IRCMessage& ircmessage) |
||||
{ |
||||
auto chan = super.getChannel(ircmessage[1]); |
||||
if (chan && chan->isPopulating()) |
||||
chan->donePopulating(); |
||||
} |
@ -0,0 +1,36 @@ |
||||
/*
|
||||
* IdealIRC - Internet Relay Chat client |
||||
* Copyright (c) 2023 Tom-Andre Barstad. |
||||
* This software is licensed under the Software Attribution License. |
||||
* See LICENSE for more information. |
||||
*/ |
||||
|
||||
#include "../IRCBasePriv.h" |
||||
|
||||
void IRCBasePriv::numRplIsupport(const IRCMessage& ircmessage) |
||||
{ |
||||
if (isOnline) |
||||
return; |
||||
|
||||
// Note: skip the first item (argument), it is always our nickname
|
||||
for (auto it = ircmessage.getArgs().cbegin() + 1; it != ircmessage.getArgs().cend(); ++it) { |
||||
const auto& arg = *it; |
||||
std::string key, val; |
||||
auto delim = std::find(arg.begin(), arg.end(), '='); |
||||
if (delim != arg.end()) { |
||||
key = std::string(arg.begin(), delim); |
||||
val = std::string(delim + 1, arg.end()); |
||||
} |
||||
else |
||||
key = arg; |
||||
isupport.insert_or_assign(key, val); |
||||
} |
||||
|
||||
const std::string& prefix = isupport.find("PREFIX")->second; |
||||
|
||||
validPrivilegeModes = std::string(std::find(prefix.begin(), prefix.end(), '(') + 1, |
||||
std::find(prefix.begin(), prefix.end(), ')')); |
||||
|
||||
validPrivilegeSymbols = std::string(std::find(prefix.begin(), prefix.end(), ')') + 1, |
||||
prefix.end()); |
||||
} |
@ -0,0 +1,60 @@ |
||||
/*
|
||||
* IdealIRC - Internet Relay Chat client |
||||
* Copyright (c) 2023 Tom-Andre Barstad. |
||||
* This software is licensed under the Software Attribution License. |
||||
* See LICENSE for more information. |
||||
*/ |
||||
|
||||
#include "../IRCBase.h" |
||||
#include "../IRCBasePriv.h" |
||||
|
||||
void IRCBasePriv::numRplNamreply(const IRCMessage& ircmessage) |
||||
{ |
||||
auto chan = super.getChannel(ircmessage[2]); |
||||
if (chan && chan->isPopulating()) { |
||||
std::istringstream ss(ircmessage.getMessage()); |
||||
|
||||
/*
|
||||
* Note these things: |
||||
* A member may contain a usermode. |
||||
* IRCv3 may enable multiple usermodes (multi-prefix). |
||||
* IRCv3 may enable each member entry to be a full hostmask (userhost-in-names) and not only a nickname. |
||||
*/ |
||||
auto members = std::vector<std::string>(std::istream_iterator<std::string>{ ss }, |
||||
std::istream_iterator<std::string>()); |
||||
|
||||
for (std::string& namem : members) { |
||||
/* Extract mode symbols */ |
||||
auto memBegin = std::find_if_not(namem.begin(), namem.end(), |
||||
[this](char ms) { |
||||
return isMemberPrivilegeSymbol(ms); |
||||
}); |
||||
std::string modesymbols = std::string(namem.begin(), memBegin); |
||||
namem.erase(namem.begin(), memBegin); |
||||
|
||||
std::optional<IRCPrefix> memprefix; |
||||
/* IRC standard reply */ |
||||
if (namem.find('!') == std::string::npos) |
||||
memprefix.emplace(IRCPrefix::fromNickname(namem)); |
||||
/* IRCv3 reply */ |
||||
else |
||||
memprefix.emplace(namem); |
||||
|
||||
auto mem = super.getMember(memprefix->nickname()); |
||||
if (!mem) { |
||||
mem = std::make_shared<IRCMember>(*memprefix); |
||||
allMembers.push_back(mem); |
||||
} |
||||
if (mem->prefix().host().empty() && !memprefix->host().empty()) |
||||
mem->setPrefix(*memprefix); |
||||
|
||||
mem->addChannel(chan); |
||||
auto& entry = chan->addMember(mem); |
||||
for (char s : modesymbols) { |
||||
char letter = memberPrivilegeSymbolToMode(s); |
||||
if (letter != '\0') |
||||
entry.addMode(letter); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,18 @@ |
||||
/*
|
||||
* IdealIRC - Internet Relay Chat client |
||||
* Copyright (c) 2023 Tom-Andre Barstad. |
||||
* This software is licensed under the Software Attribution License. |
||||
* See LICENSE for more information. |
||||
*/ |
||||
|
||||
#include "../Commands.h" |
||||
#include "../IRCBasePriv.h" |
||||
|
||||
void IRCBasePriv::numRplSaslSuccess(const IRCMessage& ircmessage) |
||||
{ |
||||
if (!saslInProgress) |
||||
return; |
||||
|
||||
saslInProgress = false; |
||||
writeNoMsg(Command::IRCv3::CAP, { Command::IRCv3::END }); |
||||
} |
@ -0,0 +1,16 @@ |
||||
/*
|
||||
* IdealIRC - Internet Relay Chat client |
||||
* Copyright (c) 2023 Tom-Andre Barstad. |
||||
* This software is licensed under the Software Attribution License. |
||||
* See LICENSE for more information. |
||||
*/ |
||||
|
||||
#include "../IRCBase.h" |
||||
#include "../IRCBasePriv.h" |
||||
|
||||
void IRCBasePriv::numRplTopic(const IRCMessage& ircmessage) |
||||
{ |
||||
auto chan = super.getChannel(ircmessage[1]); |
||||
if (chan) |
||||
chan->setTopic(ircmessage.getMessage()); |
||||
} |
@ -0,0 +1,44 @@ |
||||
#include "ExitTimeoutDialog.h" |
||||
#include "ui_ExitTimeoutDialog.h" |
||||
|
||||
#include <QString> |
||||
|
||||
ExitTimeoutDialog::ExitTimeoutDialog(QWidget *parent) : |
||||
QDialog(parent), |
||||
ui(new Ui::ExitTimeoutDialog) |
||||
{ |
||||
ui->setupUi(this); |
||||
|
||||
m_timer.setInterval(1000); |
||||
|
||||
connect(&m_timer, &QTimer::timeout, [this, i = 9]() mutable { |
||||
static QString abortLabel{ "Force exit (%1)" }; |
||||
ui->btnExit->setText(abortLabel.arg(i)); |
||||
--i; |
||||
|
||||
if (i < 0) |
||||
{ |
||||
m_timer.stop(); |
||||
on_btnExit_clicked(); |
||||
} |
||||
}); |
||||
|
||||
m_timer.start(); |
||||
} |
||||
|
||||
ExitTimeoutDialog::~ExitTimeoutDialog() |
||||
{ |
||||
delete ui; |
||||
} |
||||
|
||||
void ExitTimeoutDialog::on_btnAbort_clicked() |
||||
{ |
||||
emit abortTimeout(); |
||||
} |
||||
|
||||
|
||||
void ExitTimeoutDialog::on_btnExit_clicked() |
||||
{ |
||||
emit forceExit(); |
||||
} |
||||
|
@ -0,0 +1,33 @@ |
||||
#ifndef EXITTIMEOUTDIALOG_H |
||||
#define EXITTIMEOUTDIALOG_H |
||||
|
||||
#include <QDialog> |
||||
#include <QTimer> |
||||
|
||||
namespace Ui { |
||||
class ExitTimeoutDialog; |
||||
} |
||||
|
||||
class ExitTimeoutDialog : public QDialog |
||||
{ |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
explicit ExitTimeoutDialog(QWidget *parent = nullptr); |
||||
~ExitTimeoutDialog(); |
||||
|
||||
signals: |
||||
void abortTimeout(); |
||||
void forceExit(); |
||||
|
||||
private slots: |
||||
void on_btnAbort_clicked(); |
||||
|
||||
void on_btnExit_clicked(); |
||||
|
||||
private: |
||||
Ui::ExitTimeoutDialog *ui; |
||||
QTimer m_timer; |
||||
}; |
||||
|
||||
#endif // EXITTIMEOUTDIALOG_H
|
@ -0,0 +1,62 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<ui version="4.0"> |
||||
<class>ExitTimeoutDialog</class> |
||||
<widget class="QDialog" name="ExitTimeoutDialog"> |
||||
<property name="geometry"> |
||||
<rect> |
||||
<x>0</x> |
||||
<y>0</y> |
||||
<width>400</width> |
||||
<height>100</height> |
||||
</rect> |
||||
</property> |
||||
<property name="windowTitle"> |
||||
<string>Dialog</string> |
||||
</property> |
||||
<layout class="QVBoxLayout" name="verticalLayout"> |
||||
<item> |
||||
<widget class="QLabel" name="label"> |
||||
<property name="font"> |
||||
<font> |
||||
<bold>true</bold> |
||||
</font> |
||||
</property> |
||||
<property name="text"> |
||||
<string>Server(s) not responding</string> |
||||
</property> |
||||
<property name="alignment"> |
||||
<set>Qt::AlignCenter</set> |
||||
</property> |
||||
</widget> |
||||
</item> |
||||
<item> |
||||
<widget class="QLabel" name="label_2"> |
||||
<property name="text"> |
||||
<string>Some servers are not acknowledging the departure. |
||||
Slow or dead connections may cause this.</string> |
||||
</property> |
||||
</widget> |
||||
</item> |
||||
<item> |
||||
<layout class="QHBoxLayout" name="horizontalLayout"> |
||||
<item> |
||||
<widget class="QPushButton" name="btnAbort"> |
||||
<property name="text"> |
||||
<string>Abort</string> |
||||
</property> |
||||
</widget> |
||||
</item> |
||||
<item> |
||||
<widget class="QPushButton" name="btnExit"> |
||||
<property name="text"> |
||||
<string>Force exit (10)</string> |
||||
</property> |
||||
</widget> |
||||
</item> |
||||
</layout> |
||||
</item> |
||||
</layout> |
||||
</widget> |
||||
<resources/> |
||||
<connections/> |
||||
</ui> |
Loading…
Reference in new issue