#include "common.h" // This file is part of MRCI. // MRCI 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 3 of the License, or // (at your option) any later version. // MRCI 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 MRCI under the LICENSE.md file. If not, see // . QString sessionCountShareKey() { return QString(APP_NAME) + ".SessionCount"; } QString boolStr(bool state) { QString ret; if (state) ret = "true"; else ret = "false"; return ret; } uint rdSessionLoad() { uint ret = 0; QSharedMemory mem(sessionCountShareKey()); if (mem.attach(QSharedMemory::ReadOnly)) { mem.lock(); memcpy(&ret, mem.data(), 4); mem.unlock(); mem.detach(); } return ret; } void wrSessionLoad(uint value) { QSharedMemory mem(sessionCountShareKey()); if (mem.attach(QSharedMemory::ReadWrite)) { mem.lock(); memcpy(mem.data(), &value, 4); mem.unlock(); mem.detach(); } } QString genSerialNumber() { return QDateTime::currentDateTime().toString("yyyyMMddHHmmsszzz"); } void serializeThread(QThread *thr) { thr->setObjectName(genSerialNumber()); } QByteArray wrFrame(quint16 cmdId, const QByteArray &data, uchar dType) { QByteArray cmdBa = wrInt(cmdId, 16); QByteArray typeBa = wrInt(dType, 8); QByteArray sizeBa = wrInt(data.size(), MAX_FRAME_BITS); return typeBa + cmdBa + sizeBa + data; } QByteArray wrInt(quint64 num, int numOfBits) { QByteArray ret(numOfBits / 8, static_cast(0)); num = qToLittleEndian(num); memcpy(ret.data(), &num, static_cast(ret.size())); return ret; } QByteArray wrInt(qint64 num, int numOfBits) { return wrInt(static_cast(num), numOfBits); } QByteArray wrInt(int num, int numOfBits) { return wrInt(static_cast(num), numOfBits); } QByteArray wrInt(uint num, int numOfBits) { return wrInt(static_cast(num), numOfBits); } QByteArray toFILE_INFO(const QFileInfo &info) { // this function converts some information extracted from a QFileInfo object to // a FILE_INFO frame. // format: [1byte(flags)][8bytes(createTime)][8bytes(modTime)][8bytes(fileSize)] // [TEXT(fileName)][TEXT(symLinkTarget)] // note: the TEXT strings are 16bit NULL terminated meaning 2 bytes of 0x00 // indicate the end of the string. // note: the integer data found in flags, modTime, createTime and fileSize // are formatted in little endian byte order (unsigned). char flags = 0; if (info.isFile()) flags |= IS_FILE; if (info.isDir()) flags |= IS_DIR; if (info.isSymLink()) flags |= IS_SYMLNK; if (info.isReadable()) flags |= CAN_READ; if (info.isWritable()) flags |= CAN_WRITE; if (info.isExecutable()) flags |= CAN_EXE; if (info.exists()) flags |= EXISTS; QByteArray ret; QByteArray strTerm(2, 0); ret.append(flags); ret.append(wrInt(info.birthTime().toMSecsSinceEpoch(), 64)); ret.append(wrInt(info.lastModified().toMSecsSinceEpoch(), 64)); ret.append(wrInt(info.size(), 64)); ret.append(toTEXT(info.fileName()) + strTerm); ret.append(toTEXT(info.symLinkTarget() + strTerm)); return ret; } QByteArray toFILE_INFO(const QString &path) { return toFILE_INFO(QFileInfo(path)); } QByteArray toPEER_INFO(const SharedObjs *sharedObjs) { return *sharedObjs->sessionId + *sharedObjs->userId + fixedToTEXT(*sharedObjs->userName, 24) + fixedToTEXT(*sharedObjs->appName, 64) + fixedToTEXT(*sharedObjs->displayName, 32); } QByteArray toNEW_CMD(quint16 cmdId, const QString &cmdName, ExternCommand *cmdObj) { QByteArray idBa = wrInt(cmdId, 16); QByteArray genBa = wrInt(0, 8); if (cmdObj->handlesGenfile()) { genBa = wrInt(1, 8); } return idBa + genBa + fixedToTEXT(cmdName, 64) + fixedToTEXT(cmdObj->libText(), 64); } QByteArray toMY_INFO(const SharedObjs *sharedObjs) { Query db; db.setType(Query::PULL, TABLE_USERS); db.addColumn(COLUMN_EMAIL); db.addColumn(COLUMN_EMAIL_VERIFIED); db.addCondition(COLUMN_USERNAME, *sharedObjs->userName); db.exec(); QByteArray confirmed; if (db.getData(COLUMN_EMAIL_VERIFIED).toBool()) { confirmed.append(static_cast(0x01)); } else { confirmed.append(static_cast(0x00)); } return toPEER_INFO(sharedObjs) + fixedToTEXT(db.getData(COLUMN_EMAIL).toString(), 64) + fixedToTEXT(*sharedObjs->groupName, 12) + confirmed; } QByteArray toPEER_STAT(const QByteArray &sesId, const QByteArray &chIds, bool isDisconnecting) { if (isDisconnecting) { return sesId + chIds + QByteArray(1, 0x01); } else { return sesId + chIds + QByteArray(1, 0x00); } } QByteArray toTEXT(const QString &txt) { QByteArray ret = QTextCodec::codecForName(TXT_CODEC)->fromUnicode(txt); return ret.mid(2); // removes BOM. } QByteArray fixedToTEXT(const QString &txt, int len) { return toTEXT(txt.leftJustified(len, ' ', true)); } QString fromTEXT(const QByteArray &txt) { return QTextCodec::codecForName(TXT_CODEC)->toUnicode(txt); } quint64 rdInt(const QByteArray &bytes) { quint64 ret = 0; memcpy(&ret, bytes.data(), static_cast(bytes.size())); return qFromLittleEndian(ret); } bool noCaseMatch(const QString &strA, const QString &strB) { return strA.toLower() == strB.toLower(); } bool containsNewLine(const QString &str) { bool ret = false; for (auto&& chr : str) { if (chr.category() == QChar::Other_Control) { ret = true; } } return ret; } bool validSubId(const QString &num) { bool ret = false; if (isInt(num)) { ret = (num.toInt() >= 0) && (num.toInt() <= 255); } return ret; } bool validUserName(const QString &uName) { bool ret = false; if ((uName.size() >= 2) && (uName.size() <= 24)) { ret = !uName.contains(' ') && !containsNewLine(uName); } return ret; } bool validCommonName(const QString &name) { bool ret = false; if ((name.size() >= 1) && (name.size() <= 200)) { ret = !name.contains(' ') && !containsNewLine(name); } return ret; } bool validEmailAddr(const QString &email) { bool ret = false; QStringList spEmail = email.split('@'); if ((spEmail.size() == 2) && (email.size() >= 4) && (email.size() <= 64)) { if (!email.contains(' ') && !containsNewLine(email)) { ret = (spEmail[1].split('.').size() > 1); } } return ret; } bool validGroupName(const QString &grName) { bool ret = false; if ((grName.size() >= 1) && (grName.size() <= 12)) { ret = !grName.contains(' ') && !containsNewLine(grName); } return ret; } bool validCommandName(const QString &name) { bool ret = true; if ((name.size() >= 1) && (name.size() <= 64) && !name.contains(' ')) { for (auto&& chr : name) { if (!chr.isNumber() && !chr.isLetter() && !(chr == '_') && !(chr == '?')) { ret = false; break; } } } else { ret = false; } return ret; } bool validDispName(const QString &name) { return (name.size() <= 32) && !containsNewLine(name); } bool validChName(const QString &name) { bool ret = false; if ((name.size() >= 4) && (name.size() <= 32)) { ret = !name.contains(' ') && !containsNewLine(name); } return ret; } bool validLevel(const QString &num, bool includePub) { bool ret = false; if (isInt(num)) { if (includePub) { ret = (num.toInt() >= 1) && (num.toInt() <= PUBLIC); } else { ret = (num.toInt() >= 1) && (num.toInt() <= REGULAR); } } return ret; } bool validPassword(const QString &pw) { bool ret = false; if ((pw.size() >= 8) && (pw.size() <= 200)) { bool letters = false; bool numbers = false; bool upper = false; bool lower = false; bool special = false; for (int i = 0; i < pw.size(); ++i) { if (pw[i].isLetter()) {letters = true; break;} } for (int i = 0; (i < pw.size()) && letters; ++i) { if (pw[i].isNumber()) {numbers = true; break;} } for (int i = 0; (i < pw.size()) && numbers; ++i) { if (pw[i].isUpper()) {upper = true; break;} } for (int i = 0; (i < pw.size()) && upper; ++i) { if (pw[i].isLower()) {lower = true; break;} } for (int i = 0; (i < pw.size()) && lower; ++i) { if (pw[i].isSymbol() || pw[i].isPunct()) {special = true; break;} } ret = (letters && numbers && upper && lower && special); } return ret; } bool matchedFsObjTypes(const QString &pathA, const QString &pathB) { QFileInfo infoA(pathA); QFileInfo infoB(pathB); bool ret = false; if (infoA.isSymLink()) ret = infoB.isSymLink(); else if (infoA.isFile()) ret = infoB.isFile(); else ret = infoB.isDir(); return ret; } bool matchedVolume(const QString &pathA, const QString &pathB) { QFileInfo infoA(pathA); QFileInfo infoB(pathB); QStorageInfo storA(infoA.absolutePath()); QStorageInfo storB(infoB.absolutePath()); return storA.device() == storB.device(); } void mkPathForFile(const QString &path) { mkPath(QFileInfo(path).absolutePath()); } bool userExists(const QString &uName) { Query db; db.setType(Query::PULL, TABLE_USERS); db.addColumn(COLUMN_USERNAME); db.addCondition(COLUMN_USERNAME, uName); db.exec(); return db.rows(); } bool recoverPWExists(const QString &uName) { Query db; db.setType(Query::PULL, TABLE_PW_RECOVERY); db.addColumn(COLUMN_USERNAME); db.addCondition(COLUMN_USERNAME, uName); db.exec(); return db.rows(); } bool emailExists(const QString &email) { Query db; db.setType(Query::PULL, TABLE_USERS); db.addColumn(COLUMN_EMAIL); db.addCondition(COLUMN_EMAIL, email); db.exec(); return db.rows(); } bool groupExists(const QString &grName) { Query db; db.setType(Query::PULL, TABLE_GROUPS); db.addColumn(COLUMN_GRNAME); db.addCondition(COLUMN_GRNAME, grName); db.exec(); return db.rows(); } bool modExists(const QString &modName) { Query db; db.setType(Query::PULL, TABLE_MODULES); db.addColumn(COLUMN_MOD_NAME); db.addCondition(COLUMN_MOD_NAME, modName); db.exec(); return db.rows(); } bool rdOnlyFlagExists(const QString &chName, uchar subId, int level) { Query db; db.setType(Query::PULL, TABLE_RDONLY_CAST); db.addColumn(COLUMN_ACCESS_LEVEL); db.addCondition(COLUMN_SUB_CH_ID, subId); db.addCondition(COLUMN_CHANNEL_NAME, chName); db.addCondition(COLUMN_ACCESS_LEVEL, level); db.exec(); return db.rows(); } bool isLocked(const QString &uName) { Query db; db.setType(Query::PULL, TABLE_USERS); db.addColumn(COLUMN_LOCKED); db.addCondition(COLUMN_USERNAME, uName); db.exec(); return db.getData(COLUMN_LOCKED).toBool(); } bool commandHasRank(const QString &cmdName) { Query db; db.setType(Query::PULL, TABLE_CMD_RANKS); db.addColumn(COLUMN_COMMAND); db.addCondition(COLUMN_COMMAND, cmdName); db.exec(); return db.rows(); } bool checkRank(const QString &myGroup, const QString &targetGroup, bool equalAcceptable) { uint myRank = getRankForGroup(myGroup); uint targetRank = getRankForGroup(targetGroup); bool ret = false; if (equalAcceptable) { ret = (myRank <= targetRank); } else { ret = (myRank < targetRank); } return ret; } bool maxedInstalledMods() { Query db; db.setType(Query::PULL, TABLE_MODULES); db.addColumn(COLUMN_MOD_NAME); db.exec(); int installed = db.rows(); double max = (qPow(2, 16) - (MAX_CMDS_PER_MOD * 2)) / MAX_CMDS_PER_MOD; //max commands - (max async commands + max internal commands) / max commands per mod return installed >= max; } bool isBool(const QString &str) { bool ret = false; int binary = str.toInt(&ret); if (ret) { ret = (binary == 1) || (binary == 0); } return ret; } bool isInt(const QString &str) { bool ret; str.toULongLong(&ret); return ret; } bool containsChId(const QByteArray &chId, const QByteArray &chIds) { bool ret = false; for (int i = 0; i < chIds.size(); i += 9) { QByteArray id = QByteArray::fromRawData(chIds.data() + i, 9); if (id == chId) { ret = true; break; } } return ret; } bool matchChs(const QByteArray &chsA, const QByteArray &chsB) { bool ret = false; for (int i = 0; i < chsA.size(); i += 9) { QByteArray id = QByteArray::fromRawData(chsA.data() + i, 9); if (containsChId(id, chsB)) { ret = true; break; } } return ret; } bool containsActiveCh(const QByteArray &chIds) { bool ret = false; Query db; for (int i = 0; i < chIds.size(); i += 9) { quint64 chId = rdInt(QByteArray::fromRawData(chIds.data() + i, 8)); quint64 subId = rdInt(QByteArray::fromRawData(chIds.data() + (i + 8), 1)); db.setType(Query::PULL, TABLE_SUB_CHANNELS); db.addColumn(COLUMN_CHANNEL_ID); db.addCondition(COLUMN_CHANNEL_ID, chId); db.addCondition(COLUMN_SUB_CH_ID, subId); db.addCondition(COLUMN_ACTIVE_UPDATE, true); db.exec(); if (db.rows()) { ret = true; break; } } return ret; } bool channelExists(quint64 chId) { Query db; db.setType(Query::PULL, TABLE_CHANNELS); db.addColumn(COLUMN_CHANNEL_ID); db.addCondition(COLUMN_CHANNEL_ID, chId); db.exec(); return db.rows(); } bool channelExists(const QString &chName) { Query db; db.setType(Query::PULL, TABLE_CHANNELS); db.addColumn(COLUMN_CHANNEL_NAME); db.addCondition(COLUMN_CHANNEL_NAME, chName); db.exec(); return db.rows(); } bool channelSubExists(quint64 chId, uchar subId) { Query db; db.setType(Query::PULL, TABLE_SUB_CHANNELS); db.addColumn(COLUMN_SUB_CH_ID); db.addCondition(COLUMN_CHANNEL_ID, chId); db.addCondition(COLUMN_SUB_CH_ID, subId); db.exec(); return db.rows(); } bool channelSubExists(const QString &ch, const QString &sub) { Query db; db.setType(Query::PULL, TABLE_SUB_CHANNELS); db.addColumn(COLUMN_SUB_CH_NAME); db.addCondition(COLUMN_CHANNEL_NAME, ch); db.addCondition(COLUMN_SUB_CH_NAME, sub); db.exec(); return db.rows(); } bool inviteExists(const QString &uName, const QString &chName) { Query db; db.setType(Query::PULL, TABLE_CH_MEMBERS); db.addColumn(COLUMN_CHANNEL_NAME); db.addCondition(COLUMN_CHANNEL_NAME, chName); db.addCondition(COLUMN_USERNAME, uName); db.addCondition(COLUMN_PENDING_INVITE, true); db.exec(); return db.rows(); } bool memberExists(const QString &uName, const QString &chName) { Query db; db.setType(Query::PULL, TABLE_CH_MEMBERS); db.addColumn(COLUMN_CHANNEL_NAME); db.addCondition(COLUMN_CHANNEL_NAME, chName); db.addCondition(COLUMN_USERNAME, uName); db.exec(); return db.rows(); } bool globalActiveFlag() { Query db; db.setType(Query::PULL, TABLE_SERV_SETTINGS); db.addColumn(COLUMN_ACTIVE_UPDATE); db.exec(); return db.getData(COLUMN_ACTIVE_UPDATE).toBool(); } bool allowMemberDel(const SharedObjs *sharedObjs, const QString &targetUName, const QString &chName) { bool ret = false; if (memberExists(targetUName, chName)) { int targetLevel = channelAccessLevel(targetUName, chName); if (targetLevel != OWNER) { if (noCaseMatch(*sharedObjs->userName, targetUName)) { ret = true; } else if (channelAccessLevel(sharedObjs, chName) < targetLevel) { ret = true; } } } return ret; } bool allowLevelChange(const SharedObjs *sharedObjs, int newLevel, const QString &chName) { bool ret = false; int myLevel = channelAccessLevel(*sharedObjs->userName, chName); if ((myLevel == OWNER) && (newLevel == OWNER)) { ret = true; } else if (newLevel > myLevel) { ret = true; } return ret; } bool genSubId(const QString &chName, int *newId) { bool ret = false; Query db; db.setType(Query::PULL, TABLE_SUB_CHANNELS); db.addColumn(COLUMN_SUB_CH_ID); db.addCondition(COLUMN_CHANNEL_NAME, chName); db.exec(); if (db.rows() < maxSubChannels()) { QList subList; for (int i = 0; i < db.rows(); ++i) { subList.append(db.getData(COLUMN_SUB_CH_ID, i).toInt()); } ret = true; *newId = 0; while (subList.contains(*newId)) *newId += 1; } return ret; } bool isChOwner(const QString &uName) { Query db; db.setType(Query::PULL, TABLE_CH_MEMBERS); db.addColumn(COLUMN_USERNAME); db.addCondition(COLUMN_USERNAME, uName); db.addCondition(COLUMN_PENDING_INVITE, false); db.addCondition(COLUMN_ACCESS_LEVEL, OWNER); db.exec(); return db.rows(); } int channelAccessLevel(const QString &uName, quint64 chId) { Query db; db.setType(Query::PULL, TABLE_CH_MEMBERS); db.addColumn(COLUMN_ACCESS_LEVEL); db.addCondition(COLUMN_CHANNEL_ID, chId); db.addCondition(COLUMN_USERNAME, uName); db.addCondition(COLUMN_PENDING_INVITE, false); db.exec(); if (db.rows()) { return db.getData(COLUMN_ACCESS_LEVEL).toInt(); } else { return PUBLIC; } } int channelAccessLevel(const QString &uName, const QString &chName) { Query db; db.setType(Query::PULL, TABLE_CH_MEMBERS); db.addColumn(COLUMN_ACCESS_LEVEL); db.addCondition(COLUMN_CHANNEL_NAME, chName); db.addCondition(COLUMN_USERNAME, uName); db.addCondition(COLUMN_PENDING_INVITE, false); db.exec(); if (db.rows()) { return db.getData(COLUMN_ACCESS_LEVEL).toInt(); } else { return PUBLIC; } } int channelAccessLevel(const SharedObjs *sharedObjs, const QString &chName) { if (*sharedObjs->chOwnerOverride) { return OWNER; } else { return channelAccessLevel(*sharedObjs->userName, chName); } } int channelAccessLevel(const SharedObjs *sharedObjs, quint64 chId) { if (*sharedObjs->chOwnerOverride) { return OWNER; } else { return channelAccessLevel(*sharedObjs->userName, chId); } } int lowestAcessLevel(quint64 chId, uchar subId) { Query db; db.setType(Query::PULL, TABLE_SUB_CHANNELS); db.addColumn(COLUMN_LOWEST_LEVEL); db.addCondition(COLUMN_CHANNEL_ID, chId); db.addCondition(COLUMN_SUB_CH_ID, subId); db.exec(); if (db.rows()) { return db.getData(COLUMN_LOWEST_LEVEL).toInt(); } else { return 5000; } } int maxSubChannels() { Query db; db.setType(Query::PULL, TABLE_SERV_SETTINGS); db.addColumn(COLUMN_MAX_SUB_CH); db.exec(); return db.getData(COLUMN_MAX_SUB_CH).toInt(); } quint64 getChId(const QString &chName) { Query db; db.setType(Query::PULL, TABLE_CHANNELS); db.addColumn(COLUMN_CHANNEL_ID); db.addCondition(COLUMN_CHANNEL_NAME, chName); db.exec(); return db.getData(COLUMN_CHANNEL_ID).toULongLong(); } uchar getSubId(const QString &chName, const QString &subName) { Query db; db.setType(Query::PULL, TABLE_SUB_CHANNELS); db.addColumn(COLUMN_SUB_CH_ID); db.addCondition(COLUMN_CHANNEL_NAME, chName); db.addCondition(COLUMN_SUB_CH_NAME, subName); db.exec(); return static_cast(db.getData(COLUMN_SUB_CH_ID).toUInt()); } int chPos(const QByteArray &id, const QByteArray &chIds) { int ret = -1; for (int i = 0; i < chIds.size(); i += 9) { QByteArray chInList = QByteArray::fromRawData(chIds.data() + i, 9); if (chInList == id) { ret = i; break; } } return ret; } int countChs(const QByteArray &chIds) { int ret = 0; for (int i = 0; i < chIds.size(); i += 9) { quint64 id = rdInt(QByteArray::fromRawData(chIds.data() + i, 8)); if (id != 0) { ret++; } } return ret; } int blankChPos(const QByteArray &chIds) { int ret = -1; for (int i = 0; i < chIds.size(); i += 9) { quint64 id = rdInt(QByteArray::fromRawData(chIds.data() + i, 8)); if (id == 0) { ret = i; break; } } return ret; } uint getRankForGroup(const QString &grName) { Query db; db.setType(Query::PULL, TABLE_GROUPS); db.addColumn(COLUMN_HOST_RANK); db.addCondition(COLUMN_GRNAME, grName); db.exec(); return db.getData(COLUMN_HOST_RANK).toUInt(); } void uniqueAdd(quint16 id, QList &list) { if (!list.contains(id)) { list.append(id); } } void listDir(QList > &list, const QString &srcPath, const QString &dstPath) { QDir dir(srcPath); dir.setFilter(QDir::AllEntries | QDir::Hidden | QDir::NoDotAndDotDot); dir.setSorting(QDir::DirsFirst); QStringList ls = dir.entryList(); for (int i = 0; i < ls.size(); ++i) { QPair srcToDst(srcPath + "/" + ls[i], dstPath + "/" + ls[i]); list.append(srcToDst); } } QString getUserGroup(const QString &uName) { Query db; db.setType(Query::PULL, TABLE_USERS); db.addColumn(COLUMN_GRNAME); db.addCondition(COLUMN_USERNAME, uName); db.exec(); return db.getData(COLUMN_GRNAME).toString(); } QString getUserNameForEmail(const QString &email) { Query db; db.setType(Query::PULL, TABLE_USERS); db.addColumn(COLUMN_USERNAME); db.addCondition(COLUMN_EMAIL, email); db.exec(); return db.getData(COLUMN_USERNAME).toString(); } QString getEmailForUser(const QString &uName) { Query db; db.setType(Query::PULL, TABLE_USERS); db.addColumn(COLUMN_EMAIL); db.addCondition(COLUMN_USERNAME, uName); db.exec(); return db.getData(COLUMN_EMAIL).toString(); } QString escapeChars(const QString &str, const QChar &escapeChr, const QChar &chr) { QString ret; bool escaped = false; if (escapeChr == chr) { ret = str; } else { for (auto&& strChr : str) { if ((strChr == chr) && !escaped) { ret.append(escapeChr); ret.append(strChr); } else { escaped = false; if (strChr == escapeChr) { escaped = true; } ret.append(strChr); } } } return ret; } QList genSequence(int min, int max, int len) { QList ret; for (int i = 0; i < len; ++i) { ret.append(QRandomGenerator::global()->bounded(min, max)); } return ret; } QChar genLetter() { // generate random letter from ascii table decimal value 97-122. return QChar(static_cast(QRandomGenerator::global()->bounded(97, 122))); } QChar genNum() { // generate random number from ascii table decimal value 48-57. return QChar(static_cast(QRandomGenerator::global()->bounded(48, 57))); } QChar genSpecialChar() { static QString specialChars = "`~!@#$%^&*()-_+=[]{}\\|:;\"'<,>.?/"; return specialChars[QRandomGenerator::global()->bounded(0, specialChars.size() - 1)]; } int inRange(int pos, int min, int max) { int ret = pos; if (pos < min) ret = min; if (pos > max) ret = max; return ret; } void moveCharLeft(int pos, QString &str) { pos = inRange(pos, 0, str.size() - 1); QChar chr = str[pos]; str.remove(pos, 1); if (pos == 0) str.append(chr); else str.insert(pos - 1, chr); } void moveCharRight(int pos, QString &str) { pos = inRange(pos, 0, str.size() - 1); QChar chr = str[pos]; str.remove(pos, 1); if (pos == str.size() - 1) str.insert(0, chr); else str.insert(pos + 1, chr); } QString genPw() { QString ret; QList seq = genSequence(2, 5, 4); for (int i = 0; i < seq[0]; ++i) { ret.append(genLetter()); } for (int i = 0; i < seq[1]; ++i) { ret.append(genLetter().toUpper()); } for (int i = 0; i < seq[2]; ++i) { ret.append(genNum()); } for (int i = 0; i < seq[3]; ++i) { ret.append(genSpecialChar()); } seq = genSequence(0, ret.size() - 1, 10); bool toggle = false; for (int i : seq) { if (toggle) moveCharRight(i, ret); else moveCharLeft(i, ret); toggle = !toggle; } return ret; } QString modDataPath() { QString ret = qEnvironmentVariable(ENV_MOD_PATH, DEFAULT_MOD_PATH); if (ret.right(1) == '/') ret.chop(1); else if (ret.right(1) == '\\') ret.chop(1); ret = expandEnvVariables(ret); mkPath(ret); return ret; } QString pipesPath() { QString ret = qEnvironmentVariable(ENV_PIPE_PATH, DEFAULT_PIPE_PATH); if (ret.right(1) == '/') ret.chop(1); else if (ret.right(1) == '\\') ret.chop(1); ret = expandEnvVariables(ret); mkPath(ret); return ret; } void msgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { Q_UNUSED(type); Q_UNUSED(context); if (!msg.contains("QSslSocket: cannot resolve")) { Query db; db.setType(Query::PUSH, TABLE_DMESG); db.addColumn(COLUMN_LOGENTRY, msg); db.exec(); } } void mkPath(const QString &path) { if (!QDir().exists(path)) { QDir().mkpath(path); } } void mkFile(const QString &path) { mkPathForFile(path); QFile file(path); file.open(QFile::WriteOnly); file.write(QByteArray()); file.close(); } void wrOpenCh(RWSharedObjs *sharedObjs, const QByteArray &id) { quint64 chId = rdInt(QByteArray::fromRawData(id.data(), 8)); quint64 subId = rdInt(QByteArray::fromRawData(id.data() + 8, 1)); int rdPos = blankChPos(*sharedObjs->chIds); int wrPos = blankChPos(*sharedObjs->wrAbleChIds); int lvl; if (*sharedObjs->chOwnerOverride) { lvl = OWNER; } else { lvl = channelAccessLevel(*sharedObjs->userName, chId); } sharedObjs->chIds->replace(rdPos, 9, id); Query db; db.setType(Query::PULL, TABLE_RDONLY_CAST); db.addColumn(COLUMN_CHANNEL_ID); db.addCondition(COLUMN_CHANNEL_ID, chId); db.addCondition(COLUMN_SUB_CH_ID, subId); db.addCondition(COLUMN_ACCESS_LEVEL, lvl); db.exec(); if (db.rows() == 0) { sharedObjs->wrAbleChIds->replace(wrPos, 9, id); } if (globalActiveFlag()) { *sharedObjs->activeUpdate = true; } else { *sharedObjs->activeUpdate = containsActiveCh(*sharedObjs->chIds); } } void wrCloseCh(RWSharedObjs *sharedObjs, const QByteArray &id, QByteArray &peerStat) { int rdPos = chPos(id, *sharedObjs->chIds); int wrPos = chPos(id, *sharedObjs->wrAbleChIds); bool needPeerStat = false; QByteArray oldChIds = *sharedObjs->chIds; if (*sharedObjs->activeUpdate) { needPeerStat = true; } if (rdPos != -1) { sharedObjs->chIds->replace(rdPos, 9, QByteArray(9, static_cast(0))); if (wrPos != -1) { sharedObjs->wrAbleChIds->replace(wrPos, 9, QByteArray(9, static_cast(0))); } *sharedObjs->activeUpdate = containsActiveCh(*sharedObjs->chIds); } if (needPeerStat && (oldChIds != *sharedObjs->chIds)) { QByteArray castHeader = oldChIds + wrInt(PEER_STAT, 8); QByteArray data = toPEER_STAT(*sharedObjs->sessionId, *sharedObjs->chIds, false); peerStat = castHeader + data; } else { peerStat.clear(); } } void wrCloseCh(RWSharedObjs *sharedObjs, quint64 chId, QByteArray &peerStat) { QByteArray chBa = wrInt(chId, 64); QByteArray oldChIds = *sharedObjs->chIds; bool needPeerStat = false; if (*sharedObjs->activeUpdate) { needPeerStat = true; } for (int i = 0; i < sharedObjs->chIds->size(); i += 9) { if (chBa == QByteArray::fromRawData(sharedObjs->chIds->data() + i, 8)) { sharedObjs->chIds->replace(i, 9, QByteArray(9, static_cast(0))); } if (chBa == QByteArray::fromRawData(sharedObjs->wrAbleChIds->data() + i, 8)) { sharedObjs->wrAbleChIds->replace(i, 9, QByteArray(9, static_cast(0))); } } *sharedObjs->activeUpdate = containsActiveCh(*sharedObjs->chIds); if (needPeerStat && (oldChIds != *sharedObjs->chIds)) { QByteArray castHeader = oldChIds + wrInt(PEER_STAT, 8); QByteArray data = toPEER_STAT(*sharedObjs->sessionId, *sharedObjs->chIds, false); peerStat = castHeader + data; } else { peerStat.clear(); } } void wrCloseCh(RWSharedObjs *sharedObjs, const QByteArray &id) { QByteArray unused; wrCloseCh(sharedObjs, id, unused); } void wrCloseCh(RWSharedObjs *sharedObjs, quint64 chId) { QByteArray unused; wrCloseCh(sharedObjs, chId, unused); } QStringList parseArgs(const QByteArray &data, int maxArgs, int *pos) { QStringList ret; QString arg; QString line = fromTEXT(data); bool inDQuotes = false; bool inSQuotes = false; bool escaped = false; if (pos != nullptr) *pos = 0; for (int i = 0; i < line.size(); ++i) { if (pos != nullptr) *pos += (TXT_CODEC_BITS / 8); if ((line[i] == '\'') && !inDQuotes && !escaped) { // single quote ' inSQuotes = !inSQuotes; } else if ((line[i] == '\"') && !inSQuotes && !escaped) { // double quote " inDQuotes = !inDQuotes; } else { escaped = false; if (line[i].isSpace() && !inDQuotes && !inSQuotes) { // space if (!arg.isEmpty()) { ret.append(arg); arg.clear(); } } else { if ((line[i] == '\\') && ((i + 1) < line.size())) { if ((line[i + 1] == '\'') || (line[i + 1] == '\"')) { escaped = true; } else { arg.append(line[i]); } } else { arg.append(line[i]); } } } if ((ret.size() >= maxArgs) && (maxArgs != -1)) { break; } } if (!arg.isEmpty() && !inDQuotes && !inSQuotes) { ret.append(arg); } return ret; } QString getParam(const QString &key, const QStringList &args) { // this can be used by command objects to pick out parameters // from a command line that are pointed by a name identifier // example: -i /etc/some_file, this function should pick out // "/etc/some_file" from args if "-i" is passed into key. QString ret; int pos = args.indexOf(QRegExp(key, Qt::CaseInsensitive)); if (pos != -1) { // key found. if ((pos + 1) <= (args.size() - 1)) { // check ahead to make sure pos + 1 will not go out // of range. if (!args[pos + 1].startsWith("-")) { // the "-" used throughout this application // indicates an argument so the above 'if' // statement will check to make sure it does // not return another argument as a parameter // in case a back-to-back "-arg -arg" is // present. ret = args[pos + 1]; } } } return ret; } bool argExists(const QString &key, const QStringList &args) { return args.contains(key, Qt::CaseInsensitive); } SessionCarrier::SessionCarrier(Session *session) : QObject() { sessionObj = session; } InternCommand::InternCommand(QObject *parent) : ExternCommand(parent) { rwSharedObjs = nullptr; } void InternCommand::setWritableDataShare(RWSharedObjs *sharedObjs) { rwSharedObjs = sharedObjs; term(); } bool InternCommand::loopEnabled() { if (rwSharedObjs == nullptr) { return false; } else { return rwSharedObjs->activeLoopCmds->contains(cmdId); } } bool InternCommand::moreInputEnabled() { if (rwSharedObjs == nullptr) { return false; } else { return rwSharedObjs->moreInputCmds->contains(cmdId); } } QString InternCommand::libText() { return INTERN_MOD_NAME; } QString InternCommand::parseMd(int offset) { QFile file(":/docs/intern_commands/" + objectName() + ".md", this); QByteArray data; if (file.open(QFile::ReadOnly)) { data = file.readAll(); } file.close(); int targetTags = offset * 6; int pos = -1; int len = 0; for (int i = 0, tags = 0; i < data.size(); ++i) { if (data[i] == '#') { ++tags; if (pos != -1) { break; } } else if (tags == targetTags) { len++; if (pos == -1) { pos = i; } } } QByteArray ret = data.mid(pos, len).trimmed(); if (offset == 2) { ret.chop(3); ret.remove(0, 3); } return ret; } QString InternCommand::shortText() { return parseMd(1); } QString InternCommand::ioText() { return parseMd(2); } QString InternCommand::longText() { return parseMd(3); } CommandOutput::CommandOutput(ExternCommand *parent) : QObject(parent) { // this class is used by CmdExecutor to permanently attach a command id to // the command object's output data and isolates that id from the object // itself. this prevents objects from impersonating another object's output // data. cmdObj = parent; } void CommandOutput::setCmdId(quint16 id) { cmdId = id; } void CommandOutput::dataFromCmdObj(const QByteArray &data, uchar typeId) { emit dataOut(cmdId, data, typeId); } void CommandOutput::openChIdFromCmdObj(quint64 id, uchar subId) { emit openChById(cmdId, id, subId); } void CommandOutput::openChNameFromCmdObj(const QString &ch, const QString &sub) { emit openChByName(cmdId, ch, sub); } void CommandOutput::closeChIdFromCmdObj(quint64 id, uchar subId) { emit closeChById(cmdId, id, subId); } void CommandOutput::closeChNameFromCmdObj(const QString &ch, const QString &sub) { emit closeChByName(cmdId, ch, sub); } void CommandOutput::enableLoopFromCmdObj(bool state) { cmdObj->inLoopMode = state; emit enableLoop(cmdId, state); } void CommandOutput::enableMoreInputFromCmdObj(bool state) { cmdObj->inMoreInputMode = state; emit enableMoreInput(cmdId, state); } void CommandOutput::finished() { cmdObj->inMoreInputMode = false; cmdObj->inLoopMode = false; emit cmdFinished(cmdId); } RWSharedObjs::RWSharedObjs(QObject *parent) : QObject(parent) { commands = nullptr; cmdNames = nullptr; chList = nullptr; chIds = nullptr; wrAbleChIds = nullptr; sessionAddr = nullptr; userName = nullptr; groupName = nullptr; displayName = nullptr; appName = nullptr; clientMajor = nullptr; clientMinor = nullptr; clientPatch = nullptr; sessionId = nullptr; moreInputCmds = nullptr; activeLoopCmds = nullptr; pausedCmds = nullptr; p2pAccepted = nullptr; p2pPending = nullptr; activeUpdate = nullptr; chOwnerOverride = nullptr; hostRank = nullptr; } ShellIPC::ShellIPC(const QStringList &args, QObject *parent) : QLocalSocket(parent) { arguments = args; pipeName = pipesPath() + "/" + QString(APP_NAME) + ".TCPServer.Control"; connect(this, SIGNAL(connected()), this, SLOT(hostConnected())); connect(this, SIGNAL(disconnected()), this, SIGNAL(closeInstance())); connect(this, SIGNAL(readyRead()), this, SLOT(dataIn())); } bool ShellIPC::connectToHost() { connectToServer(pipeName); if (!waitForConnected()) { QTextStream(stdout) << "" << endl << "Host instance not running." << endl << endl; } return state() == QLocalSocket::ConnectedState; } void ShellIPC::hostConnected() { write(toTEXT(arguments.join(' '))); } void ShellIPC::dataIn() { QTextStream(stdout) << fromTEXT(readAll()); emit closeInstance(); }