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()
46 self._verify_browser_command()
49 # -----------------------------------------------------------------------
50 def authenticate(self, need_auth, block_for_auth):
52 token = self._get_request_token()
53 url = self._get_authorize_url(token)
54 verifier = block_for_auth(True)
55 self._verify_verifier(token, verifier)
58 # -----------------------------------------------------------------------
59 def get_friends(self):
60 """ Returns a Friend object for each LinkedIn connection."""
62 xml = self._make_api_request(self.LI_CONN_API_URL)
63 dom = parseString(xml)
64 friends = self._parse_dom(dom)
69 # -----------------------------------------------------------------------
70 def get_friend_details(self, url, header_value):
71 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=self.access_token, http_url=url)
72 oauth_request.sign_request(self.sig_method, self.consumer, self.access_token)
74 headers = oauth_request.to_header()
75 headers[u'x-li-auth-token'] = header_value
76 connection = httplib.HTTPConnection("api.linkedin.com")
77 connection.request(oauth_request.http_method, url, headers=headers)
78 data = connection.getresponse().read()
82 # -----------------------------------------------------------------------
83 def _make_api_request(self, url):
84 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=self.access_token, http_url=url)
85 oauth_request.sign_request(self.sig_method, self.consumer, self.access_token)
86 connection = httplib.HTTPSConnection(self.LI_SERVER)
88 connection.request(oauth_request.http_method, url, headers=oauth_request.to_header())
89 xml = connection.getresponse().read()
92 raise Exception("Failed to contact LinkedIn at " + url)
95 # -----------------------------------------------------------------------
96 def _parse_dom(self, dom):
97 def get_first_tag(node, tagName):
98 tags = node.getElementsByTagName(tagName)
99 if tags and len(tags) > 0:
102 def extract(node, tagName):
103 tag = get_first_tag(node, tagName)
105 return tag.firstChild.nodeValue
107 def extract_public_url(node):
108 tag = get_first_tag(node, 'site-standard-profile-request')
110 url = extract(tag, 'url')
111 return url.replace("&", "&")
114 errors = dom.getElementsByTagName('error')
115 if (len(errors) > 0):
118 details = " (" + extract(errors[0], "message") + ")"
121 raise Exception("LinkedIn communication errors detected" + details)
124 people = dom.getElementsByTagName('person')
127 fn = extract(p, 'first-name')
128 ln = extract(p, 'last-name')
129 photo_url = extract(p, 'picture-url')
130 id = extract(p, 'id')
131 public_url = extract_public_url(p)
134 friend = Friend(name)
135 friend.add_url(public_url)
137 friend.set_photo_url(photo_url)
139 friends.append(friend)
146 # -----------------------------------------------------------------------
147 def _get_request_token(self):
148 """Get a request token from LinkedIn"""
150 oauth_consumer_key = self.api_key
151 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, callback="oob", http_url=self.REQUEST_TOKEN_URL)
152 oauth_request.sign_request(self.sig_method, self.consumer, None)
154 connection = httplib.HTTPSConnection(self.LI_SERVER)
155 connection.request(oauth_request.http_method, self.REQUEST_TOKEN_URL, headers=oauth_request.to_header())
156 response = connection.getresponse().read()
159 token = oauth.OAuthToken.from_string(response)
161 raise Exception("Authorization failure - failed to get request token")
165 # -----------------------------------------------------------------------
166 def _get_authorize_url(self, token):
167 """The URL that the user should browse to, in order to authorize the
168 application's request to access data"""
170 oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token, http_url=self.AUTHORIZE_URL)
171 return oauth_request.to_url()
174 # -----------------------------------------------------------------------
175 def _get_access_token(self, token, verifier):
176 """If the verifier (which was displayed in the browser window) is
177 valid, then an access token is returned which should be used to
178 access data on the service."""
180 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, verifier=verifier, http_url=self.ACCESS_TOKEN_URL)
181 oauth_request.sign_request(self.sig_method, self.consumer, token)
183 connection = httplib.HTTPSConnection(self.LI_SERVER)
184 connection.request(oauth_request.http_method, self.ACCESS_TOKEN_URL, headers=oauth_request.to_header())
185 response = connection.getresponse()
186 token_str = response.read()
187 if "ouath_problem" in token_str:
188 raise Exception("Authorization failure - failed to get access token")
189 self._store_access_token_in_gconf(token_str)
190 return oauth.OAuthToken.from_string(token_str)
193 # -----------------------------------------------------------------------
194 def _verify_verifier(self, request_token, verifier):
196 self.access_token = self._get_access_token(request_token, verifier)
199 traceback.print_exc()
200 raise Exception("LinkedIn authorization failed, try again (" + e + ")")
203 # -----------------------------------------------------------------------
204 def _verify_browser_command(self):
205 # -- Check the environment is going to work...
207 if (self._gc.get_string('/desktop/gnome/url-handlers/http/command') == 'epiphany %s'):
208 raise Exception('Browser in gconf invalid (see NB#136012). Installation error.')
211 # -----------------------------------------------------------------------
212 def _store_access_token_in_gconf(self, token_str):
213 self._gc.set_string(LinkedInApi.GCONF_ACCESS_TOKEN, token_str)
216 # -----------------------------------------------------------------------
217 def get_access_token_from_gconf(self):
218 """Returns an oauth.OAuthToken, or None if the gconf value is empty"""
220 token_str = self._gc.get_string(LinkedInApi.GCONF_ACCESS_TOKEN)
223 if "oauth_problem" in token_str:
224 self._store_access_token_in_gconf("")
225 raise Exception("Authorization failure - access token reported OAuth problem")
226 return oauth.OAuthToken.from_string(token_str)
229 # -----------------------------------------------------------------------
230 def remove_access_token_from_gconf(self):
231 """Remove the oauth.OAuthToken, if any."""
233 self._gc.unset(LinkedInAPI.GCONF_ACCESS_TOKEN)