File indexing completed on 2024-05-05 05:28:47

0001 // SPDX-FileCopyrightText: 2020 Jonah BrĂ¼chert <jbb@kaidan.im>
0002 //
0003 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 
0005 #include "utils.h"
0006 
0007 #include <QClipboard>
0008 #include <QGuiApplication>
0009 #include <QProcess>
0010 #include <QQmlApplicationEngine>
0011 #include <QQuickWindow>
0012 #include <QRegularExpression>
0013 
0014 #include <KIO/ApplicationLauncherJob>
0015 #include <KPeople/PersonData>
0016 #include <KPeopleBackend/AbstractContact>
0017 #include <KTextToHTML>
0018 
0019 #include <ranges>
0020 
0021 #include "global.h"
0022 
0023 Utils *Utils::s_instance = nullptr;
0024 
0025 namespace ranges = std::ranges;
0026 
0027 template<typename T>
0028 static QVariantList toVariantList(const QVector<T> &v)
0029 {
0030     QVariantList l;
0031     l.reserve(v.size());
0032     std::transform(v.begin(), v.end(), std::back_inserter(l), [](const T &elem) {
0033         return QVariant::fromValue(elem);
0034     });
0035     return l;
0036 }
0037 
0038 static QSharedPointer<KPeople::PersonData> contactData(const QString &uri)
0039 {
0040     return QSharedPointer<KPeople::PersonData>(new KPeople::PersonData(uri));
0041 }
0042 
0043 Utils::Utils(QQmlApplicationEngine *engine)
0044     : m_engine(engine)
0045 {
0046     s_instance = this;
0047 }
0048 
0049 void Utils::showPassiveNotification(const QString &message, int timeout)
0050 {
0051     m_window = qobject_cast<QQuickWindow *>(m_engine->rootObjects().at(0));
0052 
0053     if (m_window) {
0054         QMetaObject::invokeMethod(m_window,
0055                                   "showPassiveNotification",
0056                                   Q_ARG(QVariant, message),
0057                                   Q_ARG(QVariant, timeout),
0058                                   Q_ARG(QVariant, {}),
0059                                   Q_ARG(QVariant, {}));
0060     }
0061 }
0062 
0063 void Utils::showPassiveNotification(const QString &message, Utils::PassiveNotificationDuation timeout)
0064 {
0065     m_window = qobject_cast<QQuickWindow *>(m_engine->rootObjects().at(0));
0066 
0067     QString timeoutStr;
0068     switch (timeout) {
0069     case ShortNotificationDuration:
0070         timeoutStr = SL("short");
0071         break;
0072     case LongNotificationDuration:
0073         timeoutStr = SL("long");
0074         break;
0075     }
0076     if (m_window) {
0077         QMetaObject::invokeMethod(m_window,
0078                                   "showPassiveNotification",
0079                                   Q_ARG(QVariant, message),
0080                                   Q_ARG(QVariant, timeoutStr),
0081                                   Q_ARG(QVariant, {}),
0082                                   Q_ARG(QVariant, {}));
0083     }
0084 }
0085 
0086 bool Utils::isPhoneNumber(const QString &text) const
0087 {
0088     static const QRegularExpression phoneNumberRegex(SL(R"(^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\./0-9]*$)"));
0089 
0090     return phoneNumberRegex.match(text).hasMatch();
0091 }
0092 
0093 bool Utils::isPremiumNumber(const PhoneNumberList &phoneNumberList) const
0094 {
0095     // premium SMS shortcodes across all countries with overly broad regexes removed
0096     // all of AOSP regexes combined: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/res/res/xml/sms_short_codes.xml
0097     static const QRegularExpression premiumRegex(SL(
0098         R"##(^(15191|55[56]00|11[2456]1|3024|19998882|19944444|330[12]|87744|901[234]|93(?:94|101)|9426|9525|18(?:16|423)|19(?:1[56]|35)|3336|4161|444[4689]|501[34]|7781|60999|88188|43030|543|83111|30118|7510|9\d{6,7}|11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999|1\d{3}|90\d{5}|15330|1701[0-3]|[23][57]\d{3}|280\d{2}|[79]9[57]\d{3}|0600.*|0700.*|171(?:59|63)|[4-8]\d{4}|[5-8]\d{4}|801[234]|888[239]|54\d{3}|19[0-5]\d{2}|0691227910|1784|5[3-9]\d{3}|4422|4545|4\d{4}|415[2367]|444[69]|335[02]|4161|444[469]|77[2359]0|8444|919[3-5]|968[2-5]|13[89]1|1394|16[34]5|6\d{4}|18(?:19|63|7[1-4])|53035|7766|32298|33776|4466|5040|2201|222[67]|3903|8995|4679|74240|79(?:10|866)|92525|6[1289]\d{3}|12(?:63|66|88)|13(?:14|80)|1(?:1[56]1|899)|2(?:09[57]|322|47[46]|880|990)|3[589]33|4161|44(?:4[3-9]|81)|77(?:33|81)|8424|72\d{3}|73800|[368]\d{3}|\d{4}|4\d{6}|11[3-7]1|4161|4333|444[689]|444[3-9]|70[579]4|7540|20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807))$)##"));
0099 
0100     bool isMatched = false;
0101     int i = phoneNumberList.size();
0102     while (!isMatched && i > 0) {
0103         i--;
0104 
0105         // remove country code, hyphens, and spaces
0106         auto numberString = phoneNumberList.at(i).toNational();
0107         numberString.replace(SL("-"), QString()).replace(SL(" "), QString());
0108 
0109         isMatched = premiumRegex.match(numberString).hasMatch();
0110     }
0111 
0112     return isMatched;
0113 }
0114 
0115 void Utils::launchPhonebook()
0116 {
0117     auto phonebook = KService::serviceByDesktopName(QStringLiteral("org.kde.phonebook"));
0118 
0119     if (!phonebook) {
0120         qWarning() << "Could not find plasma-phonebook";
0121         return;
0122     }
0123 
0124     auto *job = new KIO::ApplicationLauncherJob(phonebook);
0125     job->start();
0126 }
0127 
0128 void Utils::copyTextToClipboard(const QString &text) const
0129 {
0130     qGuiApp->clipboard()->setText(text);
0131 }
0132 
0133 QString Utils::textToHtml(const QString &text)
0134 {
0135     return KTextToHTML::convertToHtml(text, KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::ConvertPhoneNumbers));
0136 }
0137 
0138 Utils *Utils::instance()
0139 {
0140     return s_instance;
0141 }
0142 
0143 QQmlApplicationEngine *Utils::qmlEngine() const
0144 {
0145     return m_engine;
0146 }
0147 
0148 PhoneNumber Utils::phoneNumber(const QString &number) const
0149 {
0150     return PhoneNumber(number);
0151 }
0152 
0153 QString Utils::phoneNumberToInternationalString(const PhoneNumber &phoneNumber) const
0154 {
0155     return phoneNumber.toInternational();
0156 }
0157 
0158 PhoneNumberList Utils::phoneNumberList(const QStringList &phoneNumbers) const
0159 {
0160     auto p = phoneNumbers;
0161     std::sort(p.begin(), p.end());
0162 
0163     PhoneNumberList list;
0164     ranges::transform(p, std::back_inserter(list), [](const auto &phoneNumber) {
0165         return PhoneNumber(phoneNumber);
0166     });
0167 
0168     return list;
0169 }
0170 
0171 QString Utils::phoneNumberListToString(const PhoneNumberList &phoneNumberList) const
0172 {
0173     return phoneNumberList.toString();
0174 }
0175 
0176 bool Utils::isLocale24HourTime()
0177 {
0178     return QLocale::system().timeFormat(QLocale::ShortFormat).toLower().indexOf(SL("ap")) == -1;
0179 }
0180 
0181 QVariantList Utils::phoneNumbers(const QString &kPeopleUri)
0182 {
0183     auto person = contactData(kPeopleUri);
0184     auto vcard = person->contactCustomProperty(KPeople::AbstractContact::VCardProperty).toByteArray();
0185     auto addressee = converter.parseVCard(vcard);
0186 
0187     return toVariantList(addressee.phoneNumbers());
0188 }