File indexing completed on 2024-04-21 11:32:26

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2000-2001 Dawit Alemayehu <adawit@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "authinfo.h"
0009 
0010 #ifndef KIO_ANDROID_STUB
0011 #include <QDBusArgument>
0012 #include <QDBusMetaType>
0013 #endif
0014 #include <QDataStream>
0015 #include <QDir>
0016 #include <QFile>
0017 #include <QTextStream>
0018 
0019 #include <QStandardPaths>
0020 
0021 using namespace KIO;
0022 
0023 //////
0024 
0025 class ExtraField
0026 {
0027 public:
0028     ExtraField()
0029         : flags(AuthInfo::ExtraFieldNoFlags)
0030     {
0031     }
0032 
0033     ExtraField(const ExtraField &other)
0034         : customTitle(other.customTitle)
0035         , flags(other.flags)
0036         , value(other.value)
0037     {
0038     }
0039 
0040     ExtraField &operator=(const ExtraField &other)
0041     {
0042         customTitle = other.customTitle;
0043         flags = other.flags;
0044         value = other.value;
0045         return *this;
0046     }
0047 
0048     QString customTitle; // reserved for future use
0049     AuthInfo::FieldFlags flags;
0050     QVariant value;
0051 };
0052 Q_DECLARE_METATYPE(ExtraField)
0053 
0054 static QDataStream &operator<<(QDataStream &s, const ExtraField &extraField)
0055 {
0056     s << extraField.customTitle;
0057     s << static_cast<int>(extraField.flags);
0058     s << extraField.value;
0059     return s;
0060 }
0061 
0062 static QDataStream &operator>>(QDataStream &s, ExtraField &extraField)
0063 {
0064     s >> extraField.customTitle;
0065     int i;
0066     s >> i;
0067     extraField.flags = AuthInfo::FieldFlags(i);
0068     s >> extraField.value;
0069     return s;
0070 }
0071 
0072 #ifndef KIO_ANDROID_STUB
0073 static QDBusArgument &operator<<(QDBusArgument &argument, const ExtraField &extraField)
0074 {
0075     argument.beginStructure();
0076     argument << extraField.customTitle << static_cast<int>(extraField.flags) << QDBusVariant(extraField.value);
0077     argument.endStructure();
0078     return argument;
0079 }
0080 
0081 static const QDBusArgument &operator>>(const QDBusArgument &argument, ExtraField &extraField)
0082 {
0083     QDBusVariant value;
0084     int flag;
0085 
0086     argument.beginStructure();
0087     argument >> extraField.customTitle >> flag >> value;
0088     argument.endStructure();
0089 
0090     extraField.value = value.variant();
0091     extraField.flags = KIO::AuthInfo::FieldFlags(flag);
0092     return argument;
0093 }
0094 #endif
0095 
0096 class KIO::AuthInfoPrivate
0097 {
0098 public:
0099     QMap<QString, ExtraField> extraFields;
0100 };
0101 
0102 //////
0103 
0104 AuthInfo::AuthInfo()
0105     : d(new AuthInfoPrivate())
0106 {
0107     modified = false;
0108     readOnly = false;
0109     verifyPath = false;
0110     keepPassword = false;
0111     AuthInfo::registerMetaTypes();
0112 }
0113 
0114 AuthInfo::AuthInfo(const AuthInfo &info)
0115     : d(new AuthInfoPrivate())
0116 {
0117     (*this) = info;
0118     AuthInfo::registerMetaTypes();
0119 }
0120 
0121 AuthInfo::~AuthInfo() = default;
0122 
0123 AuthInfo &AuthInfo::operator=(const AuthInfo &info)
0124 {
0125     url = info.url;
0126     username = info.username;
0127     password = info.password;
0128     prompt = info.prompt;
0129     caption = info.caption;
0130     comment = info.comment;
0131     commentLabel = info.commentLabel;
0132     realmValue = info.realmValue;
0133     digestInfo = info.digestInfo;
0134     verifyPath = info.verifyPath;
0135     readOnly = info.readOnly;
0136     keepPassword = info.keepPassword;
0137     modified = info.modified;
0138     d->extraFields = info.d->extraFields;
0139     return *this;
0140 }
0141 
0142 bool AuthInfo::isModified() const
0143 {
0144     return modified;
0145 }
0146 
0147 void AuthInfo::setModified(bool flag)
0148 {
0149     modified = flag;
0150 }
0151 
0152 /////
0153 
0154 void AuthInfo::setExtraField(const QString &fieldName, const QVariant &value)
0155 {
0156     d->extraFields[fieldName].value = value;
0157 }
0158 
0159 void AuthInfo::setExtraFieldFlags(const QString &fieldName, const FieldFlags flags)
0160 {
0161     d->extraFields[fieldName].flags = flags;
0162 }
0163 
0164 QVariant AuthInfo::getExtraField(const QString &fieldName) const
0165 {
0166     const auto it = d->extraFields.constFind(fieldName);
0167     if (it == d->extraFields.constEnd()) {
0168         return QVariant();
0169     }
0170     return it->value;
0171 }
0172 
0173 AuthInfo::FieldFlags AuthInfo::getExtraFieldFlags(const QString &fieldName) const
0174 {
0175     const auto it = d->extraFields.constFind(fieldName);
0176     if (it == d->extraFields.constEnd()) {
0177         return AuthInfo::ExtraFieldNoFlags;
0178     }
0179     return it->flags;
0180 }
0181 
0182 void AuthInfo::registerMetaTypes()
0183 {
0184     qRegisterMetaType<ExtraField>();
0185     qRegisterMetaType<KIO::AuthInfo>();
0186 #ifndef KIO_ANDROID_STUB
0187     qDBusRegisterMetaType<ExtraField>();
0188     qDBusRegisterMetaType<KIO::AuthInfo>();
0189 #endif
0190 }
0191 
0192 /////
0193 
0194 QDataStream &KIO::operator<<(QDataStream &s, const AuthInfo &a)
0195 {
0196     s << quint8(1) << a.url << a.username << a.password << a.prompt << a.caption << a.comment << a.commentLabel << a.realmValue << a.digestInfo << a.verifyPath
0197       << a.readOnly << a.keepPassword << a.modified << a.d->extraFields;
0198     return s;
0199 }
0200 
0201 QDataStream &KIO::operator>>(QDataStream &s, AuthInfo &a)
0202 {
0203     quint8 version;
0204     s >> version >> a.url >> a.username >> a.password >> a.prompt >> a.caption >> a.comment >> a.commentLabel >> a.realmValue >> a.digestInfo >> a.verifyPath
0205         >> a.readOnly >> a.keepPassword >> a.modified >> a.d->extraFields;
0206     return s;
0207 }
0208 
0209 #ifndef KIO_ANDROID_STUB
0210 QDBusArgument &KIO::operator<<(QDBusArgument &argument, const AuthInfo &a)
0211 {
0212     argument.beginStructure();
0213     argument << quint8(1) << a.url.toString() << a.username << a.password << a.prompt << a.caption << a.comment << a.commentLabel << a.realmValue
0214              << a.digestInfo << a.verifyPath << a.readOnly << a.keepPassword << a.modified << a.d->extraFields;
0215     argument.endStructure();
0216     return argument;
0217 }
0218 
0219 const QDBusArgument &KIO::operator>>(const QDBusArgument &argument, AuthInfo &a)
0220 {
0221     QString url;
0222     quint8 version;
0223 
0224     argument.beginStructure();
0225     argument >> version >> url >> a.username >> a.password >> a.prompt >> a.caption >> a.comment >> a.commentLabel >> a.realmValue >> a.digestInfo
0226         >> a.verifyPath >> a.readOnly >> a.keepPassword >> a.modified >> a.d->extraFields;
0227     argument.endStructure();
0228 
0229     a.url = QUrl(url);
0230     return argument;
0231 }
0232 #endif
0233 
0234 typedef QList<NetRC::AutoLogin> LoginList;
0235 typedef QMap<QString, LoginList> LoginMap;
0236 
0237 class Q_DECL_HIDDEN NetRC::NetRCPrivate
0238 {
0239 public:
0240     NetRCPrivate()
0241         : isDirty(false)
0242         , index(-1)
0243     {
0244     }
0245     QString extract(const QString &buf, const QString &key);
0246     void getMachinePart(const QString &line);
0247     void getMacdefPart(const QString &line);
0248 
0249     bool isDirty;
0250     LoginMap loginMap;
0251     QTextStream fstream;
0252     QString type;
0253     int index;
0254 };
0255 
0256 NetRC *NetRC::instance = nullptr;
0257 
0258 NetRC::NetRC()
0259     : d(new NetRCPrivate)
0260 {
0261 }
0262 
0263 NetRC::~NetRC()
0264 {
0265     delete instance;
0266     instance = nullptr;
0267 }
0268 
0269 NetRC *NetRC::self()
0270 {
0271     if (!instance) {
0272         instance = new NetRC;
0273     }
0274     return instance;
0275 }
0276 
0277 bool NetRC::lookup(const QUrl &url, AutoLogin &login, bool userealnetrc, const QString &_type, LookUpMode mode)
0278 {
0279     // qDebug() << "AutoLogin lookup for: " << url.host();
0280     if (!url.isValid()) {
0281         return false;
0282     }
0283 
0284     QString type = _type;
0285     if (type.isEmpty()) {
0286         type = url.scheme();
0287     }
0288 
0289     if (d->loginMap.isEmpty() || d->isDirty) {
0290         d->loginMap.clear();
0291 
0292         QString filename = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String("/kionetrc");
0293         bool kionetrcStatus = parse(filename);
0294         bool netrcStatus = false;
0295         if (userealnetrc) {
0296             filename = QDir::homePath() + QLatin1String("/.netrc");
0297             netrcStatus = parse(filename);
0298         }
0299 
0300         if (!(kionetrcStatus || netrcStatus)) {
0301             return false;
0302         }
0303     }
0304 
0305     const auto loginIt = d->loginMap.constFind(type);
0306     if (loginIt == d->loginMap.constEnd()) {
0307         return false;
0308     }
0309 
0310     const LoginList &l = *loginIt;
0311     if (l.isEmpty()) {
0312         return false;
0313     }
0314 
0315     for (const AutoLogin &log : l) {
0316         if ((mode & defaultOnly) == defaultOnly && log.machine == QLatin1String("default") && (login.login.isEmpty() || login.login == log.login)) {
0317             login.type = log.type;
0318             login.machine = log.machine;
0319             login.login = log.login;
0320             login.password = log.password;
0321             login.macdef = log.macdef;
0322         }
0323 
0324         if ((mode & presetOnly) == presetOnly && log.machine == QLatin1String("preset") && (login.login.isEmpty() || login.login == log.login)) {
0325             login.type = log.type;
0326             login.machine = log.machine;
0327             login.login = log.login;
0328             login.password = log.password;
0329             login.macdef = log.macdef;
0330         }
0331 
0332         if ((mode & exactOnly) == exactOnly && log.machine == url.host() && (login.login.isEmpty() || login.login == log.login)) {
0333             login.type = log.type;
0334             login.machine = log.machine;
0335             login.login = log.login;
0336             login.password = log.password;
0337             login.macdef = log.macdef;
0338             break;
0339         }
0340     }
0341 
0342     return true;
0343 }
0344 
0345 void NetRC::reload()
0346 {
0347     d->isDirty = true;
0348 }
0349 
0350 bool NetRC::parse(const QString &fileName)
0351 {
0352     QFile file(fileName);
0353     if (file.permissions() != (QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser)) {
0354         return false;
0355     }
0356     if (!file.open(QIODevice::ReadOnly)) {
0357         return false;
0358     }
0359 
0360     d->fstream.setDevice(&file);
0361 
0362     QString line;
0363 
0364     while (!d->fstream.atEnd()) {
0365         line = d->fstream.readLine().simplified();
0366 
0367         // If line is a comment or is empty, read next line
0368         if ((line.startsWith(QLatin1Char('#')) || line.isEmpty())) {
0369             continue;
0370         }
0371 
0372         // If line refers to a machine, maybe it is spread in more lines.
0373         // getMachinePart() will take care of getting all the info and putting it into loginMap.
0374         if ((line.startsWith(QLatin1String("machine")) || line.startsWith(QLatin1String("default")) || line.startsWith(QLatin1String("preset")))) {
0375             d->getMachinePart(line);
0376             continue;
0377         }
0378 
0379         // If line refers to a macdef, it will be more than one line.
0380         // getMacdefPart() will take care of getting all the lines of the macro
0381         // and putting them into loginMap
0382         if (line.startsWith(QLatin1String("macdef"))) {
0383             d->getMacdefPart(line);
0384             continue;
0385         }
0386     }
0387     return true;
0388 }
0389 
0390 QString NetRC::NetRCPrivate::extract(const QString &buf, const QString &key)
0391 {
0392     QStringList stringList = buf.split(QLatin1Char(' '), Qt::SkipEmptyParts);
0393     int i = stringList.indexOf(key);
0394     if ((i != -1) && (i + 1 < stringList.size())) {
0395         return stringList.at(i + 1);
0396     } else {
0397         return QString();
0398     }
0399 }
0400 
0401 void NetRC::NetRCPrivate::getMachinePart(const QString &line)
0402 {
0403     QString buf = line;
0404     while (!(buf.contains(QLatin1String("login"))
0405              && (buf.contains(QLatin1String("password")) || buf.contains(QLatin1String("account")) || buf.contains(QLatin1String("type"))))) {
0406         buf += QLatin1Char(' ') + fstream.readLine().simplified();
0407     }
0408 
0409     // Once we've got all the info, process it.
0410     AutoLogin l;
0411     l.machine = extract(buf, QStringLiteral("machine"));
0412     if (l.machine.isEmpty()) {
0413         if (buf.contains(QLatin1String("default"))) {
0414             l.machine = QStringLiteral("default");
0415         } else if (buf.contains(QLatin1String("preset"))) {
0416             l.machine = QStringLiteral("preset");
0417         }
0418     }
0419 
0420     l.login = extract(buf, QStringLiteral("login"));
0421     l.password = extract(buf, QStringLiteral("password"));
0422     if (l.password.isEmpty()) {
0423         l.password = extract(buf, QStringLiteral("account"));
0424     }
0425 
0426     type = l.type = extract(buf, QStringLiteral("type"));
0427     if (l.type.isEmpty() && !l.machine.isEmpty()) {
0428         type = l.type = QStringLiteral("ftp");
0429     }
0430 
0431     loginMap[l.type].append(l);
0432     index = loginMap[l.type].count() - 1;
0433 }
0434 
0435 void NetRC::NetRCPrivate::getMacdefPart(const QString &line)
0436 {
0437     QString buf = line;
0438     QString macro = extract(buf, QStringLiteral("macdef"));
0439     QString newLine;
0440     while (!fstream.atEnd()) {
0441         newLine = fstream.readLine().simplified();
0442         if (!newLine.isEmpty()) {
0443             buf += QLatin1Char('\n') + newLine;
0444         } else {
0445             break;
0446         }
0447     }
0448     loginMap[type][index].macdef[macro].append(buf);
0449 }