Skip to content

Commit 231eeda

Browse files
grammmikejengelh
authored andcommitted
oxcical: implement support for VTODO and VJOURNAL
* Added new oxcical_import_todo and oxcical_import_journal routines to parse VTODO and VJOURNAL components, mapping key fields * Extended calendar classification to recognize VTODO and VJOURNAL, enabling non-VEVENT items to be grouped by UID for conversion * Updated the iCal import workflow to dispatch VTODO/VJOURNAL components to the new handlers and set appropriate message classes * Restricted task-specific export processing to IPM.Task messages so VJOURNAL items are serialized without unintended task metadata References: GXL-22, DESK-3412
1 parent 9aba54c commit 231eeda

File tree

1 file changed

+175
-7
lines changed

1 file changed

+175
-7
lines changed

lib/mapi/oxcical.cpp

Lines changed: 175 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2484,6 +2484,136 @@ static bool oxcical_import_events(const char *str_zone, uint16_t calendartype,
24842484
return true;
24852485
}
24862486

2487+
#define E_2201 "E-2201: get_propids failed for an unspecified reason"
2488+
2489+
static const char *oxcical_import_todo(const ical &pical,
2490+
const ical_component &comp, EXT_BUFFER_ALLOC alloc,
2491+
GET_PROPIDS get_propids, MESSAGE_CONTENT *pmsg)
2492+
{
2493+
static constexpr uint8_t le_true = 1;
2494+
namemap phash;
2495+
uint16_t last_propid = 0x8000;
2496+
if (!oxcical_parse_categories(comp, phash, &last_propid, pmsg))
2497+
return "E-2191: oxcical_parse_categories returned an unspecified error";
2498+
if (!oxcical_parse_class(comp, pmsg))
2499+
return "E-2192: oxcical_parse_class returned an unspecified error";
2500+
if (!oxcical_parse_body(comp, "", pmsg))
2501+
return "E-2705: oxcical_parse_body returned an unspecified error";
2502+
if (!oxcical_parse_html(comp, pmsg))
2503+
return "E-2193: oxcical_parse_html returned an unspecified error";
2504+
if (!oxcical_parse_dtstamp(comp, "", phash, &last_propid, pmsg))
2505+
return "E-2194: oxcical_parse_dtstamp returned an unspecified error";
2506+
if (!oxcical_parse_summary(comp, pmsg, alloc, nullptr, nullptr))
2507+
return "E-2706: oxcical_parse_summary returned an unspecified error";
2508+
2509+
const PROPERTY_NAME namequeries[] = {
2510+
{MNID_ID, PSETID_Task, PidLidTaskStatus},
2511+
{MNID_ID, PSETID_Task, PidLidPercentComplete},
2512+
{MNID_ID, PSETID_Task, PidLidTaskStartDate},
2513+
{MNID_ID, PSETID_Task, PidLidTaskDueDate},
2514+
{MNID_ID, PSETID_Task, PidLidTaskDateCompleted},
2515+
{MNID_ID, PSETID_Task, PidLidTaskComplete},
2516+
};
2517+
enum { l_status = 0, l_pct, l_start, l_due, l_completed, l_completeflag };
2518+
static_assert(l_completeflag + 1 == std::size(namequeries));
2519+
const PROPNAME_ARRAY propnames = {std::size(namequeries), deconst(namequeries)};
2520+
PROPID_ARRAY propids;
2521+
if (!get_propids(&propnames, &propids) || propids.size() != propnames.size())
2522+
return E_2201;
2523+
2524+
auto line = comp.get_line("STATUS");
2525+
if (line != nullptr) {
2526+
auto val = line->get_first_subvalue();
2527+
if (val != nullptr) {
2528+
uint32_t v;
2529+
if (strcasecmp(val, "NEEDS-ACTION") == 0)
2530+
v = tsvNotStarted;
2531+
else if (strcasecmp(val, "COMPLETED") == 0)
2532+
v = tsvComplete;
2533+
else if (strcasecmp(val, "IN-PROGRESS") == 0)
2534+
v = tsvInProgress;
2535+
else if (strcasecmp(val, "CANCELLED") == 0)
2536+
v = tsvDeferred;
2537+
else
2538+
v = tsvNotStarted;
2539+
pmsg->proplist.set(PROP_TAG(PT_LONG, propids[l_status]), &v);
2540+
if (v == tsvComplete)
2541+
pmsg->proplist.set(PROP_TAG(PT_BOOLEAN, propids[l_completeflag]), &le_true);
2542+
}
2543+
}
2544+
2545+
line = comp.get_line("PERCENT-COMPLETE");
2546+
if (line != nullptr) {
2547+
auto val = line->get_first_subvalue();
2548+
if (val != nullptr) {
2549+
double d = strtod(val, nullptr) / 100.0;
2550+
pmsg->proplist.set(PROP_TAG(PT_DOUBLE, propids[l_pct]), &d);
2551+
}
2552+
}
2553+
2554+
line = comp.get_line("DTSTART");
2555+
if (line != nullptr) {
2556+
auto tzid = line->get_first_paramval("TZID");
2557+
const ical_component *tzc = tzid != nullptr ? oxcical_find_vtimezone(pical, tzid) : nullptr;
2558+
ical_time itime{};
2559+
time_t utctime;
2560+
if (oxcical_parse_dtvalue(tzc, *line, &itime, &utctime)) {
2561+
auto ntt = rop_util_unix_to_nttime(utctime);
2562+
pmsg->proplist.set(PROP_TAG(PT_SYSTIME, propids[l_start]), &ntt);
2563+
}
2564+
}
2565+
2566+
line = comp.get_line("DUE");
2567+
if (line != nullptr) {
2568+
auto tzid = line->get_first_paramval("TZID");
2569+
const ical_component *tzc = tzid != nullptr ? oxcical_find_vtimezone(pical, tzid) : nullptr;
2570+
ical_time itime{};
2571+
time_t utctime;
2572+
if (oxcical_parse_dtvalue(tzc, *line, &itime, &utctime)) {
2573+
auto ntt = rop_util_unix_to_nttime(utctime);
2574+
pmsg->proplist.set(PROP_TAG(PT_SYSTIME, propids[l_due]), &ntt);
2575+
}
2576+
}
2577+
2578+
line = comp.get_line("COMPLETED");
2579+
if (line != nullptr) {
2580+
auto tzid = line->get_first_paramval("TZID");
2581+
const ical_component *tzc = tzid != nullptr ? oxcical_find_vtimezone(pical, tzid) : nullptr;
2582+
ical_time itime{};
2583+
time_t utctime;
2584+
if (oxcical_parse_dtvalue(tzc, *line, &itime, &utctime)) {
2585+
auto ntt = rop_util_unix_to_nttime(utctime);
2586+
pmsg->proplist.set(PROP_TAG(PT_SYSTIME, propids[l_completed]), &ntt);
2587+
pmsg->proplist.set(PROP_TAG(PT_BOOLEAN, propids[l_completeflag]), &le_true);
2588+
}
2589+
}
2590+
2591+
ical_time itime{};
2592+
oxcical_parse_uid(comp, itime, alloc, phash, &last_propid, pmsg);
2593+
return nullptr;
2594+
}
2595+
2596+
static const char *oxcical_import_journal(const ical &pical,
2597+
const ical_component &comp, EXT_BUFFER_ALLOC alloc,
2598+
MESSAGE_CONTENT *pmsg)
2599+
{
2600+
namemap phash;
2601+
uint16_t last_propid = 0x8000;
2602+
if (!oxcical_parse_categories(comp, phash, &last_propid, pmsg))
2603+
return "E-2191: oxcical_parse_categories returned an unspecified error";
2604+
if (!oxcical_parse_class(comp, pmsg))
2605+
return "E-2192: oxcical_parse_class returned an unspecified error";
2606+
if (!oxcical_parse_body(comp, "", pmsg))
2607+
return "E-2705: oxcical_parse_body returned an unspecified error";
2608+
if (!oxcical_parse_html(comp, pmsg))
2609+
return "E-2193: oxcical_parse_html returned an unspecified error";
2610+
if (!oxcical_parse_dtstamp(comp, "", phash, &last_propid, pmsg))
2611+
return "E-2194: oxcical_parse_dtstamp returned an unspecified error";
2612+
if (!oxcical_parse_summary(comp, pmsg, alloc, nullptr, nullptr))
2613+
return "E-2706: oxcical_parse_summary returned an unspecified error";
2614+
return nullptr;
2615+
}
2616+
24872617
/**
24882618
* Build a by-UID lookup map for @pical.
24892619
*
@@ -2495,7 +2625,9 @@ static bool oxcical_classify_calendar(const ical &pical, uidxevent_list_t &ul) t
24952625
{
24962626
for (const auto &comp : pical.component_list) {
24972627
auto pcomponent = ∁
2498-
if (strcasecmp(pcomponent->m_name.c_str(), "VEVENT") != 0)
2628+
if (strcasecmp(pcomponent->m_name.c_str(), "VEVENT") != 0 &&
2629+
strcasecmp(pcomponent->m_name.c_str(), "VTODO") != 0 &&
2630+
strcasecmp(pcomponent->m_name.c_str(), "VJOURNAL") != 0)
24992631
continue;
25002632
auto piline = pcomponent->get_line("UID");
25012633
auto puid = piline != nullptr ? piline->get_first_subvalue() : nullptr;
@@ -2580,6 +2712,40 @@ ec_error_t oxcical_import_multi(const char *str_zone, const ical &pical,
25802712
mlog(LV_ERR, "E-2412: iCal import data contained no VEVENTs with UIDs");
25812713
return ecNotFound;
25822714
}
2715+
auto first_comp = uid_list.begin()->second.front();
2716+
if (strcasecmp(first_comp->m_name.c_str(), "VTODO") == 0) {
2717+
message_ptr msg(message_content_init());
2718+
if (msg == nullptr)
2719+
return ecMAPIOOM;
2720+
msgvec.push_back(std::move(msg));
2721+
auto pmsg = msgvec.back().get();
2722+
if (pmsg->proplist.set(PR_MESSAGE_CLASS, "IPM.Task") != ecSuccess)
2723+
return ecError;
2724+
auto err = oxcical_import_todo(pical, *first_comp, alloc,
2725+
get_propids, pmsg);
2726+
if (err != nullptr) {
2727+
mlog(LV_ERR, "%s", err);
2728+
return ecError;
2729+
}
2730+
finalvec.insert(finalvec.end(), std::make_move_iterator(msgvec.begin()), std::make_move_iterator(msgvec.end()));
2731+
return ecSuccess;
2732+
} else if (strcasecmp(first_comp->m_name.c_str(), "VJOURNAL") == 0) {
2733+
message_ptr msg(message_content_init());
2734+
if (msg == nullptr)
2735+
return ecMAPIOOM;
2736+
msgvec.push_back(std::move(msg));
2737+
auto pmsg = msgvec.back().get();
2738+
if (pmsg->proplist.set(PR_MESSAGE_CLASS, "IPM.Activity") != ecSuccess)
2739+
return ecError;
2740+
auto err = oxcical_import_journal(pical, *first_comp, alloc,
2741+
pmsg);
2742+
if (err != nullptr) {
2743+
mlog(LV_ERR, "%s", err);
2744+
return ecError;
2745+
}
2746+
finalvec.insert(finalvec.end(), std::make_move_iterator(msgvec.begin()), std::make_move_iterator(msgvec.end()));
2747+
return ecSuccess;
2748+
}
25832749
piline = pical.get_line("METHOD");
25842750
if (piline == nullptr) {
25852751
if (!oxcical_import_events(str_zone, calendartype,
@@ -3192,8 +3358,6 @@ static void oxcical_export_organizer(const MESSAGE_CONTENT &msg,
31923358
line->append_param("CN", str);
31933359
}
31943360

3195-
#define E_2201 "E-2201: get_propids failed for an unspecified reason"
3196-
31973361
static const char *oxcical_export_uid(const MESSAGE_CONTENT &msg,
31983362
ical_component &com, EXT_BUFFER_ALLOC alloc, GET_PROPIDS get_propids)
31993363
{
@@ -3508,6 +3672,7 @@ static std::string oxcical_export_internal(const char *method, const char *tzid,
35083672
auto icaltype = "VEVENT";
35093673
const char *partstat = nullptr;
35103674
bool b_proposal = false, b_exceptional = true, b_recurrence = false;
3675+
bool is_task = false;
35113676
if (method == nullptr) {
35123677
b_exceptional = false;
35133678
if (class_match_prefix(str, "IPM.Appointment") == 0) {
@@ -3537,6 +3702,7 @@ static std::string oxcical_export_internal(const char *method, const char *tzid,
35373702
method = "";
35383703
icaltype = nullptr;
35393704
pical.m_name = "VTODO";
3705+
is_task = true;
35403706
} else if (class_match_prefix(str, "IPM.Activity") == 0) {
35413707
method = "";
35423708
icaltype = nullptr;
@@ -3762,10 +3928,12 @@ static std::string oxcical_export_internal(const char *method, const char *tzid,
37623928
ptz_component != nullptr ? tzid : nullptr);
37633929
}
37643930

3765-
err = oxcical_export_task(*pmsg, *pcomponent, ptz_component,
3766-
tzid, get_propids);
3767-
if (err != nullptr)
3768-
return err;
3931+
if (is_task) {
3932+
err = oxcical_export_task(*pmsg, *pcomponent, ptz_component,
3933+
tzid, get_propids);
3934+
if (err != nullptr)
3935+
return err;
3936+
}
37693937

37703938
auto sa = pmsg->proplist.get<const STRING_ARRAY>(PROP_TAG(PT_MV_UNICODE, propids[l_keywords]));
37713939
if (sa != nullptr) {

0 commit comments

Comments
 (0)