File indexing completed on 2024-04-21 03:52:45

0001 /*
0002   This file is part of the kcalcore library.
0003 
0004   SPDX-FileCopyrightText: 2002, 2006, 2010 David Jarvie <djarvie@kde.org>
0005 
0006   SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 /**
0009   @file
0010   This file is part of the API for handling calendar data and
0011   defines the CustomProperties class.
0012 
0013   @brief
0014   A class to manage custom calendar properties.
0015 
0016   @author David Jarvie \<djarvie@kde.org\>
0017 */
0018 
0019 #include "customproperties.h"
0020 
0021 #include "kcalendarcore_debug.h"
0022 #include <QDataStream>
0023 
0024 using namespace KCalendarCore;
0025 
0026 //@cond PRIVATE
0027 static bool checkName(const QByteArray &name);
0028 
0029 class Q_DECL_HIDDEN CustomProperties::Private
0030 {
0031 public:
0032     bool operator==(const Private &other) const;
0033     QMap<QByteArray, QString> mProperties; // custom calendar properties
0034     QMap<QByteArray, QString> mPropertyParameters;
0035 
0036     // Volatile properties are not written back to the serialized format and are not compared in operator==
0037     // They are only used for runtime purposes and are not part of the payload.
0038     QMap<QByteArray, QString> mVolatileProperties;
0039 
0040     bool isVolatileProperty(const QString &name) const
0041     {
0042         return name.startsWith(QLatin1String("X-KDE-VOLATILE"));
0043     }
0044 };
0045 
0046 bool CustomProperties::Private::operator==(const CustomProperties::Private &other) const
0047 {
0048     if (mProperties.count() != other.mProperties.count()) {
0049         // qCDebug(KCALCORE_LOG) << "Property count is different:" << mProperties << other.mProperties;
0050         return false;
0051     }
0052     for (auto it = mProperties.cbegin(); it != mProperties.cend(); ++it) {
0053         auto itOther = other.mProperties.constFind(it.key());
0054         if (itOther == other.mProperties.cend() || itOther.value() != it.value()) {
0055             return false;
0056         }
0057     }
0058     for (auto it = mPropertyParameters.cbegin(); it != mPropertyParameters.cend(); ++it) {
0059         auto itOther = other.mPropertyParameters.constFind(it.key());
0060         if (itOther == other.mPropertyParameters.cend() || itOther.value() != it.value()) {
0061             return false;
0062         }
0063     }
0064     return true;
0065 }
0066 //@endcond
0067 
0068 CustomProperties::CustomProperties()
0069     : d(new Private)
0070 {
0071 }
0072 
0073 CustomProperties::CustomProperties(const CustomProperties &cp)
0074     : d(new Private(*cp.d))
0075 {
0076 }
0077 
0078 CustomProperties &CustomProperties::operator=(const CustomProperties &other)
0079 {
0080     // check for self assignment
0081     if (&other == this) {
0082         return *this;
0083     }
0084 
0085     *d = *other.d;
0086     return *this;
0087 }
0088 
0089 CustomProperties::~CustomProperties()
0090 {
0091     delete d;
0092 }
0093 
0094 bool CustomProperties::operator==(const CustomProperties &other) const
0095 {
0096     return *d == *other.d;
0097 }
0098 
0099 void CustomProperties::setCustomProperty(const QByteArray &app, const QByteArray &key, const QString &value)
0100 {
0101     if (value.isNull() || key.isEmpty() || app.isEmpty()) {
0102         return;
0103     }
0104     QByteArray property = "X-KDE-" + app + '-' + key;
0105     if (!checkName(property)) {
0106         return;
0107     }
0108     customPropertyUpdate();
0109 
0110     if (d->isVolatileProperty(QLatin1String(property))) {
0111         d->mVolatileProperties[property] = value;
0112     } else {
0113         d->mProperties[property] = value;
0114     }
0115 
0116     customPropertyUpdated();
0117 }
0118 
0119 void CustomProperties::removeCustomProperty(const QByteArray &app, const QByteArray &key)
0120 {
0121     removeNonKDECustomProperty(QByteArray("X-KDE-" + app + '-' + key));
0122 }
0123 
0124 QString CustomProperties::customProperty(const QByteArray &app, const QByteArray &key) const
0125 {
0126     return nonKDECustomProperty(QByteArray("X-KDE-" + app + '-' + key));
0127 }
0128 
0129 QByteArray CustomProperties::customPropertyName(const QByteArray &app, const QByteArray &key)
0130 {
0131     QByteArray property("X-KDE-" + app + '-' + key);
0132     if (!checkName(property)) {
0133         return QByteArray();
0134     }
0135     return property;
0136 }
0137 
0138 void CustomProperties::setNonKDECustomProperty(const QByteArray &name, const QString &value, const QString &parameters)
0139 {
0140     if (value.isNull() || !checkName(name)) {
0141         return;
0142     }
0143     customPropertyUpdate();
0144     if (d->isVolatileProperty(QLatin1String(name))) {
0145         d->mVolatileProperties[name] = value;
0146     } else {
0147         d->mProperties[name] = value;
0148         d->mPropertyParameters[name] = parameters;
0149     }
0150     customPropertyUpdated();
0151 }
0152 void CustomProperties::removeNonKDECustomProperty(const QByteArray &name)
0153 {
0154     if (d->mProperties.contains(name)) {
0155         customPropertyUpdate();
0156         d->mProperties.remove(name);
0157         d->mPropertyParameters.remove(name);
0158         customPropertyUpdated();
0159     } else if (d->mVolatileProperties.contains(name)) {
0160         customPropertyUpdate();
0161         d->mVolatileProperties.remove(name);
0162         customPropertyUpdated();
0163     }
0164 }
0165 
0166 QString CustomProperties::nonKDECustomProperty(const QByteArray &name) const
0167 {
0168     return d->isVolatileProperty(QLatin1String(name)) ? d->mVolatileProperties.value(name) : d->mProperties.value(name);
0169 }
0170 
0171 QString CustomProperties::nonKDECustomPropertyParameters(const QByteArray &name) const
0172 {
0173     return d->mPropertyParameters.value(name);
0174 }
0175 
0176 void CustomProperties::setCustomProperties(const QMap<QByteArray, QString> &properties)
0177 {
0178     bool changed = false;
0179     for (auto it = properties.cbegin(); it != properties.cend(); ++it) {
0180         // Validate the property name and convert any null string to empty string
0181         if (checkName(it.key())) {
0182             if (d->isVolatileProperty(QLatin1String(it.key()))) {
0183                 d->mVolatileProperties[it.key()] = it.value().isNull() ? QLatin1String("") : it.value();
0184             } else {
0185                 d->mProperties[it.key()] = it.value().isNull() ? QLatin1String("") : it.value();
0186             }
0187             if (!changed) {
0188                 customPropertyUpdate();
0189             }
0190             changed = true;
0191         }
0192     }
0193     if (changed) {
0194         customPropertyUpdated();
0195     }
0196 }
0197 
0198 QMap<QByteArray, QString> CustomProperties::customProperties() const
0199 {
0200     QMap<QByteArray, QString> result = d->mProperties;
0201     for (auto it = d->mVolatileProperties.begin(), end = d->mVolatileProperties.end(); it != end; ++it) {
0202         result.insert(it.key(), it.value());
0203     }
0204 
0205     return result;
0206 }
0207 
0208 void CustomProperties::customPropertyUpdate()
0209 {
0210 }
0211 
0212 void CustomProperties::customPropertyUpdated()
0213 {
0214 }
0215 
0216 //@cond PRIVATE
0217 bool checkName(const QByteArray &name)
0218 {
0219     // Check that the property name starts with 'X-' and contains
0220     // only the permitted characters
0221     const char *n = name.constData();
0222     int len = name.length();
0223     if (len < 2 || n[0] != 'X' || n[1] != '-') {
0224         return false;
0225     }
0226     for (int i = 2; i < len; ++i) {
0227         char ch = n[i];
0228         if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-') {
0229             continue;
0230         }
0231         return false; // invalid character found
0232     }
0233     return true;
0234 }
0235 //@endcond
0236 
0237 QDataStream &KCalendarCore::operator<<(QDataStream &stream, const KCalendarCore::CustomProperties &properties)
0238 {
0239     return stream << properties.d->mProperties << properties.d->mPropertyParameters;
0240 }
0241 
0242 QDataStream &KCalendarCore::operator>>(QDataStream &stream, KCalendarCore::CustomProperties &properties)
0243 {
0244     properties.d->mVolatileProperties.clear();
0245     return stream >> properties.d->mProperties >> properties.d->mPropertyParameters;
0246 }