File indexing completed on 2025-02-02 05:08:37

0001 /*
0002     SPDX-FileCopyrightText: 2010 Omat Holding B.V. <info@omat.nl>
0003     SPDX-FileCopyrightText: 2014 Sandro Knauß <knauss@kolabsys.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "ispdb.h"
0009 #include "accountwizard_debug.h"
0010 #include <KLocalizedString>
0011 #include <kio/jobclasses.h>
0012 
0013 #include <QDomDocument>
0014 
0015 Ispdb::Ispdb(QObject *parent)
0016     : QObject(parent)
0017 {
0018 }
0019 
0020 Ispdb::~Ispdb() = default;
0021 
0022 QString Ispdb::email() const
0023 {
0024     return mEmail;
0025 }
0026 
0027 void Ispdb::setEmail(const QString &address)
0028 {
0029     if (mEmail == address) {
0030         return;
0031     }
0032     mEmail = address;
0033     KMime::Types::Mailbox box;
0034     box.fromUnicodeString(address);
0035     mAddr = box.addrSpec();
0036     Q_EMIT emailChanged();
0037 }
0038 
0039 QString Ispdb::password() const
0040 {
0041     return mPassword;
0042 }
0043 
0044 void Ispdb::setPassword(const QString &password)
0045 {
0046     if (mPassword == password) {
0047         return;
0048     }
0049     mPassword = password;
0050     Q_EMIT passwordChanged();
0051 }
0052 
0053 void Ispdb::start()
0054 {
0055     qCDebug(ACCOUNTWIZARD_LOG) << mAddr.asString();
0056     // we should do different things in here. But lets focus in the db first.
0057     lookupInDb(false, false);
0058 }
0059 
0060 void Ispdb::lookupInDb(bool auth, bool crypt)
0061 {
0062     setServerType(mServerType);
0063     startJob(lookupUrl(QStringLiteral("mail"), QStringLiteral("1.1"), auth, crypt));
0064 }
0065 
0066 void Ispdb::startJob(const QUrl &url)
0067 {
0068     mData.clear();
0069     QMap<QString, QVariant> map;
0070     map[QStringLiteral("errorPage")] = false;
0071 
0072     KIO::TransferJob *job = KIO::get(url, KIO::NoReload, KIO::HideProgressInfo);
0073     job->setMetaData(map);
0074     connect(job, &KIO::TransferJob::result, this, &Ispdb::slotResult);
0075     connect(job, &KIO::TransferJob::data, this, &Ispdb::dataArrived);
0076 }
0077 
0078 QUrl Ispdb::lookupUrl(const QString &type, const QString &version, bool auth, bool crypt)
0079 {
0080     QUrl url;
0081     const QString path = type + QStringLiteral("/config-v") + version + QStringLiteral(".xml");
0082     switch (mServerType) {
0083     case IspAutoConfig:
0084         url = QUrl(QStringLiteral("http://autoconfig.") + mAddr.domain.toLower() + QLatin1Char('/') + path);
0085         break;
0086     case IspWellKnow:
0087         url = QUrl(QStringLiteral("http://") + mAddr.domain.toLower() + QStringLiteral("/.well-known/autoconfig/") + path);
0088         break;
0089     case DataBase:
0090         url = QUrl(QStringLiteral("https://autoconfig.thunderbird.net/v1.1/") + mAddr.domain.toLower());
0091         break;
0092     }
0093     if (mServerType != DataBase) {
0094         if (crypt) {
0095             url.setScheme(QStringLiteral("https"));
0096         }
0097 
0098         if (auth) {
0099             url.setUserName(mAddr.asString());
0100             url.setPassword(mPassword);
0101         }
0102     }
0103     return url;
0104 }
0105 
0106 void Ispdb::slotResult(KJob *job)
0107 {
0108     if (job->error()) {
0109         qCDebug(ACCOUNTWIZARD_LOG) << "Fetching failed" << job->errorString();
0110         bool lookupFinished = false;
0111 
0112         switch (mServerType) {
0113         case IspAutoConfig:
0114             mServerType = IspWellKnow;
0115             break;
0116         case IspWellKnow:
0117             lookupFinished = true;
0118             break;
0119         case DataBase:
0120             mServerType = IspAutoConfig;
0121             break;
0122         }
0123 
0124         if (lookupFinished) {
0125             Q_EMIT finished(false);
0126             return;
0127         }
0128         lookupInDb(false, false);
0129         return;
0130     }
0131 
0132     // qCDebug(ACCOUNTWIZARD_LOG) << mData;
0133     QDomDocument document;
0134     bool ok = document.setContent(mData);
0135     if (!ok) {
0136         qCDebug(ACCOUNTWIZARD_LOG) << "Could not parse xml" << mData;
0137         Q_EMIT finished(false);
0138         return;
0139     }
0140 
0141     parseResult(document);
0142 }
0143 
0144 void Ispdb::parseResult(const QDomDocument &document)
0145 {
0146     QDomElement docElem = document.documentElement();
0147     QDomNodeList l = docElem.elementsByTagName(QStringLiteral("emailProvider"));
0148 
0149     if (l.isEmpty()) {
0150         Q_EMIT finished(false);
0151         return;
0152     }
0153 
0154     // only handle the first emailProvider entry
0155     QDomNode firstProvider = l.at(0);
0156     QDomNode n = firstProvider.firstChild();
0157     while (!n.isNull()) {
0158         QDomElement e = n.toElement();
0159         if (!e.isNull()) {
0160             // qCDebug(ACCOUNTWIZARD_LOG)  << qPrintable(e.tagName());
0161             const QString tagName(e.tagName());
0162             if (tagName == QLatin1StringView("domain")) {
0163                 mDomains << e.text();
0164             } else if (tagName == QLatin1StringView("displayName")) {
0165                 mDisplayName = e.text();
0166             } else if (tagName == QLatin1StringView("displayShortName")) {
0167                 mDisplayShortName = e.text();
0168             } else if (tagName == QLatin1StringView("incomingServer") && e.attribute(QStringLiteral("type")) == QLatin1StringView("imap")) {
0169                 Server s = createServer(e);
0170                 if (s.isValid()) {
0171                     mImapServers.append(s);
0172                 }
0173             } else if (tagName == QLatin1StringView("incomingServer") && e.attribute(QStringLiteral("type")) == QLatin1StringView("pop3")) {
0174                 Server s = createServer(e);
0175                 if (s.isValid()) {
0176                     mPop3Servers.append(s);
0177                 }
0178             } else if (tagName == QLatin1StringView("outgoingServer") && e.attribute(QStringLiteral("type")) == QLatin1StringView("smtp")) {
0179                 Server s = createServer(e);
0180                 if (s.isValid()) {
0181                     mSmtpServers.append(s);
0182                 }
0183             } else if (tagName == QLatin1StringView("identity")) {
0184                 Identity i = createIdentity(e);
0185                 if (i.isValid()) {
0186                     mIdentities.append(i);
0187                     if (i.isDefault()) {
0188                         mDefaultIdentity = mIdentities.count() - 1;
0189                     }
0190                 }
0191             }
0192         }
0193         n = n.nextSibling();
0194     }
0195 
0196     // comment this section out when you are tired of it...
0197     qCDebug(ACCOUNTWIZARD_LOG) << "------------------ summary --------------";
0198     qCDebug(ACCOUNTWIZARD_LOG) << "Domains" << mDomains;
0199     qCDebug(ACCOUNTWIZARD_LOG) << "Name" << mDisplayName << "(" << mDisplayShortName << ")";
0200     qCDebug(ACCOUNTWIZARD_LOG) << "Imap servers:";
0201     for (const Server &s : std::as_const(mImapServers)) {
0202         qCDebug(ACCOUNTWIZARD_LOG) << s.hostname << s.port << s.socketType << s.username << s.authentication;
0203     }
0204     qCDebug(ACCOUNTWIZARD_LOG) << "pop3 servers:";
0205     for (const Server &s : std::as_const(mPop3Servers)) {
0206         qCDebug(ACCOUNTWIZARD_LOG) << s.hostname << s.port << s.socketType << s.username << s.authentication;
0207     }
0208     qCDebug(ACCOUNTWIZARD_LOG) << "smtp servers:";
0209     for (const Server &s : std::as_const(mSmtpServers)) {
0210         qCDebug(ACCOUNTWIZARD_LOG) << s.hostname << s.port << s.socketType << s.username << s.authentication;
0211     }
0212     // end section.
0213 
0214     Q_EMIT finished(true);
0215 }
0216 
0217 Server Ispdb::createServer(const QDomElement &n)
0218 {
0219     QDomNode o = n.firstChild();
0220     Server s;
0221     while (!o.isNull()) {
0222         QDomElement f = o.toElement();
0223         if (!f.isNull()) {
0224             const QString tagName(f.tagName());
0225             if (tagName == QLatin1StringView("hostname")) {
0226                 s.hostname = replacePlaceholders(f.text());
0227             } else if (tagName == QLatin1StringView("port")) {
0228                 s.port = f.text().toInt();
0229             } else if (tagName == QLatin1StringView("socketType")) {
0230                 const QString type(f.text());
0231                 if (type == QLatin1StringView("plain")) {
0232                     s.socketType = None;
0233                 } else if (type == QLatin1StringView("SSL")) {
0234                     s.socketType = SSL;
0235                 }
0236                 if (type == QLatin1StringView("STARTTLS")) {
0237                     s.socketType = StartTLS;
0238                 }
0239             } else if (tagName == QLatin1StringView("username")) {
0240                 s.username = replacePlaceholders(f.text());
0241             } else if (tagName == QLatin1StringView("authentication") && s.authentication == 0) {
0242                 const QString type(f.text());
0243                 if (type == QLatin1StringView("password-cleartext") || type == QLatin1StringView("plain")) {
0244                     s.authentication = Plain;
0245                 } else if (type == QLatin1StringView("password-encrypted") || type == QLatin1StringView("secure")) {
0246                     s.authentication = CramMD5;
0247                 } else if (type == QLatin1StringView("NTLM")) {
0248                     s.authentication = NTLM;
0249                 } else if (type == QLatin1StringView("GSSAPI")) {
0250                     s.authentication = GSSAPI;
0251                 } else if (type == QLatin1StringView("client-ip-based")) {
0252                     s.authentication = ClientIP;
0253                 } else if (type == QLatin1StringView("none")) {
0254                     s.authentication = NoAuth;
0255                 } else if (type == QLatin1StringView("OAuth2")) {
0256                     s.authentication = OAuth2;
0257                 }
0258             }
0259         }
0260         o = o.nextSibling();
0261     }
0262     return s;
0263 }
0264 
0265 Identity Ispdb::createIdentity(const QDomElement &n)
0266 {
0267     QDomNode o = n.firstChild();
0268     Identity i;
0269 
0270     // type="kolab" version="1.0" is the only identity that is defined
0271     if (n.attribute(QStringLiteral("type")) != QLatin1StringView("kolab") || n.attribute(QStringLiteral("version")) != QLatin1StringView("1.0")) {
0272         qCDebug(ACCOUNTWIZARD_LOG) << "unknown type of identity element.";
0273     }
0274 
0275     while (!o.isNull()) {
0276         QDomElement f = o.toElement();
0277         if (!f.isNull()) {
0278             const QString tagName(f.tagName());
0279             if (tagName == QLatin1StringView("default")) {
0280                 i.mDefault = f.text().toLower() == QLatin1StringView("true");
0281             } else if (tagName == QLatin1StringView("email")) {
0282                 i.email = f.text();
0283             } else if (tagName == QLatin1StringView("name")) {
0284                 i.name = f.text();
0285             } else if (tagName == QLatin1StringView("organization")) {
0286                 i.organization = f.text();
0287             } else if (tagName == QLatin1StringView("signature")) {
0288                 QTextStream stream(&i.signature);
0289                 f.save(stream, 0);
0290                 i.signature = i.signature.trimmed();
0291                 if (i.signature.startsWith(QLatin1StringView("<signature>"))) {
0292                     i.signature = i.signature.mid(11, i.signature.length() - 23);
0293                     i.signature = i.signature.trimmed();
0294                 }
0295             }
0296         }
0297         o = o.nextSibling();
0298     }
0299 
0300     return i;
0301 }
0302 
0303 QString Ispdb::replacePlaceholders(const QString &in)
0304 {
0305     QString out(in);
0306     out.replace(QLatin1StringView("%EMAILLOCALPART%"), mAddr.localPart);
0307     out.replace(QLatin1StringView("%EMAILADDRESS%"), mAddr.asString());
0308     out.replace(QLatin1StringView("%EMAILDOMAIN%"), mAddr.domain);
0309     return out;
0310 }
0311 
0312 void Ispdb::dataArrived(KIO::Job *, const QByteArray &data)
0313 {
0314     mData.append(data);
0315 }
0316 
0317 // The getters
0318 
0319 QStringList Ispdb::relevantDomains() const
0320 {
0321     return mDomains;
0322 }
0323 
0324 QString Ispdb::name(Length l) const
0325 {
0326     if (l == Long) {
0327         return mDisplayName;
0328     } else if (l == Short) {
0329         return mDisplayShortName;
0330     } else {
0331         return {}; // make compiler happy. Not me.
0332     }
0333 }
0334 
0335 QList<Server> Ispdb::imapServers() const
0336 {
0337     return mImapServers;
0338 }
0339 
0340 QList<Server> Ispdb::pop3Servers() const
0341 {
0342     return mPop3Servers;
0343 }
0344 
0345 QList<Server> Ispdb::smtpServers() const
0346 {
0347     return mSmtpServers;
0348 }
0349 
0350 int Ispdb::defaultIdentity() const
0351 {
0352     return mDefaultIdentity;
0353 }
0354 
0355 QList<Identity> Ispdb::identities() const
0356 {
0357     return mIdentities;
0358 }
0359 
0360 void Ispdb::setServerType(Ispdb::SearchServerType type)
0361 {
0362     if (type != mServerType || mStart) {
0363         mServerType = type;
0364         mStart = false;
0365         switch (mServerType) {
0366         case IspAutoConfig:
0367             Q_EMIT searchType(i18n("Lookup configuration: Email provider"));
0368             break;
0369         case IspWellKnow:
0370             Q_EMIT searchType(i18n("Lookup configuration: Trying common server name"));
0371             break;
0372         case DataBase:
0373             Q_EMIT searchType(i18n("Lookup configuration: Mozilla database"));
0374             break;
0375         }
0376     }
0377 }
0378 
0379 Ispdb::SearchServerType Ispdb::serverType() const
0380 {
0381     return mServerType;
0382 }
0383 
0384 QDebug operator<<(QDebug d, const Server &t)
0385 {
0386     d << "authentication " << t.authentication;
0387     d << "socketType " << t.socketType;
0388     d << "hostname " << t.hostname;
0389     d << "username " << t.username;
0390     d << "port " << t.port;
0391     return d;
0392 }
0393 
0394 QDebug operator<<(QDebug d, const Identity &t)
0395 {
0396     d << " email " << t.email;
0397     d << " name " << t.name;
0398     d << " organization " << t.organization;
0399     d << " signature " << t.signature;
0400     d << " isDefault " << t.mDefault;
0401     return d;
0402 }
0403 
0404 #include "moc_ispdb.cpp"