[
  {
    "path": ".gitignore",
    "content": "coverage\n*.iml\n.idea\n.rvmrc\n.DS_Store\nnbproject\n"
  },
  {
    "path": "Gemfile",
    "content": "source 'http://rubygems.org'\n\ngemspec"
  },
  {
    "path": "README.md",
    "content": "# OmniContacts\n\nInspired by the popular OmniAuth, OmniContacts is a library that enables users of an application to import contacts\nfrom their email or Facebook accounts. The email providers currently supported are Gmail, Yahoo and Hotmail.\nOmniContacts is a Rack middleware, therefore you can use it with Rails, Sinatra and any other Rack-based framework.\n\nOmniContacts uses the OAuth protocol to communicate with the contacts provider. Yahoo still uses OAuth 1.0, while\n Facebook, Gmail and Hotmail support OAuth 2.0.\nIn order to use OmniContacts, it is therefore necessary to first register your application with the provider and to obtain client_id and client_secret.\n\n## Contribute!\nMe (rubytastic) and the orginal author Diego don't actively use this code at the moment, anyone interested in maintaining and contributing to this codebase please write me up in a personal message ( rubytastic )\nI try to merge pull requests in every once and a while but this code would benefit from someone actively use and contribute to it.\n\n## Gem build updates\nThere is now a new gem build out which should address many issues people had when posting on the issue tracker. Please update to the latest GEM version if you have problems before posting new issues.\n\n\n## Usage\n\nAdd OmniContacts as a dependency:\n\n```ruby\ngem \"omnicontacts\"\n\n```\n\nAs for OmniAuth, there is a Builder facilitating the usage of multiple contacts importers. In the case of a Rails application, the following code could be placed at `config/initializers/omnicontacts.rb`:\n\n```ruby\nrequire \"omnicontacts\"\n\nRails.application.middleware.use OmniContacts::Builder do\n  importer :gmail, \"client_id\", \"client_secret\", {:redirect_path => \"/oauth2callback\", :ssl_ca_file => \"/etc/ssl/certs/curl-ca-bundle.crt\"}\n  importer :yahoo, \"consumer_id\", \"consumer_secret\", {:callback_path => \"/callback\"}\n  importer :linkedin, \"consumer_id\", \"consumer_secret\", {:redirect_path => \"/oauth2callback\", :state => '<long_unique_string_value>'}\n  importer :hotmail, \"client_id\", \"client_secret\"\n  importer :outlook, \"app_id\", \"app_secret\"\n  importer :facebook, \"client_id\", \"client_secret\"\nend\n\n```\n\nEvery importer expects `client_id` and `client_secret` as mandatory, while `:redirect_path` and `:ssl_ca_file` are optional (except linkedin - `state` arg  mandatory).\nSince Yahoo implements the version 1.0 of the OAuth protocol, naming is slightly different. Instead of `:redirect_path` you should use `:callback_path` as key in the hash providing the optional parameters.\nWhile `:ssl_ca_file` is optional, it is highly recommended to set it on production environments for obvious security reasons.\nOn the other hand it makes things much easier to leave the default value for `:redirect_path` and `:callback path`, the reason of which will be clear after reading the following section.\n\n## Register your application\n\n* For Gmail : [Google API Console](https://code.google.com/apis/console/)\n\n* For Yahoo : [Yahoo Developer Network](https://developer.yahoo.com/social/contacts/)\n\n* For Hotmail : [Microsoft Developer Network](https://account.live.com/developers/applications/index)\n\n* For Outlook : [Microsoft Application Registration Portal](https://apps.dev.microsoft.com/)\n\n* For Facebook : [Facebook Developers](https://developers.facebook.com/apps)\n\n* For Linkedin : [Linkedin Developer Network](https://www.linkedin.com/secure/developer)\n\n\n##### Note:\nPlease go through [MSDN](http://msdn.microsoft.com/en-us/library/cc287659.aspx) if above Hotmail link will not work.  \nOutlook is a newer Microsoft API which allows to retrieve real email address instead of `email_hashes` when using Hotmail, it also works with all kinds of MS accounts (Office 365, Hotmail.com, Live.com, MSN.com, Outlook.com, and Passport.com).\n\n## Integrating with your Application\n\nTo use the Gem you first need to redirect your users to `/contacts/:importer`, where `:importer` can be facebook, gmail, yahoo or hotmail.\nNo changes to `config/routes.rb` are needed for this step since OmniContacts will be listening on that path and redirect the user to the email provider's website in order to authorize your app to access his contact list.\nOnce that is done the user will be redirected back to your application, to the path specified in `:redirect_path` (or `:callback_path` for yahoo).\nIf nothing is specified the default value is `/contacts/:importer/callback` (e.g. `/contacts/yahoo/callback`). This makes things simpler and you can just add the following line to `config/routes.rb`:\n\n```ruby\n  match \"/contacts/:importer/callback\" => \"your_controller#callback\"\n```\n\nThe list of contacts can be accessed via the `omnicontacts.contacts` key in the environment hash and it consists of a simple array of hashes.\nThe following table shows which fields are supported by which provider:\n\n<table>\n\t<tr>\n\t\t<th>Provider</th>\n\t\t<th>:email</th>\n\t\t<th>:id</th>\n\t\t<th>:profile_picture</th>\n\t\t<th>:name</th>\n\t\t<th>:first_name</th>\n\t\t<th>:last_name</th>\n\t\t<th>:address_1</th>\n\t\t<th>:address_2</th>\n\t\t<th>:city</th>\n\t\t<th>:region</th>\n\t\t<th>:postcode</th>\n\t\t<th>:country</th>\n\t\t<th>:phone_number</th>\n\t\t<th>:birthday</th>\n\t\t<th>:gender</th>\n\t\t<th>:relation</th>\n\t</tr>\n\t<tr>\n\t\t<td>Gmail</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td></td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t</tr>\n\t<tr>\n\t\t<td>Facebook</td>\n\t\t<td></td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t</tr>\n\t<tr>\n\t\t<td>Yahoo</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td></td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td>X</td>\n\t\t<td></td>\n\t\t<td></td>\n\t</tr>\n\t<tr>\n\t\t<td>Hotmail</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td></td>\n\t</tr>\n\t<tr>\n\t\t<td>Outlook</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td></td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td></td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td></td>\n\t\t<td>X</td>\n\t\t<td></td>\n\t\t<td></td>\n\t</tr>\n\t<tr>\n\t<td>Linkedin</td>\n\t\t<td></td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td>X</td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t\t<td></td>\n\t<tr>\n</table>\n\nObviously it may happen that some fields are blank even if supported by the provider in the case that the contact did not provide any information about them.\n\nThe information for the logged in user can also be accessed via 'omnicontacts.user' key in the environment hash. It consists of a simple hash which includes the same fields as above.\n\nThe following snippet shows how to simply print name and email of each contact, and also the the name of logged in user:\n```ruby\ndef contacts_callback\n  @contacts = request.env['omnicontacts.contacts']\n  @user = request.env['omnicontacts.user']\n  puts \"List of contacts of #{@user[:name]} obtained from #{params[:importer]}:\"\n  @contacts.each do |contact|\n    puts \"Contact found: name => #{contact[:name]}, email => #{contact[:email]}\"\n  end\nend\n```\n\nIf the user does not authorize your application to access his/her contacts list, or any other inconvenience occurs, he/she is redirected to `/contacts/failure`. The query string will contain a parameter named `error_message` which specifies why the list of contacts could not be retrieved. `error_message` can have one of the following values: `not_authorized`, `timeout` and `internal_error`.\n\n##  Tips and tricks\n\nOmniContacts supports OAuth 1.0 and OAuth 2.0 token refresh, but for both it needs to persist data between requests. OmniContacts stores access tokens in the session. If you hit the 4KB cookie storage limit you better opt for the Memcache or the Active Record storage.\n\nGmail requires you to register the redirect_path on their website along with your application. Make sure to use the same value present in the configuration file, or `/contacts/gmail/callback` if using the default. Also make sure that your full url is used including \"www\" if your site redirects from the root domain.\n\nTo configure the max number of contacts to download from Gmail, just add a max results parameter in your initializer:\n\n```ruby\nimporter :gmail, \"xxx\", \"yyy\", :max_results => 1000\n```\n\nYahoo requires you to configure the Permissions your application requires. Make sure to go the Yahoo website and to select Read permission for Contacts.\n\nHotmail presents a \"peculiar\" feature. Their API returns a Contact object which does not contain an e-mail field!\nHowever, if the contact has either name, family name or both set to null, than there is a field called name which does contain the e-mail address.\nThis means that it may happen that an Hotmail contact does not contain the email field.\n\n## Integration Testing\n\nYou can enable test mode like this:\n\n```ruby\n  OmniContacts.integration_test.enabled = true\n```\n\nIn this way all requests to `/omnicontacts/provider` will be redirected automatically to `/omnicontacts/provider/callback`.\n\nThe `mock` method allows to configure per-provider the result to return:\n\n```ruby\n  OmniContacts.integration_test.mock(:provider_name, :email => \"user@example.com\")\n```\n\nYou can either pass a single hash or an array of hashes. If you pass a string, an error will be triggered with subsequent redirect to `/contacts/failure?error_message=internal_error`\n\nYou can also pass a user to fill `omnicontacts.user` (optional)\n```ruby\n  OmniContacts.integration_test.mock(:provider_name, {:email => \"contact@example.com\"}, {:email => \"user@example.com\"})\n```\n\nFollows a full example of an integration test:\n\n```ruby\n  OmniContacts.integration_test.enabled = true\n  OmniContacts.integration_test.mock(:gmail, :email => \"user@example.com\")\n  visit '/contacts/gmail'\n  page.should have_content(\"user@example.com\")\n```\n\n## Working on localhost\n\nSince Hotmail and Facebook do not allow the usage of `localhost` as redirect path for the authorization step, a workaround is to use `ngrok`.\nThis is really useful when you need someone, the contacts provider in this case, to access your locally running application using a unique url.\n\nInstall ngrok, download from:\n\nhttps://ngrok.com/\n\nhttps://github.com/inconshreveable/ngrok\n\nUnzip the file\n```bash\nunzip /place/this/is/ngrok.zip\n```\nStart your application\n```bash\n$ rails server\n\n=> Booting WEBrick\n=> Rails 4.0.4 application starting in development on http://0.0.0.0:3000\n```\n\nIn a new terminal window, start the tunnel and pass the port where your application is running:\n```bash\n./ngrok 3000\n```\n\nCheck the output to see something like\n```bash\nngrok                                                                                                                    (Ctrl+C to quit)\n\nTunnel Status                 online\nVersion                       1.6/1.5\nForwarding                    http://274101c1e.ngrok.com -> 127.0.0.1:3000\nForwarding                    https://274101c1e.ngrok.com -> 127.0.0.1:3000\nWeb Interface                 127.0.0.1:4040\n# Conn                        0\nAvg Conn Time                 0.00ms\n```\n\nThis window will show all network transaction that your locally hosted application is processing.\nNgrok will process all of the requests and responses on your localhost. Visit:\n\n```bash\nhttp://123456789.ngrok.com # replace 123456789 with your instance\n```\n\n## Example application\n\nThanks to @sonianand11, you can find a full example of a Rails application using OmniContacts at: https://github.com/sonianand11/omnicontacts_example\n\n## Thanks\n\nAs already mentioned above, a special thanks goes to @sonianand11 for implementing an example app.\nThanks also to @asmatameem for her huge contribution. She indeed added support for Facebook and for many fields which were missing before.\n\n## License\n\nCopyright (c) 2012-2013 Diego81\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of this software and associated documentation files (the \"Software\"),\nto deal in the Software without restriction, including without limitation\nthe rights to use, copy, modify, merge, publish, distribute, sublicense,\nand/or sell copies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\nTHE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "Rakefile",
    "content": "require 'bundler'\nrequire 'rspec/core/rake_task'\n\nBundler::GemHelper.install_tasks\nRSpec::Core::RakeTask.new(:spec)\ntask :default => :spec\ntask :test => :spec\n"
  },
  {
    "path": "lib/omnicontacts/authorization/oauth1.rb",
    "content": "require \"omnicontacts/http_utils\"\nrequire \"base64\"\n\n# This module represent a OAuth 1.0 Client.\n#\n# Classes including the module must implement\n# the following methods:\n# * auth_host ->  the host of the authorization server\n# * auth_token_path -> the path to query to obtain a request token\n# * consumer_key -> the registered consumer key of the client\n# * consumer_secret -> the registered consumer secret of the client\n# * callback -> the callback to include during the redirection step\n# * auth_path -> the path on the authorization server to redirect the user to\n# * access_token_path -> the path to query in order to obtain the access token\nmodule OmniContacts\n  module Authorization\n    module OAuth1\n      include HTTPUtils\n\n      OAUTH_VERSION = \"1.0\"\n\n      # Obtain an authorization token from the server.\n      # The token is returned in an array along with the relative authorization token secret.\n      def fetch_authorization_token\n        request_token_response = https_post(auth_host, auth_token_path, request_token_req_params)\n        values_from_query_string(request_token_response, [\"oauth_token\", \"oauth_token_secret\"])\n      end\n\n      private\n\n      def request_token_req_params\n        {\n          :oauth_consumer_key => consumer_key,\n          :oauth_nonce => encode(random_string),\n          :oauth_signature_method => \"PLAINTEXT\",\n          :oauth_signature => encode(consumer_secret + \"&\"),\n          :oauth_timestamp => timestamp,\n          :oauth_version => OAUTH_VERSION,\n          :oauth_callback => callback\n        }\n      end\n\n      def random_string\n        (0...50).map { ('a'..'z').to_a[rand(26)] }.join\n      end\n\n      def timestamp\n        Time.now.to_i.to_s\n      end\n\n      def values_from_query_string query_string, keys_to_extract\n        map = query_string_to_map(query_string)\n        keys_to_extract.collect do |key|\n          if map.has_key?(key)\n            map[key]\n          else\n            raise \"No value found for #{key} in #{query_string}\"\n          end\n        end\n      end\n\n      public\n\n      # Returns the url the user has to be redirected to do in order grant permission to the client application.\n      def authorization_url auth_token\n        \"https://\" + auth_host + auth_path + \"?oauth_token=\" + auth_token\n      end\n\n      # Fetches the access token from the authorization server.\n      # The method expects the authorization token, the authorization token secret and the authorization verifier.\n      # The result comprises the access token, the access token secret and a list of additional fields extracted from the server's response.\n      # The list of additional fields to extract is specified as last parameter\n      def fetch_access_token auth_token, auth_token_secret, auth_verifier, additional_fields_to_extract = []\n        access_token_resp = https_post(auth_host, access_token_path, access_token_req_params(auth_token, auth_token_secret, auth_verifier))\n        values_from_query_string(access_token_resp, ([\"oauth_token\", \"oauth_token_secret\"] + additional_fields_to_extract))\n      end\n\n      private\n\n      def access_token_req_params auth_token, auth_token_secret, auth_verifier\n        {\n          :oauth_consumer_key => consumer_key,\n          :oauth_nonce => encode(random_string),\n          :oauth_signature_method => \"PLAINTEXT\",\n          :oauth_signature => encode(consumer_secret + \"&\" + auth_token_secret),\n          :oauth_version => OAUTH_VERSION,\n          :oauth_timestamp => timestamp,\n          :oauth_token => auth_token,\n          :oauth_verifier => auth_verifier\n        }\n      end\n\n      public\n\n      # Calculates a signature using HMAC-SHA1 according to the OAuth 1.0 specifications.\n      # \n      # The base string is given is a RFC 3986 encoded concatenation of:\n      # * Uppercase HTTP method\n      # * An '&'\n      # * A url without any parameters\n      # * An '&'\n      # * All parameters to use in the request encoded themselves and sorted by key.\n      #\n      # The signature key is given by the concatenation of:\n      # * RFC 3986 encoded consumer secret\n      # * An  '&'\n      # * RFC 3986 encoded token secret\n      def oauth_signature method, url, params, secret\n        encoded_method = encode(method.upcase)\n        encoded_url = encode(url)\n        # params must be in alphabetical order\n        encoded_params = encode(to_query_string(params.sort { |x, y| x.to_s <=> y.to_s }))\n        base_string = encoded_method + '&' + encoded_url + '&' + encoded_params\n        key = encode(consumer_secret) + '&' + secret\n        hmac_sha1 = OpenSSL::HMAC.digest('sha1', key, base_string)\n        # base64 encode results must be stripped\n        encode(Base64.encode64(hmac_sha1).strip)\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "lib/omnicontacts/authorization/oauth2.rb",
    "content": "require \"omnicontacts/http_utils\"\nrequire \"json\"\n\n# This module represents an OAuth 2.0 client.\n#\n# Classes including the module must implement\n# the following methods:\n# * auth_host -> the host of the authorization server\n# * authorize_path -> the path on the authorization server the redirect the use to\n# * client_id -> the registered client id of the client\n# * client_secret -> the registered client secret of the client\n# * redirect_path -> the path the authorization server has to redirect the user back after authorization\n# * auth_token_path -> the path to query once the user has granted permission to the application\n# * scope -> the scope necessary to acquire the contacts list.\nmodule OmniContacts\n  module Authorization\n    module OAuth2\n      include HTTPUtils\n\n      # Calculates the URL the user has to be redirected to in order to authorize\n      # the application to access his contacts list.\n      def authorization_url\n        \"https://\" + auth_host + authorize_path + \"?\" + authorize_url_params\n      end\n\n      private\n\n      def authorize_url_params\n        to_query_string({\n            :client_id => client_id,\n            :scope => encode(scope),\n            :response_type => \"code\",\n            :access_type => \"online\",\n            :approval_prompt => \"auto\",\n            :redirect_uri => encode(redirect_uri)\n          })\n      end\n\n      public\n\n      # Fetches the access token from the authorization server using the given authorization code.\n      def fetch_access_token code\n        access_token_from_response https_post(auth_host, auth_token_path, token_req_params(code))\n      end\n\n      private\n\n      def token_req_params code\n        {\n          :client_id => client_id,\n          :client_secret => client_secret,\n          :code => code,\n          :redirect_uri => encode(redirect_uri),\n          :grant_type => \"authorization_code\"\n        }\n      end\n\n      def access_token_from_response response\n        if auth_host == \"graph.facebook.com\"\n          response = query_string_to_map(response).to_json\n        end\n        json = JSON.parse(response)\n        raise json[\"error\"] if json[\"error\"]\n        [json[\"access_token\"], json[\"token_type\"], json[\"refresh_token\"]]\n      end\n\n      public\n\n      # Refreshes the access token using the provided refresh_token.\n      def refresh_access_token refresh_token\n        access_token_from_response https_post(auth_host, auth_token_path, refresh_token_req_params(refresh_token))\n      end\n\n      private\n\n      def refresh_token_req_params refresh_token\n        {\n          :client_id => client_id,\n          :client_secret => client_secret,\n          :refresh_token => refresh_token,\n          :grant_type => \"refresh_token\"\n        }\n\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/omnicontacts/builder.rb",
    "content": "require \"omnicontacts\"\n\nmodule OmniContacts\n  class Builder < Rack::Builder\n    def initialize(app, &block)\n      if rack14?\n        super\n      else\n        @app = app\n        super(&block)\n      end\n    end\n\n    def rack14?\n      v = Rack.release.split('.')\n      v[0].to_i >= 1 || v[1].to_i >= 4\n    end\n\n    def importer importer, *args\n      middleware = OmniContacts::Importer.const_get(importer.to_s.capitalize)\n      use middleware, *args\n    rescue NameError\n      raise LoadError, \"Could not find importer #{importer}.\"\n    end\n\n    def call env\n      @ins << @app unless rack14? || @ins.include?(@app)\n      to_app.call(env)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/omnicontacts/http_utils.rb",
    "content": "require \"net/http\"\nrequire \"net/https\"\nrequire \"cgi\"\nrequire \"openssl\"\n\n# This module contains a set of utility methods  related to the HTTP protocol.\nmodule OmniContacts\n  module HTTPUtils\n\n    SSL_PORT = 443\n\n    module_function\n\n    def query_string_to_map query_string\n      query_string.split('&').reduce({}) do |memo, key_value|\n        (key, value) = key_value.split('=')\n        memo[key]= value\n        memo\n      end\n    end\n\n    def to_query_string map\n      map.collect do |key, value|\n        key.to_s + \"=\" + value.to_s\n      end.join(\"&\")\n    end\n\n    # Encodes the given input according to RFC 3986\n    def encode to_encode\n      CGI.escape(to_encode)\n    end\n\n    # Calculates the url of the host from a Rack environment.\n    # The result is in the form scheme://host:port\n    # If port is 80 the result is scheme://host\n    # According to Rack specification the HTTP_HOST variable is preferred over SERVER_NAME.\n    def host_url_from_rack_env env\n      port = ((env[\"SERVER_PORT\"] == 80) && \"\") || \":#{env['SERVER_PORT']}\"\n      host = (env[\"HTTP_HOST\"]) || (env[\"SERVER_NAME\"] + port)\n      \"#{scheme(env)}://#{host}\"\n    end\n\n    def scheme env\n      if env['HTTPS'] == 'on'\n        'https'\n      elsif env['HTTP_X_FORWARDED_SSL'] == 'on'\n        'https'\n      elsif env['HTTP_X_FORWARDED_PROTO']\n        env['HTTP_X_FORWARDED_PROTO'].split(',').first\n      else\n        env[\"rack.url_scheme\"]\n      end\n    end\n\n    # Classes including the module must respond to the ssl_ca_file message in order to use the following methods.\n    # The response will be the path to the CA file to use when making https requests.\n    # If the result of ssl_ca_file is nil no file is used. In this case a warn message is logged.\n    private\n\n    # Executes an HTTP GET request.\n    # It raises a RuntimeError if the response code is not equal to 200\n    def http_get host, path, params\n      connection = Net::HTTP.new(host)\n      process_http_response connection.request_get(path + \"?\" + to_query_string(params))\n    end\n\n    # Executes an HTTP POST request over SSL\n    # It raises a RuntimeError if the response code is not equal to 200\n    def https_post host, path, params\n      https_connection host do |connection|\n        connection.request_post(path, to_query_string(params))\n      end\n    end\n\n    # Executes an HTTP GET request over SSL\n    # It raises a RuntimeError if the response code is not equal to 200\n    def https_get host, path, params, headers = nil\n      https_connection host do |connection|\n        connection.request_get(path + \"?\" + to_query_string(params), headers)\n      end\n    end\n\n    def https_connection (host)\n      connection = Net::HTTP.new(host, SSL_PORT)\n      connection.use_ssl = true\n      if ssl_ca_file\n        connection.ca_file = ssl_ca_file\n      else\n        logger << \"No SSL ca file provided. It is highly reccomended to use one in production envinronments\" if respond_to?(:logger) && logger\n        connection.verify_mode = OpenSSL::SSL::VERIFY_NONE\n      end\n      process_http_response(yield(connection))\n    end\n\n    def process_http_response response\n      raise response.body if response.code != \"200\"\n      response.body\n    end\n\n  end\nend\n"
  },
  {
    "path": "lib/omnicontacts/importer/facebook.rb",
    "content": "require \"omnicontacts/parse_utils\"\nrequire \"omnicontacts/middleware/oauth2\"\nrequire \"json\"\n\nmodule OmniContacts\n  module Importer\n    class Facebook < Middleware::OAuth2\n      include ParseUtils\n\n      attr_reader :auth_host, :authorize_path, :auth_token_path, :scope\n\n      def initialize *args\n        super *args\n        @auth_host = 'graph.facebook.com'\n        @authorize_path = '/oauth/authorize'\n        @scope = 'email,user_relationships,user_birthday,user_friends'\n        @auth_token_path = '/oauth/access_token'\n        @contacts_host = 'graph.facebook.com'\n        @friends_path = '/v2.5/me/friends'\n        @family_path = '/v2.5/me/family'\n        @self_path = '/v2.5/me'\n      end\n\n      def fetch_contacts_using_access_token access_token, access_token_secret\n        self_response = fetch_current_user access_token\n        user = current_user self_response\n        set_current_user user\n        spouse_id = extract_spouse_id self_response\n        spouse_response = nil\n        if spouse_id\n          spouse_path = \"/#{spouse_id}\"\n          spouse_response = https_get(@contacts_host, spouse_path, {:access_token => access_token, :fields => 'first_name,last_name,name,id,gender,birthday,picture'})\n        end\n        family_response = https_get(@contacts_host, @family_path, {:access_token => access_token, :fields => 'first_name,last_name,name,id,gender,birthday,picture,relationship'})\n        friends_response = https_get(@contacts_host, @friends_path, {:access_token => access_token, :fields => 'first_name,last_name,name,id,gender,birthday,picture'})\n        contacts_from_response(spouse_response, family_response, friends_response)\n      end\n\n      def fetch_current_user access_token\n        self_response = https_get(@contacts_host, @self_path, {:access_token => access_token, :fields => 'first_name,last_name,name,id,gender,birthday,picture,relationship_status,significant_other,email'})\n        self_response = JSON.parse(self_response) if self_response\n        self_response\n      end\n\n      private\n\n      def extract_spouse_id response\n        return nil if response.nil?\n        id = nil\n        if response['significant_other'] && response['relationship_status'] == 'Married'\n          id = response['significant_other']['id']\n        end\n        id\n      end\n\n      def contacts_from_response(spouse_response, family_response, friends_response)\n        contacts = []\n        family_ids = Set.new\n        if spouse_response\n          spouse_contact = create_contact_element(JSON.parse(spouse_response))\n          spouse_contact[:relation] = 'spouse'\n          contacts << spouse_contact\n          family_ids.add(spouse_contact[:id])\n        end\n        if family_response\n          family_response = JSON.parse(family_response)\n          family_response['data'].each do |family_contact|\n            contacts << create_contact_element(family_contact)\n            family_ids.add(family_contact['id'])\n          end\n        end\n        if friends_response\n          friends_response = JSON.parse(friends_response)\n          friends_response['data'].each do |friends_contact|\n            contacts << create_contact_element(friends_contact) unless family_ids.include?(friends_contact['id'])\n          end\n        end\n        contacts\n      end\n\n      def create_contact_element contact_info\n        # creating nil fields to keep the fields consistent across other networks\n        contact = {:id => nil, :first_name => nil, :last_name => nil, :name => nil, :email => nil, :gender => nil, :birthday => nil, :profile_picture=> nil, :relation => nil}\n        contact[:id] = contact_info['id']\n        contact[:first_name] = normalize_name(contact_info['first_name'])\n        contact[:last_name] = normalize_name(contact_info['last_name'])\n        contact[:name] = contact_info['name']\n        contact[:email] = contact_info['email']\n        contact[:gender] = contact_info['gender']\n        contact[:birthday] = birthday(contact_info['birthday'])\n        contact[:profile_picture] = image_url(contact_info['id'])\n        contact[:relation] = contact_info['relationship']\n        contact\n      end\n\n      def image_url fb_id\n        return \"https://graph.facebook.com/\" + fb_id + \"/picture\" if fb_id\n      end\n\n      def escape_windows_format value\n        value.gsub(/[\\r\\s]/, '')\n      end\n\n      def birthday dob\n        return nil if dob.nil?\n        birthday = dob.split('/')\n        return birthday_format(birthday[0],birthday[1],birthday[2])\n      end\n\n      def current_user me\n        return nil if me.nil?\n        user = {:id => me['id'], :email => me['email'],\n                :name => me['name'], :first_name => normalize_name(me['first_name']),\n                :last_name => normalize_name(me['last_name']), :birthday => birthday(me['birthday']),\n                :gender => me['gender'], :profile_picture => image_url(me['id'])\n        }\n        user\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "lib/omnicontacts/importer/gmail.rb",
    "content": "require \"omnicontacts/parse_utils\"\nrequire \"omnicontacts/middleware/oauth2\"\n\nmodule OmniContacts\n  module Importer\n    class Gmail < Middleware::OAuth2\n      include ParseUtils\n\n      attr_reader :auth_host, :authorize_path, :auth_token_path, :scope\n\n      def initialize *args\n        super *args\n        @auth_host = \"accounts.google.com\"\n        @authorize_path = \"/o/oauth2/auth\"\n        @auth_token_path = \"/o/oauth2/token\"\n        @scope = (args[3] && args[3][:scope]) || \"https://www.googleapis.com/auth/contacts.readonly https://www.googleapis.com/auth/userinfo#email https://www.googleapis.com/auth/userinfo.profile\"\n        @contacts_host = \"www.google.com\"\n        @contacts_path = \"/m8/feeds/contacts/default/full\"\n        @max_results =  (args[3] && args[3][:max_results]) || 100\n        @self_host = \"www.googleapis.com\"\n        @profile_path = \"/oauth2/v3/userinfo\"\n      end\n\n      def fetch_contacts_using_access_token access_token, token_type\n        fetch_current_user(access_token, token_type)\n        contacts_response = https_get(@contacts_host, @contacts_path, contacts_req_params, contacts_req_headers(access_token, token_type))\n        contacts_from_response(contacts_response, access_token)\n      end\n\n      def fetch_current_user access_token, token_type\n        self_response = https_get(@self_host, @profile_path, contacts_req_params, contacts_req_headers(access_token, token_type))\n        user = current_user(self_response, access_token, token_type)\n        set_current_user user\n      end\n\n      private\n\n      def contacts_req_params\n        {'max-results' => @max_results.to_s, 'alt' => 'json'}\n      end\n\n      def contacts_req_headers token, token_type\n        {\"GData-Version\" => \"3.0\", \"Authorization\" => \"#{token_type} #{token}\"}\n      end\n\n      def contacts_from_response(response_as_json, access_token)\n        response = JSON.parse(response_as_json)\n\n        return [] if response['feed'].nil? || response['feed']['entry'].nil?\n        contacts = []\n        return contacts if response.nil?\n        response['feed']['entry'].each do |entry|\n          # creating nil fields to keep the fields consistent across other networks\n\n          contact = { :id => nil,\n                      :first_name => nil,\n                      :last_name => nil,\n                      :name => nil,\n                      :emails => nil,\n                      :gender => nil,\n                      :birthday => nil,\n                      :profile_picture=> nil,\n                      :relation => nil,\n                      :addresses => nil,\n                      :phone_numbers => nil,\n                      :dates => nil,\n                      :company => nil,\n                      :position => nil\n          }\n          contact[:id] = entry['id']['$t'] if entry['id']\n          if entry['gd$name']\n            gd_name = entry['gd$name']\n            contact[:first_name] = normalize_name(entry['gd$name']['gd$givenName']['$t']) if gd_name['gd$givenName']\n            contact[:last_name] = normalize_name(entry['gd$name']['gd$familyName']['$t']) if gd_name['gd$familyName']\n            contact[:name] = normalize_name(entry['gd$name']['gd$fullName']['$t']) if gd_name['gd$fullName']\n            contact[:name] = full_name(contact[:first_name],contact[:last_name]) if contact[:name].nil?\n          end\n\n          contact[:emails] = []\n          entry['gd$email'].each do |email|\n            if email['rel']\n              split_index = email['rel'].index('#')\n              contact[:emails] << {:name => email['rel'][split_index + 1, email['rel'].length - 1], :email => email['address']}\n            elsif email['label']\n              contact[:emails] << {:name => email['label'], :email => email['address']}\n            end\n          end if entry['gd$email']\n\n          # Support older versions of the gem by keeping singular entries around\n          contact[:email] = contact[:emails][0][:email] if contact[:emails][0]\n          contact[:first_name], contact[:last_name], contact[:name] = email_to_name(contact[:name]) if !contact[:name].nil? && contact[:name].include?('@')\n          contact[:first_name], contact[:last_name], contact[:name] = email_to_name(contact[:emails][0][:email]) if (contact[:name].nil? && contact[:emails][0] && contact[:emails][0][:email])\n          #format - year-month-date\n          contact[:birthday] = birthday(entry['gContact$birthday']['when'])  if entry['gContact$birthday']\n\n          # value is either \"male\" or \"female\"\n          contact[:gender] = entry['gContact$gender']['value']  if entry['gContact$gender']\n\n          if entry['gContact$relation']\n            if entry['gContact$relation'].is_a?(Hash)\n              contact[:relation] = entry['gContact$relation']['rel']\n            elsif entry['gContact$relation'].is_a?(Array)\n              contact[:relation] = entry['gContact$relation'].first['rel']\n            end\n          end\n\n          contact[:addresses] = []\n          entry['gd$structuredPostalAddress'].each do |address|\n            if address['rel']\n              split_index = address['rel'].index('#')\n              new_address = {:name => address['rel'][split_index + 1, address['rel'].length - 1]}\n            elsif address['label']\n              new_address = {:name => address['label']}\n            end\n\n            new_address[:address_1] = address['gd$street']['$t'] if address['gd$street']\n            new_address[:address_1] = address['gd$formattedAddress']['$t'] if new_address[:address_1].nil? && address['gd$formattedAddress']\n            if !new_address[:address_1].nil? && new_address[:address_1].index(\"\\n\")\n              parts = new_address[:address_1].split(\"\\n\")\n              new_address[:address_1] = parts.first\n              # this may contain city/state/zip if user jammed it all into one string.... :-(\n              new_address[:address_2] = parts[1..-1].join(', ')\n            end\n            new_address[:city] = address['gd$city']['$t'] if address['gd$city']\n            new_address[:region] = address['gd$region']['$t'] if address['gd$region'] # like state or province\n            new_address[:country] = address['gd$country']['code'] if address['gd$country']\n            new_address[:postcode] = address['gd$postcode']['$t'] if address['gd$postcode']\n            contact[:addresses] << new_address\n          end if entry['gd$structuredPostalAddress']\n\n          # Support older versions of the gem by keeping singular entries around\n          if contact[:addresses][0]\n            contact[:address_1] = contact[:addresses][0][:address_1]\n            contact[:address_2] = contact[:addresses][0][:address_2]\n            contact[:city] = contact[:addresses][0][:city]\n            contact[:region] = contact[:addresses][0][:region]\n            contact[:country] = contact[:addresses][0][:country]\n            contact[:postcode] = contact[:addresses][0][:postcode]\n          end\n\n          contact[:phone_numbers] = []\n          entry['gd$phoneNumber'].each do |phone_number|\n            if phone_number['rel']\n              split_index = phone_number['rel'].index('#')\n              contact[:phone_numbers] << {:name => phone_number['rel'][split_index + 1, phone_number['rel'].length - 1], :number => phone_number['$t']}\n            elsif phone_number['label']\n              contact[:phone_numbers] << {:name => phone_number['label'], :number => phone_number['$t']}\n            end\n          end if entry['gd$phoneNumber']\n\n          # Support older versions of the gem by keeping singular entries around\n          contact[:phone_number] = contact[:phone_numbers][0][:number] if contact[:phone_numbers][0]\n\n          if entry[\"link\"] && entry[\"link\"].is_a?(Array)\n            entry[\"link\"].each do |link|\n              if link[\"type\"] == 'image/*' && link[\"gd$etag\"]\n                contact[:profile_picture] = link[\"href\"] + \"?&access_token=\" + access_token\n                break\n              end\n            end\n          end\n\n          if entry['gContact$event']\n            contact[:dates] = []\n            entry['gContact$event'].each do |event|\n              if event['rel']\n                contact[:dates] << {:name => event['rel'], :date => birthday(event['gd$when']['startTime'])}\n              elsif event['label']\n                contact[:dates] << {:name => event['label'], :date => birthday(event['gd$when']['startTime'])}\n              end\n            end\n          end\n\n          if entry['gd$organization']\n            contact[:company] = entry['gd$organization'][0]['gd$orgName']['$t'] if entry['gd$organization'][0]['gd$orgName']\n            contact[:position] = entry['gd$organization'][0]['gd$orgTitle']['$t'] if entry['gd$organization'][0]['gd$orgTitle']\n          end\n\n          contacts << contact if contact[:name]\n        end\n        contacts.uniq! {|c| c[:email] || c[:profile_picture] || c[:name]}\n        contacts\n      end\n\n      def current_user me, access_token, token_type\n        return nil if me.nil?\n        me = JSON.parse(me)\n        user = {:id => me['id'], :email => me['email'], :name => me['name'], :first_name => me['given_name'],\n                :last_name => me['family_name'], :gender => me['gender'], :birthday => birthday(me['birthday']), :profile_picture => me[\"picture\"],\n                :access_token => access_token, :token_type => token_type\n        }\n        user\n      end\n\n      def birthday dob\n        return nil if dob.nil?\n        birthday = dob.split('-')\n        return birthday_format(birthday[2], birthday[3], nil) if birthday.size == 4\n        return birthday_format(birthday[1], birthday[2], birthday[0]) if birthday.size == 3\n      end\n\n      def contact_id(profile_url)\n        id = (profile_url.present?) ? File.basename(profile_url) : nil\n        id\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "lib/omnicontacts/importer/hotmail.rb",
    "content": "require \"omnicontacts/middleware/oauth2\"\nrequire \"omnicontacts/parse_utils\"\nrequire \"json\"\n\nmodule OmniContacts\n  module Importer\n    class Hotmail < Middleware::OAuth2\n      include ParseUtils\n\n      attr_reader :auth_host, :authorize_path, :auth_token_path, :scope\n\n      def initialize app, client_id, client_secret, options ={}\n        super app, client_id, client_secret, options\n        @auth_host = \"login.live.com\"\n        @authorize_path = \"/oauth20_authorize.srf\"\n        @scope = options[:permissions] || \"wl.signin, wl.basic, wl.birthday , wl.emails ,wl.contacts_birthday , wl.contacts_photos, wl.contacts_emails\"\n        @auth_token_path = \"/oauth20_token.srf\"\n        @contacts_host = \"apis.live.net\"\n        @contacts_path = \"/v5.0/me/contacts\"\n        @self_path = \"/v5.0/me\"\n      end\n\n      def fetch_contacts_using_access_token access_token, access_token_secret\n        fetch_current_user(access_token)\n        contacts_response = https_get(@contacts_host, @contacts_path, :access_token => access_token)\n        contacts_from_response contacts_response\n      end\n\n      def fetch_current_user access_token\n        self_response =  https_get(@contacts_host, @self_path, :access_token => access_token)\n        user = current_user self_response\n        set_current_user user\n      end\n\n      private\n\n      def contacts_from_response response_as_json\n        response = JSON.parse(response_as_json)\n        contacts = []\n        response['data'].each do |entry|\n          # creating nil fields to keep the fields consistent across other networks\n          contact = {:id => nil, :first_name => nil, :last_name => nil, :name => nil, :email => nil, :gender => nil, :birthday => nil, :profile_picture=> nil, :relation => nil, :email_hashes => []}\n          contact[:id] = entry['user_id'] ? entry['user_id'] : entry['id']\n\t        contact[:email] = parse_email(entry['emails']) if valid_email? parse_email(entry['emails'])\n\t        contact[:first_name] = normalize_name(entry['first_name'])\n\t        contact[:last_name] = normalize_name(entry['last_name'])\n\t        contact[:name] = normalize_name(entry['name'])\n          contact[:birthday] = birthday_format(entry['birth_month'], entry['birth_day'], entry['birth_year'])\n          contact[:gender] = entry['gender']\n          contact[:profile_picture] = image_url(entry['user_id'])\n          contact[:email_hashes] = entry['email_hashes']\n          contacts << contact if contact[:name] || contact[:first_name]\n        end\n        contacts\n      end\n\n      def parse_email(emails)\n        return nil if emails.nil?\n        emails['account'] || emails['preferred'] || emails['personal'] || emails['business'] || emails['other']\n      end\n\n      def current_user me\n        return nil if me.nil?\n        me = JSON.parse(me)\n        email = parse_email(me['emails'])\n        user = {:id => me['id'], :email => email, :name => me['name'], :first_name => me['first_name'],\n                :last_name => me['last_name'], :gender => me['gender'], :profile_picture => image_url(me['id']),\n                :birthday => birthday_format(me['birth_month'], me['birth_day'], me['birth_year'])\n        }\n        user\n      end\n\n\n      def image_url hotmail_id\n        return 'https://apis.live.net/v5.0/' + hotmail_id + '/picture' if hotmail_id\n      end\n\n      def escape_windows_format value\n        value.gsub(/[\\r\\s]/, '')\n      end\n\n      def valid_email? value\n        /\\A[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]+\\z/.match(value)\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "lib/omnicontacts/importer/linkedin.rb",
    "content": "require \"omnicontacts/parse_utils\"\nrequire \"omnicontacts/middleware/oauth2\"\n\nmodule OmniContacts\n  module Importer\n    class Linkedin < Middleware::OAuth2\n      include ParseUtils\n\n      attr_reader :auth_host, :authorize_path, :auth_token_path, :scope, :state\n\n      def initialize *args\n        super *args\n        @auth_host = \"www.linkedin.com\"\n        @authorize_path = \"/uas/oauth2/authorization\"\n        @auth_token_path = \"/uas/oauth2/accessToken\"\n        @scope = (args[3] && args[3][:scope]) || \"r_network\"\n        @contacts_host = \"api.linkedin.com\"\n        @contacts_path = \"/v1/people/~/connections:(id,first-name,last-name,picture-url)\"\n        @self_host = \"www.linkedin.com\"\n        @profile_path = \"/oauth2/v1/userinfo\"\n        @state = (args[3] && args[3][:state])\n      end\n\n      def fetch_contacts_using_access_token access_token, token_type\n        token_type = \"Bearer\" if token_type.nil?\n        contacts_response = https_get(@contacts_host, @contacts_path, contacts_req_params, contacts_req_headers(access_token, token_type))\n        contacts_from_response contacts_response\n      end\n\n      private\n\n      def contacts_req_params\n        {'format' => 'json'}\n      end\n\n      def contacts_req_headers token, token_type\n        {\"Authorization\" => \"#{token_type} #{token}\"}\n      end\n\n      def contacts_from_response response_as_json\n        response = JSON.parse(response_as_json)\n        return [] if response['values'].nil?\n        contacts = []\n        return contacts if response.nil?\n        response['values'].map do |entry|\n          {\n           id: entry['id'],\n            first_name: normalize_name(entry['firstName']),\n            last_name: normalize_name(entry['lastName']),\n            name: full_name(entry['firstName'],entry['lastName']),\n            profile_picture: entry['pictureUrl']\n          }\n        end\n      end\n\n      def authorize_url_params\n        # merge state param required by LinkedIn\n        _params = super\n        _params += \"&\" + to_query_string(state: @state)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/omnicontacts/importer/outlook.rb",
    "content": "require \"omnicontacts/middleware/oauth2\"\nrequire \"omnicontacts/parse_utils\"\nrequire \"json\"\n\n# API Docs: https://msdn.microsoft.com/en-us/office/office365/api/api-catalog#Outlookcontacts\nmodule OmniContacts\n  module Importer\n    class Outlook < Middleware::OAuth2\n      include ParseUtils\n\n      attr_reader :auth_host, :authorize_path, :auth_token_path, :scope\n\n      def initialize app, client_id, client_secret, options ={}\n        super app, client_id, client_secret, options\n        @auth_host = \"login.microsoftonline.com\"\n        @authorize_path = \"/common/oauth2/v2.0/authorize\"\n        @scope = options[:permissions] || \"https://outlook.office.com/contacts.read\"\n        @auth_token_path = \"/common/oauth2/v2.0/token\"\n        @contacts_host = \"outlook.office.com\"\n        @contacts_path = \"/api/v2.0/me/contacts\"\n        @self_path = \"/api/v2.0/me\"\n      end\n\n      def fetch_contacts_using_access_token access_token, token_type\n        fetch_current_user(access_token, token_type)\n        contacts_response = https_get(@contacts_host, @contacts_path, {}, contacts_req_headers(access_token, token_type))\n        contacts_from_response contacts_response\n      end\n\n      def fetch_current_user access_token, token_type\n        self_response = https_get(@contacts_host, @self_path, {}, contacts_req_headers(access_token, token_type))\n        user = current_user self_response\n        set_current_user user\n      end\n\n      private\n\n      def contacts_req_headers token, token_type\n        { \"Authorization\" => \"#{token_type} #{token}\" }\n      end\n\n      def current_user me\n        return nil if me.nil?\n        me = JSON.parse(me)\n\n        name_splitted = me[\"DisplayName\"].split(\" \")\n        first_name = name_splitted.first\n        last_name = name_splitted.last if name_splitted.size > 1\n\n        user = empty_contact\n        user[:id]         = me[\"Id\"]\n        user[:email]      = me[\"EmailAddress\"]\n        user[:name]       = me[\"DisplayName\"]\n        user[:first_name] = normalize_name(first_name)\n        user[:last_name]  = normalize_name(last_name)\n        user\n      end\n\n      def contacts_from_response response_as_json\n        response = JSON.parse(response_as_json)\n        contacts = []\n        response[\"value\"].each do |entry|\n          contact = empty_contact\n          # Full fields reference:\n          # https://msdn.microsoft.com/office/office365/api/complex-types-for-mail-contacts-calendar#RESTAPIResourcesContact\n          contact[:id]         = entry[\"Id\"]\n          contact[:first_name] = entry[\"GivenName\"]\n          contact[:last_name]  = entry[\"Surname\"]\n          contact[:name]       = entry[\"DisplayName\"]\n          contact[:email]      = parse_email(entry[\"EmailAddresses\"])\n          contact[:birthday]   = birthday(entry[\"Birthday\"])\n\n          address = [entry[\"HomeAddress\"], entry[\"BusinessAddress\"], entry[\"OtherAddress\"]].reject(&:empty?).first\n          if address\n            contact[:address_1] = address[\"Street\"]\n            contact[:city]      = address[\"City\"]\n            contact[:region]    = address[\"State\"]\n            contact[:postcode]  = address[\"PostalCode\"]\n            contact[:country]   = address[\"CountryOrRegion\"]\n          end\n\n          contacts << contact if contact[:name] || contact[:first_name]\n        end\n        contacts\n      end\n\n      def empty_contact\n        { :id => nil, :first_name => nil, :last_name => nil, :name => nil, :email => nil,\n          :gender => nil, :birthday => nil, :profile_picture => nil, :address_1 => nil,\n          :address_2 => nil, :city => nil, :region => nil, :postcode => nil, :relation => nil }\n      end\n\n      def parse_email emails\n        return nil if emails.nil?\n        emails.map! { |email| email[\"Address\"] }\n        emails.select! { |email| valid_email? email }\n        emails.first\n      end\n\n      def birthday dob\n        return nil if dob.nil?\n        birthday = dob[0..9].split(\"-\")\n        birthday[0] = nil if birthday[0].to_i < 1900 # if year is not set it returns 1604\n        return birthday_format(birthday[1], birthday[2], birthday[0])\n      end\n\n      def valid_email? value\n        /\\A[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]+\\z/.match(value)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/omnicontacts/importer/yahoo.rb",
    "content": "require \"omnicontacts/parse_utils\"\nrequire \"omnicontacts/middleware/oauth1\"\nrequire \"json\"\n\nmodule OmniContacts\n  module Importer\n    class Yahoo < Middleware::OAuth1\n      include ParseUtils\n\n      attr_reader :auth_host, :auth_token_path, :auth_path, :access_token_path\n\n      def initialize *args\n        super *args\n        @auth_host = 'api.login.yahoo.com'\n        @auth_token_path = '/oauth/v2/get_request_token'\n        @auth_path = '/oauth/v2/request_auth'\n        @access_token_path = '/oauth/v2/get_token'\n        @contacts_host = 'social.yahooapis.com'\n      end\n\n      def fetch_contacts_from_token_and_verifier auth_token, auth_token_secret, auth_verifier\n        (access_token, access_token_secret, guid) = fetch_access_token(auth_token, auth_token_secret, auth_verifier, ['xoauth_yahoo_guid'])\n        fetch_current_user(access_token, access_token_secret, guid)\n        contacts_path = \"/v1/user/#{guid}/contacts\"\n        contacts_response = https_get(@contacts_host, contacts_path, contacts_req_params(access_token, access_token_secret, contacts_path))\n        contacts_from_response contacts_response\n      end\n\n      def fetch_current_user access_token, access_token_secret, guid\n        self_path = \"/v1/user/#{guid}/profile\"\n        self_response =  https_get(@contacts_host, self_path, contacts_req_params(access_token, access_token_secret, self_path))\n        user = current_user self_response\n        set_current_user user\n      end\n\n      private\n\n      def contacts_req_params access_token, access_token_secret, contacts_path\n        params = {\n            :format => 'json',\n            :oauth_consumer_key => consumer_key,\n            :oauth_nonce => encode(random_string),\n            :oauth_signature_method => 'HMAC-SHA1',\n            :oauth_timestamp => timestamp,\n            :oauth_token => access_token,\n            :oauth_version => OmniContacts::Authorization::OAuth1::OAUTH_VERSION\n        }\n        contacts_url = \"https://#{@contacts_host}#{contacts_path}\"\n        params['oauth_signature'] = oauth_signature('GET', contacts_url, params, access_token_secret)\n        params\n      end\n\n      def contacts_from_response response_as_json\n        response = JSON.parse(response_as_json)\n        contacts = []\n        return contacts unless response['contacts']['contact']\n        response['contacts']['contact'].each do |entry|\n          # creating nil fields to keep the fields consistent across other networks\n          contact = { :id => nil,\n                      :first_name => nil,\n                      :last_name => nil,\n                      :name => nil,\n                      :email => nil,\n                      :gender => nil,\n                      :birthday => nil,\n                      :profile_picture=> nil,\n                      :address_1 => nil,\n                      :address_2 => nil,\n                      :city => nil,\n                      :region => nil,\n                      :postcode => nil,\n                      :relation => nil }\n          yahoo_id = nil\n          contact[:id] = entry['id'].to_s\n          entry['fields'].each do |field|\n            case field['type']\n            when 'name'\n              contact[:first_name] = normalize_name(field['value']['givenName'])\n              contact[:last_name] = normalize_name(field['value']['familyName'])\n              contact[:name] = full_name(contact[:first_name],contact[:last_name])\n            when 'email'\n              contact[:email] = field['value'] if field['type'] == 'email'\n            when 'yahooid'\n              yahoo_id = field['value']\n            when 'address'\n              value = field['value']\n              contact[:address_1] = street = value['street']\n              if street.index(\"\\n\")\n                parts = street.split(\"\\n\")\n                contact[:address_1] = parts.first\n                contact[:address_2] = parts[1..-1].join(', ')\n              end\n              contact[:city] = value['city']\n              contact[:region] = value['stateOrProvince']\n              contact[:postcode] = value['postalCode']\n            when 'birthday'\n              contact[:birthday] = birthday_format(field['value']['month'], field['value']['day'],field['value']['year'])\n            end\n            contact[:first_name], contact[:last_name], contact[:name] = email_to_name(contact[:email]) if contact[:name].nil? && contact[:email]\n            # contact[:first_name], contact[:last_name], contact[:name] = email_to_name(yahoo_id) if (yahoo_id && contact[:name].nil? && contact[:email].nil?)\n\n            if yahoo_id\n              contact[:profile_picture] = image_url(yahoo_id)\n            else\n              contact[:profile_picture] = image_url_from_email(contact[:email])\n            end\n          end\n          contacts << contact if contact[:name]\n        end\n        contacts.uniq! {|c| c[:email] || c[:profile_picture] || c[:name]}\n        contacts\n      end\n\n      def image_url yahoo_id\n        return 'https://img.msg.yahoo.com/avatar.php?yids=' + yahoo_id if yahoo_id\n      end\n\n      def parse_email(emails)\n        return nil if emails.nil?\n        email = nil\n        if emails.is_a?(Hash)\n          if emails.has_key?(\"primary\")\n            email = emails['handle']\n          end\n        elsif emails.is_a?(Array)\n          emails.each do |e|\n            if e.has_key?('primary') && e['primary']\n              email = e['handle']\n              break\n            end\n          end\n        end\n        email\n      end\n\n      def birthday dob\n        return nil if dob.nil?\n        birthday = dob.split('/')\n        return birthday_format(birthday[0], birthday[1], birthday[3]) if birthday.size == 3\n        return birthday_format(birthday[0], birthday[1], nil) if birthday.size == 2\n\n      end\n\n      def gender g\n        return \"female\" if g == \"F\"\n        return \"male\" if g == \"M\"\n      end\n\n      def my_image img\n        return nil if img.nil?\n        return img['imageUrl']\n      end\n\n      def current_user me\n        return nil if me.nil?\n        me = JSON.parse(me)\n        me = me['profile']\n        email = parse_email(me['emails'])\n        user = {:id => me[\"guid\"], :email => email, :name => full_name(me['givenName'],me['familyName']), :first_name => normalize_name(me['givenName']),\n                :last_name => normalize_name(me['familyName']), :gender => gender(me['gender']), :birthday => birthday(me['birthdate']),\n                :profile_picture => my_image(me['image'])\n               }\n        user\n      end\n\n      #def profile_image_url(guid, access_token, access_token_secret)\n      #  image_path = \"/v1/user/#{guid}/profile/image/48x48\"\n      #  response = https_get(@contacts_host, image_path, contacts_req_params(access_token, access_token_secret, image_path))\n      #  image_data = JSON.parse(response)\n      #  return image_data['image']['imageUrl'] if image_data['image']\n      #  return nil\n      #end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/omnicontacts/importer.rb",
    "content": "module OmniContacts\n  module Importer\n\n    autoload :Gmail, \"omnicontacts/importer/gmail\"\n    autoload :Yahoo, \"omnicontacts/importer/yahoo\"\n    autoload :Hotmail, \"omnicontacts/importer/hotmail\"\n    autoload :Outlook, \"omnicontacts/importer/outlook\"\n    autoload :Facebook, \"omnicontacts/importer/facebook\"\n    autoload :Linkedin, \"omnicontacts/importer/linkedin\"\n\n  end\nend\n"
  },
  {
    "path": "lib/omnicontacts/integration_test.rb",
    "content": "require 'singleton'\n\nclass IntegrationTest\n  include Singleton\n\n  attr_accessor :enabled\n\n  def initialize\n    enabled = false\n    clear_mocks\n  end\n\n  def clear_mocks\n    @user_mocks = {}\n    @contact_mocks = {}\n  end\n\n  def mock provider, contacts, user = {}\n    @contact_mocks[provider.to_sym] = contacts\n    @user_mocks[provider.to_sym] = user\n  end\n\n  def mock_authorization_from_user provider\n    [302, {\"Content-Type\" => \"application/x-www-form-urlencoded\", \"location\" => provider.redirect_path}, []]\n  end\n\n  def mock_fetch_contacts provider\n    result = @contact_mocks[provider.class_name.to_sym] || []\n    if result.is_a? Array\n      result\n    elsif result.is_a? Hash\n      [result]\n    else\n      raise result.to_s\n    end\n  end\n\n  def mock_fetch_user provider\n    @user_mocks[provider.class_name.to_sym] || {}\n  end\nend\n"
  },
  {
    "path": "lib/omnicontacts/middleware/base_oauth.rb",
    "content": "# This class contains the common behavior for middlewares\n# implementing either versions of OAuth.\n#\n# Extending classes are required to implement\n# the following methods:\n# * request_authorization_from_user\n# * fetch_contatcs\nmodule OmniContacts\n  module Middleware\n    class BaseOAuth\n\n      attr_reader :ssl_ca_file\n\n      def initialize app, options\n        @app = app\n        @listening_path = MOUNT_PATH + class_name\n        @ssl_ca_file = options[:ssl_ca_file]\n      end\n\n      def class_name\n        self.class.name.split('::').last.downcase\n      end\n\n      # Rack callback. It handles three cases:\n      # * user visit middleware entry point.\n      #   In this case request_authorization_from_user is called\n      # * user is redirected back to the application\n      #   from the authorization site. In this case the list\n      #   of contacts is fetched and stored in the variables\n      #   omnicontacts.contacts within the Rack env variable.\n      #   Once that is done the next middleware component is called.\n      # * user visits any other resource. In this case the request\n      #   is simply forwarded to the next middleware component.\n      def call env\n        @env = env\n        if env[\"PATH_INFO\"] =~ /^#{@listening_path}\\/?$/\n          session['omnicontacts.params'] = Rack::Request.new(env).params\n          handle_initial_request\n        elsif env[\"PATH_INFO\"] =~ /^#{redirect_path}/\n          env['omnicontacts.params'] = session.delete('omnicontacts.params')\n          handle_callback\n        else\n          @app.call(env)\n        end\n      end\n\n      private\n\n      def test_mode?\n        IntegrationTest.instance.enabled\n      end\n\n      def handle_initial_request\n        execute_and_rescue_exceptions do\n          if test_mode?\n            IntegrationTest.instance.mock_authorization_from_user(self)\n          else\n            request_authorization_from_user\n          end\n        end\n      end\n\n      def handle_callback\n        execute_and_rescue_exceptions do\n          @env[\"omnicontacts.contacts\"] = if test_mode?\n            IntegrationTest.instance.mock_fetch_contacts(self)\n          else\n            fetch_contacts\n          end\n          set_current_user IntegrationTest.instance.mock_fetch_user(self) if test_mode?\n          @app.call(@env)\n        end\n      end\n\n      def set_current_user user\n        @env[\"omnicontacts.user\"] = user\n      end\n\n      #  This method rescues executes a block of code and\n      #  rescue all exceptions. In case of an exception the\n      #  user is redirected to the failure endpoint.\n      def execute_and_rescue_exceptions\n        yield\n      rescue AuthorizationError => e\n        handle_error :not_authorized, e\n      rescue ::Timeout::Error, ::Errno::ETIMEDOUT => e\n        handle_error :timeout, e\n      rescue ::RuntimeError => e\n        handle_error :internal_error, e\n      end\n\n      def handle_error error_type, exception\n        logger.puts(\"Error #{error_type} while processing #{@env[\"PATH_INFO\"]}: #{exception.message}\") if logger\n        failure_url = \"#{ MOUNT_PATH }failure?error_message=#{error_type}&importer=#{class_name}\"\n        params_url = append_request_params(failure_url)\n        target_url = append_state_query(params_url)\n        [302, {\"Content-Type\" => \"text/html\", \"location\" => target_url}, []]\n      end\n\n      def session\n        raise \"You must provide a session to use OmniContacts\" unless @env[\"rack.session\"]\n        @env[\"rack.session\"]\n      end\n\n      def logger\n        @env[\"rack.errors\"] if @env\n      end\n\n      def base_prop_name\n        \"omnicontacts.\" + class_name\n      end\n\n      def append_request_params(target_url)\n        return target_url unless @env['omnicontacts.params']\n        params = Rack::Utils.build_query(@env['omnicontacts.params'])\n        unless params.nil? or params.empty?\n          target_url = target_url + (target_url.include?(\"?\")?\"&\":\"?\") + params\n        end\n        return target_url\n      end\n\n      def append_state_query(target_url)\n        state = Rack::Utils.parse_query(@env['QUERY_STRING'])['state']\n        unless state.nil?\n          target_url = target_url + (target_url.include?(\"?\")?\"&\":\"?\") + 'state=' + state\n        end\n\n        return target_url\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/omnicontacts/middleware/oauth1.rb",
    "content": "require \"omnicontacts/authorization/oauth1\"\nrequire \"omnicontacts/middleware/base_oauth\"\n\n# This class is an OAuth 1.0 Rack middleware.\n#\n# Extending classes are required to \n# implement the following methods:\n# * fetch_token_from_token_and_verifier -> this method has to\n#   fetch the list of contacts from the authorization server.\nmodule OmniContacts\n  module Middleware\n    class OAuth1 < BaseOAuth\n      include Authorization::OAuth1\n\n      attr_reader :consumer_key, :consumer_secret, :callback_path\n\n      def initialize app, consumer_key, consumer_secret, options = {}\n        super app, options\n        @consumer_key = consumer_key\n        @consumer_secret = consumer_secret\n        @callback_path = options[:callback_path] || \"#{ MOUNT_PATH }#{class_name}/callback\"\n        @token_prop_name = \"#{base_prop_name}.oauth_token\"\n      end\n\n      def callback\n        host_url_from_rack_env(@env) + callback_path\n      end\n\n      alias :redirect_path :callback_path\n\n      # Obtains an authorization token from the server,\n      # stores it and the session and redirect the user\n      # to the authorization website.\n      def request_authorization_from_user\n        (auth_token, auth_token_secret) = fetch_authorization_token\n        session[@token_prop_name] = auth_token\n        session[token_secret_prop_name(auth_token)] = auth_token_secret\n        redirect_to_authorization_site(auth_token)\n      end\n\n      def token_secret_prop_name oauth_token\n        \"#{base_prop_name}.#{oauth_token}.oauth_token_secret\"\n      end\n\n      def redirect_to_authorization_site auth_token\n        authorization_url = authorization_url(auth_token)\n        target_url = append_state_query(authorization_url)\n        [302, {\"Content-Type\" => \"application/x-www-form-urlencoded\", \"location\" => target_url}, []]\n      end\n\n      # Parses the authorization token from the query string and \n      # obtain the relative secret from the session.\n      # Finally it calls fetch_contacts_from_token_and_verifier.\n      # If token is found in the query string an AuhorizationError\n      # is raised.\n      def fetch_contacts\n        params = query_string_to_map(@env[\"QUERY_STRING\"])\n        oauth_token = params[\"oauth_token\"]\n        oauth_verifier = params[\"oauth_verifier\"]\n        oauth_token_secret = session[token_secret_prop_name(oauth_token)]\n        if oauth_token && oauth_verifier && oauth_token_secret\n          fetch_contacts_from_token_and_verifier(oauth_token, oauth_token_secret, oauth_verifier)\n        else\n          raise AuthorizationError.new(\"User did not grant access to contacts list\")\n        end\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "lib/omnicontacts/middleware/oauth2.rb",
    "content": "require \"omnicontacts/authorization/oauth2\"\nrequire \"omnicontacts/middleware/base_oauth\"\n\n# This class is a OAuth 2 Rack middleware.\n#\n# Extending class are required to implement\n# the following methods:\n# * fetch_contacts_using_access_token -> it\n#   fetches the list of contacts from the authorization\n#   server.\nmodule OmniContacts\n  module Middleware\n    class OAuth2 < BaseOAuth\n      include Authorization::OAuth2\n\n      attr_reader :client_id, :client_secret, :redirect_path\n\n      def initialize app, client_id, client_secret, options ={}\n        super app, options\n        @client_id = client_id\n        @client_secret = client_secret\n        @redirect_path = options[:redirect_path] || \"#{ MOUNT_PATH }#{class_name}/callback\"\n        @ssl_ca_file = options[:ssl_ca_file]\n      end\n\n      def request_authorization_from_user\n        target_url = append_state_query(authorization_url)\n        [302, {\"Content-Type\" => \"application/x-www-form-urlencoded\", \"location\" => target_url}, []]\n      end\n\n      def redirect_uri\n        host_url_from_rack_env(@env) + redirect_path\n      end\n\n      # It extract the authorization code from the query string.\n      # It uses it to obtain an access token.\n      # If the authorization code has a refresh token associated\n      # with it in the session, it uses the obtain an access token.\n      # It fetches the list of contacts and stores the refresh token\n      # associated with the access token in the session.\n      # Finally it returns the list of contacts.\n      # If no authorization code is found in the query string an\n      # AuthoriazationError is raised.\n      def fetch_contacts\n        code = query_string_to_map(@env[\"QUERY_STRING\"])[\"code\"]\n        if code\n          refresh_token = session[refresh_token_prop_name(code)]\n          (access_token, token_type, refresh_token) = if refresh_token\n                                                        refresh_access_token(refresh_token)\n                                                      else\n                                                        fetch_access_token(code)\n                                                      end\n          contacts = fetch_contacts_using_access_token(access_token, token_type)\n          session[refresh_token_prop_name(code)] = refresh_token if refresh_token\n          contacts\n        else\n          raise AuthorizationError.new(\"User did not grant access to contacts list\")\n        end\n      end\n\n      def refresh_token_prop_name code\n        \"#{base_prop_name}.#{code}.refresh_token\"\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "lib/omnicontacts/parse_utils.rb",
    "content": "module OmniContacts\n  module ParseUtils\n\n    # return has of birthday day, month and year\n    def birthday_format month, day, year\n      return {:day => day.to_i, :month => month.to_i, :year => year.to_i}if year && month && day\n      return {:day => day.to_i, :month => month.to_i, :year => nil} if !year && month && day\n      return nil if (!year && !month) || (!year && !day)\n    end\n\n    # normalize the name\n    def normalize_name name\n      return nil if name.nil?\n      name.chomp!\n      name.squeeze!(' ')\n      name.strip!\n      return name\n    end\n\n    # create a full name given the individual first and last name\n    def full_name first_name, last_name\n      return \"#{first_name} #{last_name}\" if first_name && last_name\n      return \"#{first_name}\" if first_name && !last_name\n      return \"#{last_name}\" if !first_name && last_name\n      return nil\n    end\n\n    # create a username/name from a given email\n    def email_to_name username_or_email\n      username_or_email = username_or_email.split('@').first if username_or_email.include?('@')\n      if group = (/(?<first>[a-z|A-Z]+)[\\.|_](?<last>[a-z|A-Z]+)/).match(username_or_email)\n        first_name = normalize_name(group[:first])\n        last_name = normalize_name(group[:last])\n        return first_name, last_name, \"#{first_name} #{last_name}\"\n      end\n      username = normalize_name(username_or_email)\n      return username, nil, username\n    end\n\n    # create an image_url from a gmail or yahoo email id.\n    def image_url_from_email email\n      return nil if email.nil? || !email.include?('@')\n      username, domain = *(email.split('@'))\n      return nil if username.nil? or domain.nil?\n      gmail_base_url = \"https://profiles.google.com/s2/photos/profile/\"\n      yahoo_base_url = \"https://img.msg.yahoo.com/avatar.php?yids=\"\n      if domain.include?('gmail')\n        image_url = gmail_base_url + username\n      elsif domain.include?('yahoo')\n        image_url = yahoo_base_url + username\n      end\n      image_url\n    end\n\n  end\nend\n"
  },
  {
    "path": "lib/omnicontacts.rb",
    "content": "module OmniContacts\n  \n  VERSION = \"0.3.10\"\n\n  MOUNT_PATH = \"/contacts/\"\n\n  autoload :Builder, \"omnicontacts/builder\"\n  autoload :Importer, \"omnicontacts/importer\"\n  autoload :IntegrationTest, \"omnicontacts/integration_test\"\n\n  class AuthorizationError < RuntimeError\n  end\n\n\n  def self.integration_test\n    IntegrationTest.instance\n  end\n  \nend\n"
  },
  {
    "path": "omnicontacts.gemspec",
    "content": "# encoding: utf-8\nrequire File.expand_path('../lib/omnicontacts', __FILE__)\n\nGem::Specification.new do |gem|\n  gem.name = 'omnicontacts'\n  gem.description = %q{A generalized Rack middleware for importing contacts from major email providers.}\n  gem.authors = ['Diego Castorina', 'Jordan Lance', 'Asma Tameem', 'Randy Villanueva']\n  gem.email = ['diegocastorina@gmail.com', 'voorruby@gmail.com']\n\n  gem.add_runtime_dependency 'rack'\n  gem.add_runtime_dependency 'json'\n\n  gem.add_development_dependency 'simplecov'\n  gem.add_development_dependency 'rake'\n  gem.add_development_dependency 'rack-test'\n  gem.add_development_dependency 'rspec'\n\n  gem.version = OmniContacts::VERSION\n  gem.files = `git ls-files`.split(\"\\n\")\n  gem.homepage = 'http://github.com/Diego81/omnicontacts'\n  gem.require_paths = ['lib']\n  gem.required_rubygems_version = Gem::Requirement.new('>= 1.3.6') if gem.respond_to? :required_rubygems_version=\n  gem.summary = gem.description\n  gem.test_files = `git ls-files -- {spec}/*`.split(\"\\n\")\nend\n"
  },
  {
    "path": "spec/omnicontacts/authorization/oauth1_spec.rb",
    "content": "require \"spec_helper\"\nrequire \"omnicontacts/authorization/oauth1\"\n\ndescribe OmniContacts::Authorization::OAuth1 do\n\n  before(:all) do\n    OAuth1TestClass= Struct.new(:consumer_key, :consumer_secret, :auth_host, :auth_token_path, :auth_path, :access_token_path, :callback)\n    class OAuth1TestClass\n      include OmniContacts::Authorization::OAuth1\n    end\n  end\n\n  let(:test_target) do\n    OAuth1TestClass.new(\"consumer_key\", \"secret1\", \"auth_host\", \"auth_token_path\", \"auth_path\", \"access_token_path\", \"callback\")\n  end\n\n  describe \"fetch_authorization_token\" do\n\n    it \"should request the token providing all mandatory parameters\" do\n      test_target.should_receive(:https_post) do |host, path, params|\n        host.should eq(test_target.auth_host)\n        path.should eq(test_target.auth_token_path)\n        params[:oauth_consumer_key].should eq(test_target.consumer_key)\n        params[:oauth_nonce].should_not be_nil\n        params[:oauth_signature_method].should eq(\"PLAINTEXT\")\n        params[:oauth_signature].should eq(test_target.consumer_secret + \"%26\")\n        params[:oauth_timestamp].should_not be_nil\n        params[:oauth_version].should eq(\"1.0\")\n        params[:oauth_callback].should eq(test_target.callback)\n        \"oauth_token=token&oauth_token_secret=token_secret\"\n      end\n      test_target.fetch_authorization_token\n    end\n\n    it \"should successfully parse the result\" do\n      test_target.should_receive(:https_post).and_return(\"oauth_token=token&oauth_token_secret=token_secret\")\n      test_target.fetch_authorization_token.should eq([\"token\", \"token_secret\"])\n    end\n\n    it \"should raise an error if request is invalid\" do\n      test_target.should_receive(:https_post).and_return(\"invalid_request\")\n      expect { test_target.fetch_authorization_token }.to raise_error\n    end\n\n  end\n\n  describe \"authorization_url\" do\n    subject { test_target.authorization_url(\"token\") }\n    it { should eq(\"https://#{test_target.auth_host}#{test_target.auth_path}?oauth_token=token\") }\n  end\n\n  describe \"fetch_access_token\" do\n    it \"should request the access token using all required parameters\" do\n      auth_token = \"token\"\n      auth_token_secret = \"token_secret\"\n      auth_verifier = \"verifier\"\n      test_target.should_receive(:https_post) do |host, path, params|\n        host.should eq(test_target.auth_host)\n        path.should eq(test_target.access_token_path)\n        params[:oauth_consumer_key].should eq(test_target.consumer_key)\n        params[:oauth_nonce].should_not be_nil\n        params[:oauth_signature_method].should eq(\"PLAINTEXT\")\n        params[:oauth_version].should eq(\"1.0\")\n        params[:oauth_signature].should eq(\"#{test_target.consumer_secret}%26#{auth_token_secret}\")\n        params[:oauth_token].should eq(auth_token)\n        params[:oauth_verifier].should eq(auth_verifier)\n        \"oauth_token=access_token&oauth_token_secret=access_token_secret&other_param=other_value\"\n      end\n      test_target.fetch_access_token auth_token, auth_token_secret, auth_verifier, [\"other_param\"]\n    end\n\n    it \"should successfully extract access_token and the other fields\" do\n      test_target.should_receive(:https_post).and_return(\"oauth_token=access_token&oauth_token_secret=access_token_secret&other_param=other_value\")\n      test_target.fetch_access_token(\"token\", \"token_scret\", \"verified\", [\"other_param\"]).should eq([\"access_token\", \"access_token_secret\", \"other_value\"])\n    end\n  end\n\n  describe \"oauth_signature\" do\n    subject { test_target.oauth_signature(\"GET\", \"https://social.yahooapis.com/v1/user\", {:name => \"diego\", :surname => \"castorina\"}, \"secret2\") }\n    it { should eq(\"xfumZoyVYUbHXSAafdha3HZUqQg%3D\") }\n  end\nend\n"
  },
  {
    "path": "spec/omnicontacts/authorization/oauth2_spec.rb",
    "content": "require \"spec_helper\"\nrequire \"omnicontacts/authorization/oauth2\"\n\ndescribe OmniContacts::Authorization::OAuth2 do\n\n  before(:all) do\n    OAuth2TestClass= Struct.new(:auth_host, :authorize_path, :client_id, :client_secret, :scope, :redirect_uri, :auth_token_path)\n    class OAuth2TestClass\n      include OmniContacts::Authorization::OAuth2\n    end\n  end\n\n  let(:test_target) do\n    OAuth2TestClass.new(\"auth_host\", \"authorize_path\", \"client_id\", \"client_secret\", \"scope\", \"redirect_uri\", \"auth_token_path\")\n  end\n\n  describe \"authorization_url\" do\n\n    subject { test_target.authorization_url }\n\n    it { should include(\"https://#{test_target.auth_host}#{test_target.authorize_path}\") }\n    it { should include(\"client_id=#{test_target.client_id}\") }\n    it { should include(\"scope=#{test_target.scope}\") }\n    it { should include(\"redirect_uri=#{test_target.redirect_uri}\") }\n    it { should include(\"access_type=online\") }\n    it { should include(\"response_type=code\") }\n  end\n\n  let(:access_token_response) { %[{\"access_token\": \"access_token\", \"token_type\":\"token_type\", \"refresh_token\":\"refresh_token\"}] }\n\n  describe \"fetch_access_token\" do\n\n    it \"should provide all mandatory parameters in a https post request\" do\n      code = \"code\"\n      test_target.should_receive(:https_post) do |host, path, params|\n        host.should eq(test_target.auth_host)\n        path.should eq(test_target.auth_token_path)\n        params[:code].should eq(code)\n        params[:client_id].should eq(test_target.client_id)\n        params[:client_secret].should eq(test_target.client_secret)\n        params[:redirect_uri].should eq(test_target.redirect_uri)\n        params[:grant_type].should eq(\"authorization_code\")\n        access_token_response\n      end\n      test_target.fetch_access_token code\n    end\n\n    it \"should successfully parse the token from the JSON response\" do\n      test_target.should_receive(:https_post).and_return(access_token_response)\n      (access_token, token_type, refresh_token) = test_target.fetch_access_token \"code\"\n      access_token.should eq(\"access_token\")\n      token_type.should eq(\"token_type\")\n      refresh_token.should eq(\"refresh_token\")\n    end\n\n    it \"should raise if the http request fails\" do\n      test_target.should_receive(:https_post).and_raise(\"Invalid code\")\n      expect { test_target.fetch_access_token(\"code\") }.to raise_error\n    end\n\n    it \"should raise an error if the JSON response contains an error field\" do\n      test_target.should_receive(:https_post).and_return(%[{\"error\": \"error_message\"}])\n      expect { test_target.fetch_access_token(\"code\") }.to raise_error\n    end\n  end\n\n  describe \"refresh_access_token\" do\n    it \"should provide all mandatory fields in a https post request\" do\n      refresh_token = \"refresh_token\"\n      test_target.should_receive(:https_post) do |host, path, params|\n        host.should eq(test_target.auth_host)\n        path.should eq(test_target.auth_token_path)\n        params[:client_id].should eq(test_target.client_id)\n        params[:client_secret].should eq(test_target.client_secret)\n        params[:refresh_token].should eq(refresh_token)\n        params[:grant_type].should eq(\"refresh_token\")\n        access_token_response\n      end\n      test_target.refresh_access_token refresh_token\n    end\n\n    it \"should successfully parse the token from the JSON response\" do\n      test_target.should_receive(:https_post).and_return(access_token_response)\n      (access_token, token_type, refresh_token) = test_target.refresh_access_token \"refresh_token\"\n      access_token.should eq(\"access_token\")\n      token_type.should eq(\"token_type\")\n      refresh_token.should eq(\"refresh_token\")\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "spec/omnicontacts/http_utils_spec.rb",
    "content": "require \"spec_helper\"\nrequire \"omnicontacts/http_utils\"\n\ndescribe OmniContacts::HTTPUtils do\n\n  describe \"to_query_string\" do\n    it \"should create a query string from a map\" do\n      result = OmniContacts::HTTPUtils.to_query_string(:name => \"john\", :surname => \"doe\")\n      if result.match(/^name/)\n        result.should eq(\"name=john&surname=doe\")\n      else\n        result.should eq(\"surname=doe&name=john\")\n      end\n    end\n\n    it \"should work for integer values in the map\" do\n      result = OmniContacts::HTTPUtils.to_query_string(:client_id => 1234, :secret => \"1234HJL8\")\n      result.should eq(\"client_id=1234&secret=1234HJL8\")\n    end\n\n  end\n\n  describe \"encode\" do\n    it \"should encode the space\" do\n      OmniContacts::HTTPUtils.encode(\"name=\\\"john\\\"\").should eq(\"name%3D%22john%22\")\n    end\n  end\n\n  describe \"query_string_to_map\" do\n    it \"should split a query string into a map\" do\n      query_string = \"name=john&surname=doe\"\n      result = OmniContacts::HTTPUtils.query_string_to_map(query_string)\n      result[\"name\"].should eq(\"john\")\n      result[\"surname\"].should eq(\"doe\")\n    end\n  end\n\n  describe \"host_url_from_rack_env\" do\n    it \"should calculate the host url using the HTTP_HOST variable\" do\n      env = {\"rack.url_scheme\" => \"http\", \"HTTP_HOST\" => \"localhost:8080\", \"SERVER_NAME\" => \"localhost\", \"SERVER_PORT\" => 8080}\n      OmniContacts::HTTPUtils.host_url_from_rack_env(env).should eq(\"http://localhost:8080\")\n    end\n\n    it \"should calculate the host url using SERVER_NAME and SERVER_PORT variables\" do\n      env = {\"rack.url_scheme\" => \"http\", \"SERVER_NAME\" => \"localhost\", \"SERVER_PORT\" => 8080}\n      OmniContacts::HTTPUtils.host_url_from_rack_env(env).should eq(\"http://localhost:8080\")\n    end\n  end\n\n  describe \"https_post\" do\n\n    before(:each) do\n      @connection = double\n      Net::HTTP.should_receive(:new).and_return(@connection)\n      @connection.should_receive(:use_ssl=).with(true)\n      @test_target = Object.new\n      @test_target.extend OmniContacts::HTTPUtils\n      @response = double\n    end\n\n    it \"should execute a request with success\" do\n      @test_target.should_receive(:ssl_ca_file).and_return(nil)\n      @connection.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)\n      @connection.should_receive(:request_post).and_return(@response)\n      @response.should_receive(:code).and_return(\"200\")\n      @response.should_receive(:body).and_return(\"some content\")\n      @test_target.send(:https_post, \"host\", \"path\", {})\n    end\n\n    it \"should raise an exception with response code != 200\" do\n      @test_target.should_receive(:ssl_ca_file).and_return(nil)\n      @connection.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)\n      @connection.should_receive(:request_get).and_return(@response)\n      @response.should_receive(:code).and_return(\"500\")\n      @response.should_receive(:body).and_return(\"some error message\")\n      expect { @test_target.send(:https_get, \"host\", \"path\", {}) }.to raise_error\n    end\n  end\nend\n"
  },
  {
    "path": "spec/omnicontacts/importer/facebook_spec.rb",
    "content": "require \"spec_helper\"\nrequire \"omnicontacts/importer/facebook\"\n\ndescribe OmniContacts::Importer::Facebook do\n\n  let(:facebook) { OmniContacts::Importer::Facebook.new({}, \"client_id\", \"client_secret\") }\n\n  let(:self_response) {\n    '{\n        \"first_name\":\"Chris\",\n        \"last_name\":\"Johnson\",\n        \"name\":\"Chris Johnson\",\n        \"id\":\"543216789\",\n        \"gender\":\"male\",\n        \"birthday\":\"06/21/1982\",\n        \"significant_other\":{\"id\": \"243435322\"},\n        \"relationship_status\": \"Married\",\n        \"picture\":{\"data\":{\"url\":\"http://profile.ak.fbcdn.net/hprofile-ak-snc6/186364_543216789_2089044200_q.jpg\",\"is_silhouette\":false}},\n        \"email\": \"chrisjohnson@gmail.com\"\n    }'\n  }\n\n  let(:spouse_response) {\n    '{\n        \"first_name\":\"Mary\",\n        \"last_name\":\"Johnson\",\n        \"name\":\"Mary Johnson\",\n        \"id\":\"243435322\",\n        \"gender\":\"female\",\n        \"birthday\":\"01/21\",\n        \"picture\":{\"data\":{\"url\":\"http://profile.ak.fbcdn.net/hprofile-ak-snc6/186364_243435322_2089044200_q.jpg\",\"is_silhouette\":false}}\n    }'\n  }\n\n  let(:contacts_as_json) {\n    '{\"data\":[\n        {\n          \"first_name\":\"John\",\n          \"last_name\":\"Smith\",\n          \"name\":\"John Smith\",\n          \"id\":\"608061886\",\n          \"gender\":\"male\",\n          \"birthday\":\"06/21\",\n          \"relationship\":\"cousin\",\n          \"picture\":{\"data\":{\"url\":\"http://profile.ak.fbcdn.net/hprofile-ak-snc6/186364_608061886_2089044200_q.jpg\",\"is_silhouette\":false}}\n        }\n      ]\n    }' }\n\n  describe \"fetch_contacts_using_access_token\" do\n    let(:token) { \"token\" }\n    let(:token_type) { \"token_type\" }\n\n    before(:each) do\n      facebook.instance_variable_set(:@env, {})\n    end\n\n    it \"should request the contacts by providing the token in the url\" do\n      facebook.should_receive(:https_get) do |host, self_path, params, headers|\n        params[:access_token].should eq(token)\n        params[:fields].should eq('first_name,last_name,name,id,gender,birthday,picture,relationship_status,significant_other,email')\n        self_response\n      end\n      facebook.should_receive(:https_get) do |host, spouse_path, params, headers|\n        params[:access_token].should eq(token)\n        params[:fields].should eq('first_name,last_name,name,id,gender,birthday,picture')\n        spouse_response\n      end\n      facebook.should_receive(:https_get) do |host, path, params, headers|\n        params[:access_token].should eq(token)\n        params[:fields].should eq('first_name,last_name,name,id,gender,birthday,picture,relationship')\n        contacts_as_json\n      end.exactly(1).times\n      facebook.should_receive(:https_get) do |host, path, params, headers|\n        params[:access_token].should eq(token)\n        params[:fields].should eq('first_name,last_name,name,id,gender,birthday,picture')\n        contacts_as_json\n      end.exactly(1).times\n\n      facebook.fetch_contacts_using_access_token token, token_type\n    end\n\n    it \"should correctly parse id, name,email,gender, birthday, profile picture and relation\" do\n      1.times { facebook.should_receive(:https_get).and_return(self_response) }\n      1.times { facebook.should_receive(:https_get) }\n      2.times { facebook.should_receive(:https_get).and_return(contacts_as_json) }\n      result = facebook.fetch_contacts_using_access_token token, token_type\n      result.size.should be(1)\n      result.first[:id].should eq('608061886')\n      result.first[:first_name].should eq('John')\n      result.first[:last_name].should eq('Smith')\n      result.first[:name].should eq('John Smith')\n      result.first[:email].should be_nil\n      result.first[:gender].should eq('male')\n      result.first[:birthday].should eq({:day=>21, :month=>06, :year=>nil})\n      result.first[:profile_picture].should eq('https://graph.facebook.com/608061886/picture')\n      result.first[:relation].should eq('cousin')\n    end\n\n    it \"should correctly parse and set logged in user information\" do\n      1.times { facebook.should_receive(:https_get).and_return(self_response) }\n      1.times { facebook.should_receive(:https_get) }\n      2.times { facebook.should_receive(:https_get).and_return(contacts_as_json) }\n\n      facebook.fetch_contacts_using_access_token token, token_type\n\n      user = facebook.instance_variable_get(:@env)[\"omnicontacts.user\"]\n      user.should_not be_nil\n      user[:id].should eq(\"543216789\")\n      user[:first_name].should eq(\"Chris\")\n      user[:last_name].should eq(\"Johnson\")\n      user[:name].should eq(\"Chris Johnson\")\n      user[:email].should eq(\"chrisjohnson@gmail.com\")\n      user[:gender].should eq(\"male\")\n      user[:birthday].should eq({:day=>21, :month=>06, :year=>1982})\n      user[:profile_picture].should eq(\"https://graph.facebook.com/543216789/picture\")\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/omnicontacts/importer/gmail_spec.rb",
    "content": "require \"spec_helper\"\nrequire \"omnicontacts/importer/gmail\"\n\ndescribe OmniContacts::Importer::Gmail do\n  let(:gmail) { OmniContacts::Importer::Gmail.new({}, \"client_id\", \"client_secret\") }\n\n  let(:gmail_with_scope_args) {\n    OmniContacts::Importer::Gmail.new(\n      {},\n      \"client_id\",\n      \"client_secret\",\n      {\n        scope: %w(\n          https://www.googleapis.com/auth/contacts.readonly\n          https://www.googleapis.com/auth/userinfo#email\n          https://www.googleapis.com/auth/userinfo.profile\n        ).join(\" \")\n      }\n    )\n  }\n\n  let(:self_response) {\n    '{\n      \"id\":\"16482944006464829443\",\n      \"email\":\"chrisjohnson@gmail.com\",\n      \"name\":\"Chris Johnson\",\n      \"given_name\":\"Chris\",\n      \"family_name\":\"Johnson\",\n      \"picture\":\"https://lh3.googleusercontent.com/-b8aFbTBM/AAAAAAI/IWA/vsek/photo.jpg\",\n      \"gender\":\"male\",\n      \"birthday\":\"1982-06-21\"\n    }'\n  }\n\n  let(:contacts_as_json) {\n    '{\"version\":\"1.0\",\"encoding\":\"UTF-8\",\n        \"feed\":{\n          \"xmlns\":\"http://www.w3.org/2005/Atom\",\n          \"xmlns$openSearch\":\"http://a9.com/-/spec/opensearch/1.1/\",\n          \"xmlns$gContact\":\"http://schemas.google.com/contact/2008\",\n          \"xmlns$batch\":\"http://schemas.google.com/gdata/batch\",\n          \"xmlns$gd\":\"http://schemas.google.com/g/2005\",\n          \"gd$etag\":\"W/\\\"C0YHRno7fSt7I2A9WhBSQ0Q.\\\"\",\n\n          \"id\":{\"$t\":\"logged_in_user@gmail.com\"},\n          \"updated\":{\"$t\":\"2013-02-20T20:12:17.405Z\"},\n          \"category\":[{\n            \"scheme\":\"http://schemas.google.com/g/2005#kind\",\n            \"term\":\"http://schemas.google.com/contact/2008#contact\"\n           }],\n\n          \"title\":{\"$t\":\"Users\\'s Contacts\"},\n          \"link\":[\n            {\"rel\":\"http://schemas.google.com/contacts/2008/rel#photo\",\"type\":\"image/*\",\n             \"href\":\"https://www.google.com/m8/feeds/photos/media/logged_in_user%40gmail.com/6b41d030b05abc\",\"gd$etag\":\"\\\"VSxuN0cISit7I2A1UVUSdy12KHwgBFkE333.\\\"\"},\n            {\"rel\":\"alternate\",\"type\":\"text/html\",\"href\":\"http://www.google.com/\"},\n            {\"rel\":\"http://schemas.google.com/g/2005#feed\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full\"},\n            {\"rel\":\"http://schemas.google.com/g/2005#post\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full\"},\n            {\"rel\":\"http://schemas.google.com/g/2005#batch\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full/batch\"},\n            {\"rel\":\"self\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full?alt\\u003djson\\u0026max-results\\u003d1\"},\n            {\"rel\":\"next\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full?alt\\u003djson\\u0026start-index\\u003d2\\u0026max-results\\u003d1\"}\n          ],\n          \"author\":[{\"name\":{\"$t\":\"Edward\"},\"email\":{\"$t\":\"logged_in_user@gmail.com\"}}],\n          \"generator\":{\"version\":\"1.0\",\"uri\":\"http://www.google.com/m8/feeds\",\"$t\":\"Contacts\"},\n          \"openSearch$totalResults\":{\"$t\":\"1007\"},\n          \"openSearch$startIndex\":{\"$t\":\"1\"},\n          \"openSearch$itemsPerPage\":{\"$t\":\"1\"},\n          \"entry\":[\n            {\n            \"gd$etag\":\"\\\"R3oyfDVSLyt7I2A9WhBTSEULRA0.\\\"\",\n            \"id\":{\"$t\":\"http://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/base/1\"},\n            \"updated\":{\"$t\":\"2013-02-14T22:36:36.494Z\"},\n            \"app$edited\":{\"xmlns$app\":\"http://www.w3.org/2007/app\",\"$t\":\"2013-02-14T22:36:36.494Z\"},\n            \"category\":[{\"scheme\":\"http://schemas.google.com/g/2005#kind\",\"term\":\"http://schemas.google.com/contact/2008#contact\"}],\n            \"title\":{\"$t\":\"Edward Bennet\"},\n            \"link\":[\n              {\"rel\":\"http://schemas.google.com/contacts/2008/rel#photo\",\"type\":\"image/*\",\n               \"href\":\"https://www.google.com/m8/feeds/photos/media/logged_in_user%40gmail.com/6b41d030b05abc\", \"gd$etag\":\"\\\"VSxuN0cISit7I2A1UVUSdy12KHwgBFkE333.\\\"\"},\n              {\"rel\":\"self\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full/1\"},\n              {\"rel\":\"edit\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full/1\"}\n            ],\n            \"gd$name\":{\n              \"gd$fullName\":{\"$t\":\"Edward Bennet\"},\n              \"gd$givenName\":{\"$t\":\"Edward\"},\n              \"gd$familyName\":{\"$t\":\"Bennet\"}\n            },\n            \"gd$organization\":[{\"rel\":\"http://schemas.google.com/g/2005#other\",\"gd$orgName\":{\"$t\":\"Google\"},\"gd$orgTitle\":{\"$t\":\"Master Developer\"}}],\n            \"gContact$birthday\":{\"when\":\"1954-07-02\"},\n            \"gContact$relation\":{\"rel\":\"father\"},\n            \"gContact$gender\":{\"value\":\"male\"},\n            \"gContact$event\":[{\"rel\":\"anniversary\",\"gd$when\":{\"startTime\":\"1983-04-21\"}},{\"label\":\"New Job\",\"gd$when\":{\"startTime\":\"2014-12-01\"}}],\n            \"gd$email\":[{\"rel\":\"http://schemas.google.com/g/2005#other\",\"address\":\"bennet@gmail.com\",\"primary\":\"true\"}],\n            \"gContact$groupMembershipInfo\":[{\"deleted\":\"false\",\"href\":\"http://www.google.com/m8/feeds/groups/logged_in_user%40gmail.com/base/6\"}],\n            \"gd$structuredPostalAddress\":[{\"rel\":\"http://schemas.google.com/g/2005#home\",\"gd$formattedAddress\":{\"$t\":\"1313 Trashview Court\\nApt. 13\\nNowheresville, OK 66666\"},\"gd$street\":{\"$t\":\"1313 Trashview Court\\nApt. 13\"},\"gd$postcode\":{\"$t\":\"66666\"},\"gd$country\":{\"code\":\"VA\",\"$t\":\"Valoran\"},\"gd$city\":{\"$t\":\"Nowheresville\"},\"gd$region\":{\"$t\":\"OK\"}}],\n            \"gd$phoneNumber\":[{\"rel\":\"http://schemas.google.com/g/2005#mobile\",\"uri\":\"tel:+34-653-15-76-88\",\"$t\":\"653157688\"}]\n          },\n          {\n            \"gd$etag\":\"\\\"R3oyfDVSLyt7I2A9WhBTSEULRA0.\\\"\",\n            \"id\":{\"$t\":\"http://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/base/1\"},\n            \"updated\":{\"$t\":\"2013-02-15T22:36:36.494Z\"},\n            \"app$edited\":{\"xmlns$app\":\"http://www.w3.org/2007/app\",\"$t\":\"2013-02-15T22:36:36.494Z\"},\n            \"category\":[{\"scheme\":\"http://schemas.google.com/g/2005#kind\",\"term\":\"http://schemas.google.com/contact/2008#contact\"}],\n            \"title\":{\"$t\":\"Emilia Fox\"},\n            \"link\":[\n              {\"rel\":\"http://schemas.google.com/contacts/2008/rel#photo\",\"type\":\"image/*\",\"href\":\"https://www.google.com/m8/feeds/photos/media/logged_in_user%40gmail.com/1\"},\n              {\"rel\":\"self\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full/1\"},\n              {\"rel\":\"edit\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full/1\"}\n            ],\n            \"gd$name\":{\n              \"gd$fullName\":{\"$t\":\"Emilia Fox\"},\n              \"gd$givenName\":{\"$t\":\"Emilia\"},\n              \"gd$familyName\":{\"$t\":\"Fox\"}\n            },\n            \"gContact$birthday\":{\"when\":\"1974-02-10\"},\n            \"gContact$relation\":[{\"rel\":\"spouse\"}],\n            \"gContact$gender\":{\"value\":\"female\"},\n            \"gd$email\":[{\"rel\":\"http://schemas.google.com/g/2005#other\",\"address\":\"emilia.fox@gmail.com\",\"primary\":\"true\"}],\n            \"gContact$groupMembershipInfo\":[{\"deleted\":\"false\",\"href\":\"http://www.google.com/m8/feeds/groups/logged_in_user%40gmail.com/base/6\"}]\n          }]\n        }\n      }'\n  }\n\n  describe \"fetch_contacts_using_access_token\" do\n    let(:token) { \"token\" }\n    let(:token_type) { \"token_type\" }\n\n    before(:each) do\n      gmail.instance_variable_set(:@env, {})\n      gmail_with_scope_args.instance_variable_set(:@env, {})\n    end\n\n    it \"should request the contacts by specifying version and code in the http headers\" do\n      gmail.should_receive(:https_get) do |host, path, params, headers|\n        headers[\"GData-Version\"].should eq(\"3.0\")\n        headers[\"Authorization\"].should eq(\"#{token_type} #{token}\")\n        self_response\n      end\n      gmail.should_receive(:https_get) do |host, path, params, headers|\n        headers[\"GData-Version\"].should eq(\"3.0\")\n        headers[\"Authorization\"].should eq(\"#{token_type} #{token}\")\n        contacts_as_json\n      end\n      gmail.fetch_contacts_using_access_token token, token_type\n\n      gmail.scope.should eq \"https://www.googleapis.com/auth/contacts.readonly https://www.googleapis.com/auth/userinfo#email https://www.googleapis.com/auth/userinfo.profile\"\n      gmail_with_scope_args.scope.should eq \"https://www.googleapis.com/auth/contacts.readonly https://www.googleapis.com/auth/userinfo#email https://www.googleapis.com/auth/userinfo.profile\"\n    end\n\n    it \"should correctly parse id, name, email, gender, birthday, profile picture and relation for 1st contact\" do\n      gmail.should_receive(:https_get)\n      gmail.should_receive(:https_get).and_return(contacts_as_json)\n      result = gmail.fetch_contacts_using_access_token token, token_type\n\n      result.size.should be(2)\n      result.first[:id].should eq('http://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/base/1')\n      result.first[:first_name].should eq('Edward')\n      result.first[:last_name].should eq('Bennet')\n      result.first[:name].should eq(\"Edward Bennet\")\n      result.first[:email].should eq(\"bennet@gmail.com\")\n      result.first[:gender].should eq(\"male\")\n      result.first[:birthday].should eq({ :day => 02, :month => 07, :year => 1954 })\n      result.first[:relation].should eq('father')\n      result.first[:profile_picture].should eq(\"https://www.google.com/m8/feeds/photos/media/logged_in_user%40gmail.com/6b41d030b05abc?&access_token=token\")\n      result.first[:dates][0][:name].should eq(\"anniversary\")\n    end\n\n    it \"should correctly parse id, name, email, gender, birthday, profile picture, snailmail address, phone and relation for 2nd contact\" do\n      gmail.should_receive(:https_get)\n      gmail.should_receive(:https_get).and_return(contacts_as_json)\n      result = gmail.fetch_contacts_using_access_token token, token_type\n      result.size.should be(2)\n      result.last[:id].should eq('http://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/base/1')\n      result.last[:first_name].should eq('Emilia')\n      result.last[:last_name].should eq('Fox')\n      result.last[:name].should eq(\"Emilia Fox\")\n      result.last[:email].should eq(\"emilia.fox@gmail.com\")\n      result.last[:gender].should eq(\"female\")\n      result.last[:birthday].should eq({ :day => 10, :month => 02, :year => 1974 })\n      result.last[:profile_picture].should be_nil\n      result.last[:relation].should eq('spouse')\n      result.first[:address_1].should eq('1313 Trashview Court')\n      result.first[:address_2].should eq('Apt. 13')\n      result.first[:city].should eq('Nowheresville')\n      result.first[:region].should eq('OK')\n      result.first[:country].should eq('VA')\n      result.first[:postcode].should eq('66666')\n      result.first[:phone_number].should eq('653157688')\n    end\n\n    it \"should correctly parse and set logged in user information\" do\n      gmail.should_receive(:https_get).and_return(self_response)\n      gmail.should_receive(:https_get).and_return(contacts_as_json)\n\n      gmail.fetch_contacts_using_access_token token, token_type\n\n      user = gmail.instance_variable_get(:@env)[\"omnicontacts.user\"]\n      user.should_not be_nil\n      user[:id].should eq(\"16482944006464829443\")\n      user[:first_name].should eq(\"Chris\")\n      user[:last_name].should eq(\"Johnson\")\n      user[:name].should eq(\"Chris Johnson\")\n      user[:email].should eq(\"chrisjohnson@gmail.com\")\n      user[:gender].should eq(\"male\")\n      user[:birthday].should eq({ :day => 21, :month => 06, :year => 1982 })\n      user[:profile_picture].should eq(\"https://lh3.googleusercontent.com/-b8aFbTBM/AAAAAAI/IWA/vsek/photo.jpg\")\n    end\n\n    context \"when address_1 is nil\" do\n      let(:contacts_as_json) {\n        '{\"version\":\"1.0\",\"encoding\":\"UTF-8\",\n        \"feed\":{\n          \"xmlns\":\"http://www.w3.org/2005/Atom\",\n          \"xmlns$openSearch\":\"http://a9.com/-/spec/opensearch/1.1/\",\n          \"xmlns$gContact\":\"http://schemas.google.com/contact/2008\",\n          \"xmlns$batch\":\"http://schemas.google.com/gdata/batch\",\n          \"xmlns$gd\":\"http://schemas.google.com/g/2005\",\n          \"gd$etag\":\"W/\\\"C0YHRno7fSt7I2A9WhBSQ0Q.\\\"\",\n\n          \"id\":{\"$t\":\"logged_in_user@gmail.com\"},\n          \"updated\":{\"$t\":\"2013-02-20T20:12:17.405Z\"},\n          \"category\":[{\n            \"scheme\":\"http://schemas.google.com/g/2005#kind\",\n            \"term\":\"http://schemas.google.com/contact/2008#contact\"\n           }],\n\n          \"title\":{\"$t\":\"Users\\'s Contacts\"},\n          \"link\":[\n            {\"rel\":\"http://schemas.google.com/contacts/2008/rel#photo\",\"type\":\"image/*\",\n             \"href\":\"https://www.google.com/m8/feeds/photos/media/logged_in_user%40gmail.com/6b41d030b05abc\",\"gd$etag\":\"\\\"VSxuN0cISit7I2A1UVUSdy12KHwgBFkE333.\\\"\"},\n            {\"rel\":\"alternate\",\"type\":\"text/html\",\"href\":\"http://www.google.com/\"},\n            {\"rel\":\"http://schemas.google.com/g/2005#feed\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full\"},\n            {\"rel\":\"http://schemas.google.com/g/2005#post\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full\"},\n            {\"rel\":\"http://schemas.google.com/g/2005#batch\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full/batch\"},\n            {\"rel\":\"self\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full?alt\\u003djson\\u0026max-results\\u003d1\"},\n            {\"rel\":\"next\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full?alt\\u003djson\\u0026start-index\\u003d2\\u0026max-results\\u003d1\"}\n          ],\n          \"author\":[{\"name\":{\"$t\":\"Edward\"},\"email\":{\"$t\":\"logged_in_user@gmail.com\"}}],\n          \"generator\":{\"version\":\"1.0\",\"uri\":\"http://www.google.com/m8/feeds\",\"$t\":\"Contacts\"},\n          \"openSearch$totalResults\":{\"$t\":\"1007\"},\n          \"openSearch$startIndex\":{\"$t\":\"1\"},\n          \"openSearch$itemsPerPage\":{\"$t\":\"1\"},\n          \"entry\":[\n            {\n            \"gd$etag\":\"\\\"R3oyfDVSLyt7I2A9WhBTSEULRA0.\\\"\",\n            \"id\":{\"$t\":\"http://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/base/1\"},\n            \"updated\":{\"$t\":\"2013-02-14T22:36:36.494Z\"},\n            \"app$edited\":{\"xmlns$app\":\"http://www.w3.org/2007/app\",\"$t\":\"2013-02-14T22:36:36.494Z\"},\n            \"category\":[{\"scheme\":\"http://schemas.google.com/g/2005#kind\",\"term\":\"http://schemas.google.com/contact/2008#contact\"}],\n            \"title\":{\"$t\":\"Edward Bennet\"},\n            \"link\":[\n              {\"rel\":\"http://schemas.google.com/contacts/2008/rel#photo\",\"type\":\"image/*\",\n               \"href\":\"https://www.google.com/m8/feeds/photos/media/logged_in_user%40gmail.com/6b41d030b05abc\", \"gd$etag\":\"\\\"VSxuN0cISit7I2A1UVUSdy12KHwgBFkE333.\\\"\"},\n              {\"rel\":\"self\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full/1\"},\n              {\"rel\":\"edit\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full/1\"}\n            ],\n            \"gd$name\":{\n              \"gd$fullName\":{\"$t\":\"Edward Bennet\"},\n              \"gd$givenName\":{\"$t\":\"Edward\"},\n              \"gd$familyName\":{\"$t\":\"Bennet\"}\n            },\n            \"gd$organization\":[{\"rel\":\"http://schemas.google.com/g/2005#other\",\"gd$orgName\":{\"$t\":\"Google\"},\"gd$orgTitle\":{\"$t\":\"Master Developer\"}}],\n            \"gContact$birthday\":{\"when\":\"1954-07-02\"},\n            \"gContact$relation\":{\"rel\":\"father\"},\n            \"gContact$gender\":{\"value\":\"male\"},\n            \"gContact$event\":[{\"rel\":\"anniversary\",\"gd$when\":{\"startTime\":\"1983-04-21\"}},{\"label\":\"New Job\",\"gd$when\":{\"startTime\":\"2014-12-01\"}}],\n            \"gd$email\":[{\"rel\":\"http://schemas.google.com/g/2005#other\",\"address\":\"bennet@gmail.com\",\"primary\":\"true\"}],\n            \"gContact$groupMembershipInfo\":[{\"deleted\":\"false\",\"href\":\"http://www.google.com/m8/feeds/groups/logged_in_user%40gmail.com/base/6\"}],\n            \"gd$structuredPostalAddress\":[{\"rel\":\"http://schemas.google.com/g/2005#home\",\"gd$formattedAddress\":{},\"gd$street\":{},\"gd$postcode\":{\"$t\":\"66666\"},\"gd$country\":{\"code\":\"VA\",\"$t\":\"Valoran\"},\"gd$city\":{\"$t\":\"Nowheresville\"},\"gd$region\":{\"$t\":\"OK\"}}],\n            \"gd$phoneNumber\":[{\"rel\":\"http://schemas.google.com/g/2005#mobile\",\"uri\":\"tel:+34-653-15-76-88\",\"$t\":\"653157688\"}]\n          },\n          {\n            \"gd$etag\":\"\\\"R3oyfDVSLyt7I2A9WhBTSEULRA0.\\\"\",\n            \"id\":{\"$t\":\"http://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/base/1\"},\n            \"updated\":{\"$t\":\"2013-02-15T22:36:36.494Z\"},\n            \"app$edited\":{\"xmlns$app\":\"http://www.w3.org/2007/app\",\"$t\":\"2013-02-15T22:36:36.494Z\"},\n            \"category\":[{\"scheme\":\"http://schemas.google.com/g/2005#kind\",\"term\":\"http://schemas.google.com/contact/2008#contact\"}],\n            \"title\":{\"$t\":\"Emilia Fox\"},\n            \"link\":[\n              {\"rel\":\"http://schemas.google.com/contacts/2008/rel#photo\",\"type\":\"image/*\",\"href\":\"https://www.google.com/m8/feeds/photos/media/logged_in_user%40gmail.com/1\"},\n              {\"rel\":\"self\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full/1\"},\n              {\"rel\":\"edit\",\"type\":\"application/atom+xml\",\"href\":\"https://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/full/1\"}\n            ],\n            \"gd$name\":{\n              \"gd$fullName\":{\"$t\":\"Emilia Fox\"},\n              \"gd$givenName\":{\"$t\":\"Emilia\"},\n              \"gd$familyName\":{\"$t\":\"Fox\"}\n            },\n            \"gContact$birthday\":{\"when\":\"1974-02-10\"},\n            \"gContact$relation\":[{\"rel\":\"spouse\"}],\n            \"gContact$gender\":{\"value\":\"female\"},\n            \"gd$email\":[{\"rel\":\"http://schemas.google.com/g/2005#other\",\"address\":\"emilia.fox@gmail.com\",\"primary\":\"true\"}],\n            \"gContact$groupMembershipInfo\":[{\"deleted\":\"false\",\"href\":\"http://www.google.com/m8/feeds/groups/logged_in_user%40gmail.com/base/6\"}]\n          }]\n        }\n      }'\n      }\n\n      it \"should correctly parse id, name, email, gender, birthday, profile picture, snailmail address, phone and relation for 2nd contact\" do\n        gmail.should_receive(:https_get)\n        gmail.should_receive(:https_get).and_return(contacts_as_json)\n        result = gmail.fetch_contacts_using_access_token token, token_type\n        result.size.should be(2)\n        result.last[:id].should eq('http://www.google.com/m8/feeds/contacts/logged_in_user%40gmail.com/base/1')\n        result.last[:first_name].should eq('Emilia')\n        result.last[:last_name].should eq('Fox')\n        result.last[:name].should eq(\"Emilia Fox\")\n        result.last[:email].should eq(\"emilia.fox@gmail.com\")\n        result.last[:gender].should eq(\"female\")\n        result.last[:birthday].should eq({ :day => 10, :month => 02, :year => 1974 })\n        result.last[:profile_picture].should be_nil\n        result.last[:relation].should eq('spouse')\n        result.first[:address_1].should eq(nil)\n        result.first[:address_2].should eq(nil)\n        result.first[:city].should eq('Nowheresville')\n        result.first[:region].should eq('OK')\n        result.first[:country].should eq('VA')\n        result.first[:postcode].should eq('66666')\n        result.first[:phone_number].should eq('653157688')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/omnicontacts/importer/hotmail_spec.rb",
    "content": "require \"spec_helper\"\nrequire \"omnicontacts/importer/hotmail\"\n\ndescribe OmniContacts::Importer::Hotmail do\n\n  let(:permissions) { \"perm1, perm2\" }\n  let(:hotmail) { OmniContacts::Importer::Hotmail.new({}, \"client_id\", \"client_secret\", {:permissions => permissions}) }\n\n  let(:self_response) {\n    '{\n      \"id\": \"4502de12390223d0\",\n      \"name\": \"Chris Johnson\",\n      \"first_name\": \"Chris\",\n      \"last_name\": \"Johnson\",\n      \"birth_day\": 21,\n      \"birth_month\": 6,\n      \"birth_year\": 1982,\n      \"gender\": null,\n      \"emails\": {\"preferred\":\"chrisjohnson@gmail.com\", \"account\":\"chrisjohn@gmail.com\", \"personal\":null, \"business\":null}\n    }'\n  }\n\n  let(:contacts_as_json) {\n    '{\n   \"data\": [\n       {\n         \"id\": \"contact.7fac34bb000000000000000000000000\",\n         \"first_name\": \"John\",\n         \"last_name\": \"Smith\",\n         \"name\": \"John Smith\",\n         \"gender\": null,\n         \"user_id\": \"123456\",\n         \"is_friend\": false,\n         \"is_favorite\": false,\n         \"birth_day\": 5,\n         \"birth_month\": 6,\n         \"birth_year\":1952,\n         \"email_hashes\":[\"1234567890\"]\n      }\n    ]}'\n  }\n\n  describe \"fetch_contacts_using_access_token\" do\n\n    let(:token) { \"token\" }\n    let(:token_type) { \"token_type\" }\n\n    before(:each) do\n      hotmail.instance_variable_set(:@env, {\"HTTP_HOST\" => \"http://example.com\"})\n    end\n\n    it \"should request the contacts by providing the token in the url\" do\n      hotmail.should_receive(:https_get) do |host, path, params, headers|\n        params[:access_token].should eq(token)\n        self_response\n      end\n\n      hotmail.should_receive(:https_get) do |host, path, params, headers|\n        params[:access_token].should eq(token)\n        contacts_as_json\n      end\n      hotmail.fetch_contacts_using_access_token token, token_type\n    end\n\n    it \"should set requested permissions in the authorization url\" do\n      hotmail.authorization_url.should match(/scope=#{Regexp.quote(CGI.escape(permissions))}/)\n    end\n\n    it \"should correctly parse id, name, email, gender, birthday, profile picture, relation and email hashes\" do\n      hotmail.should_receive(:https_get).and_return(self_response)\n      hotmail.should_receive(:https_get).and_return(contacts_as_json)\n      result = hotmail.fetch_contacts_using_access_token token, token_type\n\n      result.size.should be(1)\n      result.first[:id].should eq('123456')\n      result.first[:first_name].should eq(\"John\")\n      result.first[:last_name].should eq('Smith')\n      result.first[:name].should eq(\"John Smith\")\n      result.first[:email].should be_nil\n      result.first[:gender].should be_nil\n      result.first[:birthday].should eq({:day=>5, :month=>6, :year=>1952})\n      result.first[:profile_picture].should eq('https://apis.live.net/v5.0/123456/picture')\n      result.first[:relation].should be_nil\n      result.first[:email_hashes].should eq([\"1234567890\"])\n    end\n\n    it \"should correctly parse and set logged in user information\" do\n      hotmail.should_receive(:https_get).and_return(self_response)\n      hotmail.should_receive(:https_get).and_return(contacts_as_json)\n\n      hotmail.fetch_contacts_using_access_token token, token_type\n\n      user = hotmail.instance_variable_get(:@env)[\"omnicontacts.user\"]\n      user.should_not be_nil\n      user[:id].should eq('4502de12390223d0')\n      user[:first_name].should eq('Chris')\n      user[:last_name].should eq('Johnson')\n      user[:name].should eq('Chris Johnson')\n      user[:gender].should be_nil\n      user[:birthday].should eq({:day=>21, :month=>06, :year=>1982})\n      user[:email].should eq('chrisjohn@gmail.com')\n      user[:profile_picture].should eq('https://apis.live.net/v5.0/4502de12390223d0/picture')\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/omnicontacts/importer/linkedin_spec.rb",
    "content": "require \"spec_helper\"\nrequire \"omnicontacts/importer/linkedin\"\n\ndescribe OmniContacts::Importer::Linkedin do\n\n  let(:linkedin) { OmniContacts::Importer::Linkedin.new({}, \"client_id\", \"client_secret\", state: \"ipsaeumeaque\") }\n\n  let(:contacts_as_json) do\n    \"{\n      \\n  \\\"_total\\\":  2,\n      \\n  \\\"values\\\":  [\n        \\n    {\n          \\n      \\\"firstName\\\":  \\\"Adolf\\\",\n          \\n      \\\"id\\\":  \\\"k71S5q6MKe\\\",\n          \\n      \\\"lastName\\\":  \\\"Witting\\\",\n          \\n      \\\"pictureUrl\\\":  \\\"https://media.licdn.com/mpr/mprx/0_mLnj-7szw130pFRLB8Op7-p1Sxoyv53U3B47Scp1Sxoyv53U3B47Scp1Sxoyv53U3B47Sc\\\"\\n\n        },\n        \\n    {\n          \\n      \\\"firstName\\\":  \\\"Emmet\\\",\n          \\n      \\\"id\\\":  \\\"ms5r3lI3J2\\\",\n          \\n      \\\"lastName\\\":  \\\"Little\\\",\n          \\n      \\\"pictureUrl\\\":  \\\"https://media.licdn.com/mpr/mprx/0_iH9m158zCdISt1X6iH9m158zCdISt1X6iH9m158zCdISt1X6iH9m158zCdISt1X6iH9m158zCdISt1X6\\\"\\n\n        }\n      ]\\n\n    }\"\n  end\n\n  describe \"fetch_contacts_using_access_token\" do\n    let(:token) { \"token\" }\n    let(:token_type) { nil }\n\n    before(:each) do\n      linkedin.instance_variable_set(:@env, {})\n    end\n\n    it \"should request the contacts by specifying code in the http headers\" do\n      linkedin.should_receive(:https_get) do |host, path, params, headers|\n        headers[\"Authorization\"].should eq(\"Bearer #{token}\")\n        contacts_as_json\n      end\n      linkedin.fetch_contacts_using_access_token token, token_type\n    end\n\n    it \"should correctly parse id, name, and profile picture for 1st contact\" do\n      linkedin.should_receive(:https_get).and_return(contacts_as_json)\n      result = linkedin.fetch_contacts_using_access_token token, token_type\n\n      result.size.should be(2)\n      result.first[:id].should eq('k71S5q6MKe')\n      result.first[:first_name].should eq('Adolf')\n      result.first[:last_name].should eq('Witting')\n      result.first[:name].should eq(\"Adolf Witting\")\n      result.first[:profile_picture].should eq(\"https://media.licdn.com/mpr/mprx/0_mLnj-7szw130pFRLB8Op7-p1Sxoyv53U3B47Scp1Sxoyv53U3B47Scp1Sxoyv53U3B47Sc\")\n    end\n\n    it \"should correctly parse id, name, and profile picture for 2nd contact\" do\n      linkedin.should_receive(:https_get).and_return(contacts_as_json)\n      result = linkedin.fetch_contacts_using_access_token token, token_type\n      result.size.should be(2)\n      result.last[:id].should eq('ms5r3lI3J2')\n      result.last[:first_name].should eq('Emmet')\n      result.last[:last_name].should eq('Little')\n      result.last[:name].should eq(\"Emmet Little\")\n      result.last[:profile_picture].should eq(\"https://media.licdn.com/mpr/mprx/0_iH9m158zCdISt1X6iH9m158zCdISt1X6iH9m158zCdISt1X6iH9m158zCdISt1X6iH9m158zCdISt1X6\")\n    end\n  end\nend"
  },
  {
    "path": "spec/omnicontacts/importer/outlook_spec.rb",
    "content": "require \"spec_helper\"\nrequire \"omnicontacts/importer/outlook\"\n\ndescribe OmniContacts::Importer::Outlook do\n\n  let(:permissions) { \"Contacts.Read\" }\n  let(:outlook) { OmniContacts::Importer::Outlook.new({}, \"app_id\", \"app_secret\", {:permissions => permissions}) }\n\n  let(:self_response) {\n    '{\n      \"@odata.context\": \"https://outlook.office.com/api/v2.0/$metadata#Me\",\n      \"@odata.id\": \"https://outlook.office.com/api/v2.0/Users(\\'00034001-df52-d3d5-0000-000000000000@84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa\\')\",\n      \"Id\": \"00034001-df52-d3d5-0000-000000000000@84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa\",\n      \"EmailAddress\": \"test.user@outlook.com\",\n      \"DisplayName\": \"Test User\",\n      \"Alias\": \"puid-00034001DF52D3D5\",\n      \"MailboxGuid\": \"00034001-df52-d3d5-0000-000000000000\"\n    }'\n  }\n\n  let(:contacts_as_json) {\n    '{\n      \"@odata.context\": \"https://outlook.office.com/api/v2.0/$metadata#Me/Contacts\",\n      \"value\": [{\n        \"@odata.id\": \"https://outlook.office.com/api/v2.0/Users(\\'00034001-df52-d3d5-0000-000000000000@84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa\\')/Contacts(\\'AQMkADAwATM0MDAAMS1kZjUyLWQzZDUtMDACLTAwCgBGAAADQ1hAWLJpwk6DZYyOhnclvgcAxCL3G7jnpkiRUVmiNrhjJgAAAgEOAAAAxCL3G7jnpkiRUVmiNrhjJgAAAixsAAAA\\')\",\n        \"@odata.etag\": \"W/\\\"EQAAABYAAADEIvcbuOemSJFRWaI2uGMmAAAAlo4t\\\"\",\n        \"Id\": \"AQMkADAwATM0MDAAMS1kZjUyLWQzZDUtMDACLTAwCgBGAAADQ1hAWLJpwk6DZYyOhnclvgcAxCL3G7jnpkiRUVmiNrhjJgAAAgEOAAAAxCL3G7jnpkiRUVmiNrhjJgAAAixsAAAA\",\n        \"CreatedDateTime\": \"2016-04-13T21:25:24Z\",\n        \"LastModifiedDateTime\": \"2016-04-14T19:36:55Z\",\n        \"ChangeKey\": \"EQAAABYAAADEIvcbuOemSJFRWaI2uGMmAAAAlo4t\",\n        \"Categories\": [],\n        \"ParentFolderId\": \"AQMkADAwATM0MDAAMS1kZjUyLWQzZDUtMDACLTAwCgAuAAADQ1hAWLJpwk6DZYyOhnclvgEAxCL3G7jnpkiRUVmiNrhjJgAAAgEOAAAA\",\n        \"Birthday\": \"1604-08-14T00:00:00Z\",\n        \"FileAs\": \"Contact, First\",\n        \"DisplayName\": \"First Contact\",\n        \"GivenName\": \"First\",\n        \"Initials\": null,\n        \"MiddleName\": null,\n        \"NickName\": null,\n        \"Surname\": \"Contact\",\n        \"Title\": null,\n        \"YomiGivenName\": null,\n        \"YomiSurname\": null,\n        \"YomiCompanyName\": null,\n        \"Generation\": null,\n        \"EmailAddresses\": [{\n          \"Name\": \"contact.first@email.com\",\n          \"Address\": \"contact.first@email.com\"\n        }, {\n          \"Name\": \"contact.second@email.com\",\n          \"Address\": \"contact.second@email.com\"\n        }],\n        \"ImAddresses\": [],\n        \"JobTitle\": null,\n        \"CompanyName\": null,\n        \"Department\": null,\n        \"OfficeLocation\": null,\n        \"Profession\": null,\n        \"BusinessHomePage\": null,\n        \"AssistantName\": null,\n        \"Manager\": null,\n        \"HomePhones\": [],\n        \"MobilePhone1\": null,\n        \"BusinessPhones\": [],\n        \"HomeAddress\": {\n          \"Street\": \"address1\",\n          \"City\": \"city\",\n          \"State\": \"state\",\n          \"CountryOrRegion\": \"US\",\n          \"PostalCode\": \"89111\"\n        },\n        \"BusinessAddress\": {},\n        \"OtherAddress\": {},\n        \"SpouseName\": null,\n        \"PersonalNotes\": null,\n        \"Children\": []\n      }]\n    }'\n  }\n\n  describe \"fetch_contacts_using_access_token\" do\n\n    let(:access_token) { \"access_token\" }\n    let(:token_type) { \"token_type\" }\n\n    before(:each) do\n      outlook.instance_variable_set(:@env, {\"HTTP_HOST\" => \"http://example.com\"})\n    end\n\n    it \"should request the contacts by providing the authorization header with token_type and access_token\" do\n      outlook.should_receive(:https_get) do |host, path, params, headers|\n        params.should eq({})\n        headers[\"Authorization\"].should eq(\"token_type access_token\")\n        self_response\n      end\n\n      outlook.should_receive(:https_get) do |host, path, params, headers|\n        params.should eq({})\n        headers[\"Authorization\"].should eq(\"token_type access_token\")\n        contacts_as_json\n      end\n      outlook.fetch_contacts_using_access_token access_token, token_type\n    end\n\n    it \"should set requested permissions in the authorization url\" do\n      outlook.authorization_url.should match(/scope=#{Regexp.quote(CGI.escape(permissions))}/)\n    end\n\n    it \"should correctly parse id, name and email\" do\n      outlook.should_receive(:https_get).and_return(self_response)\n      outlook.should_receive(:https_get).and_return(contacts_as_json)\n      result = outlook.fetch_contacts_using_access_token access_token, token_type\n\n      result.size.should be(1)\n      result.first[:id].should eq('AQMkADAwATM0MDAAMS1kZjUyLWQzZDUtMDACLTAwCgBGAAADQ1hAWLJpwk6DZYyOhnclvgcAxCL3G7jnpkiRUVmiNrhjJgAAAgEOAAAAxCL3G7jnpkiRUVmiNrhjJgAAAixsAAAA')\n      result.first[:first_name].should eq(\"First\")\n      result.first[:last_name].should eq(\"Contact\")\n      result.first[:name].should eq(\"First Contact\")\n      result.first[:email].should eq(\"contact.first@email.com\")\n      result.first[:birthday].should eq({ :day => 14, :month => 8, :year => nil })\n      result.first[:address_1].should eq(\"address1\")\n      result.first[:address_2].should be_nil\n      result.first[:city].should eq(\"city\")\n      result.first[:region].should eq(\"state\")\n      result.first[:postcode].should eq(\"89111\")\n      result.first[:country].should eq(\"US\")\n      result.first[:gender].should be_nil\n      result.first[:profile_picture].should be_nil\n      result.first[:relation].should be_nil\n    end\n\n    it \"should correctly parse and set logged in user information\" do\n      outlook.should_receive(:https_get).and_return(self_response)\n      outlook.should_receive(:https_get).and_return(contacts_as_json)\n\n      outlook.fetch_contacts_using_access_token access_token, token_type\n\n      user = outlook.instance_variable_get(:@env)[\"omnicontacts.user\"]\n      user.should_not be_nil\n      user[:id].should eq('00034001-df52-d3d5-0000-000000000000@84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa')\n      user[:first_name].should eq(\"Test\")\n      user[:last_name].should eq(\"User\")\n      user[:name].should eq(\"Test User\")\n      user[:email].should eq(\"test.user@outlook.com\")\n      user[:gender].should be_nil\n      user[:birthday].should be_nil\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/omnicontacts/importer/yahoo_spec.rb",
    "content": "require \"spec_helper\"\nrequire \"omnicontacts/importer/yahoo\"\n\ndescribe OmniContacts::Importer::Yahoo do\n\n  describe \"fetch_contacts_from_token_and_verifier\" do\n    let(:self_response) {\n      '{\"profile\":{\n                  \"guid\":\"PCLASP523T3E2R5TFMHDW9KWQQ\",\n                  \"birthdate\": \"06/21\",\n                  \"emails\":[{\"handle\":\"chrisjohnson@gmail.com\", \"id\":10, \"primary\":true, \"type\":\"HOME\"}, {\"handle\":\"xyz@xyz.com\", \"id\":11, \"type\":\"HOME\"}],\n                  \"familyName\": \"Johnson\",\n                  \"gender\":\"M\",\n                  \"givenName\":\"Chris\",\n                  \"image\":{\"imageUrl\":\"https://avatars.zenfs.com/users/23T3E2R5TFMHDW-AFE-I7lUpIsGQ==.large.png\"}\n                }\n      }'\n    }\n\n    let(:contacts_as_json) {\n      '{\n        \"contacts\": {\n          \"start\":1,\n          \"count\":1,\n          \"contact\":[\n            {\n              \"id\":10,\n              \"fields\":[\n                {\"id\":819, \"type\":\"email\", \"value\":\"johnny@yahoo.com\"},\n                {\"id\":806,\"type\":\"name\",\"value\":{\"givenName\":\"John\",\"middleName\":\"\",\"familyName\":\"Smith\"},\"editedBy\":\"OWNER\",\"categories\":[]},\n                {\"id\":33555343,\"type\":\"guid\",\"value\":\"7ET6MYV2UQ6VR6CBSNMCLFJIVI\"},\n                {\"id\":946,\"type\":\"birthday\",\"value\":{\"day\":\"22\",\"month\":\"2\",\"year\":\"1952\"},\"editedBy\":\"OWNER\",\"categories\":[]},\n                {\"id\":21, \"type\":\"address\", \"value\":{\"street\":\"1313 Trashview Court\\nApt. 13\", \"city\":\"Nowheresville\", \"stateOrProvince\":\"OK\", \"postalCode\":\"66666\", \"country\":\"\", \"countryCode\":\"\"}, \"editedBy\":\"OWNER\", \"flags\":[\"HOME\"], \"categories\":[]}\n              ]\n            }\n          ]\n        }\n      }' }\n\n    let(:yahoo) { OmniContacts::Importer::Yahoo.new({}, \"consumer_key\", \"consumer_secret\") }\n\n    before(:each) do\n      yahoo.instance_variable_set(:@env, {})\n    end\n\n    it \"should request the contacts by specifying all required parameters\" do\n      yahoo.should_receive(:fetch_access_token).and_return([\"access_token\", \"access_token_secret\", \"guid\"])\n\n      yahoo.should_receive(:https_get) do |host, path, params|\n        params[:format].should eq(\"json\")\n        params[:oauth_consumer_key].should eq(\"consumer_key\")\n        params[:oauth_nonce].should_not be_nil\n        params[:oauth_signature_method].should eq(\"HMAC-SHA1\")\n        params[:oauth_timestamp].should_not be_nil\n        params[:oauth_token].should eq(\"access_token\")\n        params[:oauth_version].should eq(\"1.0\")\n        self_response\n      end\n\n      yahoo.should_receive(:https_get) do |host, path, params|\n        params[:format].should eq(\"json\")\n        params[:oauth_consumer_key].should eq(\"consumer_key\")\n        params[:oauth_nonce].should_not be_nil\n        params[:oauth_signature_method].should eq(\"HMAC-SHA1\")\n        params[:oauth_timestamp].should_not be_nil\n        params[:oauth_token].should eq(\"access_token\")\n        params[:oauth_version].should eq(\"1.0\")\n        contacts_as_json\n      end\n      yahoo.fetch_contacts_from_token_and_verifier \"auth_token\", \"auth_token_secret\", \"oauth_verifier\"\n    end\n\n    it \"should correctly parse id, name,email,gender, birthday, snailmail address, image source and relation for contact and logged in user\" do\n      yahoo.should_receive(:fetch_access_token).and_return([\"access_token\", \"access_token_secret\", \"guid\"])\n      yahoo.should_receive(:https_get).and_return(self_response)\n      yahoo.should_receive(:https_get).and_return(contacts_as_json)\n      result = yahoo.fetch_contacts_from_token_and_verifier \"auth_token\", \"auth_token_secret\", \"oauth_verifier\"\n\n      result.size.should be(1)\n      result.first[:id].should eq('10')\n      result.first[:first_name].should eq('John')\n      result.first[:last_name].should eq('Smith')\n      result.first[:name].should eq(\"John Smith\")\n      result.first[:email].should eq(\"johnny@yahoo.com\")\n      result.first[:gender].should be_nil\n      result.first[:birthday].should eq({:day=>22, :month=>2, :year=>1952})\n      result.first[:address_1].should eq('1313 Trashview Court')\n      result.first[:address_2].should eq('Apt. 13')\n      result.first[:city].should eq('Nowheresville')\n      result.first[:region].should eq('OK')\n      result.first[:postcode].should eq('66666')\n      result.first[:relation].should be_nil\n    end\n\n    it \"should return an empty list of contacts\" do\n      empty_contacts_list = '{\"contacts\": {\"start\":0, \"count\":0}}'\n      yahoo.should_receive(:fetch_access_token).and_return([\"access_token\", \"access_token_secret\", \"guid\"])\n      yahoo.should_receive(:https_get).and_return(self_response)\n      yahoo.should_receive(:https_get).and_return(empty_contacts_list)\n      result = yahoo.fetch_contacts_from_token_and_verifier \"auth_token\", \"auth_token_secret\", \"oauth_verifier\"\n\n      result.should be_empty\n    end\n\n    it \"should correctly parse and set logged in user information\" do\n      yahoo.should_receive(:fetch_access_token).and_return([\"access_token\", \"access_token_secret\", \"guid\"])\n      yahoo.should_receive(:https_get).and_return(self_response)\n      yahoo.should_receive(:https_get).and_return(contacts_as_json)\n      yahoo.fetch_contacts_from_token_and_verifier \"auth_token\", \"auth_token_secret\", \"oauth_verifier\"\n\n      user = yahoo.instance_variable_get(:@env)[\"omnicontacts.user\"]\n      user.should_not be_nil\n      user[:id].should eq('PCLASP523T3E2R5TFMHDW9KWQQ')\n      user[:first_name].should eq('Chris')\n      user[:last_name].should eq('Johnson')\n      user[:name].should eq('Chris Johnson')\n      user[:gender].should eq('male')\n      user[:birthday].should eq({:day=>21, :month=>06, :year=>nil})\n      user[:email].should eq('chrisjohnson@gmail.com')\n      user[:profile_picture].should eq('https://avatars.zenfs.com/users/23T3E2R5TFMHDW-AFE-I7lUpIsGQ==.large.png')\n    end\n\n  end\nend\n"
  },
  {
    "path": "spec/omnicontacts/integration_test_spec.rb",
    "content": "require \"spec_helper\"\nrequire \"omnicontacts/integration_test\"\n\ndescribe IntegrationTest do\n\n  context \"mock_initial_request\" do\n    it \"should redirect to the provider's redirect_path\" do\n      provider = mock\n      redirect_path = \"/redirect_path\"\n      provider.stub(:redirect_path => redirect_path)\n      IntegrationTest.instance.mock_authorization_from_user(provider)[1][\"location\"].should eq(redirect_path)\n    end\n  end\n\n  context \"mock_callback\" do\n\n    before(:each) {\n      @env = {}\n      @provider = self.mock\n      @provider.stub(:class_name => \"test\")\n      IntegrationTest.instance.clear_mocks\n    }\n\n    it \"should return an empty contacts list\" do\n      IntegrationTest.instance.mock_fetch_contacts(@provider).should be_empty\n    end\n\n    it \"should return a configured list of contacts \" do\n      contacts = [:name => 'John Doe', :email => 'john@doe.com']\n      IntegrationTest.instance.mock('test', contacts)\n      result = IntegrationTest.instance.mock_fetch_contacts(@provider)\n      result.size.should be(1)\n      result.first[:email].should eq(contacts.first[:email])\n      result.first[:name].should eq(contacts.first[:name])\n    end\n\n    it \"should return a single element list of contacts \" do\n      contact = {:name => 'John Doe', :email => 'john@doe.com'}\n      IntegrationTest.instance.mock('test', contact)\n      result = IntegrationTest.instance.mock_fetch_contacts(@provider)\n      result.size.should be(1)\n      result.first[:email].should eq(contact[:email])\n      result.first[:name].should eq(contact[:name])\n    end\n\n    it \"should return a user\" do\n      contact = {:name => 'John Doe', :email => 'john@doe.com'}\n      user = {:name => 'Mary Smith', :email => 'mary@smith.com'}\n      IntegrationTest.instance.mock('test', contact, user)\n      result = IntegrationTest.instance.mock_fetch_user(@provider)\n      result[:email].should eq(user[:email])\n      result[:name].should eq(user[:name])\n    end\n\n    it \"should throw an exception\" do\n      IntegrationTest.instance.mock('test', :some_error)\n      expect {IntegrationTest.instance.mock_fetch_contacts(@provider)}.to raise_error\n    end\n  end\nend\n"
  },
  {
    "path": "spec/omnicontacts/middleware/base_oauth_spec.rb",
    "content": "require \"spec_helper\"\nrequire \"omnicontacts\"\nrequire \"omnicontacts/middleware/base_oauth\"\n\ndescribe OmniContacts::Middleware::BaseOAuth do\n  \n  before(:all) do \n    class TestProvider < OmniContacts::Middleware::BaseOAuth\n      def initialize app, consumer_key, consumer_secret, options = {}\n        super app, options\n      end\n      \n      def redirect_path\n        \"#{ MOUNT_PATH }testprovider/callback\"\n      end\n\n      def self.mock_session\n        @mock_session ||= {}\n      end\n\n      def session\n        TestProvider.mock_session\n      end\n    end\n    OmniContacts.integration_test.enabled = true\n  end\n\n  let(:app) {\n    Rack::Builder.new do |b|\n      b.use TestProvider, \"consumer_id\", \"consumer_secret\"\n      b.run lambda { |env| [200, {\"Content-Type\" => \"text/html\"}, [\"Hello World\"]] }\n    end.to_app\n  }\n  \n  it \"should return a preconfigured list of contacts\" do\n    OmniContacts.integration_test.mock(:testprovider, :email => \"user@example.com\")\n    get \"#{ MOUNT_PATH }testprovider\"\n    get \"#{ MOUNT_PATH }testprovider/callback\"\n    last_request.env[\"omnicontacts.contacts\"].first[:email].should eq(\"user@example.com\")\n  end\n\n  it \"should redirect to failure url\" do\n    OmniContacts.integration_test.mock(:testprovider, \"some_error\" )\n    get \"#{ MOUNT_PATH }testprovider\"\n    get \"#{MOUNT_PATH }testprovider/callback\"\n    last_response.should be_redirect\n    last_response.headers[\"location\"].should eq(\"#{ MOUNT_PATH }failure?error_message=internal_error&importer=testprovider\")\n  end\n  \n  it \"should pass through state query params to the failure url\" do\n    OmniContacts.integration_test.mock(:testprovider, \"some_error\" )\n    get \"#{MOUNT_PATH }testprovider/callback?state=/parent/resource/id\"\n    last_response.headers[\"location\"].should eq(\"#{ MOUNT_PATH }failure?error_message=internal_error&importer=testprovider&state=/parent/resource/id\")\n  end\n\n  it \"should store request params in session\" do\n    OmniContacts.integration_test.mock(:testprovider, :email => \"user@example.com\")\n    get \"#{ MOUNT_PATH }testprovider?foo=bar\"\n    app.session['omnicontacts.params'].should eq({'foo' => 'bar'})\n  end\n\n  it \"should pass the params from session to callback environment \" do\n    OmniContacts.integration_test.mock(:testprovider, :email => \"user@example.com\")\n    app.session.merge!({'omnicontacts.params' => {'foo' => 'bar'}})\n    get \"#{MOUNT_PATH }testprovider/callback?state=/parent/resource/id\"\n    last_request.env[\"omnicontacts.params\"].should eq({'foo' => 'bar'})\n  end\n\n  it \"should pass the params from session on failure\" do\n    OmniContacts.integration_test.mock(:testprovider, \"some_error\" )\n    get \"#{ MOUNT_PATH }testprovider\"\n    app.session.merge!({'omnicontacts.params' => {'foo' => 'bar'}})\n    get \"#{MOUNT_PATH }testprovider/callback\"\n    last_response.should be_redirect\n    last_response.headers[\"location\"].should be_include(\"foo=bar\")\n  end\n  \n  after(:all) do \n    OmniContacts.integration_test.enabled = false\n    OmniContacts.integration_test.clear_mocks\n  end\nend\n"
  },
  {
    "path": "spec/omnicontacts/middleware/oauth1_spec.rb",
    "content": "require \"spec_helper\"\nrequire \"omnicontacts/middleware/oauth1\"\n\ndescribe OmniContacts::Middleware::OAuth1 do\n\n  before(:all) do\n    class OAuth1Middleware < OmniContacts::Middleware::OAuth1\n      def self.mock_auth_token_resp\n        @mock_auth_token_resp ||= Object.new\n      end\n\n      def fetch_authorization_token\n        OAuth1Middleware.mock_auth_token_resp.body\n      end\n\n      def authorization_url auth_token\n        \"http://www.example.com\"\n      end\n\n      def fetch_contacts_from_token_and_verifier oauth_token, ouath_token_secret, oauth_verifier\n        [{:name => \"John Doe\", :email => \"john@example.com\"}]\n      end\n\n      def self.mock_session\n        @mock_session ||= {}\n      end\n\n      def session\n        OAuth1Middleware.mock_session\n      end\n    end\n  end\n\n  let(:app) {\n    Rack::Builder.new do |b|\n      b.use OAuth1Middleware, \"consumer_id\", \"consumer_secret\"\n      b.run lambda { |env| [200, {\"Content-Type\" => \"text/html\"}, [\"Hello World\"]] }\n    end.to_app\n  }\n\n  context \"visiting the listening path\" do\n    it \"should save the authorization token and redirect to the authorization url\" do\n      OAuth1Middleware.mock_auth_token_resp.should_receive(:body).and_return([\"auth_token\", \"auth_token_secret\"])\n      get \"#{ MOUNT_PATH }oauth1middleware\"\n      last_response.should be_redirect\n      last_response.headers['location'].should eq(\"http://www.example.com\")\n    end\n\n    it \"should pass through state query params visiting the listening path\" do\n      OAuth1Middleware.mock_auth_token_resp.should_receive(:body).and_return([\"auth_token\", \"auth_token_secret\"])\n      get \"#{ MOUNT_PATH }oauth1middleware?state=/parent/resource/id\"\n      last_response.headers['location'].should eq(\"http://www.example.com?state=/parent/resource/id\")\n    end\n\n    it \"should redirect to failure url if fetching the request token does not succeed\" do\n      OAuth1Middleware.mock_auth_token_resp.should_receive(:body).and_raise(\"Request failed\")\n      get \"contacts/oauth1middleware\"\n      last_response.should be_redirect\n      last_response.headers[\"location\"].should eq(\"#{ MOUNT_PATH }failure?error_message=internal_error&importer=oauth1middleware\")\n    end\n  end\n\n  context \"visiting the callback url after authorization\" do\n    it \"should return the list of contacts\" do\n      OAuth1Middleware.mock_session.should_receive(:[]).and_return(\"oauth_token_secret\")\n      get \"#{ MOUNT_PATH }oauth1middleware/callback?oauth_token=token&oauth_verifier=verifier\"\n      last_response.should be_ok\n      last_request.env[\"omnicontacts.contacts\"].size.should be(1)\n    end\n\n    it \"should redirect to failure url if oauth_token_secret is not found in the session\" do\n      OAuth1Middleware.mock_session.should_receive(:[]).and_return(nil)\n      get \"#{ MOUNT_PATH }oauth1middleware/callback?oauth_token=token&oauth_verifier=verifier\"\n      last_response.should be_redirect\n      last_response.headers[\"location\"].should eq(\"#{ MOUNT_PATH }failure?error_message=not_authorized&importer=oauth1middleware\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/omnicontacts/middleware/oauth2_spec.rb",
    "content": "require \"spec_helper\"\nrequire \"omnicontacts/middleware/oauth2\"\n\ndescribe OmniContacts::Middleware::OAuth2 do\n\n  before(:all) do\n    class OAuth2Middleware < OmniContacts::Middleware::OAuth2\n      def authorization_url\n        \"http://www.example.com\"\n      end\n\n      def redirect_path\n        \"/redirect_path\"\n      end\n\n      def self.mock_session\n        @mock_session ||= {}\n      end\n\n      def session\n        OAuth2Middleware.mock_session\n      end\n\n      def fetch_access_token code\n        [\"access_token\", \"token_type\", \"token_refresh\"]\n      end\n\n      def fetch_contacts_using_access_token token, token_type\n        [{:name => \"John Doe\", :email => \"john@example.com\"}]\n      end\n    end\n  end\n\n  let(:app) {\n    Rack::Builder.new do |b|\n      b.use OAuth2Middleware, \"client_id\", \"client_secret\"\n      b.run lambda { |env| [200, {\"Content-Type\" => \"text/html\"}, [\"Hello World\"]] }\n    end.to_app\n  }\n\n  context \"visiting the listening path\" do\n    it \"should redirect to authorization site when visiting the listening path\" do\n      get \"#{ MOUNT_PATH }oauth2middleware\"\n      last_response.should be_redirect\n      last_response.headers['location'].should eq(\"http://www.example.com\")\n    end\n\n    it \"should pass through state query params visiting the listening path\" do\n      get \"#{ MOUNT_PATH }oauth2middleware?state=/parent/resource/id\"\n      last_response.headers['location'].should eq(\"http://www.example.com?state=/parent/resource/id\")\n    end\n  end\n\n  context \"visiting the callback url after authorization\" do\n    it \"should fetch the contacts\" do\n      get '/redirect_path?code=ABC'\n      last_response.should be_ok\n      last_request.env[\"omnicontacts.contacts\"].size.should be(1)\n    end\n\n    it \"should redirect to failure page because user did not allow access to contacts list\" do\n      get '/redirect_path?error=not_authorized'\n      last_response.should be_redirect\n      last_response.headers[\"location\"].should eq(\"#{ MOUNT_PATH }failure?error_message=not_authorized&importer=oauth2middleware\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/omnicontacts/parse_utils_spec.rb",
    "content": "require \"spec_helper\"\nrequire \"omnicontacts/parse_utils\"\n\ninclude OmniContacts::ParseUtils\n\ndescribe OmniContacts::ParseUtils do\n  describe \"normalize_name\" do\n    it \"should remove trailing spaces\" do\n      result = normalize_name(\"John \")\n      result.should eq(\"John\")\n    end\n\n    it \"should preserve capitalization\" do\n      result = normalize_name(\"John McDonald\")\n      result.should eq(\"John McDonald\")\n    end\n  end\n\n  describe \"full_name\" do\n    it \"should preserve capitalization\" do\n      result = full_name(\"John\", \"McDonald\")\n      result.should eq(\"John McDonald\")\n    end\n\n    it \"returns only first name if no last name present\" do\n      result = full_name(\"John\", nil)\n      result.should eq(\"John\")\n    end\n\n    it \"returns only last name if no first name present\" do\n      result = full_name(nil, \"McDonald\")\n      result.should eq(\"McDonald\")\n    end\n  end\n\n  describe \"birthday_format\" do\n    it \"returns nil if (!year && !month) || (!year && !day)\" do\n      result = birthday_format(nil, Date.today, nil)\n      result.should eq(nil)\n\n      result = birthday_format(Date.today.month, nil, nil)\n      result.should eq(nil)\n    end\n  end\n\n  describe \"email_to_name\" do\n    it \"create a probable name from email\" do\n      username_or_email = \"foo.bar@test.com\"\n      result = email_to_name(username_or_email)\n      result.should eq ['foo','bar',\"foo bar\"]\n    end\n  end\nend\n"
  },
  {
    "path": "spec/spec_helper.rb",
    "content": "require \"simplecov\"\nSimpleCov.start do\n  add_filter \"spec/\"\nend\n\nrequire \"rspec\"\nrequire \"rack/test\"\nRSpec.configure do |config|\n  config.include Rack::Test::Methods\nend\n\nMOUNT_PATH = \"/contacts/\"\n"
  }
]