File indexing completed on 2024-12-01 08:10:07

0001 /*
0002     SPDX-FileCopyrightText: 2010-2012 Lamarque Souza <lamarque@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "mobileproviders.h"
0008 #include "plasma_nm_editor.h"
0009 
0010 #include <QFile>
0011 #include <QLocale>
0012 #include <QRegularExpression>
0013 #include <QTextStream>
0014 
0015 const QString MobileProviders::ProvidersFile = QStringLiteral(BROADBANDPROVIDER_DATABASE);
0016 
0017 bool localeAwareCompare(const QString &one, const QString &two)
0018 {
0019     return one.localeAwareCompare(two) < 0;
0020 }
0021 
0022 MobileProviders::MobileProviders()
0023 {
0024     for (int c = 1; c <= QLocale::LastCountry; c++) {
0025         const auto country = static_cast<QLocale::Country>(c);
0026         QLocale locale(QLocale::AnyLanguage, country);
0027         if (locale.country() == country) {
0028             const QString localeName = locale.name();
0029             const auto idx = localeName.indexOf(QLatin1Char('_'));
0030             if (idx != -1) {
0031                 const QString countryCode = localeName.mid(idx + 1);
0032                 QString countryName = locale.nativeCountryName();
0033                 if (countryName.isEmpty()) {
0034                     countryName = QLocale::countryToString(country);
0035                 }
0036                 mCountries.insert(countryCode, countryName);
0037             }
0038         }
0039     }
0040     mError = Success;
0041 
0042     QFile file2(ProvidersFile);
0043 
0044     if (file2.open(QIODevice::ReadOnly)) {
0045         if (mDocProviders.setContent(&file2)) {
0046             docElement = mDocProviders.documentElement();
0047 
0048             if (docElement.isNull()) {
0049                 qCWarning(PLASMA_NM_EDITOR_LOG) << ProvidersFile << ": document is null";
0050                 mError = ProvidersIsNull;
0051             } else {
0052                 if (docElement.isNull() || docElement.tagName() != QLatin1String("serviceproviders")) {
0053                     qCWarning(PLASMA_NM_EDITOR_LOG) << ProvidersFile << ": wrong format";
0054                     mError = ProvidersWrongFormat;
0055                 } else {
0056                     if (docElement.attribute(QStringLiteral("format")) != QLatin1String("2.0")) {
0057                         qCWarning(PLASMA_NM_EDITOR_LOG) << ProvidersFile << ": mobile broadband provider database format '"
0058                                                         << docElement.attribute(QStringLiteral("format")) << "' not supported.";
0059                         mError = ProvidersFormatNotSupported;
0060                     } else {
0061                         // qCDebug(PLASMA_NM_EDITOR_LOG) << "Everything is alright so far";
0062                     }
0063                 }
0064             }
0065         }
0066 
0067         file2.close();
0068     } else {
0069         qCWarning(PLASMA_NM_EDITOR_LOG) << "Error opening providers file" << ProvidersFile;
0070         mError = ProvidersMissing;
0071     }
0072 }
0073 
0074 MobileProviders::~MobileProviders() = default;
0075 
0076 QStringList MobileProviders::getCountryList() const
0077 {
0078     QStringList temp = mCountries.values();
0079     std::sort(temp.begin(), temp.end(), localeAwareCompare);
0080     return temp;
0081 }
0082 
0083 QString MobileProviders::countryFromLocale() const
0084 {
0085     const QString localeName = QLocale().name();
0086     const auto idx = localeName.indexOf(QLatin1Char('_'));
0087     if (idx != -1) {
0088         return localeName.mid(idx + 1);
0089     }
0090     return {};
0091 }
0092 
0093 QStringList MobileProviders::getProvidersList(QString country, NetworkManager::ConnectionSettings::ConnectionType type)
0094 {
0095     mProvidersGsm.clear();
0096     mProvidersCdma.clear();
0097     QDomNode n = docElement.firstChild();
0098 
0099     // country is a country name and we parse country codes.
0100     if (!mCountries.key(country).isNull()) {
0101         country = mCountries.key(country);
0102     }
0103     QMap<QString, QString> sortedGsm;
0104     QMap<QString, QString> sortedCdma;
0105     while (!n.isNull()) {
0106         QDomElement e = n.toElement(); // <country ...>
0107 
0108         if (!e.isNull() && e.attribute(QStringLiteral("code")).toUpper() == country) {
0109             QDomNode n2 = e.firstChild();
0110             while (!n2.isNull()) {
0111                 QDomElement e2 = n2.toElement(); // <provider ...>
0112 
0113                 if (!e2.isNull() && e2.tagName().toLower() == QLatin1String("provider")) {
0114                     QDomNode n3 = e2.firstChild();
0115                     bool hasGsm = false;
0116                     bool hasCdma = false;
0117                     QMap<QString, QString> localizedProviderNames;
0118 
0119                     while (!n3.isNull()) {
0120                         QDomElement e3 = n3.toElement(); // <name | gsm | cdma>
0121 
0122                         if (!e3.isNull()) {
0123                             if (e3.tagName().toLower() == QLatin1String("gsm")) {
0124                                 hasGsm = true;
0125                             } else if (e3.tagName().toLower() == QLatin1String("cdma")) {
0126                                 hasCdma = true;
0127                             } else if (e3.tagName().toLower() == QLatin1String("name")) {
0128                                 QString lang = e3.attribute(QStringLiteral("xml:lang"));
0129                                 if (lang.isEmpty()) {
0130                                     lang = QStringLiteral("en"); // English is default
0131                                 } else {
0132                                     lang = lang.toLower();
0133                                     lang.remove(QRegularExpression(QStringLiteral("\\-.*$"))); // Remove everything after '-' in xml:lang attribute.
0134                                 }
0135                                 localizedProviderNames.insert(lang, e3.text());
0136                             }
0137                         }
0138                         n3 = n3.nextSibling();
0139                     }
0140                     const QString name = getNameByLocale(localizedProviderNames);
0141                     if (hasGsm) {
0142                         mProvidersGsm.insert(name, e2.firstChild());
0143                         sortedGsm.insert(name.toLower(), name);
0144                     }
0145                     if (hasCdma) {
0146                         mProvidersCdma.insert(name, e2.firstChild());
0147                         sortedCdma.insert(name.toLower(), name);
0148                     }
0149                 }
0150                 n2 = n2.nextSibling();
0151             }
0152             break;
0153         }
0154         n = n.nextSibling();
0155     }
0156 
0157     if (type == NetworkManager::ConnectionSettings::Gsm) {
0158         return sortedGsm.values();
0159     }
0160     return sortedCdma.values();
0161 }
0162 
0163 QStringList MobileProviders::getApns(const QString &provider)
0164 {
0165     mApns.clear();
0166     mNetworkIds.clear();
0167     if (!mProvidersGsm.contains(provider)) {
0168         return {};
0169     }
0170 
0171     QDomNode n = mProvidersGsm[provider];
0172 
0173     while (!n.isNull()) {
0174         QDomElement e = n.toElement(); // <gsm | cdma>
0175 
0176         if (!e.isNull() && e.tagName().toLower() == QLatin1String("gsm")) {
0177             QDomNode n2 = e.firstChild();
0178             while (!n2.isNull()) {
0179                 QDomElement e2 = n2.toElement(); // <apn | network-id>
0180 
0181                 if (!e2.isNull() && e2.tagName().toLower() == QLatin1String("apn")) {
0182                     bool isInternet = true;
0183                     QDomNode n3 = e2.firstChild();
0184                     while (!n3.isNull()) {
0185                         QDomElement e3 = n3.toElement(); // <usage>
0186                         if (!e3.isNull() && e3.tagName().toLower() == QLatin1String("usage") && !e3.attribute(QStringLiteral("type")).isNull()
0187                             && e3.attribute(QStringLiteral("type")).toLower() != QLatin1String("internet")) {
0188                             // qCDebug(PLASMA_NM_EDITOR_LOG) << "apn" << e2.attribute("value") << "ignored because of usage" << e3.attribute("type");
0189                             isInternet = false;
0190                             break;
0191                         }
0192                         n3 = n3.nextSibling();
0193                     }
0194                     if (isInternet) {
0195                         mApns.insert(e2.attribute(QStringLiteral("value")), e2.firstChild());
0196                     }
0197                 } else if (!e2.isNull() && e2.tagName().toLower() == QLatin1String("network-id")) {
0198                     mNetworkIds.append(e2.attribute(QStringLiteral("mcc")) + '-' + e2.attribute(QStringLiteral("mnc")));
0199                 }
0200 
0201                 n2 = n2.nextSibling();
0202             }
0203         }
0204         n = n.nextSibling();
0205     }
0206 
0207     QStringList temp = mApns.keys();
0208     temp.sort();
0209     return temp;
0210 }
0211 
0212 ProviderData MobileProviders::parseProvider(const QDomNode &providerNode)
0213 {
0214     ProviderData result;
0215 
0216     QMap<QString, QString> localizedProviderNames;
0217 
0218     QDomNode c = providerNode.firstChild(); // <name | gsm | cdma>
0219 
0220     while (!c.isNull()) {
0221         QDomElement ce = c.toElement();
0222 
0223         if (ce.tagName().toLower() == QLatin1String("gsm")) {
0224             QDomNode gsmNode = c.firstChild();
0225 
0226             while (!gsmNode.isNull()) {
0227                 QDomElement gsmElement = gsmNode.toElement();
0228 
0229                 if (gsmElement.tagName().toLower() == QLatin1String("network-id")) {
0230                     result.mccmncs.append(gsmElement.attribute(QStringLiteral("mcc")) + gsmElement.attribute(QStringLiteral("mnc")));
0231                 }
0232                 gsmNode = gsmNode.nextSibling();
0233             }
0234         } else if (ce.tagName().toLower() == QLatin1String("name")) {
0235             QString lang = ce.attribute(QStringLiteral("xml:lang"));
0236             if (lang.isEmpty()) {
0237                 lang = QStringLiteral("en"); // English is default
0238             } else {
0239                 lang = lang.toLower();
0240                 lang.remove(QRegularExpression(QStringLiteral("\\-.*$"))); // Remove everything after '-' in xml:lang attribute.
0241             }
0242             localizedProviderNames.insert(lang, ce.text());
0243         }
0244         c = c.nextSibling();
0245     }
0246 
0247     result.name = getNameByLocale(localizedProviderNames);
0248 
0249     return result;
0250 }
0251 
0252 QStringList MobileProviders::getProvidersFromMCCMNC(const QString &targetMccMnc)
0253 {
0254     QStringList result;
0255 
0256     QDomNode n = docElement.firstChild();
0257 
0258     while (!n.isNull()) {
0259         QDomElement e = n.toElement(); // <country ...>
0260 
0261         if (!e.isNull()) {
0262             QDomNode n2 = e.firstChild();
0263             while (!n2.isNull()) {
0264                 QDomElement e2 = n2.toElement(); // <provider ...>
0265 
0266                 if (!e2.isNull() && e2.tagName().toLower() == QLatin1String("provider")) {
0267                     ProviderData data = parseProvider(e2);
0268 
0269                     if (data.mccmncs.contains(targetMccMnc)) {
0270                         result << data.name;
0271                     }
0272                 }
0273                 n2 = n2.nextSibling();
0274             }
0275         }
0276         n = n.nextSibling();
0277     }
0278 
0279     return result;
0280 }
0281 
0282 QStringList MobileProviders::getNetworkIds(const QString &provider)
0283 {
0284     if (mNetworkIds.isEmpty()) {
0285         getApns(provider);
0286     }
0287     return mNetworkIds;
0288 }
0289 
0290 QVariantMap MobileProviders::getApnInfo(const QString &apn)
0291 {
0292     QVariantMap temp;
0293     QDomNode n = mApns[apn];
0294     QStringList dnsList;
0295     QMap<QString, QString> localizedPlanNames;
0296 
0297     while (!n.isNull()) {
0298         QDomElement e = n.toElement(); // <name|username|password|dns(*)>
0299 
0300         if (!e.isNull()) {
0301             if (e.tagName().toLower() == QLatin1String("name")) {
0302                 QString lang = e.attribute(QStringLiteral("xml:lang"));
0303                 if (lang.isEmpty()) {
0304                     lang = QStringLiteral("en"); // English is default
0305                 } else {
0306                     lang = lang.toLower();
0307                     lang.remove(QRegularExpression(QStringLiteral("\\-.*$"))); // Remove everything after '-' in xml:lang attribute.
0308                 }
0309                 localizedPlanNames.insert(lang, e.text());
0310             } else if (e.tagName().toLower() == QLatin1String("username")) {
0311                 temp.insert(QStringLiteral("username"), e.text());
0312             } else if (e.tagName().toLower() == QLatin1String("password")) {
0313                 temp.insert(QStringLiteral("password"), e.text());
0314             } else if (e.tagName().toLower() == QLatin1String("dns")) {
0315                 dnsList.append(e.text());
0316             }
0317         }
0318 
0319         n = n.nextSibling();
0320     }
0321 
0322     QString name = getNameByLocale(localizedPlanNames);
0323     if (!name.isEmpty()) {
0324         temp.insert(QStringLiteral("name"), QVariant::fromValue(name));
0325     }
0326     temp.insert(QStringLiteral("number"), getGsmNumber());
0327     temp.insert(QStringLiteral("apn"), apn);
0328     temp.insert(QStringLiteral("dnsList"), dnsList);
0329 
0330     return temp;
0331 }
0332 
0333 QVariantMap MobileProviders::getCdmaInfo(const QString &provider)
0334 {
0335     if (!mProvidersCdma.contains(provider)) {
0336         return {};
0337     }
0338 
0339     QVariantMap temp;
0340     QDomNode n = mProvidersCdma[provider];
0341     QStringList sidList;
0342 
0343     while (!n.isNull()) {
0344         QDomElement e = n.toElement(); // <gsm or cdma ...>
0345 
0346         if (!e.isNull() && e.tagName().toLower() == QLatin1String("cdma")) {
0347             QDomNode n2 = e.firstChild();
0348             while (!n2.isNull()) {
0349                 QDomElement e2 = n2.toElement(); // <name | username | password | sid>
0350 
0351                 if (!e2.isNull()) {
0352                     if (e2.tagName().toLower() == QLatin1String("username")) {
0353                         temp.insert(QStringLiteral("username"), e2.text());
0354                     } else if (e2.tagName().toLower() == QLatin1String("password")) {
0355                         temp.insert(QStringLiteral("password"), e2.text());
0356                     } else if (e2.tagName().toLower() == QLatin1String("sid")) {
0357                         sidList.append(e2.text());
0358                     }
0359                 }
0360 
0361                 n2 = n2.nextSibling();
0362             }
0363         }
0364         n = n.nextSibling();
0365     }
0366 
0367     temp.insert(QStringLiteral("number"), getCdmaNumber());
0368     temp.insert(QStringLiteral("sidList"), sidList);
0369     return temp;
0370 }
0371 
0372 QString MobileProviders::getNameByLocale(const QMap<QString, QString> &localizedNames) const
0373 {
0374     QString name;
0375     const QStringList locales = QLocale().uiLanguages();
0376     for (const QString &locale : locales) {
0377         QString language = locale.split(QLatin1Char('-')).at(0);
0378 
0379         if (localizedNames.contains(language)) {
0380             return localizedNames[language];
0381         }
0382     }
0383 
0384     name = localizedNames[QStringLiteral("en")];
0385 
0386     // Use any language if no proper localized name were found.
0387     if (name.isEmpty() && !localizedNames.isEmpty()) {
0388         name = localizedNames.constBegin().value();
0389     }
0390     return name;
0391 }