Warning, file /pim/kalarm/src/kalarmcalendar/kacalendar.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * kacalendar.cpp - KAlarm kcal library calendar and event functions 0003 * This file is part of kalarmcalendar library, which provides access to KAlarm 0004 * calendar data. 0005 * Program: kalarm 0006 * SPDX-FileCopyrightText: 2001-2022 David Jarvie <djarvie@kde.org> 0007 * 0008 * SPDX-License-Identifier: LGPL-2.0-or-later 0009 */ 0010 0011 #include "kacalendar.h" 0012 0013 #include "kaevent.h" 0014 #include "version.h" 0015 #include "kalarmcal_debug.h" 0016 0017 #include <KCalendarCore/Alarm> 0018 #include <KCalendarCore/CalFormat> 0019 0020 #include <KLocalizedString> 0021 0022 #include <QFile> 0023 #include <QFileInfo> 0024 #include <QMap> 0025 0026 using namespace KCalendarCore; 0027 0028 namespace KAlarmCal 0029 { 0030 0031 const QLatin1String MIME_BASE("application/x-vnd.kde.alarm"); 0032 const QLatin1String MIME_ACTIVE("application/x-vnd.kde.alarm.active"); 0033 const QLatin1String MIME_ARCHIVED("application/x-vnd.kde.alarm.archived"); 0034 const QLatin1String MIME_TEMPLATE("application/x-vnd.kde.alarm.template"); 0035 0036 static const QByteArray VERSION_PROPERTY("VERSION"); // X-KDE-KALARM-VERSION VCALENDAR property 0037 0038 class Private 0039 { 0040 public: 0041 static int readKAlarmVersion(const FileStorage::Ptr&, QString& subVersion, QString& versionString); 0042 0043 static QByteArray mIcalProductId; 0044 }; 0045 0046 QByteArray Private::mIcalProductId; 0047 0048 //============================================================================= 0049 0050 namespace KACalendar 0051 { 0052 0053 const QByteArray APPNAME("KALARM"); 0054 0055 void setProductId(const QByteArray& progName, const QByteArray& progVersion) 0056 { 0057 Private::mIcalProductId = QByteArray("-//K Desktop Environment//NONSGML " + progName + " " + progVersion + "//EN"); 0058 KCalendarCore::CalFormat::setApplication(QString::fromLatin1(progName), QString::fromLatin1(Private::mIcalProductId)); 0059 } 0060 0061 QByteArray icalProductId() 0062 { 0063 return Private::mIcalProductId.isEmpty() ? QByteArray("-//K Desktop Environment//NONSGML //EN") : Private::mIcalProductId; 0064 } 0065 0066 /****************************************************************************** 0067 * Set the X-KDE-KALARM-VERSION property in a calendar. 0068 */ 0069 void setKAlarmVersion(const Calendar::Ptr& calendar) 0070 { 0071 calendar->setCustomProperty(APPNAME, VERSION_PROPERTY, QString::fromLatin1(KAEvent::currentCalendarVersionString())); 0072 } 0073 0074 /****************************************************************************** 0075 * Check the version of KAlarm which wrote a calendar file, and convert it in 0076 * memory to the current KAlarm format if possible. The storage file is not 0077 * updated. The compatibility of the calendar format is indicated by the return 0078 * value. 0079 */ 0080 int updateVersion(const FileStorage::Ptr& fileStorage, QString& versionString) 0081 { 0082 QString subVersion; 0083 const int version = Private::readKAlarmVersion(fileStorage, subVersion, versionString); 0084 if (version == CurrentFormat) 0085 return CurrentFormat; // calendar is in the current KAlarm format 0086 if (version == IncompatibleFormat || version > KAEvent::currentCalendarVersion()) 0087 return IncompatibleFormat; // calendar was created by another program, or an unknown version of KAlarm 0088 0089 // Calendar was created by an earlier version of KAlarm. 0090 // Convert events to current KAlarm format for when/if the calendar is saved. 0091 qCDebug(KALARMCAL_LOG) << "KAlarm version" << version; 0092 KAEvent::convertKCalEvents(fileStorage->calendar(), version); 0093 // Set the new calendar version. 0094 setKAlarmVersion(fileStorage->calendar()); 0095 return version; 0096 } 0097 0098 } // namespace KACalendar 0099 0100 /****************************************************************************** 0101 * Return the KAlarm version which wrote the calendar which has been loaded. 0102 * The format is, for example, 000507 for 0.5.7. 0103 * Reply = CurrentFormat if the calendar was created by the current version of KAlarm 0104 * = IncompatibleFormat if it was created by KAlarm pre-0.3.5, or another program 0105 * = version number if created by another KAlarm version. 0106 */ 0107 int Private::readKAlarmVersion(const FileStorage::Ptr& fileStorage, QString& subVersion, QString& versionString) 0108 { 0109 subVersion.clear(); 0110 Calendar::Ptr calendar = fileStorage->calendar(); 0111 versionString = calendar->customProperty(KACalendar::APPNAME, VERSION_PROPERTY); 0112 qCDebug(KALARMCAL_LOG) << "File=" << fileStorage->fileName() << ", version=" << versionString; 0113 0114 if (versionString.isEmpty()) 0115 { 0116 // Pre-KAlarm 1.4 defined the KAlarm version number in the PRODID field. 0117 // If another application has written to the file, this may not be present. 0118 const QString prodid = calendar->productId(); 0119 if (prodid.isEmpty()) 0120 { 0121 // Check whether the calendar file is empty, in which case 0122 // it can be written to freely. 0123 const QFileInfo fi(fileStorage->fileName()); 0124 if (!fi.size()) 0125 return KACalendar::CurrentFormat; 0126 } 0127 0128 // Find the KAlarm identifier 0129 QString progname = QStringLiteral(" KAlarm "); 0130 int i = prodid.indexOf(progname, 0, Qt::CaseInsensitive); 0131 if (i < 0) 0132 { 0133 // Older versions used KAlarm's translated name in the product ID, which 0134 // could have created problems using a calendar in different locales. 0135 progname = QLatin1String(" ") + i18n("KAlarm") + QLatin1Char(' '); 0136 i = prodid.indexOf(progname, 0, Qt::CaseInsensitive); 0137 if (i < 0) 0138 return KACalendar::IncompatibleFormat; // calendar wasn't created by KAlarm 0139 } 0140 0141 // Extract the KAlarm version string 0142 versionString = prodid.mid(i + progname.length()).trimmed(); 0143 i = versionString.indexOf(QLatin1Char('/')); 0144 const int j = versionString.indexOf(QLatin1Char(' ')); 0145 if (j >= 0 && j < i) 0146 i = j; 0147 if (i <= 0) 0148 return KACalendar::IncompatibleFormat; // missing version string 0149 versionString.truncate(i); // 'versionString' now contains the KAlarm version string 0150 } 0151 if (versionString == QLatin1String(KAEvent::currentCalendarVersionString())) 0152 return KACalendar::CurrentFormat; // the calendar is in the current KAlarm format 0153 const int ver = KAlarmCal::getVersionNumber(versionString, &subVersion); 0154 if (ver == KAEvent::currentCalendarVersion()) 0155 return KACalendar::CurrentFormat; // the calendar is in the current KAlarm format 0156 return KAlarmCal::getVersionNumber(versionString, &subVersion); 0157 } 0158 0159 //============================================================================= 0160 0161 namespace CalEvent 0162 { 0163 0164 // Struct to contain static strings, to allow use of Q_GLOBAL_STATIC 0165 // to delete them on program termination. 0166 struct StaticStrings 0167 { 0168 StaticStrings() 0169 : STATUS_PROPERTY("TYPE") 0170 , ACTIVE_STATUS(QStringLiteral("ACTIVE")) 0171 , TEMPLATE_STATUS(QStringLiteral("TEMPLATE")) 0172 , ARCHIVED_STATUS(QStringLiteral("ARCHIVED")) 0173 , DISPLAYING_STATUS(QStringLiteral("DISPLAYING")) 0174 , ARCHIVED_UID(QStringLiteral("exp-")) 0175 , DISPLAYING_UID(QStringLiteral("disp-")) 0176 , OLD_ARCHIVED_UID(QStringLiteral("-exp-")) 0177 , OLD_TEMPLATE_UID(QStringLiteral("-tmpl-")) 0178 {} 0179 // Event custom properties. 0180 // Note that all custom property names are prefixed with X-KDE-KALARM- in the calendar file. 0181 const QByteArray STATUS_PROPERTY; // X-KDE-KALARM-TYPE property 0182 const QString ACTIVE_STATUS; 0183 const QString TEMPLATE_STATUS; 0184 const QString ARCHIVED_STATUS; 0185 const QString DISPLAYING_STATUS; 0186 0187 // Event ID identifiers 0188 const QString ARCHIVED_UID; 0189 const QString DISPLAYING_UID; 0190 0191 // Old KAlarm format identifiers 0192 const QString OLD_ARCHIVED_UID; 0193 const QString OLD_TEMPLATE_UID; 0194 }; 0195 Q_GLOBAL_STATIC(StaticStrings, staticStrings) 0196 0197 /****************************************************************************** 0198 * Convert a unique ID to indicate that the event is in a specified calendar file. 0199 * This is done by prefixing archived or displaying alarms with "exp-" or "disp-", 0200 * while active alarms have no prefix. 0201 * Note that previously, "-exp-" was inserted in the middle of the UID. 0202 */ 0203 QString uid(const QString& id, Type status) 0204 { 0205 QString result = id; 0206 Type oldType; 0207 int i; 0208 int len; 0209 if (result.startsWith(staticStrings->ARCHIVED_UID)) 0210 { 0211 oldType = ARCHIVED; 0212 len = staticStrings->ARCHIVED_UID.length(); 0213 } 0214 else if (result.startsWith(staticStrings->DISPLAYING_UID)) 0215 { 0216 oldType = DISPLAYING; 0217 len = staticStrings->DISPLAYING_UID.length(); 0218 } 0219 else 0220 { 0221 if ((i = result.indexOf(staticStrings->OLD_ARCHIVED_UID)) > 0) 0222 result.remove(i, staticStrings->OLD_ARCHIVED_UID.length()); 0223 oldType = ACTIVE; 0224 len = 0; 0225 } 0226 if (status != oldType) 0227 { 0228 QString part; 0229 switch (status) 0230 { 0231 case ARCHIVED: part = staticStrings->ARCHIVED_UID; break; 0232 case DISPLAYING: part = staticStrings->DISPLAYING_UID; break; 0233 case ACTIVE: 0234 break; 0235 case TEMPLATE: 0236 case EMPTY: 0237 default: 0238 return result; 0239 } 0240 result.replace(0, len, part); 0241 } 0242 return result; 0243 } 0244 0245 /****************************************************************************** 0246 * Check an event to determine its type - active, archived, template or empty. 0247 * The default type is active if it contains alarms and there is nothing to 0248 * indicate otherwise. 0249 * Note that the mere fact that all an event's alarms have passed does not make 0250 * an event archived, since it may be that they have not yet been able to be 0251 * triggered. They will be archived once KAlarm tries to handle them. 0252 * Do not call this function for the displaying alarm calendar. 0253 */ 0254 Type status(const Event::Ptr& event, QString* param) 0255 { 0256 // Set up a static quick lookup for type strings 0257 using PropertyMap = QMap<QString, Type>; 0258 static PropertyMap properties; 0259 if (properties.isEmpty()) 0260 { 0261 properties[staticStrings->ACTIVE_STATUS] = ACTIVE; 0262 properties[staticStrings->TEMPLATE_STATUS] = TEMPLATE; 0263 properties[staticStrings->ARCHIVED_STATUS] = ARCHIVED; 0264 properties[staticStrings->DISPLAYING_STATUS] = DISPLAYING; 0265 } 0266 0267 if (param) 0268 param->clear(); 0269 if (!event) 0270 return EMPTY; 0271 const Alarm::List alarms = event->alarms(); 0272 if (alarms.isEmpty()) 0273 return EMPTY; 0274 0275 const QString property = event->customProperty(KACalendar::APPNAME, staticStrings->STATUS_PROPERTY); 0276 if (!property.isEmpty()) 0277 { 0278 // There's a X-KDE-KALARM-TYPE property. 0279 // It consists of the event type, plus an optional parameter. 0280 PropertyMap::ConstIterator it = properties.constFind(property); 0281 if (it != properties.constEnd()) 0282 return it.value(); 0283 const int i = property.indexOf(QLatin1Char(';')); 0284 if (i < 0) 0285 return EMPTY; 0286 it = properties.constFind(property.left(i)); 0287 if (it == properties.constEnd()) 0288 return EMPTY; 0289 if (param) 0290 *param = property.mid(i + 1); 0291 return it.value(); 0292 } 0293 0294 // The event either wasn't written by KAlarm, or was written by a pre-2.0 version. 0295 // Check first for an old KAlarm format, which indicated the event type in the 0296 // middle of its UID. 0297 const QString euid = event->uid(); 0298 if (euid.indexOf(staticStrings->OLD_ARCHIVED_UID) > 0) 0299 return ARCHIVED; 0300 if (euid.indexOf(staticStrings->OLD_TEMPLATE_UID) > 0) 0301 return TEMPLATE; 0302 0303 // Otherwise, assume it's an active alarm 0304 return ACTIVE; 0305 } 0306 0307 /****************************************************************************** 0308 * Set the event's type - active, archived, template, etc. 0309 * If a parameter is supplied, it will be appended as a second parameter to the 0310 * custom property. 0311 */ 0312 void setStatus(const Event::Ptr& event, Type status, const QString& param) 0313 { 0314 if (!event) 0315 return; 0316 QString text; 0317 switch (status) 0318 { 0319 case ACTIVE: text = staticStrings->ACTIVE_STATUS; break; 0320 case TEMPLATE: text = staticStrings->TEMPLATE_STATUS; break; 0321 case ARCHIVED: text = staticStrings->ARCHIVED_STATUS; break; 0322 case DISPLAYING: text = staticStrings->DISPLAYING_STATUS; break; 0323 default: 0324 event->removeCustomProperty(KACalendar::APPNAME, staticStrings->STATUS_PROPERTY); 0325 return; 0326 } 0327 if (!param.isEmpty()) 0328 text += QLatin1Char(';') + param; 0329 event->setCustomProperty(KACalendar::APPNAME, staticStrings->STATUS_PROPERTY, text); 0330 } 0331 0332 Type type(const QString& mimeType) 0333 { 0334 if (mimeType == MIME_ACTIVE) 0335 return ACTIVE; 0336 if (mimeType == MIME_ARCHIVED) 0337 return ARCHIVED; 0338 if (mimeType == MIME_TEMPLATE) 0339 return TEMPLATE; 0340 return EMPTY; 0341 } 0342 0343 Types types(const QStringList& mimeTypes) 0344 { 0345 Types types = {}; 0346 for (const QString& mtype : mimeTypes) 0347 { 0348 if (mtype == MIME_ACTIVE) 0349 types |= ACTIVE; 0350 else if (mtype == MIME_ARCHIVED) 0351 types |= ARCHIVED; 0352 else if (mtype == MIME_TEMPLATE) 0353 types |= TEMPLATE; 0354 } 0355 return types; 0356 } 0357 0358 QString mimeType(Type mtype) 0359 { 0360 switch (mtype) 0361 { 0362 case ACTIVE: return MIME_ACTIVE; 0363 case ARCHIVED: return MIME_ARCHIVED; 0364 case TEMPLATE: return MIME_TEMPLATE; 0365 default: 0366 return {}; 0367 } 0368 } 0369 0370 QStringList mimeTypes(Types types) 0371 { 0372 QStringList mimes; 0373 for (int i = 1; types; i <<= 1) 0374 { 0375 if (types & i) 0376 { 0377 mimes += mimeType(Type(i)); 0378 types &= ~i; 0379 } 0380 } 0381 return mimes; 0382 } 0383 0384 } // namespace CalEvent 0385 0386 } // namespace KAlarmCal 0387 0388 QDebug operator<<(QDebug debug, KAlarmCal::CalEvent::Type type) 0389 { 0390 const char* str; 0391 switch (type) 0392 { 0393 case KAlarmCal::CalEvent::ACTIVE: str = "Active alarms"; break; 0394 case KAlarmCal::CalEvent::ARCHIVED: str = "Archived alarms"; break; 0395 case KAlarmCal::CalEvent::TEMPLATE: str = "Alarm templates"; break; 0396 default: return debug; 0397 } 0398 debug << str; 0399 return debug; 0400 } 0401 0402 // vim: et sw=4: