File indexing completed on 2023-10-03 03:19:51
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 }