require "cgi" require "uri" require "net/http" require "net/https" require "open-uri" require "nkf" require "time" Net::HTTP.version_1_2 module GoogleCalendar class AuthenticationFailed < StandardError; end #:nodoc: all # # This class interacts with google calendar service. # class Service # Server name to Authenticate AUTH_SERVER = "www.google.com" # Server Path to authenticate AUTH_PATH = "/accounts/ClientLogin" # URL to get calendar list CALENDAR_LIST_PATH = "http://www.google.com/calendar/feeds/" # proxy server address @@proxy_addr = nil def self.proxy_addr @@proxy_addr end def self.proxy_addr=(addr) @@proxy_addr=addr end # proxy server port number @@proxy_port = nil def self.proxy_port @@proxy_port end def self.proxy_port=(port) @@proxy_port = port end def initialize(email, pass) @email = email @pass = pass @session = nil @cookie = nil @auth = nil end # # get the list of user's calendars and returns http response object # def calendar_list auth unless @auth uri = URI.parse(CALENDAR_LIST_PATH + @email) do_get(uri, "Authorization" => "GoogleLogin auth=#{@auth}") end alias :calendars :calendar_list # # send query for events of a calendar and returns http response object. # available condtions: # :q => query string # :max-results => max contents count. (default: 25) # :start-index => 1-based index of the first result to be retrieved # :orderby => the order of retrieved data. # :published-min => Bounds on the entry publication date(oldest) # :published-max => Bounds on the entry publication date(newest) # :updated-min => Bounds on the entry update date(oldest) # :updated-max => Bounds on the entry update date(newest) # :author => Entry author # def query(cal_url, conditions) auth unless @auth uri = URI.parse(cal_url) uri.query = conditions.map do |key, val| "#{key}=#{URI.escape(val.kind_of?(Time) ? val.getutc.iso8601 : val.to_s)}" end.join("&") do_get(uri, "Authorization" => "GoogleLogin auth=#{@auth}") end # # delete an event. # def delete(feed) auth unless @auth uri = URI.parse(feed) do_post(uri, {"X-HTTP-Method-Override" => "DELETE", "Authorization" => "GoogleLogin auth=#{@auth}"}, "DELETE " + uri.path) end # # insert an event # def insert(feed, event) auth unless @auth uri = URI.parse(feed) do_post(uri, {"Authorization" => "GoogleLogin auth=#{@auth}", "Content-Type" => "application/atom+xml", "Content-Length" => event.length.to_s}, event) end # # update an event. # def update(feed, event) auth unless @auth uri = URI.parse(feed) do_post(uri, {"X-HTTP-Method-Override" => "PUT", "Authorization" => "GoogleLogin auth=#{@auth}", "Content-Type" => "application/atom+xml", "Content-Length" => event.length.to_s}, event) end private # authencate def auth https = Net::HTTP.new(AUTH_SERVER, 443, @@proxy_addr, @@proxy_port) https.use_ssl = true https.verify_mode = OpenSSL::SSL::VERIFY_NONE head = {'Content-Type' => 'application/x-www-form-urlencoded'} https.start do |w| res = w.post(AUTH_PATH, "Email=#{@email}&Passwd=#{@pass}&source=company-app-1&service=cl", head) if res.body =~ /Auth=(.+)/ @auth = $1 else raise AuthenticationFailed end end end def do_post(uri, header, content) res = nil try_http(uri, header, content) do |http,path,head,args| cont = args[0] res = http.post(path, cont, head) end res end def do_get(uri, header) res = nil try_http(uri, header) do |http,path,head| res = http.get(path, head) end res end def try_http(uri, header, *args) res = nil Net::HTTP.start(uri.host, uri.port, @@proxy_addr, @@proxy_port) do |http| header["Cookie"] = @cookie if @cookie res = yield(http, path_with_authorized_query(uri), header, args) if res.code == "302" ck = sess = nil ck = res["set-cookie"] if res.key?("set-cookie") uri = URI.parse(res["location"]) if res.key?("location") if uri && uri.query qr = CGI.parse(uri.query) sess = qr["gsessionid"][0] if qr.key?("gsessionid") end if ck && sess header["Cookie"] = @cookie = ck @session = sess res = yield(http, path_with_authorized_query(uri), header, args) else p res end end end res end def path_with_authorized_query(uri) query = CGI.parse(uri.query.nil? ? "" : uri.query) query["gsessionid"] = [@session] if @session qs = query.map do |k,v| "#{CGI.escape(k)}=#{CGI.escape(v[0])}" end.join("&") qs.empty? ? uri.path : "#{uri.path}?#{qs}" end end # class Service end # module