utils.cpp 7.05 KB
Newer Older
Sandro Knauß's avatar
Sandro Knauß committed
1
/*
2
    SPDX-FileCopyrightText: 2010 Tobias Koenig <tokoe@kde.org>
Sandro Knauß's avatar
Sandro Knauß committed
3

4
    SPDX-License-Identifier: GPL-2.0-or-later
Sandro Knauß's avatar
Sandro Knauß committed
5
6
7
8
9
10
11
*/

#include "utils.h"
#include "davprotocolattribute.h"

#include <KDAV/DavItem>
#include <KDAV/DavUrl>
Volker Krause's avatar
Volker Krause committed
12
#include <KDAV/ProtocolInfo>
Sandro Knauß's avatar
Sandro Knauß committed
13
14

#include <AkonadiCore/Collection>
15
16
17
#include <KCalendarCore/ICalFormat>
#include <KCalendarCore/Incidence>
#include <KCalendarCore/MemoryCalendar>
Laurent Montel's avatar
Laurent Montel committed
18
19
#include <KContacts/Addressee>
#include <KContacts/VCardConverter>
Sandro Knauß's avatar
Sandro Knauß committed
20
21
22

#include <KLocalizedString>

Laurent Montel's avatar
Laurent Montel committed
23
#include <QByteArray>
Laurent Montel's avatar
Laurent Montel committed
24
#include <QRandomGenerator>
Laurent Montel's avatar
Laurent Montel committed
25
#include <QString>
26
#include <QTimeZone>
Sandro Knauß's avatar
Sandro Knauß committed
27
28
29

#include "davresource_debug.h"

Laurent Montel's avatar
Laurent Montel committed
30
using IncidencePtr = QSharedPointer<KCalendarCore::Incidence>;
Sandro Knauß's avatar
Sandro Knauß committed
31

Volker Krause's avatar
Volker Krause committed
32
33
34
static QString createUniqueId()
{
    const qint64 time = QDateTime::currentMSecsSinceEpoch() / 1000;
Laurent Montel's avatar
Laurent Montel committed
35
    const int r = QRandomGenerator::global()->bounded(1000);
Volker Krause's avatar
Volker Krause committed
36
37
38
39
40
    const QString id = QLatin1Char('R') + QString::number(r);
    const QString uid = QString::number(time) + QLatin1Char('.') + id;
    return uid;
}

Sandro Knauß's avatar
Sandro Knauß committed
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
QString Utils::translatedProtocolName(KDAV::Protocol protocol)
{
    QString protocolName;

    switch (protocol) {
    case KDAV::CalDav:
        protocolName = i18n("CalDav");
        break;
    case KDAV::CardDav:
        protocolName = i18n("CardDav");
        break;
    case KDAV::GroupDav:
        protocolName = i18n("GroupDav");
        break;
    }

    return protocolName;
}

KDAV::Protocol Utils::protocolByTranslatedName(const QString &name)
{
    KDAV::Protocol protocol = KDAV::CalDav;

    if (name == i18n("CalDav")) {
        protocol = KDAV::CalDav;
    } else if (name == i18n("CardDav")) {
        protocol = KDAV::CardDav;
    } else if (name == i18n("GroupDav")) {
        protocol = KDAV::GroupDav;
    }

    return protocol;
}

KDAV::DavItem Utils::createDavItem(const Akonadi::Item &item, const Akonadi::Collection &collection, const Akonadi::Item::List &dependentItems)
{
    QByteArray rawData;
    QString mimeType;
    QUrl url;
    KDAV::DavItem davItem;
    const QString basePath = collection.remoteId();

    if (item.hasPayload<KContacts::Addressee>()) {
Laurent Montel's avatar
Laurent Montel committed
84
        const auto contact = item.payload<KContacts::Addressee>();
Volker Krause's avatar
Volker Krause committed
85
        const QString fileName = createUniqueId();
Sandro Knauß's avatar
Sandro Knauß committed
86
87
88

        url = QUrl::fromUserInput(basePath + fileName + QLatin1String(".vcf"));

Laurent Montel's avatar
Laurent Montel committed
89
        const auto protoAttr = collection.attribute<DavProtocolAttribute>();
Sandro Knauß's avatar
Sandro Knauß committed
90
        if (protoAttr) {
Volker Krause's avatar
Volker Krause committed
91
            mimeType = KDAV::ProtocolInfo::contactsMimeType(KDAV::Protocol(protoAttr->davProtocol()));
Sandro Knauß's avatar
Sandro Knauß committed
92
93
94
95
96
97
98
99
        } else {
            mimeType = KContacts::Addressee::mimeType();
        }

        KContacts::VCardConverter converter;
        // rawData is already UTF-8
        rawData = converter.exportVCard(contact, KContacts::VCardConverter::v3_0);
    } else if (item.hasPayload<IncidencePtr>()) {
100
        const KCalendarCore::MemoryCalendar::Ptr calendar(new KCalendarCore::MemoryCalendar(QTimeZone::systemTimeZone()));
Sandro Knauß's avatar
Sandro Knauß committed
101
        calendar->addIncidence(item.payload<IncidencePtr>());
102
        for (const Akonadi::Item &dependentItem : std::as_const(dependentItems)) {
Sandro Knauß's avatar
Sandro Knauß committed
103
104
105
            calendar->addIncidence(dependentItem.payload<IncidencePtr>());
        }

Volker Krause's avatar
Volker Krause committed
106
        const QString fileName = createUniqueId();
Sandro Knauß's avatar
Sandro Knauß committed
107
108
109
110

        url = QUrl::fromUserInput(basePath + fileName + QLatin1String(".ics"));
        mimeType = QStringLiteral("text/calendar");

111
        KCalendarCore::ICalFormat formatter;
Sandro Knauß's avatar
Sandro Knauß committed
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
        rawData = formatter.toString(calendar, QString()).toUtf8();
    }

    davItem.setContentType(mimeType);
    davItem.setData(rawData);
    davItem.setUrl(KDAV::DavUrl(url, KDAV::CalDav));
    davItem.setEtag(item.remoteRevision());

    return davItem;
}

bool Utils::parseDavData(const KDAV::DavItem &source, Akonadi::Item &target, Akonadi::Item::List &extraItems)
{
    const QString data = QString::fromUtf8(source.data());

    if (target.mimeType() == KContacts::Addressee::mimeType()) {
        KContacts::VCardConverter converter;
        const KContacts::Addressee contact = converter.parseVCard(source.data());

        if (contact.isEmpty()) {
            return false;
        }

        target.setPayloadFromData(source.data());
    } else {
137
138
        KCalendarCore::ICalFormat formatter;
        const KCalendarCore::MemoryCalendar::Ptr calendar(new KCalendarCore::MemoryCalendar(QTimeZone::systemTimeZone()));
Sandro Knauß's avatar
Sandro Knauß committed
139
        formatter.fromString(calendar, data);
140
        KCalendarCore::Incidence::List incidences = calendar->incidences();
Sandro Knauß's avatar
Sandro Knauß committed
141
142
143
144
145
146
147
148
149
150

        if (incidences.isEmpty()) {
            return false;
        }

        // All items must have the same uid in a single object.
        // Find the main VEVENT (if that's indeed what we have,
        // could be a VTODO or a VJOURNAL but that doesn't matter)
        // and then apply the recurrence exceptions
        IncidencePtr mainIncidence;
151
        KCalendarCore::Incidence::List exceptions;
Sandro Knauß's avatar
Sandro Knauß committed
152

153
        for (const IncidencePtr &incidence : std::as_const(incidences)) {
Sandro Knauß's avatar
Sandro Knauß committed
154
155
156
157
158
159
160
161
162
            if (incidence->hasRecurrenceId()) {
                qCDebug(DAVRESOURCE_LOG) << "Exception found with ID" << incidence->instanceIdentifier();
                exceptions << incidence;
            } else {
                mainIncidence = incidence;
            }
        }

        if (!mainIncidence) {
163
164
165
166
            // Some broken events have only one incidence, with a recurrence ID - like a detached exception.
            // Rather than skipping those, make them appear: pick first incidence as the main one
            mainIncidence = incidences.at(0);
            exceptions.removeFirst();
Sandro Knauß's avatar
Sandro Knauß committed
167
168
        }

169
        for (const IncidencePtr &exception : std::as_const(exceptions)) {
170
            if (exception->status() == KCalendarCore::Incidence::StatusCanceled) {
Laurent Montel's avatar
Laurent Montel committed
171
                const QDateTime exDateTime(exception->recurrenceId());
Sandro Knauß's avatar
Sandro Knauß committed
172
173
174
175
176
                mainIncidence->recurrence()->addExDateTime(exDateTime);
            } else {
                // The exception remote id will contain a fragment pointing to
                // its instance identifier to distinguish it from the main
                // event.
Laurent Montel's avatar
Laurent Montel committed
177
                const QString rid = target.remoteId() + QLatin1String("#") + exception->instanceIdentifier();
Sandro Knauß's avatar
Sandro Knauß committed
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
                qCDebug(DAVRESOURCE_LOG) << "Extra incidence at" << rid;
                Akonadi::Item extraItem = target;
                extraItem.setRemoteId(rid);
                extraItem.setRemoteRevision(source.etag());
                extraItem.setMimeType(exception->mimeType());
                extraItem.setPayload<IncidencePtr>(exception);
                extraItems << extraItem;
            }
        }

        target.setPayload<IncidencePtr>(mainIncidence);
        // fix mime type for CalDAV collections
        target.setMimeType(mainIncidence->mimeType());

        /*
        foreach ( const IncidencePtr &incidence, incidences ) {
          QString rid = item.remoteId() + QLatin1String( "#" ) + incidence->instanceIdentifier();
          Akonadi::Item extraItem = item;
          extraItem.setRemoteId( rid );
          extraItem.setRemoteRevision( davItem.etag() );
          extraItem.setMimeType( incidence->mimeType() );
          extraItem.setPayload<IncidencePtr>( incidence );
          items << extraItem;
        }
        */
    }

    return true;
}