The complete source code of IdealIRC
http://www.idealirc.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
537 lines
15 KiB
537 lines
15 KiB
#include "ScriptGeneralUtils.h"
|
|
#include "Script/Builtin/Error.h"
|
|
#include "Script/ValueExtract.h"
|
|
#include "Script/ScriptException.h"
|
|
#include "Script/Manager.h"
|
|
#include "IRC.h"
|
|
#include "IWin/IWinStatus.h"
|
|
#include "MdiManager.h"
|
|
#include "IniFile.h"
|
|
#include "config.h"
|
|
|
|
#include <QInputDialog>
|
|
#include <QMessageBox>
|
|
#include <QDateTime>
|
|
#include <random>
|
|
#include <unordered_map>
|
|
#include <fstream>
|
|
#include <fmt/format.h>
|
|
|
|
namespace {
|
|
std::unordered_map<int,IniFile> iniFiles;
|
|
int iniFileHandleCount{ 1 };
|
|
IniFile& getIniHandle(const std::string& callingFunctionName, int handle)
|
|
{
|
|
try {
|
|
return iniFiles.at(handle);
|
|
}
|
|
catch (const std::out_of_range&) {
|
|
Builtin::Error::throwUnrecognizedIniHandle(callingFunctionName, handle);
|
|
}
|
|
}
|
|
bool createFileIfNotExists(const std::string& path)
|
|
{
|
|
/*
|
|
* Opening a file in ostream will create it if it doesn't exist.
|
|
* Tested with GNU stdlib (Linux) and Microsoft's stdlib (Windows).
|
|
*/
|
|
std::ofstream fs(path, std::fstream::app);
|
|
auto ok = fs.is_open();
|
|
fs.close();
|
|
return ok;
|
|
}
|
|
std::string setRelativePath(const std::string& path)
|
|
{
|
|
if (path.empty())
|
|
return std::string{};
|
|
|
|
#if defined(Q_OS_LINUX)
|
|
if (path[0] != '/') {
|
|
#elif defined(Q_OS_WIN32) | defined(Q_OS_WIN64)
|
|
if (path[1] != ':') {
|
|
#else
|
|
#error Update ScriptGeneralUtils.cpp setRelativePath to work with the target.
|
|
#endif
|
|
return fmt::format("{}/{}", LOCAL_PATH.toStdString(), path);
|
|
}
|
|
else {
|
|
return path;
|
|
}
|
|
}
|
|
}
|
|
|
|
using namespace Builtin::Error;
|
|
|
|
namespace ScriptFunctions {
|
|
|
|
ValueHolder input(Script&, std::vector<ValueHolder>& args)
|
|
{
|
|
if (args.size() < 2 || args.size() > 3)
|
|
throwInsufficientParameters("input", args.size(), 2);
|
|
|
|
if (args[0].getType() != ValueHolder::Type::String)
|
|
throwNotAString("input", 1);
|
|
|
|
if (args[1].getType() != ValueHolder::Type::String)
|
|
throwNotAString("input", 2);
|
|
|
|
QString defaultText;
|
|
if (args.size() == 3) {
|
|
if (args[2].getType() != ValueHolder::Type::String)
|
|
throwNotAString("input", 2);
|
|
defaultText = ValueExtract(args[2]).toString().c_str();
|
|
}
|
|
|
|
QString title(ValueExtract(args[0]).toString().c_str());
|
|
QString message(ValueExtract(args[1]).toString().c_str());
|
|
|
|
bool ok = false;
|
|
QString input = QInputDialog::getText(nullptr, title, message, QLineEdit::Normal, defaultText, &ok);
|
|
if (!ok)
|
|
return ValueHolder();
|
|
return input.toStdString();
|
|
}
|
|
|
|
ValueHolder showmessage(Script&, std::vector<ValueHolder>& args)
|
|
{
|
|
if (args.size() != 4)
|
|
throwInsufficientParameters("showmessage", args.size(), 4);
|
|
|
|
if (args[0].getType() != ValueHolder::Type::String)
|
|
throwNotAString("showmessage", 1);
|
|
|
|
if (args[1].getType() != ValueHolder::Type::String)
|
|
throwNotAString("showmessage", 2);
|
|
|
|
if (args[2].getType() != ValueHolder::Type::String)
|
|
throwNotAString("showmessage", 3);
|
|
|
|
if (args[3].getType() != ValueHolder::Type::String)
|
|
throwNotAString("showmessage", 4);
|
|
|
|
QString title(ValueExtract(args[0]).toString().c_str());
|
|
QString message(ValueExtract(args[1]).toString().c_str());
|
|
QString type(ValueExtract(args[2]).toString().c_str());
|
|
QString buttons(ValueExtract(args[3]).toString().c_str());
|
|
|
|
QMessageBox::StandardButtons dlgbtns = QMessageBox::NoButton;
|
|
for (QChar c : buttons) {
|
|
switch (c.toLatin1()) {
|
|
case 'y':
|
|
dlgbtns |= QMessageBox::Yes;
|
|
break;
|
|
case 'n':
|
|
dlgbtns |= QMessageBox::No;
|
|
break;
|
|
case 'o':
|
|
dlgbtns |= QMessageBox::Ok;
|
|
break;
|
|
case 'c':
|
|
dlgbtns |= QMessageBox::Cancel;
|
|
break;
|
|
case 'a':
|
|
dlgbtns |= QMessageBox::Abort;
|
|
break;
|
|
case 'r':
|
|
dlgbtns |= QMessageBox::Retry;
|
|
break;
|
|
case 'i':
|
|
dlgbtns |= QMessageBox::Ignore;
|
|
break;
|
|
default:
|
|
{
|
|
std::string cs;
|
|
cs += c.toLatin1();
|
|
throw ScriptException("showmessage: Unknown button type: " + cs);
|
|
}
|
|
}
|
|
}
|
|
|
|
QMessageBox::StandardButtons btnPressed;
|
|
if (type == "question")
|
|
btnPressed = QMessageBox::question(nullptr, title, message, dlgbtns);
|
|
else if (type == "info")
|
|
btnPressed = QMessageBox::information(nullptr, title, message, dlgbtns);
|
|
else if (type == "warn")
|
|
btnPressed = QMessageBox::warning(nullptr, title, message, dlgbtns);
|
|
else if (type == "crit")
|
|
btnPressed = QMessageBox::critical(nullptr, title, message, dlgbtns);
|
|
else
|
|
throw ScriptException("showmessage: Unknown message box type: " + type.toStdString());
|
|
|
|
switch (btnPressed) {
|
|
case QMessageBox::Yes:
|
|
return std::string("y");
|
|
case QMessageBox::No:
|
|
return std::string("n");
|
|
case QMessageBox::Abort:
|
|
return std::string("a");
|
|
case QMessageBox::Retry:
|
|
return std::string("r");
|
|
case QMessageBox::Ignore:
|
|
return std::string("i");
|
|
case QMessageBox::Ok:
|
|
return std::string("o");
|
|
case QMessageBox::Cancel: [[fallthrough]];
|
|
default:
|
|
return std::string("c");
|
|
}
|
|
}
|
|
|
|
ValueHolder context(Script&, std::vector<ValueHolder>& args)
|
|
{
|
|
if (!args.empty())
|
|
throwInsufficientParameters("context", args.size(), 0);
|
|
|
|
const auto& connection = ScriptManager::getContext()->getConnection();
|
|
|
|
ValueArray ret;
|
|
|
|
ret.emplace("online", new ValueHolder{ connection.isOnline() });
|
|
ret.emplace("ssl", new ValueHolder{ connection.isSSL() });
|
|
|
|
ret.emplace("host", new ValueHolder{ connection.getHostname() });
|
|
ret.emplace("port", new ValueHolder{ connection.getPort() });
|
|
|
|
ret.emplace("nickname", new ValueHolder{ connection.getNickname() });
|
|
|
|
return ValueHolder(ret);
|
|
}
|
|
|
|
ValueHolder unixts(Script&, std::vector<ValueHolder>& args)
|
|
{
|
|
if (args.empty())
|
|
throwInsufficientParameters("unixts", args.size(), 1);
|
|
|
|
qint64 ts = ValueExtract(args[0]).toInt();
|
|
auto datetime = QDateTime::fromSecsSinceEpoch(ts);
|
|
if (args.size() > 1) {
|
|
QString format = ValueExtract(args[1]).toString().c_str();
|
|
return datetime.toString(format).toStdString();
|
|
}
|
|
else
|
|
return datetime.toString(Qt::ISODate).toStdString();
|
|
}
|
|
|
|
ValueHolder unixnow(Script&, std::vector<ValueHolder>& /*args*/)
|
|
{
|
|
return static_cast<int>( QDateTime::currentSecsSinceEpoch() );
|
|
}
|
|
|
|
ValueHolder now(Script&, std::vector<ValueHolder>& args)
|
|
{
|
|
if (args.empty())
|
|
return QDateTime::currentDateTime().toString(Qt::ISODate).toStdString();
|
|
else {
|
|
const auto format = QString::fromStdString( ValueExtract(args[0]).toString() );
|
|
const auto dt = QDateTime::currentDateTime();
|
|
const auto dtStr = dt.toString(format);
|
|
return dtStr.toStdString();
|
|
}
|
|
}
|
|
|
|
ValueHolder subwintype(Script&, std::vector<ValueHolder>& args)
|
|
{
|
|
if (!args.empty())
|
|
throwInsufficientParameters("subwintype", args.size(), 0);
|
|
|
|
const auto& mdiman = MdiManager::instance();
|
|
const auto* cwin = mdiman.currentWindow();
|
|
|
|
IWin::Type wintype = IWin::Type::Undefined;
|
|
if (cwin)
|
|
wintype = cwin->getType();
|
|
|
|
return IWin::TypeString.at(wintype).toStdString();
|
|
}
|
|
|
|
ValueHolder subwinname(Script&, std::vector<ValueHolder>& args)
|
|
{
|
|
if (!args.empty())
|
|
throwInsufficientParameters("subwinname", args.size(), 0);
|
|
|
|
const auto& mdiman = MdiManager::instance();
|
|
const auto* cwin = mdiman.currentWindow();
|
|
|
|
if (!cwin)
|
|
return ValueHolder{}; // void
|
|
|
|
return cwin->getButtonText().toStdString();
|
|
}
|
|
|
|
ValueHolder openini(Script&, std::vector<ValueHolder>& args)
|
|
{
|
|
if (args.empty())
|
|
throwInsufficientParameters("openini", args.size(), 1);
|
|
|
|
if (args[0].getType() != ValueHolder::Type::String)
|
|
throwNotAString("openini", 1);
|
|
|
|
const std::string fname = setRelativePath( ValueExtract(args[0]).toString() );
|
|
|
|
if (!createFileIfNotExists(fname)) {
|
|
throw ScriptException("openini: Unable to open INI file: " + fname);
|
|
}
|
|
|
|
++iniFileHandleCount;
|
|
iniFiles.emplace(iniFileHandleCount, IniFile(fname));
|
|
|
|
const auto& ini = iniFiles.at(iniFileHandleCount);
|
|
if (ini.error() != IniFile::Error::NoError) {
|
|
iniFiles.erase(iniFileHandleCount);
|
|
throw ScriptException("openini: Unable to open INI file: " + fname);
|
|
}
|
|
else
|
|
return iniFileHandleCount;
|
|
}
|
|
|
|
ValueHolder closeini(Script&, std::vector<ValueHolder>& args)
|
|
{
|
|
if (args.empty())
|
|
throwInsufficientParameters("closeini", args.size(), 1);
|
|
|
|
if (args[0].getType() != ValueHolder::Type::Integer)
|
|
throwNotAnIniHandle("closeini", 1);
|
|
|
|
int hndl = ValueExtract(args[0]).toInt();
|
|
|
|
auto& ini = getIniHandle("readiniitem", ValueExtract(args[0]).toInt());
|
|
ini.flush();
|
|
|
|
iniFiles.erase(hndl);
|
|
|
|
return {};
|
|
}
|
|
|
|
ValueHolder readini(Script&, std::vector<ValueHolder>& args)
|
|
{
|
|
if (args.size() != 3 && args.size() != 4)
|
|
throwInsufficientParameters("readini", args.size(), 3, 4);
|
|
|
|
if (args[0].getType() != ValueHolder::Type::Integer)
|
|
throwNotAnIniHandle("readini", 1);
|
|
|
|
int sectN = -1;
|
|
int itemN = -1;
|
|
std::string sectS;
|
|
std::string itemS;
|
|
|
|
std::string defaultValue;
|
|
|
|
/* Section parameter */
|
|
if (args[1].getType() == ValueHolder::Type::String)
|
|
sectS = ValueExtract(args[1]).toString();
|
|
else if (args[1].getType() == ValueHolder::Type::Integer)
|
|
sectN = ValueExtract(args[1]).toInt();
|
|
else
|
|
throwNotAStringOrWholeNumber("readini", 2);
|
|
|
|
/* Item parameter */
|
|
if (args[2].getType() == ValueHolder::Type::String)
|
|
itemS = ValueExtract(args[2]).toString();
|
|
else if (args[2].getType() == ValueHolder::Type::Integer)
|
|
itemN = ValueExtract(args[2]).toInt();
|
|
else
|
|
throwNotAStringOrWholeNumber("readini", 2);
|
|
|
|
/* Default value parameter */
|
|
if (args.size() == 4)
|
|
defaultValue = args[3].toStdString();
|
|
|
|
auto& ini = getIniHandle("readini", ValueExtract(args[0]).toInt());
|
|
|
|
/* Section and Item by key */
|
|
if (sectN == -1 && itemN == -1) {
|
|
return ini.read(sectS, itemS, defaultValue);
|
|
}
|
|
|
|
/* Section by key, item by position */
|
|
else if (sectN == -1 && itemN > -1) {
|
|
return ini.read(sectS, itemN, defaultValue);
|
|
}
|
|
|
|
/* Section by position, item by key */
|
|
else if (sectN > -1 && itemN == -1) {
|
|
return ini.read(sectN, itemS, defaultValue);
|
|
}
|
|
|
|
/* Section and item by position */
|
|
else {
|
|
return ini.read(sectN, itemN, defaultValue);
|
|
}
|
|
}
|
|
|
|
ValueHolder readiniitem(Script&, std::vector<ValueHolder>& args)
|
|
{
|
|
if (args.size() != 3)
|
|
throwInsufficientParameters("readiniitem", args.size(), 3);
|
|
|
|
if (args[0].getType() != ValueHolder::Type::Integer)
|
|
throwNotAnIniHandle("readiniitem", 1);
|
|
|
|
int sectN = -1;
|
|
int itemN = -1;
|
|
std::string sectS;
|
|
|
|
/* Section parameter */
|
|
if (args[1].getType() == ValueHolder::Type::String)
|
|
sectS = ValueExtract(args[1]).toString();
|
|
else if (args[1].getType() == ValueHolder::Type::Integer)
|
|
sectN = ValueExtract(args[1]).toInt();
|
|
else
|
|
throwNotAStringOrWholeNumber("readiniitem", 2);
|
|
|
|
/* Item parameter */
|
|
if (args[2].getType() == ValueHolder::Type::Integer)
|
|
itemN = ValueExtract(args[2]).toInt();
|
|
else
|
|
throwNotAWholeNumber("readiniitem", 2);
|
|
|
|
auto& ini = getIniHandle("readiniitem", ValueExtract(args[0]).toInt());
|
|
|
|
/* Section by key, item by position */
|
|
if (sectN == -1 && itemN > -1) {
|
|
return ini.item(sectS, itemN);
|
|
}
|
|
|
|
/* Section by position, item by position */
|
|
else {
|
|
return ini.item(sectN, itemN);
|
|
}
|
|
}
|
|
|
|
ValueHolder readinisection(Script&, std::vector<ValueHolder>& args)
|
|
{
|
|
if (args.size() != 2)
|
|
throwInsufficientParameters("readinisection", args.size(), 2);
|
|
|
|
if (args[0].getType() != ValueHolder::Type::Integer)
|
|
throwNotAnIniHandle("readinisection", 1);
|
|
|
|
int sectN = -1;
|
|
|
|
/* Section parameter */
|
|
if (args[1].getType() == ValueHolder::Type::Integer)
|
|
sectN = ValueExtract(args[1]).toInt();
|
|
else
|
|
throwNotAWholeNumber("readinisection", 2);
|
|
|
|
auto& ini = getIniHandle("readinisection", ValueExtract(args[0]).toInt());
|
|
|
|
return ini.section(sectN);
|
|
}
|
|
|
|
ValueHolder countini(Script&, std::vector<ValueHolder>& args)
|
|
{
|
|
/* Return amount of sections */
|
|
if (args.size() == 1) {
|
|
if (args[0].getType() != ValueHolder::Type::Integer)
|
|
throwNotAnIniHandle("countini", 1);
|
|
|
|
auto& ini = getIniHandle("countini", ValueExtract(args[0]).toInt());
|
|
return static_cast<int>( ini.count() );
|
|
}
|
|
|
|
/* Return amount of items in section */
|
|
else if (args.size() == 2) {
|
|
if (args[0].getType() != ValueHolder::Type::Integer)
|
|
throwNotAnIniHandle("countini", 1);
|
|
|
|
auto& ini = getIniHandle("countini", ValueExtract(args[0]).toInt());
|
|
const auto& section = ValueExtract(args[1]).toString();
|
|
return static_cast<int>( ini.count(section) );
|
|
}
|
|
|
|
else {
|
|
throwInsufficientParameters("countini", args.size(), 0, 1);
|
|
}
|
|
}
|
|
|
|
ValueHolder writeini(Script&, std::vector<ValueHolder>& args)
|
|
{
|
|
if (args.size() < 4)
|
|
throwInsufficientParameters("writeini", args.size(), 4);
|
|
|
|
if (args[0].getType() != ValueHolder::Type::Integer)
|
|
throwNotAnIniHandle("writeini", 1);
|
|
|
|
if (args[1].getType() != ValueHolder::Type::String)
|
|
throwNotAString("writeini", 2);
|
|
|
|
if (args[2].getType() != ValueHolder::Type::String)
|
|
throwNotAString("writeini", 3);
|
|
|
|
|
|
const auto section = ValueExtract(args[1]).toString();
|
|
const auto item = ValueExtract(args[2]).toString();
|
|
const auto value = args[3].toStdString();
|
|
|
|
auto& ini = getIniHandle("writeini", ValueExtract(args[0]).toInt());
|
|
ini.write(section, item, value);
|
|
return {};
|
|
}
|
|
|
|
ValueHolder sreadini(Script&, std::vector<ValueHolder>& args)
|
|
{
|
|
if (args.size() < 3)
|
|
throwInsufficientParameters("sreadini", args.size(), 0);
|
|
|
|
if (args[0].getType() != ValueHolder::Type::String)
|
|
throwNotAString("sreadini", 1);
|
|
|
|
if (args[1].getType() != ValueHolder::Type::String)
|
|
throwNotAString("sreadini", 2);
|
|
|
|
if (args[2].getType() != ValueHolder::Type::String)
|
|
throwNotAString("sreadini", 3);
|
|
|
|
const auto filename = setRelativePath( ValueExtract(args[0]).toString() );
|
|
const auto section = ValueExtract(args[1]).toString();
|
|
const auto item = ValueExtract(args[2]).toString();
|
|
|
|
IniFile f(filename);
|
|
|
|
if (f.error() != IniFile::Error::NoError) {
|
|
// Could be that the file didn't exist.
|
|
// For "simple readini" we will return an empty value as if it isn't an error.
|
|
return std::string{};
|
|
}
|
|
|
|
return f.read(section, item);
|
|
}
|
|
|
|
ValueHolder swriteini(Script&, std::vector<ValueHolder>& args)
|
|
{
|
|
if (args.size() < 4)
|
|
throwInsufficientParameters("swriteini", args.size(), 0);
|
|
|
|
if (args[0].getType() != ValueHolder::Type::String)
|
|
throwNotAString("swriteini", 1);
|
|
|
|
if (args[1].getType() != ValueHolder::Type::String)
|
|
throwNotAString("swriteini", 2);
|
|
|
|
if (args[2].getType() != ValueHolder::Type::String)
|
|
throwNotAString("swriteini", 3);
|
|
|
|
const auto filename = setRelativePath( ValueExtract(args[0]).toString() );
|
|
const auto section = ValueExtract(args[1]).toString();
|
|
const auto item = ValueExtract(args[2]).toString();
|
|
const auto value = args[3].toStdString();
|
|
|
|
if (!createFileIfNotExists(filename)) {
|
|
throw ScriptException("swriteini: Unable to open INI file: " + filename);
|
|
}
|
|
|
|
IniFile f(filename);
|
|
|
|
if (f.error() != IniFile::Error::NoError) {
|
|
throw ScriptException("swriteini: Unable to open INI file: " + filename);
|
|
}
|
|
|
|
f.write(section, item, value);
|
|
f.flush();
|
|
return {};
|
|
}
|
|
|
|
}
|
|
|