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(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()
158 token = oauth.OAuthToken.from_string(response)
162 # -----------------------------------------------------------------------
163 def _get_authorize_url(self, token):
164 """The URL that the user should browse to, in order to authorize the
165 application's request to access data"""
167 oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token, http_url=self.AUTHORIZE_URL)
168 return oauth_request.to_url()
171 # -----------------------------------------------------------------------
172 def _get_access_token(self, token, verifier):
173 """If the verifier (which was displayed in the browser window) is
174 valid, then an access token is returned which should be used to
175 access data on the service."""
177 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, verifier=verifier, http_url=self.ACCESS_TOKEN_URL)
178 oauth_request.sign_request(self.sig_method, self.consumer, token)
180 connection = httplib.HTTPSConnection(self.LI_SERVER)
181 connection.request(oauth_request.http_method, self.ACCESS_TOKEN_URL, headers=oauth_request.to_header())
182 response = connection.getresponse()
183 token_str = response.read()
184 if "ouath_problem" in token_str:
185 raise Exception("Authorization failure - failed to get access token")
186 self._store_access_token_in_gconf(token_str)
187 return oauth.OAuthToken.from_string(token_str)
190 # -----------------------------------------------------------------------
191 def _verify_verifier(self, request_token, verifier):
193 self.access_token = self._get_access_token(request_token, verifier)
196 traceback.print_exc()
197 raise Exception("LinkedIn authorization failed, try again (" + e + ")")
200 # -----------------------------------------------------------------------
201 def _verify_browser_command(self):
202 # -- Check the environment is going to work...
204 if (self._gc.get_string('/desktop/gnome/url-handlers/http/command') == 'epiphany %s'):
205 raise Exception('Browser in gconf invalid (see NB#136012). Installation error.')
208 # -----------------------------------------------------------------------
209 def _store_access_token_in_gconf(self, token_str):
210 self._gc.set_string(LinkedInApi.GCONF_ACCESS_TOKEN, token_str)
213 # -----------------------------------------------------------------------
214 def get_access_token_from_gconf(self):
215 """Returns an oauth.OAuthToken, or None if the gconf value is empty"""
217 token_str = self._gc.get_string(LinkedInApi.GCONF_ACCESS_TOKEN)
220 if "oauth_problem" in token_str:
221 self._store_access_token_in_gconf("")
222 raise Exception("Authorization failure - access token reported OAuth problem")
223 return oauth.OAuthToken.from_string(token_str)
226 # -----------------------------------------------------------------------
227 def remove_access_token_from_gconf(self):
228 """Remove the oauth.OAuthToken, if any."""
230 self._gc.unset(LinkedInAPI.GCONF_ACCESS_TOKEN)