icaldocumentprocessor.cpp 3.11 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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
84
85
/*
    SPDX-FileCopyrightText: 2020-2021 Volker Krause <vkrause@kde.org>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/

#include "config-kitinerary.h"
#include "icaldocumentprocessor.h"
#include "logging.h"

#include <KItinerary/ExtractorDocumentNodeFactory>
#include <KItinerary/ExtractorEngine>
#include <KItinerary/ExtractorFilter>
#include <KItinerary/ExtractorResult>

#ifdef HAVE_KCAL
#include <KCalendarCore/ICalFormat>
#include <KCalendarCore/MemoryCalendar>
#endif

#include <QJSEngine>
#include <QJSValue>
#include <QJsonArray>
#include <QJsonDocument>
#include <QMetaProperty>
#include <QTimeZone>

#include <cstring>

using namespace KItinerary;

static bool contentStartsWith(const QByteArray &data, const char *str)
{
    auto it = data.begin();
    while (it != data.end() && std::isspace(*it)) {
        ++it;
    }

    const auto len = std::strlen(str);
    if ((int)len >= std::distance(it, data.end())) {
        return false;
    }
    return std::strncmp(it, str, len) == 0;
}

bool IcalCalendarProcessor::canHandleData(const QByteArray &encodedData, QStringView fileName) const
{
    return contentStartsWith(encodedData, "BEGIN:VCALENDAR")
        || fileName.endsWith(QLatin1String(".ics"), Qt::CaseInsensitive)
        || fileName.endsWith(QLatin1String(".ical"), Qt::CaseInsensitive);
}

ExtractorDocumentNode IcalCalendarProcessor::createNodeFromData(const QByteArray &encodedData) const
{
#ifdef HAVE_KCAL
    KCalendarCore::Calendar::Ptr calendar(new KCalendarCore::MemoryCalendar(QTimeZone::systemTimeZone()));
    KCalendarCore::ICalFormat format;
    if (format.fromRawString(calendar, encodedData)) {
        calendar->setProductId(format.loadedProductId());
        ExtractorDocumentNode node;
        node.setContent(calendar);
        return node;
    } else {
        qCDebug(Log) << "Failed to parse iCal content.";
    }
#endif
    return {};
}

void IcalCalendarProcessor::expandNode(ExtractorDocumentNode &node, const ExtractorEngine *engine) const
{
#ifdef HAVE_KCAL
    const auto cal = node.content<KCalendarCore::Calendar::Ptr>();
    for (const auto &event : cal->events()) {
        auto child = engine->documentNodeFactory()->createNode(QVariant::fromValue(event), u"internal/event");
        node.appendChild(child);
    }
#endif
}


bool IcalEventProcessor::matches(const ExtractorFilter &filter, const ExtractorDocumentNode &node) const
{
#ifdef HAVE_KCAL
    const auto event = node.content<KCalCore::Event::Ptr>();
86
    return matchesGadget(filter, event.data());
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#else
    return false;
#endif
}

void IcalEventProcessor::preExtract(ExtractorDocumentNode &node, [[maybe_unused]] const ExtractorEngine *engine) const
{
#ifdef HAVE_KCAL
    const auto event = node.content<KCalCore::Event::Ptr>();
    const auto data = event->customProperty("KITINERARY", "RESERVATION");
    if (!data.isEmpty()) {
        node.addResult(QJsonDocument::fromJson(data.toUtf8()).array());
    }
#endif
}

QJSValue IcalEventProcessor::contentToScriptValue(const ExtractorDocumentNode &node, QJSEngine *engine) const
{
#ifdef HAVE_KCAL
    return engine->toScriptValue(*node.content<KCalendarCore::Event::Ptr>().data());
#else
    return {};
#endif
}