Skip to content

Conversation

@SebastianKrupinski
Copy link
Contributor

@SebastianKrupinski SebastianKrupinski commented May 4, 2025

Summary

  • Added logic to handle system user email aliases
  • Added logic to handle email addresses in different cases (lower, upper, camel, etc)
  • Simplified handling logic

Testing

  • Requires external account for sending meeting invitation updates
  • Send an initial meeting invitation to a mail account configured on NC
  • Add the meeting to your calendar
  • Then send a meeting update from external account
  • Wait for mail app to sync so that the email is found
  • Trigger mail background imip service
  • Meeting should update with new information
    (Make sure "Restrict users to only share with users in their groups" (Administration Settings -> Sharing) is off) (There is a unrelated bug that will cause imip processing to fail)

Checklist

@codecov
Copy link

codecov bot commented May 8, 2025

❌ We are unable to process any of the uploaded JUnit XML files. Please ensure your files are in the right format.

Copy link
Member

@ChristophWurst ChristophWurst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High level feedback

Did not test.

*
* @deprecated 32.0.0 Use IHandleImip instead
*/
public function handleIMipMessage(string $name, string $calendarData): void;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method uses calendar data as string intentionally. It was chosen over a Sabre type to not leak Sabre into the public API.
Having Sabre in the public API means we might be forced to break our own API if Sabre changes.

With Symfony and Doctrine we have seen that. Our own layer of abstraction helps us to work around some of the 3rdparty changes. In rare cases API changes are necessary.

I will leave the decision with you.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your point but I don't think this is an issue.

I think the main entry point for iMip (and most other functions) should be the calendar manager (string), and the manager then finds the correct calendar and forwards the request to the calendar it self.

External App -> CalendarManager::handleImip(string) -> Calendar::handleImip(VCALENDAR)

Any external app does not need to know anything about calendar searching or the sabre interface, the only to components that do are the manager and the calendar it self. But if any app does want to work directly with the calendar then it would need to know how to work with Sabre.

I think the benefits of not serializing and de-serializing every time we exit and enter a function, out weight the possibility of having to change our API in the future. I could also wrap the VCALENDAR object in a DTO, but that might be over kill.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the point. The issue is that you make consumers of the Nextcloud OCP API also dependent on Sabre code.

This will be tricky in terms of tooling (Psalm, IDEs) and impose potential breakage should Sabre change.

I can't imagine the serialization to be a real world problem, as we are passing one event at a time. IMO the ics is still the most flexible and framework-agnostic type here.

@SebastianKrupinski
Copy link
Contributor Author

Design Consideration.

The current logic replicates the current imip handling behaviour, that only handles changes to existing events.

But taking in to consideration functionality from other apps, GH and client requests it might be a good idea to modify the CalendarManager::handleImip with a extra flag to create a new event if one does not already exist. aka Automatically adding tentative ivites

@ChristophWurst i'll leave this decision to you.

@ChristophWurst
Copy link
Member

But taking in to consideration functionality from other apps, GH and client requests it might be a good idea to modify the CalendarManager::handleImip with a extra flag to create a new event if one does not already exist. aka Automatically adding tentative ivites

Let's take baby steps. Fix the alias mismatch in this PR and we look into importing of tentative events in another one.

@kesselb
Copy link
Contributor

kesselb commented May 22, 2025

Thanks 👍

The change to loop over the attendees in handleImip also fixes #51115. Therefore, I have sunsetted #53046. I will check, when you are done here, if it makes sense to port some of the test cases, but most of them were relevant for creating the extra cancellation message, which is not required here.

@SebastianKrupinski
Copy link
Contributor Author

Okay, I've made the calendar interface take a string instead of a vObject.

Can we get this in, please. I want to use this for a new calendar feature instead of the old flow.

@ChristophWurst @kesselb

@SebastianKrupinski SebastianKrupinski marked this pull request as ready for review June 3, 2025 17:00
@SebastianKrupinski SebastianKrupinski requested review from a team, st3iny and tcitworld as code owners June 3, 2025 17:00
@SebastianKrupinski SebastianKrupinski requested review from artonge, come-nc and yemkareems and removed request for a team June 3, 2025 17:00
@SebastianKrupinski SebastianKrupinski added 3. to review Waiting for reviews and removed 2. developing Work in progress labels Jun 3, 2025
@ChristophWurst
Copy link
Member

Could you please explain why the old interface is insufficient and replaced by a new one with almost an identical signature, and how this is a requirement for #40891?

@come-nc come-nc removed their request for review June 5, 2025 15:32
@SebastianKrupinski
Copy link
Contributor Author

Could you please explain why the old interface is insufficient and replaced by a new one with almost an identical signature, and how this is a requirement for #40891?

I've for now removed the new interface so we can back port this for some fixes, but will need to (most likely) make a new interface for an up coming feature, i will need flags to create tentative events.

}

/** @var VEvent|null $vEvent */
$eventObject = $calendarObject->VEVENT;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the variable still makes sense because it helps Psalm and PHPStorm with the wiggly types of Sabre objects

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, we can make it play nice with phpstorm. 😆

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a problem with that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope

$this->assertFalse($result);
}

public function testHandleImipCancelOrganiserInReplyTo(): void {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did all of these test cases need to go?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because all the methods just forward to the same method handleIMipCancel() -> handleIMip(). testHandleImipCancelEventNotFound, testHandleImipReplyEventNotFound, testHandleImipRequestEventNotFound is the same test the only reason we had so many of them is because we had separate handling code for each imip type, now there is only one. Also some of the logic was moved to the CalendarImpl.

But I did notice that the test names got renamed in the rebase, so I fixed them.

Copy link
Member

@ChristophWurst ChristophWurst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Public API changes seem avoidable

string $recipient,
string $calendarData,
): bool {
$userId = substr($principalUri, 17);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Magic number. Consider replacing it with a strlen of a literal
  2. It's assuming a certain prefix, I guess. I'd really like to see a check here to catch when someone uses the method wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put in a check for the prefix. The magic number should not be an issue now.

Comment on lines +322 to +330
$userId = substr($principalUri, 17);
return $this->handleIMip($userId, $calendarData);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can see it's very well possible to keep the existing methods. Therefore it appears like we are changing public API within just one major version when there is no hard requirement for it.
This is not how we want to build our platform. Even if the three methods introduced in 31 could be simplified, we shouldn't change them just for that purpose.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the deprecation's. There was only one method adding in 31, the rest are 25.

try {
/** @var VCalendar $vObject|null */
$calendarObject = Reader::read($calendarData);
$vObject = Reader::read($message);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tip for future PRs and containing diff sizes: don't rename variables unless there is a good reason for it.


/**
* @since 31.0.0
* @since 32.0.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: don't use since annotations for private API. this gives a wrong sense of public API

@ChristophWurst
Copy link
Member

@st3iny could you please give this a review 🙏

Copy link
Member

@st3iny st3iny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code changes look mostly like merging all methods into a single one and adding some capitalization logic. Did some IMip testing.

Worked

  1. Regular IMip stuff -> no regressions
  2. Email addresses with inconsistent capitalization (see note below).

Didn't work

  1. Aliases. I sent an invite to the secondary email address of the local user and imported the event. However, any incoming iMip update emails were not propagated to my calendar. The call to $schedulingPlugin->getAddressesForPrincipal(...) returned the whole set, so the scheduling/iTip logic afterwards seems to be wrong.
image

The mail app will still complain about the capitalization. However, when I manually import the event, the iMip logic will continue to work from there one (despite the Mail app complaining about the capitalization).

Screenshot_20250827_175228

(Needs to be fixed there I guess.)

@SebastianKrupinski
Copy link
Contributor Author

@st3iny

Code changes look mostly like merging all methods into a single one and adding some capitalization logic.

  • Correct
1. Aliases. I sent an invite to the secondary email address of the local user and imported the event. However, any incoming iMip update emails were not propagated to my calendar. The call to `$schedulingPlugin->getAddressesForPrincipal(...)` returned the whole set, so the scheduling/iTip logic afterwards seems to be wrong.
  • Took a minute to remember what I was thinking when I made this in May. So the logic in this PR was correct, the logic in the ITipBroker was not (the 3rdparty code we use does not check for aliases), I was original going to fix the iTipBroker in a separate PR because I didn't want to refactor to much at once. BUT... I found another way, but swapping the alias email address for the actual address to make the 3rdparty code work as is (not my favorite solution but the best at the moment).

The mail app will still complain about the capitalization. However, when I manually import the event, the iMip logic will continue to work from there one (despite the Mail app complaining about the capitalization).

  • Correct the mail and calendar app will need more adjustments for both capitalization and aliases

Copy link
Member

@st3iny st3iny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aliases work now.

@SebastianKrupinski SebastianKrupinski merged commit 94c80aa into master Aug 28, 2025
210 of 212 checks passed
@SebastianKrupinski SebastianKrupinski deleted the fix/refactor-imip branch August 28, 2025 22:30
@github-project-automation github-project-automation bot moved this from 🏗️ In progress to ☑️ Done in 💌 📅 👥 Groupware team Aug 28, 2025
@SebastianKrupinski
Copy link
Contributor Author

@kesselb @kesselb

I was think of backporting this to at least 31. Any objections?

@kesselb
Copy link
Contributor

kesselb commented Aug 31, 2025

/backport to stable31

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3. to review Waiting for reviews

Projects

Development

Successfully merging this pull request may close these issues.

[Bug]: iMIP cancellation messages are not propagated to the personal calendar CalDAV: handle email aliases for users

5 participants