File indexing completed on 2024-05-05 05:36:39

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