#!/usr/bin/ruby require 'net/imap' class ExchangeCalendar def initialize(servername, user, password) @imap = Net::IMAP.new(servername) @imap.login(user, password) @imap.examine('Calendar') end def each_calendar @imap.search("ALL").each do |sequence_id| fetchdata = @imap.fetch(sequence_id, ["UID", "BODYSTRUCTURE"])[0] # Find the calendar part. uid, bodystructure = fetchdata.attr["UID"], fetchdata.attr["BODYSTRUCTURE"] catch :nocalendar do partno = part_number_for_calendar(bodystructure) yield @imap.uid_fetch(uid, "BODY.PEEK[#{partno}]")[0].attr["BODY[#{partno}]"] end end end def finish @imap.logout @imap.disconnect end protected def part_number_for_calendar(msg) if msg.media_type == "TEXT" and msg.subtype == "CALENDAR" return 1 elsif msg.multipart? i = 1 msg.parts.each do |part| if part.media_type == "TEXT" and part.subtype == "CALENDAR" return i end i += 1 end else throw :nocalendar end end end class IcalAccumulator def initialize() @events = [] @timezones = [] @tzids_seen = {} end def push(calendar) calendar = clean_timezone(calendar) calendar.scan(/^BEGIN:VEVENT.*?^END:VEVENT|^BEGIN:VTIMEZONE.*?^END:VTIMEZONE/m).each do |component| case component when /^BEGIN:VEVENT/ @events << component when /^BEGIN:VTIMEZONE/ component =~ /^TZID:.*/ if !@tzids_seen[$&] @tzids_seen[$&] = true @timezones << component end end end end def to_s cal = <