require 'contacts' require File.join(File.dirname(__FILE__), %w{.. .. vendor windowslivelogin}) require 'hpricot' require 'uri' module Contacts # = How I can fetch Windows Live Contacts? # To gain access to a Windows Live user's data in the Live Contacts service, # a third-party developer first must ask the owner for permission. You must # do that through Windows Live Delegated Authentication. # # This library give you access to Windows Live Delegated Authentication System # and Windows Live Contacts API. Just follow the steps below and be happy! # # === Registering your app # First of all, follow the steps in this # page[http://msdn.microsoft.com/en-us/library/cc287659.aspx] to register your # app. # # === Configuring your Windows Live YAML # After registering your app, you will have an *appid*, a secret key and # a return URL. Use their values to fill in the config/contacts.yml file. # The policy URL field inside the YAML config file must contain the URL # of the privacy policy of your Web site for Delegated Authentication. # # === Authenticating your user and fetching his contacts # # wl = Contacts::WindowsLive.new # auth_url = wl.get_authentication_url # # Use that *auth_url* to redirect your user to Windows Live. He will authenticate # there and Windows Live will POST to your return URL. You have to get the # body of that POST, let's call it post_body. (if you're using Rails, you can # get the POST body through request.raw_post, in the context of an action inside # ActionController) # # Now, to fetch his contacts, just do this: # # contacts = wl.contacts(post_body) # #-> [ ['Fitzgerald', 'fubar@gmail.com', 'fubar@example.com'], # ['William Paginate', 'will.paginate@gmail.com'], ... # ] #-- # This class has two responsibilities: # 1. Access the Windows Live Contacts API through Delegated Authentication # 2. Import contacts from Windows Live and deliver it inside an Array # class WindowsLive attr_accessor :wll # Initialize a new WindowsLive object. # # ==== Paramaters # * config_file :: The contacts YAML config file name #-- # You can check an example of a config file inside config/ directory # def initialize(config_file) confs = YAML.load_file(config_file)['windows_live'] @wll = WindowsLiveLogin.new(confs['appid'], confs['secret'], confs['security_algorithm'], nil, confs['policy_url'], confs['return_url']) end # Windows Live Contacts API need to authenticate the user that is giving you # access to his contacts. To do that, you must give him a URL. That method # generates that URL. The user must access that URL, and after he has done # authentication, hi will be redirected to your application. # def get_authentication_url(context=nil) @wll.getConsentUrl("Contacts.View", context) end # After the user has been authenticaded, Windows Live Delegated Authencation # Service redirects to your application, through a POST HTTP method. Along # with the POST, Windows Live send to you a Consent that you must process # to access the user's contacts. This method process the Consent # to you. # # ==== Paramaters # * consent :: A string containing the Consent given to you inside # the redirection POST from Windows Live # def process_consent(consent) consent.strip! consent = URI.unescape(consent) @consent_token = @wll.processConsent(consent) end def process_consent_token(consent_token) @wll.processConsentToken consent_token end # This method return the user's contacts inside an Array in the following # format: # # [ # ['Brad Fitzgerald', 'fubar@gmail.com'], # [nil, 'nagios@hotmail.com'], # ['William Paginate', 'will.paginate@yahoo.com'] ... # ] # # ==== Paramaters # * consent :: A string containing the Consent given to you inside # the redirection POST from Windows Live # def contacts(consent) if consent.is_a? WindowsLiveLogin::ConsentToken @consent_token = consent else process_consent(consent) end contacts_xml = access_live_contacts_api() contacts_list = WindowsLive.parse_xml(contacts_xml) end # This method access the Windows Live Contacts API Web Service to get # the XML contacts document # def access_live_contacts_api http = http = Net::HTTP.new('livecontacts.services.live.com', 443) http.use_ssl = true response = nil http.start do |http| request = Net::HTTP::Get.new("/users/@L@#{@consent_token.locationid}/rest/LiveContacts", {"Authorization" => "DelegatedToken dt=\"#{@consent_token.delegationtoken}\""}) response = http.request(request) end response.body end # This method parses the XML Contacts document and returns the contacts # inside an Array # # ==== Paramaters # * xml :: A string containing the XML contacts document # def self.parse_xml(xml) doc = Hpricot::XML(xml) contacts = [] doc.search('/LiveContacts/Contacts/Contact') do |contact| contact_id = text_value contact, "ID" first_name = text_value contact, "Profiles/Personal/FirstName" last_name = text_value contact, "Profiles/Personal/LastName" name = "#{first_name} #{last_name}".strip emails = contact.search('Emails/Email').collect {|e| text_value e, "Address"} phones = contact.search('Phones/Phone').collect do |e| type=convert_type(text_value(e, "PhoneType")) { "type" => type, "value" => (text_value e, "Number") } end addresses = contact.search('Locations/Location').collect do |e| street = text_value(e, "StreetLine") postal_code = text_value(e, "PostalCode") sub_division = text_value(e, "Subdivision") city = text_value(e, "PrimaryCity") country_code = text_value(e, "CountryRegion") formatted = [street, city, sub_division, postal_code, country_code].compact.join(", ") type = convert_type(text_value(e, "LocationType")) { "formatted" => formatted, "type" => type, "streetAddress" => street, "locality" => city, "region" => sub_division, "postalCode" => postal_code, "country" => country_code} end new_contact = Contact.new(nil, name, nil, first_name, last_name) new_contact.emails = emails new_contact.phones = phones new_contact.addresses = addresses new_contact.service_id = contact_id contacts << new_contact end return contacts end def self.text_value(elem,path) elem.at(path).inner_text rescue nil end def self.convert_type(t) {"personal" => "home", "business" => "work"}[t.downcase] || "other" end end end