1 /*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2000-2001 Dawit Alemayehu <adawit@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8 #include "authinfo.h"
9
10 #ifndef KIO_ANDROID_STUB
11 #include <QDBusArgument>
12 #include <QDBusMetaType>
13 #endif
14 #include <QDataStream>
15 #include <QDir>
16 #include <QFile>
17 #include <QTextStream>
18
19 #include <QStandardPaths>
20
21 using namespace KIO;
22
23 //////
24
25 class ExtraField
26 {
27 public:
ExtraField()28 ExtraField()
29 : flags(AuthInfo::ExtraFieldNoFlags)
30 {
31 }
32
ExtraField(const ExtraField & other)33 ExtraField(const ExtraField &other)
34 : customTitle(other.customTitle)
35 , flags(other.flags)
36 , value(other.value)
37 {
38 }
39
operator =(const ExtraField & other)40 ExtraField &operator=(const ExtraField &other)
41 {
42 customTitle = other.customTitle;
43 flags = other.flags;
44 value = other.value;
45 return *this;
46 }
47
48 QString customTitle; // reserved for future use
49 AuthInfo::FieldFlags flags;
50 QVariant value;
51 };
52 Q_DECLARE_METATYPE(ExtraField)
53
54 static QDataStream &operator<<(QDataStream &s, const ExtraField &extraField)
55 {
56 s << extraField.customTitle;
57 s << static_cast<int>(extraField.flags);
58 s << extraField.value;
59 return s;
60 }
61
operator >>(QDataStream & s,ExtraField & extraField)62 static QDataStream &operator>>(QDataStream &s, ExtraField &extraField)
63 {
64 s >> extraField.customTitle;
65 int i;
66 s >> i;
67 extraField.flags = AuthInfo::FieldFlags(i);
68 s >> extraField.value;
69 return s;
70 }
71
72 #ifndef KIO_ANDROID_STUB
operator <<(QDBusArgument & argument,const ExtraField & extraField)73 static QDBusArgument &operator<<(QDBusArgument &argument, const ExtraField &extraField)
74 {
75 argument.beginStructure();
76 argument << extraField.customTitle << static_cast<int>(extraField.flags) << QDBusVariant(extraField.value);
77 argument.endStructure();
78 return argument;
79 }
80
operator >>(const QDBusArgument & argument,ExtraField & extraField)81 static const QDBusArgument &operator>>(const QDBusArgument &argument, ExtraField &extraField)
82 {
83 QDBusVariant value;
84 int flag;
85
86 argument.beginStructure();
87 argument >> extraField.customTitle >> flag >> value;
88 argument.endStructure();
89
90 extraField.value = value.variant();
91 extraField.flags = KIO::AuthInfo::FieldFlags(flag);
92 return argument;
93 }
94 #endif
95
96 class KIO::AuthInfoPrivate
97 {
98 public:
99 QMap<QString, ExtraField> extraFields;
100 };
101
102 //////
103
AuthInfo()104 AuthInfo::AuthInfo()
105 : d(new AuthInfoPrivate())
106 {
107 modified = false;
108 readOnly = false;
109 verifyPath = false;
110 keepPassword = false;
111 AuthInfo::registerMetaTypes();
112 }
113
AuthInfo(const AuthInfo & info)114 AuthInfo::AuthInfo(const AuthInfo &info)
115 : d(new AuthInfoPrivate())
116 {
117 (*this) = info;
118 AuthInfo::registerMetaTypes();
119 }
120
~AuthInfo()121 AuthInfo::~AuthInfo()
122 {
123 delete d;
124 }
125
operator =(const AuthInfo & info)126 AuthInfo &AuthInfo::operator=(const AuthInfo &info)
127 {
128 url = info.url;
129 username = info.username;
130 password = info.password;
131 prompt = info.prompt;
132 caption = info.caption;
133 comment = info.comment;
134 commentLabel = info.commentLabel;
135 realmValue = info.realmValue;
136 digestInfo = info.digestInfo;
137 verifyPath = info.verifyPath;
138 readOnly = info.readOnly;
139 keepPassword = info.keepPassword;
140 modified = info.modified;
141 d->extraFields = info.d->extraFields;
142 return *this;
143 }
144
isModified() const145 bool AuthInfo::isModified() const
146 {
147 return modified;
148 }
149
setModified(bool flag)150 void AuthInfo::setModified(bool flag)
151 {
152 modified = flag;
153 }
154
155 /////
156
setExtraField(const QString & fieldName,const QVariant & value)157 void AuthInfo::setExtraField(const QString &fieldName, const QVariant &value)
158 {
159 d->extraFields[fieldName].value = value;
160 }
161
setExtraFieldFlags(const QString & fieldName,const FieldFlags flags)162 void AuthInfo::setExtraFieldFlags(const QString &fieldName, const FieldFlags flags)
163 {
164 d->extraFields[fieldName].flags = flags;
165 }
166
getExtraField(const QString & fieldName) const167 QVariant AuthInfo::getExtraField(const QString &fieldName) const
168 {
169 const auto it = d->extraFields.constFind(fieldName);
170 if (it == d->extraFields.constEnd()) {
171 return QVariant();
172 }
173 return it->value;
174 }
175
getExtraFieldFlags(const QString & fieldName) const176 AuthInfo::FieldFlags AuthInfo::getExtraFieldFlags(const QString &fieldName) const
177 {
178 const auto it = d->extraFields.constFind(fieldName);
179 if (it == d->extraFields.constEnd()) {
180 return AuthInfo::ExtraFieldNoFlags;
181 }
182 return it->flags;
183 }
184
registerMetaTypes()185 void AuthInfo::registerMetaTypes()
186 {
187 qRegisterMetaType<ExtraField>();
188 qRegisterMetaType<KIO::AuthInfo>();
189 #ifndef KIO_ANDROID_STUB
190 qDBusRegisterMetaType<ExtraField>();
191 qDBusRegisterMetaType<KIO::AuthInfo>();
192 #endif
193 }
194
195 /////
196
operator <<(QDataStream & s,const AuthInfo & a)197 QDataStream &KIO::operator<<(QDataStream &s, const AuthInfo &a)
198 {
199 s << quint8(1) << a.url << a.username << a.password << a.prompt << a.caption << a.comment << a.commentLabel << a.realmValue << a.digestInfo << a.verifyPath
200 << a.readOnly << a.keepPassword << a.modified << a.d->extraFields;
201 return s;
202 }
203
operator >>(QDataStream & s,AuthInfo & a)204 QDataStream &KIO::operator>>(QDataStream &s, AuthInfo &a)
205 {
206 quint8 version;
207 s >> version >> a.url >> a.username >> a.password >> a.prompt >> a.caption >> a.comment >> a.commentLabel >> a.realmValue >> a.digestInfo >> a.verifyPath
208 >> a.readOnly >> a.keepPassword >> a.modified >> a.d->extraFields;
209 return s;
210 }
211
212 #ifndef KIO_ANDROID_STUB
operator <<(QDBusArgument & argument,const AuthInfo & a)213 QDBusArgument &KIO::operator<<(QDBusArgument &argument, const AuthInfo &a)
214 {
215 argument.beginStructure();
216 argument << quint8(1) << a.url.toString() << a.username << a.password << a.prompt << a.caption << a.comment << a.commentLabel << a.realmValue
217 << a.digestInfo << a.verifyPath << a.readOnly << a.keepPassword << a.modified << a.d->extraFields;
218 argument.endStructure();
219 return argument;
220 }
221
operator >>(const QDBusArgument & argument,AuthInfo & a)222 const QDBusArgument &KIO::operator>>(const QDBusArgument &argument, AuthInfo &a)
223 {
224 QString url;
225 quint8 version;
226
227 argument.beginStructure();
228 argument >> version >> url >> a.username >> a.password >> a.prompt >> a.caption >> a.comment >> a.commentLabel >> a.realmValue >> a.digestInfo
229 >> a.verifyPath >> a.readOnly >> a.keepPassword >> a.modified >> a.d->extraFields;
230 argument.endStructure();
231
232 a.url = QUrl(url);
233 return argument;
234 }
235 #endif
236
237 typedef QList<NetRC::AutoLogin> LoginList;
238 typedef QMap<QString, LoginList> LoginMap;
239
240 class Q_DECL_HIDDEN NetRC::NetRCPrivate
241 {
242 public:
NetRCPrivate()243 NetRCPrivate()
244 : isDirty(false)
245 , index(-1)
246 {
247 }
248 QString extract(const QString &buf, const QString &key);
249 void getMachinePart(const QString &line);
250 void getMacdefPart(const QString &line);
251
252 bool isDirty;
253 LoginMap loginMap;
254 QTextStream fstream;
255 QString type;
256 int index;
257 };
258
259 NetRC *NetRC::instance = nullptr;
260
NetRC()261 NetRC::NetRC()
262 : d(new NetRCPrivate)
263 {
264 }
265
~NetRC()266 NetRC::~NetRC()
267 {
268 delete instance;
269 instance = nullptr;
270 delete d;
271 }
272
self()273 NetRC *NetRC::self()
274 {
275 if (!instance) {
276 instance = new NetRC;
277 }
278 return instance;
279 }
280
lookup(const QUrl & url,AutoLogin & login,bool userealnetrc,const QString & _type,LookUpMode mode)281 bool NetRC::lookup(const QUrl &url, AutoLogin &login, bool userealnetrc, const QString &_type, LookUpMode mode)
282 {
283 // qDebug() << "AutoLogin lookup for: " << url.host();
284 if (!url.isValid()) {
285 return false;
286 }
287
288 QString type = _type;
289 if (type.isEmpty()) {
290 type = url.scheme();
291 }
292
293 if (d->loginMap.isEmpty() || d->isDirty) {
294 d->loginMap.clear();
295
296 QString filename = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String("/kionetrc");
297 bool kionetrcStatus = parse(filename);
298 bool netrcStatus = false;
299 if (userealnetrc) {
300 filename = QDir::homePath() + QLatin1String("/.netrc");
301 netrcStatus = parse(filename);
302 }
303
304 if (!(kionetrcStatus || netrcStatus)) {
305 return false;
306 }
307 }
308
309 const auto loginIt = d->loginMap.constFind(type);
310 if (loginIt == d->loginMap.constEnd()) {
311 return false;
312 }
313
314 const LoginList &l = *loginIt;
315 if (l.isEmpty()) {
316 return false;
317 }
318
319 for (const AutoLogin &log : l) {
320 if ((mode & defaultOnly) == defaultOnly && log.machine == QLatin1String("default") && (login.login.isEmpty() || login.login == log.login)) {
321 login.type = log.type;
322 login.machine = log.machine;
323 login.login = log.login;
324 login.password = log.password;
325 login.macdef = log.macdef;
326 }
327
328 if ((mode & presetOnly) == presetOnly && log.machine == QLatin1String("preset") && (login.login.isEmpty() || login.login == log.login)) {
329 login.type = log.type;
330 login.machine = log.machine;
331 login.login = log.login;
332 login.password = log.password;
333 login.macdef = log.macdef;
334 }
335
336 if ((mode & exactOnly) == exactOnly && log.machine == url.host() && (login.login.isEmpty() || login.login == log.login)) {
337 login.type = log.type;
338 login.machine = log.machine;
339 login.login = log.login;
340 login.password = log.password;
341 login.macdef = log.macdef;
342 break;
343 }
344 }
345
346 return true;
347 }
348
reload()349 void NetRC::reload()
350 {
351 d->isDirty = true;
352 }
353
parse(const QString & fileName)354 bool NetRC::parse(const QString &fileName)
355 {
356 QFile file(fileName);
357 if (file.permissions() != (QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser)) {
358 return false;
359 }
360 if (!file.open(QIODevice::ReadOnly)) {
361 return false;
362 }
363
364 d->fstream.setDevice(&file);
365
366 QString line;
367
368 while (!d->fstream.atEnd()) {
369 line = d->fstream.readLine().simplified();
370
371 // If line is a comment or is empty, read next line
372 if ((line.startsWith(QLatin1Char('#')) || line.isEmpty())) {
373 continue;
374 }
375
376 // If line refers to a machine, maybe it is spread in more lines.
377 // getMachinePart() will take care of getting all the info and putting it into loginMap.
378 if ((line.startsWith(QLatin1String("machine")) || line.startsWith(QLatin1String("default")) || line.startsWith(QLatin1String("preset")))) {
379 d->getMachinePart(line);
380 continue;
381 }
382
383 // If line refers to a macdef, it will be more than one line.
384 // getMacdefPart() will take care of getting all the lines of the macro
385 // and putting them into loginMap
386 if (line.startsWith(QLatin1String("macdef"))) {
387 d->getMacdefPart(line);
388 continue;
389 }
390 }
391 return true;
392 }
393
extract(const QString & buf,const QString & key)394 QString NetRC::NetRCPrivate::extract(const QString &buf, const QString &key)
395 {
396 QStringList stringList = buf.split(QLatin1Char(' '), Qt::SkipEmptyParts);
397 int i = stringList.indexOf(key);
398 if ((i != -1) && (i + 1 < stringList.size())) {
399 return stringList.at(i + 1);
400 } else {
401 return QString();
402 }
403 }
404
getMachinePart(const QString & line)405 void NetRC::NetRCPrivate::getMachinePart(const QString &line)
406 {
407 QString buf = line;
408 while (!(buf.contains(QLatin1String("login"))
409 && (buf.contains(QLatin1String("password")) || buf.contains(QLatin1String("account")) || buf.contains(QLatin1String("type"))))) {
410 buf += QLatin1Char(' ') + fstream.readLine().simplified();
411 }
412
413 // Once we've got all the info, process it.
414 AutoLogin l;
415 l.machine = extract(buf, QStringLiteral("machine"));
416 if (l.machine.isEmpty()) {
417 if (buf.contains(QLatin1String("default"))) {
418 l.machine = QStringLiteral("default");
419 } else if (buf.contains(QLatin1String("preset"))) {
420 l.machine = QStringLiteral("preset");
421 }
422 }
423
424 l.login = extract(buf, QStringLiteral("login"));
425 l.password = extract(buf, QStringLiteral("password"));
426 if (l.password.isEmpty()) {
427 l.password = extract(buf, QStringLiteral("account"));
428 }
429
430 type = l.type = extract(buf, QStringLiteral("type"));
431 if (l.type.isEmpty() && !l.machine.isEmpty()) {
432 type = l.type = QStringLiteral("ftp");
433 }
434
435 loginMap[l.type].append(l);
436 index = loginMap[l.type].count() - 1;
437 }
438
getMacdefPart(const QString & line)439 void NetRC::NetRCPrivate::getMacdefPart(const QString &line)
440 {
441 QString buf = line;
442 QString macro = extract(buf, QStringLiteral("macdef"));
443 QString newLine;
444 while (!fstream.atEnd()) {
445 newLine = fstream.readLine().simplified();
446 if (!newLine.isEmpty()) {
447 buf += QLatin1Char('\n') + newLine;
448 } else {
449 break;
450 }
451 }
452 loginMap[type][index].macdef[macro].append(buf);
453 }
454