require 'net/http' require 'net/https' require 'uri' require "rexml/document" class Calendar attr_accessor :product_id, :version, :scale, :method, :events def add(event) # create events if it doesn't exist @events ||= [] @events.push event end def to_s data = "########## calendar ##########\n" data << 'version: ' + @version.to_s + "\n" data << 'scale: ' + @scale.to_s + "\n" data << 'method: ' + @method.to_s + "\n" data << 'number of events: ' + @events.size.to_s + "\n" @events.each do |event| data << event.to_s end return data end end class GCalendar attr_reader :title, :url def initialize(title, url) @title = title @url = url end end class Event attr_accessor :start_date, :end_date, :time_stamp, :class_name, :created, :last_modified, :status, :summary, :description, :location, :rrule def to_s data = "---------- event ----------\n" data << 'start_date: ' + @start_date.to_s + "\n" data << 'end_date: ' + @end_date.to_s + "\n" data << 'time_stamp: ' + @time_stamp.to_s + "\n" data << 'class_name: ' + @class_name.to_s + "\n" data << 'created: ' + @created.to_s + "\n" data << 'last_modified: ' + @last_modified.to_s + "\n" data << 'status: ' + @status.to_s + "\n" data << 'rrule: ' + @rrule.to_s + "\n" data << 'summary: ' + @summary.to_s + "\n" data << 'desription: ' + @desription.to_s + "\n" data << 'location: ' + @location.to_s + "\n" return data end # 'FREQ=WEEKLY;BYDAY=MO;WKST=MO' def rrule_as_hash array = @rrule.split(';') hash = Hash.new array.each do |item| pair = item.split('=') hash[pair[0]] = pair[1] end return hash end end # # Make it east to use some of the convenience methods using https # module Net class HTTPS < HTTP def initialize(address, port = nil) super(address, port) self.use_ssl = true end end end # A ruby class to wrap calls to the Google Data API # # More informations # # Google calendar API: http://code.google.com/apis/calendar/developers_guide_protocol.html class GData attr_accessor :google_url def initialize(google='www.google.com') @calendars = [] @google_url = google end #Log into google data, this method needs to be call once before using other methods of the class #* Email The user's email address. #* Passwd The user's password. #* source Identifies your client application. Should take the form companyName-applicationName-versionID #*Warning* Replace the default value with something like: #+companyName-applicationName-versionID+ def login(email, pwd, source='googlecalendar.rubyforge.org-googlecalendar-default') # service The string cl, which is the service name for Google Calendar. @user_id = email response = Net::HTTPS.post_form(URI.parse("https://#{@google_url}/accounts/ClientLogin"), { 'Email' => email, 'Passwd' => pwd, 'source' => source, 'accountType' => 'HOSTED_OR_GOOGLE', 'service' => 'cl'}) response.error! unless response.kind_of? Net::HTTPSuccess @token = response.body.split(/=/).last @headers = { 'Authorization' => "GoogleLogin auth=#{@token}", 'Content-Type' => 'application/atom+xml' } return @token end # Reset reminders def reset_reminders(event) event[:reminders] = "" end # Add a reminder to the event hash #* reminderMinutes #* reminderMethod [email, alert, sms, none] def add_reminder(event, reminderMinutes, reminderMethod) event[:reminders] = event[:reminders].to_s + "\n" end # Create a quick add event # # text = 'Tennis with John April 11 3pm-3:30pm' # # http://code.google.com/apis/calendar/developers_guide_protocol.html#CreatingQuickAdd def quick_add(text) content = < #{text} EOF post_event(content) end #'event' param is a hash containing #* :title #* :content #* :author #* :email #* :where #* :startTime '2007-06-06T15:00:00.000Z' #* :endTime '2007-06-06T17:00:00.000Z' # # Use add_reminder(event, reminderMinutes, reminderMethod) method to add reminders def new_event(event={},calendar = nil) new_event = template(event) post_event(new_event, calendar) end def post_event(xml, calendar = nil) #Get calendar url calendar_url = if calendar get_calendars find_calendar(calendar).url else # We will use user'default calendar in this case '/calendar/feeds/default/private/full' end http = Net::HTTP.new(@google_url, 80) response, data = http.post(calendar_url, xml, @headers) case response when Net::HTTPSuccess, Net::HTTPRedirection redirect_response, redirect_data = http.post(response['location'], xml, @headers) case response when Net::HTTPSuccess, Net::HTTPRedirection return redirect_response else response.error! end else response.error! end end # Retreive user's calendar urls. def get_calendars http = Net::HTTP.new(@google_url, 80) response, data = http.get("http://#{@google_url}/calendar/feeds/" + @user_id, @headers) case response when Net::HTTPSuccess, Net::HTTPRedirection redirect_response, redirect_data = http.get(response['location'], @headers) case response when Net::HTTPSuccess, Net::HTTPRedirection doc = REXML::Document.new redirect_data doc.elements.each('//entry')do |e| title = e.elements['title'].text url = e.elements['link'].attributes['href'] @calendars << GCalendar.new(title, url.sub!("http://#{@google_url}",'')) end return redirect_response else response.error! end else response.error! end end def find_calendar(x) @calendars.find {|c| c.title.match x} end # The atom event template to submit a new event def template(event={}) content = < #{event[:title]} #{event[:content]} #{event[:author]} #{event[:email]} #{event[:reminders]} EOF end end # GData class class ICALParser attr_reader :calendar def parse(data) lines = data.split("\n") reset_prefix lines.each do |line| handle_element(line) end return @calendar end def handle_element(line) pair = line.split(':') name = pair[0] value = pair[1] handler_method = @method_prefix + name.split(';')[0].tr('-', '_').downcase if self.respond_to? handler_method self.send(handler_method, value.chomp) end end def reset_prefix @method_prefix = "handle_" end def handle_begin(value) if value == "VCALENDAR" handle_vcalendar_begin(value) elsif value == "VEVENT" handle_vevent_begin(value) end end def handle_vcalendar_end(value) reset_prefix end def handle_vcalendar_begin(value) @calendar = Calendar.new @method_prefix = @method_prefix + value.downcase + "_" end def handle_vcalendar_version(value) @calendar.version = value end def handle_vcalendar_calscale(value) @calendar.scale = value end def handle_vcalendar_method(value) @calendar.method = value # FIXME don't like to do this! reset_prefix end def handle_vevent_begin(value) event = Event.new @calendar.add event @method_prefix = @method_prefix + value.downcase + "_" end def handle_vevent_end(value) reset_prefix end def handle_vevent_dtstart(value) @calendar.events.last.start_date = Date.parse(value) end def handle_vevent_dtend(value) @calendar.events.last.end_date = Date.parse(value) end def handle_vevent_dtstamp(value) @calendar.events.last.time_stamp = DateTime.parse(value) end def handle_vevent_class(value) @calendar.events.last.class_name = value end def handle_vevent_created(value) @calendar.events.last.created = DateTime.parse(value) end def handle_vevent_last_modified(value) @calendar.events.last.last_modified = DateTime.parse(value) end def handle_vevent_status(value) @calendar.events.last.status = value end def handle_vevent_summary(value) @calendar.events.last.summary = value end def handle_vevent_rrule(value) @calendar.events.last.rrule = value end def handle_vevent_description(value) @calendar.events.last.description = value end def handle_vevent_location(value) @calendar.events.last.location = value end end def parse(data) parser = ICALParser.new parser.parse(data) end def scan(ical_url, base_url='www.google.com') Net::HTTP.start(base_url, 80) do |http| response, data = http.get(ical_url) case response when Net::HTTPSuccess, Net::HTTPRedirection return data else response.error! end end end def scan_proxy(proxy_addr, proxy_port, ical_url, base_url='www.google.com') Net::HTTP::Proxy(proxy_addr, proxy_port).start(base_url, 80) do |http| response, data = http.get(ical_url) case response when Net::HTTPSuccess, Net::HTTPRedirection return data else response.error! end end end