3 from oauth import oauth
4 from xml.dom.minidom import parseString
5 from org.maemo.hermes.engine.friend import Friend
8 """LinkedIn API for Hermes.
10 Copyright (c) Fredrik Wendt <fredrik@wendt.se> 2010.
11 Released under the Artistic Licence."""
13 GCONF_API_KEY = '/apps/maemo/hermes/linkedin_key'
14 GCONF_API_SECRET = '/apps/maemo/hermes/linkedin_secret'
15 GCONF_ACCESS_TOKEN = '/apps/maemo/hermes/linkedin_access_token'
17 LI_SERVER = "api.linkedin.com"
18 LI_API_URL = "https://api.linkedin.com"
19 LI_CONN_API_URL = LI_API_URL + "/v1/people/~/connections"
21 REQUEST_TOKEN_URL = LI_API_URL + "/uas/oauth/requestToken"
22 AUTHORIZE_URL = LI_API_URL + "/uas/oauth/authorize"
23 ACCESS_TOKEN_URL = LI_API_URL + "/uas/oauth/accessToken"
26 # -----------------------------------------------------------------------
27 def __init__(self, gconf=None):
28 """Initialize the LinkedIn service, finding LinkedIn API keys in gconf and
29 having a gui_callback available."""
31 if gconf: self._gc = gconf
32 else: self._gc = gnome.gconf.client_get_default()
34 api_key = self._gc.get_string(LinkedInApi.GCONF_API_KEY)
35 secret_key = self._gc.get_string(LinkedInApi.GCONF_API_SECRET)
36 self.api_key = api_key
38 if api_key is None or secret_key is None:
39 raise Exception('No LinkedIn application keys found. Installation error.')
41 self.access_token = self.get_access_token_from_gconf()
43 self.consumer = oauth.OAuthConsumer(api_key, secret_key)
44 self.sig_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
47 # -----------------------------------------------------------------------
48 def authenticate(self, need_auth, block_for_auth):
50 token = self._get_request_token()
51 url = self._get_authorize_url(token)
52 verifier = block_for_auth(True, url)
53 self._verify_verifier(token, verifier)
56 # -----------------------------------------------------------------------
57 def get_friends(self):
58 """ Returns a Friend object for each LinkedIn connection."""
60 xml = self._make_api_request(self.LI_CONN_API_URL)
61 dom = parseString(xml)
62 friends = self._parse_dom(dom)
67 # -----------------------------------------------------------------------
68 def get_friend_details(self, url, header_value):
69 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=self.access_token, http_url=url)
70 oauth_request.sign_request(self.sig_method, self.consumer, self.access_token)
72 headers = oauth_request.to_header()
73 headers[u'x-li-auth-token'] = header_value
74 connection = httplib.HTTPConnection("api.linkedin.com")
75 connection.request(oauth_request.http_method, url, headers=headers)
76 data = connection.getresponse().read()
80 # -----------------------------------------------------------------------
81 def _make_api_request(self, url):
82 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=self.access_token, http_url=url)
83 oauth_request.sign_request(self.sig_method, self.consumer, self.access_token)
84 connection = httplib.HTTPSConnection(self.LI_SERVER)
86 connection.request(oauth_request.http_method, url, headers=oauth_request.to_header())
87 xml = connection.getresponse().read()
90 raise Exception("Failed to contact LinkedIn at " + url)
93 # -----------------------------------------------------------------------
94 def _parse_dom(self, dom):
95 def get_first_tag(node, tagName):
96 tags = node.getElementsByTagName(tagName)
97 if tags and len(tags) > 0:
100 def extract(node, tagName):
101 tag = get_first_tag(node, tagName)
103 return tag.firstChild.nodeValue
105 def extract_public_url(node):
106 tag = get_first_tag(node, 'site-standard-profile-request')
108 url = extract(tag, 'url')
109 return url.replace("&", "&")
112 errors = dom.getElementsByTagName('error')
113 if (len(errors) > 0):
116 details = " (" + extract(errors[0], "message") + ")"
119 raise Exception("LinkedIn communication errors detected" + details)
122 people = dom.getElementsByTagName('person')
125 fn = extract(p, 'first-name')
126 ln = extract(p, 'last-name')
127 photo_url = extract(p, 'picture-url')
128 id = extract(p, 'id')
129 public_url = extract_public_url(p)
132 friend = Friend(name)
133 friend.add_url(public_url)
135 friend.set_photo_url(photo_url)
137 friends.append(friend)
144 # -----------------------------------------------------------------------
145 def _get_request_token(self):
146 """Get a request token from LinkedIn"""
148 oauth_consumer_key = self.api_key
149 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, callback="oob", http_url=self.REQUEST_TOKEN_URL)
150 oauth_request.sign_request(self.sig_method, self.consumer, None)
152 connection = httplib.HTTPSConnection(self.LI_SERVER)
153 connection.request(oauth_request.http_method, self.REQUEST_TOKEN_URL, headers=oauth_request.to_header())
154 response = connection.getresponse().read()
157 token = oauth.OAuthToken.from_string(response)
160 traceback.print_exc()
162 raise Exception("Authorization failure - failed to get request token")
166 # -----------------------------------------------------------------------
167 def _get_authorize_url(self, token):
168 """The URL that the user should browse to, in order to authorize the
169 application's request to access data"""
171 oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token, http_url=self.AUTHORIZE_URL)
172 return oauth_request.to_url()
175 # -----------------------------------------------------------------------
176 def _get_access_token(self, token, verifier):
177 """If the verifier (which was displayed in the browser window) is
178 valid, then an access token is returned which should be used to
179 access data on the service."""
181 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, verifier=verifier, http_url=self.ACCESS_TOKEN_URL)
182 oauth_request.sign_request(self.sig_method, self.consumer, token)
184 connection = httplib.HTTPSConnection(self.LI_SERVER)
185 connection.request(oauth_request.http_method, self.ACCESS_TOKEN_URL, headers=oauth_request.to_header())
186 response = connection.getresponse()
187 token_str = response.read()
188 if 'oauth_problem' in token_str:
189 raise Exception("Authorization failure - failed to get access token (" + token_str + ")")
190 self._store_access_token_in_gconf(token_str)
191 return oauth.OAuthToken.from_string(token_str)
194 # -----------------------------------------------------------------------
195 def _verify_verifier(self, request_token, verifier):
197 self.access_token = self._get_access_token(request_token, verifier)
200 traceback.print_exc()
201 raise Exception("LinkedIn authorization failed, try again")
204 # -----------------------------------------------------------------------
205 def _store_access_token_in_gconf(self, token_str):
206 self._gc.set_string(LinkedInApi.GCONF_ACCESS_TOKEN, token_str)
209 # -----------------------------------------------------------------------
210 def get_access_token_from_gconf(self):
211 """Returns an oauth.OAuthToken, or None if the gconf value is empty"""
213 token_str = self._gc.get_string(LinkedInApi.GCONF_ACCESS_TOKEN)
216 if "oauth_problem" in token_str:
217 self._store_access_token_in_gconf("")
218 raise Exception("Authorization failure - access token reported OAuth problem")
219 return oauth.OAuthToken.from_string(token_str)
222 # -----------------------------------------------------------------------
223 def remove_access_token_from_gconf(self):
224 """Remove the oauth.OAuthToken, if any."""
226 self._gc.unset(LinkedInAPI.GCONF_ACCESS_TOKEN)