9

How to receive iCalendar (.ics or .ical) meeting invitations from outlook.office365.com, so that they can be parsed and added to a calendar (such as remind + wyrd)?

I've searched long and hard for an answer, but haven't found any, so I wonder if I'm just missing something really trivial.

I am using Mutt 1.5.22 with OfflineIMAP 6.5.5 on Fedora 20 (Heisenbug).

There are many scripts out there for parsing iCalendar messages into formats that can be imported into calendars. This is not my problem. My problem is that I'm not even receiving iCalendar messages, so I have nothing to parse.

A meeting invitation arrives in my inbox as a base64 encoded text/html message. It is not a multipart message and there are no attachments. The body of the message contains a link to Microsoft Outlook Web Access (OWA). Following the link doesn't seem to do anything except take me to WebMail. The rest of the email body contains the description of the meeting invitation.

I tried forwarding the invite and forwarding the invite as an attachment, but neither of these affected the format of the message.

I inspected the headers of the message, but nothing stood out as being important. I've copied them here, in case they mean something to anyone:

Received: from [...] by [...] with Microsoft SMTP Server (TLS) id
        [...] via Mailbox Transport; [timestamp]
Received: from [...] by [...] with Microsoft SMTP Server (TLS) id
        [...]; [timestamp]
Received: from [...] by [...] with Microsoft SMTP Server (TLS) id
        [...]; [timestamp]
Received: from [...] by [...] with mapi id [...]; [timestamp]
From: [meeting organiser]
To: [meeting attendees]
Subject: [meeting subject]
Thread-Topic: [meeting subject]
Thread-Index: [...]
Sender: [sender on behalf of meeting organiser]
Date: [timestamp]
Message-ID: <[...]>
Accept-Language: en-US
Content-Language: en-US
X-MS-Exchange-Organization-AuthAs: Internal
X-MS-Exchange-Organization-AuthMechanism: 03
X-MS-Exchange-Organization-AuthSource: [...]
X-MS-Has-Attach:
X-MS-Exchange-Organization-SCL: -1
X-MS-TNEF-Correlator:
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: base64
MIME-Version: 1.0

I also inspected the headers of the message in WebMail. There were more headers (some related to tnef), but again, nothing seemed related to calendars or invitations.

I compared the headers of a "meeting invite" with the headers of a normal email. The only difference was that a normal email contains an extra header: "X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply".

My .offlineimaprc's folderfilter is set to not sync the Calendar folder. This is because every time OfflineIMAP tried to sync, it would run into over a hundred instances of the same error when syncing the Calendar folder: "ERROR: IMAP server 'remote' does not have a message with UID '[...]'."

Using Python's imaplib to inspect the Calendar folder:

>>> import imaplib
>>> i = imaplib.IMAP4_SSL("outlook.office365.com", 993)
>>> i.login("NAME@COMPANY.com", "PASSWORD")
('OK', ['LOGIN completed.'])
>>> i.select("Calendar")
('OK', ['159'])
>>> i.fetch(159, "(RFC822)")
('OK', [None])
>>> i.fetch(159, "(RFC822)")

The second time I call fetch, it returns a message: "The server couldn't retrieve the following message. The message hasn't been deleted. You might be able to view it using either Outlook or Outlook Web App. You can also contact the sender to find out what the message says."

In WebMail, I see that the Calendar folder (accessible via the Calendar tab) has permissions for visibility outside organization. It's currently set to "Not shared". Other options are "Availability only", "Limited details", or "Full details". Setting the permissions to "Full details" and then sending a meeting invitation to myself didn't have any effect on the format of the meeting invite.

As a workaround for not receiving iCalendar message, I have Thunderbird open with the Lightning add-on. Somehow, the Lightning add-on knows how to receive meeting invitations. The messages still show up as text/html, but there's a link in Thunderbird's status bar. Clicking it opens a prompt asking whether to deny or accept the invitation.

Anyone have any ideas why meeting invitations from Outlook 365 are arriving as simple text/html messages rather than iCalendar messages? Is there anything I can do? If the Lightning add-on can work with the invitations, then there must be a solution, such as using Microsoft Exchange Web Services (EWS). If the solution will require me to code up a tool, so be it. A push in the right direction would be greatly appreciated.

3

You can set it so Office 365/Outlook sends invitations formatted in iCalendar format via the web interface.

  1. Login to your Office 365 account
  2. Open Mail App (Outlook)
  3. Hit the gear icon in upper right corner (settings)
  4. In the search box type "imap"
  5. Select "Pop and IMAP"
  6. At bottom select "Send event invitations in iCalendar format" for POP and/or IMAP.
  7. Hit "Save"

You should now get all invitations in the iCalendar format.

  • I got tired of trying to work with email attachments to wrote my own outlook calendar to remind exporting program. It calls the Office-365 API directly and can output various things including remind formatted entries. Named it ihol (I Hate OutLook) .. github.com/eikenb/ihol – John Eikenberry Jul 31 '18 at 20:15
1

The link that SEoF provided was a great suggestion. The blog post is incomplete, in my opinion, but it did get me started on the right track. I did some more digging and came up with a complete list of steps for getting Office 365 to send me meeting invites in iCalendar (.ics) format.

Note that I'm not an administrator and I didn't need to contact anyone to give me any privileges. The only caveat is that you need access to a Windows box. Once you change the settings via Windows, you can go back to using whatever OS you were using before. I did the following on my own as a normal user using Windows 7:

  1. Download and install Microsoft .NET Framework 4.5 from http://www.microsoft.com/en-us/download/details.aspx?id=30653.
  2. Download and install Windows Management Framework 4.0 (Windows6.1-KB2819745-x64-MultiPkg.msu) from http://www.microsoft.com/en-us/download/details.aspx?id=40855.
  3. Run PowerShell as an administrator.
  4. Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
  5. $UserCredential = Get-Credential
    • Enter your email address and password.
  6. $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection
  7. $ImportResults = Import-PSSession $Session
  8. Get-CASMailbox -identity YOUR@EMAIL.ADDRESS | Format-List
  9. Set-CASMailbox -identity YOUR@EMAIL.ADDRESS -PopUseProtocolDefaults:$FALSE -ImapUseProtocolDefaults:$FALSE -PopForceICalForCalendarRetrievalOption:$TRUE -ImapForceICalForCalendarRetrievalOption:$TRUE
  10. Remove-PSSession $Session
  11. Set-ExecutionPolicy -ExecutionPolicy Restricted

Now, my mutt on Linux is retrieving meeting invitations in iCalendar (.ics) format.

  • This worked perfectly. Thank you for the detailed instructions. – Jason Day Oct 23 '14 at 13:52
0

Not exactly what OP asked, but if 3rd party logic is allowed, then check out fantastic exchangelib (python). Quick py3 example of fetching events for the next 3 months (note that recurring items have been extended):

import arrow  # not required, but highly recommended lib for time processing
import base64
from tzlocal import get_localzone
from subprocess import call

from exchangelib import DELEGATE, Account, Credentials, EWSDateTime

CREDENTIALS = Credentials(username='mymail@company.com', password='topsecret')
ACCOUNT = Account(primary_smtp_address='mymail@company.com', credentials=CREDENTIALS,
                autodiscover=True, access_type=DELEGATE)

def sanitize(p):
    '''some .ical files didn't have proper end:vcalendar closure'''
    end = 'END:VCALENDAR\n'
    s = base64.b64decode(p).decode().replace("\r\n", "\n")
    if not s.endswith(end):
        s += end

    return s

tz = get_localzone()
now = arrow.now()
upper_limit = now.replace(months=+3)
items = ACCOUNT.calendar.view(
    start=tz.localize(EWSDateTime(now.year, now.month, now.day)),
    end=tz.localize(EWSDateTime(upper_limit.year, upper_limit.month, upper_limit.day)),
)

cals = []  # list of .ical files' contents
for item in items:
    cals.append(sanitize(item.mime_content))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.