@@ -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 */
0 commit comments