from xml.dom.minidom import parseString
from org.maemo.hermes.engine.friend import Friend
-# httplib.HTTPSConnection.debuglevel = 1
-
class LinkedInApi():
"""LinkedIn API for Hermes.
Copyright (c) Fredrik Wendt <fredrik@wendt.se> 2010.
Released under the Artistic Licence."""
-
+ GCONF_API_KEY = '/apps/maemo/hermes/linkedin_key'
+ GCONF_API_SECRET = '/apps/maemo/hermes/linkedin_secret'
+ GCONF_ACCESS_TOKEN = '/apps/maemo/hermes/linkedin_access_token'
+ GCONF_USER = '/apps/maemo/hermes/linkedin_user'
+
LI_SERVER = "api.linkedin.com"
LI_API_URL = "https://api.linkedin.com"
LI_CONN_API_URL = LI_API_URL + "/v1/people/~/connections"
+ LI_PROFILE_API_URL = LI_API_URL + "/v1/people/~"
REQUEST_TOKEN_URL = LI_API_URL + "/uas/oauth/requestToken"
AUTHORIZE_URL = LI_API_URL + "/uas/oauth/authorize"
# -----------------------------------------------------------------------
- def __init__(self):
+ def __init__(self, gconf=None):
"""Initialize the LinkedIn service, finding LinkedIn API keys in gconf and
having a gui_callback available."""
- self._gc = gnome.gconf.client_get_default()
+ if gconf: self._gc = gconf
+ else: self._gc = gnome.gconf.client_get_default()
- # -- Check the environment is going to work...
- # FIXME: duplication
- if (self._gc.get_string('/desktop/gnome/url-handlers/http/command') == 'epiphany %s'):
- raise Exception('Browser in gconf invalid (see NB#136012). Installation error.')
-
- api_key = self._gc.get_string('/apps/maemo/hermes/linkedin_api_key')
- secret_key = self._gc.get_string('/apps/maemo/hermes/linkedin_key_secret')
+ api_key = self._gc.get_string(LinkedInApi.GCONF_API_KEY)
+ secret_key = self._gc.get_string(LinkedInApi.GCONF_API_SECRET)
+ self.api_key = api_key
if api_key is None or secret_key is None:
raise Exception('No LinkedIn application keys found. Installation error.')
- self.access_token = self._get_access_token_from_gconf()
+ self.access_token = self.get_access_token_from_gconf()
self.consumer = oauth.OAuthConsumer(api_key, secret_key)
self.sig_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
-
- # IMPROVEMENT: verify that the access_token is valid for at least another hour
- self._verify_access_token()
-
+
+ # -----------------------------------------------------------------------
def authenticate(self, need_auth, block_for_auth):
need_auth()
token = self._get_request_token()
url = self._get_authorize_url(token)
verifier = block_for_auth(url)
- self._verify_verifier(verifier)
+ self._verify_verifier(token, verifier)
# -----------------------------------------------------------------------
# -----------------------------------------------------------------------
- def _verify_verifier(self, verifier):
- try:
- self.access_token = self._get_access_token(self.request_token, verifier)
- self._store_access_token_in_gconf()
- except:
- raise Exception("authorization failed, try again")
-
-
- # -----------------------------------------------------------------------
- def _verify_access_token(self):
- return True
-
-
- # -----------------------------------------------------------------------
- def _store_access_token_in_gconf(self, token_str):
- self._gc.set_string('/apps/maemo/hermes/linkedin_access_token', token_str)
-
-
- # -----------------------------------------------------------------------
- def _get_access_token_from_gconf(self):
- token_str = self._gc.get_string('/apps/maemo/hermes/linkedin_access_token')
- return oauth.OAuthToken.from_string(token_str)
+ def get_friend_details(self, url, header_value):
+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=self.access_token, http_url=url)
+ oauth_request.sign_request(self.sig_method, self.consumer, self.access_token)
+ headers = oauth_request.to_header()
+ headers[u'x-li-auth-token'] = header_value
+ connection = httplib.HTTPConnection("api.linkedin.com")
+ connection.request(oauth_request.http_method, url, headers=headers)
+ data = connection.getresponse().read()
+ return data
+
# -----------------------------------------------------------------------
def _make_api_request(self, url):
- print "_make_api_request", url
oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=self.access_token, http_url=url)
oauth_request.sign_request(self.sig_method, self.consumer, self.access_token)
connection = httplib.HTTPSConnection(self.LI_SERVER)
try:
connection.request(oauth_request.http_method, url, headers=oauth_request.to_header())
- return connection.getresponse().read()
+ xml = connection.getresponse().read()
+ return xml
except:
raise Exception("Failed to contact LinkedIn at " + url)
# -----------------------------------------------------------------------
def _parse_dom(self, dom):
- print "parse_dom", dom
def get_first_tag(node, tagName):
tags = node.getElementsByTagName(tagName)
if tags and len(tags) > 0:
# look for errors
errors = dom.getElementsByTagName('error')
if (len(errors) > 0):
- print "Error" # FIXME: handle this better
- return []
+ details = ""
+ try:
+ details = " (" + extract(errors[0], "message") + ")"
+ except:
+ pass
+ raise Exception("LinkedIn communication errors detected" + details)
friends = []
people = dom.getElementsByTagName('person')
friends.append(friend)
except:
- pass
+ pass
+
+ return friends
+
+ # -----------------------------------------------------------------------
+ def _get_request_token(self):
+ """Get a request token from LinkedIn"""
+
+ oauth_consumer_key = self.api_key
+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, callback="oob", http_url=self.REQUEST_TOKEN_URL)
+ oauth_request.sign_request(self.sig_method, self.consumer, None)
+
+ connection = httplib.HTTPSConnection(self.LI_SERVER)
+ connection.request(oauth_request.http_method, self.REQUEST_TOKEN_URL, headers=oauth_request.to_header())
+ response = connection.getresponse().read()
+
+ try:
+ token = oauth.OAuthToken.from_string(response)
+ except Exception, e:
+ import traceback
+ traceback.print_exc()
+ print response
+ raise Exception("Authorization failure - failed to get request token")
+ return token
# -----------------------------------------------------------------------
+ def _get_authorize_url(self, token):
+ """The URL that the user should browse to, in order to authorize the
+ application's request to access data"""
+
+ oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token, http_url=self.AUTHORIZE_URL)
+ return oauth_request.to_url()
+
+
+ # -----------------------------------------------------------------------
def _get_access_token(self, token, verifier):
- """If the verifier (which was displayed in the browser window) is valid,
- then an access token is returned which should be used to access data on the service."""
+ """If the verifier (which was displayed in the browser window) is
+ valid, then an access token is returned which should be used to
+ access data on the service."""
oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, verifier=verifier, http_url=self.ACCESS_TOKEN_URL)
oauth_request.sign_request(self.sig_method, self.consumer, token)
connection.request(oauth_request.http_method, self.ACCESS_TOKEN_URL, headers=oauth_request.to_header())
response = connection.getresponse()
token_str = response.read()
+ if 'oauth_problem' in token_str:
+ raise Exception("Authorization failure - failed to get access token (" + token_str + ")")
self._store_access_token_in_gconf(token_str)
return oauth.OAuthToken.from_string(token_str)
# -----------------------------------------------------------------------
- def _get_authorize_url(self, token):
- """The URL that the user should browse to, in order to authorize the application to acess data"""
-
- oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token, http_url=self.AUTHORIZE_URL)
- return oauth_request.to_url()
+ def _verify_verifier(self, request_token, verifier):
+ try:
+ self.access_token = self._get_access_token(request_token, verifier)
+ xml = self._make_api_request(self.LI_PROFILE_API_URL)
+ dom = parseString(xml)
+ friends = self._parse_dom(dom)
+ self._gc.set_string(LinkedInApi.GCONF_USER, friends[0].get_name())
+ except Exception, e:
+ import traceback
+ traceback.print_exc()
+ raise Exception("LinkedIn authorization failed, try again")
# -----------------------------------------------------------------------
- def _get_request_token(self):
- """Get a request token from LinkedIn"""
+ def _store_access_token_in_gconf(self, token_str):
+ if "oauth_problem" in token_str:
+ raise Exception("Authorization failure - access token reported OAuth problem")
- oauth_consumer_key = self.api_key
- oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, callback="oob", http_url=self.REQUEST_TOKEN_URL)
- oauth_request.sign_request(self.sig_method, self.consumer, None)
+ self._gc.set_string(LinkedInApi.GCONF_ACCESS_TOKEN, token_str)
- connection = httplib.HTTPSConnection(self.LI_SERVER)
- connection.request(oauth_request.http_method, self.REQUEST_TOKEN_URL, headers=oauth_request.to_header())
- response = self.connection.getresponse().read()
- token = oauth.OAuthToken.from_string(response)
- return token
+ # -----------------------------------------------------------------------
+ def get_access_token_from_gconf(self):
+ """Returns an oauth.OAuthToken, or None if the gconf value is empty"""
+
+ token_str = self._gc.get_string(LinkedInApi.GCONF_ACCESS_TOKEN)
+ if not token_str:
+ return None
+ return oauth.OAuthToken.from_string(token_str)
+
+ # -----------------------------------------------------------------------
+ def remove_access_token_from_gconf(self):
+ """Remove the oauth.OAuthToken, if any."""
+
+ self._gc.unset(LinkedInApi.GCONF_ACCESS_TOKEN)
+ self._gc.unset(LinkedInApi.GCONF_USER)