Importing Gmail Contacts list to Rails Application.

To invite user’s friends to the application, need was to get emails id’s of user’s friends from his gmail account. Most of the plugins found for importing contact data were combined ones with gmail, yahoo and rediff import facility but none good for dedicated gmail import. Little searching and from google documentation i found that, it’s just one file code to import contact data and it’s good to implement on yourself rather than rely on some plugin.

While googling found this tutorial very interesting but first couldn’t figure out how to use code given there for exchanging auth tokens and importing data. With that blog post and refrence from maran’s Gmail Contacts Demo on github, i am here trying to simplify things for this task.

Google offers follwing auth mechanisms, of which we have to choose –

  • OAuth – proivde authentication for all Google API’s for data access.
  • Hybrid Protocol – provides single sign in service as well as authorization for data.
  • OpenID – provides single sign in authentication for users to login to your site.

So, for our purpose of getting user’s contact data, we are going to use OAuth protocol. More documentation about google authentications can be found here. A very good introduction to the OAuth – Beginner’s guide to OAuth – Hueniverse guide. With OAuth protocol to get data, following steps will be followed –

  • You have to redirect user to google to authenticate your application.
  • On authentication by google, you will receive a access token.
  • This access token then have to be exchanged for permanent token.
  • This permanent token can be used any no. of times to get data throught google API’s.

Enough of disccussion, let’s start coding….

Here is my imported_contacts controller which does the work and stores imported contacts in my imported_contact model ( hereafter comments will tell the story )-

class ImportedContactsController << ApplicationController
   require 'net/http'
   require 'net/https'
   require 'uri'
   #THIS METHOD TO SEND USER TO THE GOOGLE AUTHENTICATION PAGE.
    def authenticate
        # initiate authentication w/ gmail
        # create url with url-encoded params to initiate connection with contacts api
        # next - The URL of the page that Google should redirect the user to after authentication.
        # scope - Indicates that the application is requesting a token to access contacts feeds.
        # secure - Indicates whether the client is requesting a secure token.
        # session - Indicates whether the token returned can be exchanged for a multi-use (session) token.
        next_param = PLANNER_HOST.to_s + authorise_imported_contacts_path.to_s
        scope_param = &quot;https%3A%2F%2Fwww.google.com%2Fm8%2Ffeeds%2F&quot;
        session_param = &quot;1&quot;
        root_url = &quot;https://www.google.com/accounts/AuthSubRequest&quot;
        #TO FIND MORE AOBUT THESE PARAMTERS AND TEHRE MEANING SEE GOOGLE API DOCS.
        query_string = &quot;?scope=#{scope_param}&amp;session=#{session_param}&amp;next=#{next_param}&quot;
        redirect_to root_url + query_string
    end

    # YOU WILL BE REDIRECTED TO THIS ACTION AFTER COMPLETION OF AUTHENTICATION PROCESS WITH TEMPORARY SINGLE USE AUTH TOKEN.
    def authorise
        #FIRST SINGEL USE TOKEN WILL BE RECEIVED HERE..
        token = params[:token]
        #PREPAIRING FOR SECOND REQUEST WITH AUTH TOKEN IN HEADER.. WHICH WILL BE EXCHANED FOR PERMANENT AUTH TOKEN.
        uri = URI.parse(&quot;https://www.google.com&quot;)
        http = Net::HTTP.new(uri.host, uri.port)
        http.use_ssl = true
        http.verify_mode = OpenSSL::SSL::VERIFY_NONE
        path = '/accounts/AuthSubSessionToken'
        headers = {'Authorization' =&gt; &quot;AuthSub token=#{token}&quot;}

        #GET REQUEST ON URI WITH SPECIFIED PATH...
        resp, data = http.get(path, headers)
        #SPLIT OUT TOKEN FROM RESPONSE DATA.
        if resp.code == &quot;200&quot;
            token = ''
            data.split.each do |str|
                if not (str =~ /Token=/).nil?
                    token = str.gsub(/Token=/, '')
                end
            end
           return redirect_to(:action =&gt; 'import', :token =&gt; token)
        else
            redirect_to PLANNER_HOST.to_s + planner_path.to_s
        end
    end

    #USING PERMANENT TOKEN IN THIS ACTION TO GET USER CONTACT DATA.
    def import
    # GET http://www.google.com/m8/feeds/contacts/default/base
    token = params[:token]
    uri = URI.parse(&quot;https://www.google.com&quot;)
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    path = &quot;/m8/feeds/contacts/default/full?max-results=10000&quot;
    headers = {'Authorization' =&gt; &quot;AuthSub token=#{token}&quot;,
    'GData-Version' =&gt; &quot;3.0&quot;}
    resp, data = http.get(path, headers)
    # extract the name and email address from the response data
    # HERE USING REXML TO PARSE GOOGLE GIVEN XML DATA
    xml = REXML::Document.new(data)
    contacts = []
    xml.elements.each('//entry') do |entry|
        person = {}
        person['name'] = entry.elements['title'].text
        gd_email = entry.elements['gd:email']
        person['email'] = gd_email.attributes['address'] if gd_email
        @imported_contact = current_user.imported_contacts.create(person)
    end
    redirect_to applications_planner_path
end

In above code, importing will be done in import actiona and after that user will be directed to the next page (applications_planner_path) in my case. If api calls for data and xml parsing taken longer, user will stop ideal for most of the time. To prevent this, required data should be taken. For above case “/m8/feeds/contacts/default/base” can be used for path insted of “/m8/feeds/contacts/default/full”. For more information about these paths and API see this.

In next article, will try to write code for importing user’s photos as well and saving them with paperclip.

P.S. Gist of above code can be found here – https://gist.github.com/742461

Email