Skip to content

Commit 0b2be33

Browse files
committed
Merge GXL 'refs/merge-requests/237/head'
References: GXL-573, DESK-3752, DESK-3834
2 parents edbba2c + 3f12d52 commit 0b2be33

File tree

3 files changed

+145
-0
lines changed

3 files changed

+145
-0
lines changed

exch/ews/context.cpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,142 @@ sItem EWSContext::create(const std::string& dir, const sFolderSpec& parent, cons
511511
return loadItem(dir, parent.folderId, *messageId, retshape);
512512
}
513513

514+
/**
515+
* @brief Find calendar item using goid and clean goid
516+
*
517+
* @param calendarFolder The calendar folder
518+
* @param calendarDir Store directory
519+
* @param content Item content
520+
*
521+
* @return PidTagMid value of item matching goid or clean goid
522+
*/
523+
std::optional<uint64_t> EWSContext::findExistingByGoid(const sFolderSpec& calendarFolder, const std::string& calendarDir,
524+
const MESSAGE_CONTENT& content) const
525+
{
526+
std::array<RESTRICTION_PROPERTY, 2> propertyRestrictions{};
527+
std::array<RESTRICTION, 2> childRestrictions{};
528+
size_t restrictionCount = 0;
529+
530+
auto addRestriction = [&](const BINARY* value, uint16_t propId) {
531+
if (value == nullptr || value->cb == 0 || value->pb == nullptr || propId == 0 ||
532+
restrictionCount >= propertyRestrictions.size())
533+
return;
534+
uint32_t tag = PROP_TAG(PT_BINARY, propId);
535+
auto &propRestriction = propertyRestrictions[restrictionCount];
536+
propRestriction.relop = RELOP_EQ;
537+
propRestriction.proptag = tag;
538+
propRestriction.propval.proptag = tag;
539+
propRestriction.propval.pvalue = deconst(value);
540+
541+
childRestrictions[restrictionCount].rt = RES_PROPERTY;
542+
childRestrictions[restrictionCount].prop = &propRestriction;
543+
++restrictionCount;
544+
};
545+
546+
uint16_t pidGlobalId = getNamedPropId(calendarDir, NtGlobalObjectId, true);
547+
uint16_t pidCleanGlobalId = getNamedPropId(calendarDir, NtCleanGlobalObjectId, true);
548+
const BINARY* goid = content.proplist.get<const BINARY>(PROP_TAG(PT_BINARY, pidGlobalId));
549+
const BINARY* cleanGoid = content.proplist.get<const BINARY>(PROP_TAG(PT_BINARY, pidCleanGlobalId));
550+
addRestriction(goid, pidGlobalId);
551+
addRestriction(cleanGoid, pidCleanGlobalId);
552+
553+
if (restrictionCount == 0)
554+
return std::nullopt;
555+
556+
RESTRICTION restriction{};
557+
RESTRICTION_AND_OR orGroup{};
558+
if (restrictionCount == 1) {
559+
restriction.rt = RES_PROPERTY;
560+
restriction.prop = &propertyRestrictions[0];
561+
} else {
562+
orGroup.count = static_cast<uint32_t>(restrictionCount);
563+
orGroup.pres = childRestrictions.data();
564+
restriction.rt = RES_OR;
565+
restriction.andor = &orGroup;
566+
}
567+
568+
uint32_t tableId = 0, rowCount = 0;
569+
const char *calUser = effectiveUser(calendarFolder);
570+
if (!m_plugin.exmdb.load_content_table(calendarDir.c_str(), CP_ACP,
571+
calendarFolder.folderId, calUser, TABLE_FLAG_NONOTIFICATIONS,
572+
&restriction, nullptr, &tableId, &rowCount))
573+
throw EWSError::ItemPropertyRequestFailed(E3245);
574+
auto unloadTable = HX::make_scope_exit([&, tableId]{
575+
m_plugin.exmdb.unload_table(calendarDir.c_str(), tableId);
576+
});
577+
if (rowCount == 0)
578+
return std::nullopt;
579+
580+
const uint32_t midTagValue = PidTagMid;
581+
PROPTAG_ARRAY proptags{1, deconst(&midTagValue)};
582+
TARRAY_SET rows{};
583+
if (!m_plugin.exmdb.query_table(calendarDir.c_str(), calUser, CP_ACP,
584+
tableId, &proptags, 0, 1, &rows))
585+
throw EWSError::ItemCorrupt(E3284);
586+
if (rows.count == 0 || rows.pparray[0] == nullptr)
587+
return std::nullopt;
588+
auto mid = rows.pparray[0]->get<const uint64_t>(PidTagMid);
589+
if (!mid)
590+
return std::nullopt;
591+
return std::optional<uint64_t>(*mid);
592+
}
593+
594+
/**
595+
* @brief Create a calendar item after accepting a meeting request
596+
*
597+
* @param refId Item id
598+
* @param response Response type
599+
*/
600+
void EWSContext::createCalendarItemFromMeetingRequest(const tItemId &refId, uint32_t response) const
601+
{
602+
// Duplicate the original request and persist it as a calendar appointment.
603+
assertIdType(refId.type, tItemId::ID_ITEM);
604+
sMessageEntryId requestId(refId.Id.data(), refId.Id.size());
605+
sFolderSpec requestFolder = resolveFolder(requestId);
606+
std::string dir = getDir(requestFolder);
607+
validate(dir, requestId);
608+
const char *username = effectiveUser(requestFolder);
609+
610+
MESSAGE_CONTENT *content = nullptr;
611+
if (!m_plugin.exmdb.read_message(dir.c_str(), username, CP_ACP, requestId.messageId(), &content) ||
612+
content == nullptr)
613+
throw EWSError::ItemNotFound(E3143);
614+
615+
MCONT_PTR calendarItem(content->dup());
616+
if (!calendarItem)
617+
throw EWSError::ItemSave(E3254);
618+
619+
auto &props = calendarItem->proplist;
620+
// Remove PidTagMid and PidTagChangeNumber, otherwise the calendar item won't be
621+
// created / updated
622+
static constexpr uint32_t rmProps[] = {PidTagMid, PidTagChangeNumber};
623+
for (uint32_t tag : rmProps)
624+
props.erase(tag);
625+
626+
sFolderSpec calendarFolder = requestFolder;
627+
calendarFolder.folderId = rop_util_make_eid_ex(1, PRIVATE_FID_CALENDAR);
628+
calendarFolder.location = sFolderSpec::PRIVATE;
629+
if (!calendarFolder.target)
630+
calendarFolder.target = m_auth_info.username;
631+
std::string calendarDir = getDir(calendarFolder);
632+
633+
if (!(permissions(calendarDir, calendarFolder.folderId) & (frightsOwner | frightsCreate)))
634+
throw EWSError::AccessDenied(E3130);
635+
636+
if (props.set(PR_MESSAGE_CLASS, "IPM.Appointment") != ecSuccess)
637+
throw EWSError::ItemSave(E3254);
638+
639+
std::optional<uint64_t> existingMid = findExistingByGoid(calendarFolder, calendarDir, *content);
640+
if (existingMid && props.set(PidTagMid, construct<uint64_t>(*existingMid)) != ecSuccess)
641+
throw EWSError::ItemSave(E3254);
642+
643+
ec_error_t err = ecSuccess;
644+
uint64_t newMid = 0, newCn = 0;
645+
if (!m_plugin.exmdb.write_message(calendarDir.c_str(), CP_ACP, calendarFolder.folderId,
646+
calendarItem.get(), {}, &newMid, &newCn, &err) || err != ecSuccess)
647+
throw EWSError::ItemSave(E3254);
648+
}
649+
514650
/**
515651
* @brief Schedule notification stream for closing
516652
*/

exch/ews/ews.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ class EWSContext {
224224

225225
Structures::sFolder create(const std::string&, const Structures::sFolderSpec&, const Structures::sFolder&) const;
226226
Structures::sItem create(const std::string&, const Structures::sFolderSpec&, const MESSAGE_CONTENT&) const;
227+
void createCalendarItemFromMeetingRequest(const Structures::tItemId&, uint32_t) const;
227228
void disableEventStream();
228229
const char* effectiveUser(const Structures::sFolderSpec&) const;
229230
void enableEventStream(int);
@@ -338,6 +339,7 @@ class EWSContext {
338339
void toContent(const std::string &, Structures::tAcceptItem &, Structures::sShape &, MCONT_PTR &) const;
339340
void toContent(const std::string &, Structures::tTentativelyAcceptItem &, Structures::sShape &, MCONT_PTR &) const;
340341
void toContent(const std::string &, Structures::tDeclineItem &, Structures::sShape &, MCONT_PTR &) const;
342+
std::optional<uint64_t> findExistingByGoid(const Structures::sFolderSpec&, const std::string&, const MESSAGE_CONTENT&) const;
341343

342344
inline void updateProps(Structures::tItem&, Structures::sShape&, const TPROPVAL_ARRAY&) const {}
343345
void updateProps(Structures::tCalendarItem&, Structures::sShape&, const TPROPVAL_ARRAY&) const;

exch/ews/requests.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,19 +433,26 @@ void process(mCreateItemRequest&& request, XMLElement* response, const EWSContex
433433
auto rstat = EWSContext::construct<uint32_t>(resp);
434434
uint32_t state = asfMeeting | asfReceived;
435435
auto astat = EWSContext::construct<uint32_t>(state);
436+
uint32_t busyValue = resp == respAccepted ? olBusy :
437+
resp == respTentative ? olTentative : olFree;
438+
auto bstat = EWSContext::construct<uint32_t>(busyValue);
436439
uint16_t pidResp = ctx.getNamedPropId(rdir, NtResponseStatus, true);
437440
uint16_t pidReply = ctx.getNamedPropId(rdir, NtAppointmentReplyTime, true);
438441
uint16_t pidState = ctx.getNamedPropId(rdir, NtAppointmentStateFlags, true);
442+
uint16_t pidBusy = ctx.getNamedPropId(rdir, NtBusyStatus, true);
439443
TAGGED_PROPVAL props[] = {
440444
{PROP_TAG(PT_LONG, pidResp), rstat},
441445
{PROP_TAG(PT_SYSTIME, pidReply), now},
442446
{PROP_TAG(PT_LONG, pidState), astat},
447+
{PROP_TAG(PT_LONG, pidBusy), bstat},
443448
};
444449
TPROPVAL_ARRAY proplist{std::size(props), props};
445450
PROBLEM_ARRAY problems;
446451
if (!ctx.plugin().exmdb.set_message_properties(rdir.c_str(), username, CP_ACP,
447452
mid.messageId(), &proplist, &problems))
448453
throw EWSError::ItemSave(E3092);
454+
if (resp == respAccepted || resp == respTentative)
455+
ctx.createCalendarItemFromMeetingRequest(refId, resp);
449456
};
450457
if (auto acc = std::get_if<tAcceptItem>(&item)) {
451458
if (acc->ReferenceItemId)

0 commit comments

Comments
 (0)