Cancelling an iCalEvent?

I’ve just added a feature to my app where, when the the user makes an appointment, an ICS file is emailed to both parties who are participating in the appointment. This enables their email clients to automatically recognize the appointment as a calendar event. The ICS file looks like this:

BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:adamgibbons/ics
METHOD:REQUEST
X-PUBLISHED-TTL:PT1H
BEGIN:VEVENT
UID:zi2yKuwWfcMAZ7Z_29vOb
SUMMARY:Appointment with your Nurse at myMeteorWebApp.com
DTSTAMP:20221206T143700Z
DTSTART:20221219T170500Z
SEQUENCE:1
DESCRIPTION:test appt 12/19 12:05pm
URL:https://www.myMeteorWebApp.com/
STATUS:TENTATIVE
ORGANIZER;CN=Joe Smith
CLASS:PRIVATE
ATTENDEE;RSVP=FALSE;CN=Joe Smith
ATTENDEE;RSVP=FALSE;CN=Jane Smith
DURATION:PT15M
END:VEVENT
END:VCALENDAR

I discovered the Meteor email param for iCalEvent, and it works great! The ICS file is delivered with the email, and my Mac Outlook email client automatically recognizes the calendar event.

Now, if the event is cancelled, I send this ICS file in another email. It has STATUS:CANCELLED:

BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:adamgibbons/ics
METHOD:REQUEST
X-PUBLISHED-TTL:PT1H
BEGIN:VEVENT
UID:6cdZAZ3nI_peB7CugyPvs
SUMMARY:Appointment with your Nurse at myMeteorWebApp.com
DTSTAMP:20221206T143700Z
DTSTART:20221219T170500Z
SEQUENCE:1
DESCRIPTION:test appt 12/19 12:05pm
URL:https://www.myMeteorWebApp.com/
STATUS:CANCELLED
ORGANIZER;CN=Joe Smith
CLASS:PRIVATE
ATTENDEE;RSVP=FALSE;CN=Joe Smith
ATTENDEE;RSVP=FALSE;CN=Jane Smith
DURATION:PT15M
END:VEVENT
END:VCALENDAR

My email client does not yet recognize the cancellation, and instead automatically adds it to the calendar as a new duplicate event.

According to this SO post, if an event is being cancelled, the ICS file must be attached to the email with a specific value in the email method parameter:

After much (MUCH) looking around for solve the problem, I finally realized that the problem was not within the ics file, but how the file was attached to the email. I added it with method:REQUEST. After changing that to method:CANCEL it works well in all clients.

contype = new System.Net.Mime.ContentType("text/calendar");
//contype.Parameters.Add("method", "REQUEST");
contype.Parameters.Add("method", "CANCEL");
contype.Parameters.Add("charSet", "utf-8");
contype.Parameters.Add("name", "invite.ics");

Is the Meteor email function doing this yet?

Update:

Continuing from my previous post in this thread… I don’t think it is.

I just traced code execution through to meteor://app/packages/email/email.js where I see:

const smtpSend = function(transport, mail) {
  transport._syncSendMail(mail);
};

…which calls packages/meteor.js:

Meteor.wrapAsync = function (fn, context) {
  return function (/* arguments */) {
    var self = context || this;
    var newArgs = Array.prototype.slice.call(arguments);
    var callback;

    for (var i = newArgs.length - 1; i >= 0; --i) {
      var arg = newArgs[i];
      var type = typeof arg;
      if (type !== "undefined") {
        if (type === "function") {
          callback = arg;
        }
        break;
      }
    }

    if (! callback) {
      if (Meteor.isClient) {
        callback = logErr;
      } else {
        var fut = new Future();
        callback = fut.resolver();
      }
      ++i; // Insert the callback just after arg.
    }

    newArgs[i] = Meteor.bindEnvironment(callback);
    var result = fn.apply(self, newArgs);
    return fut ? fut.wait() : result;
  };
};

I don’t see anything in here with special handling for the icalEventString option.

I’m in a little deep here – there’s code here like fut.resolver() which I guess is some sort of promise but I don’t know how it is normally used. :slight_smile:

Does anyone here who understands this stuff better, want to collaborate with me on a pull request to improve ICS cancellation?

"
mailComposer MailComposer

A MailComposer object representing the message to be sent. Overrides all other options. You can create a MailComposer object via new EmailInternals.NpmModules.mailcomposer.module. "

"

  • icalEvent - iCalendar event, same usage as with text and html. Event method attribute defaults to ‘PUBLISH’ or define it yourself: {method: 'REQUEST', content: iCalString} . This value is added as an additional alternative to html or text. Only utf-8 content is allowed
    "

I think you can overwrite your email object so you can pass the method in.

Thanks! I did as you recommended. The event still isn’t being cancelled in Outlook but it seems that Meteor is doing everything correctly as expected.

I see you can pass some headers too, maybe there is something missing there. Ok … I have no clue, just suppositions.

1 Like