OAuth providers. Fixes MB#11275.
gconftool-2 -s /apps/maemo/hermes/gravatar_key b14ec179822b --type string
gconftool-2 -s /apps/maemo/hermes/linkedin_key 1et4G-VtmtqNfY7gF8PHtxMOf0KNWl9ericlTEtdKJeoA4ubk4wEQwf8lSL8AnYE --type string
gconftool-2 -s /apps/maemo/hermes/linkedin_secret uk--OtmWcxER-Yh6Py5p0VeLPNlDJSMaXj1xfHILoFzrK7fM9eepNo5RbwGdkRo_ --type string
-
-# Version 0.2.1 changed type of /apps/maemo/hermes/uid
-OLD_UID=`gconftool-2 -g /apps/maemo/hermes/uid`
-[ -z "$OLD_UID" ] || gconftool-2 -s /apps/maemo/hermes/uid "$OLD_UID" --type string
+gconftool-2 -s /apps/maemo/hermes/twitter_key pBpcz09kp71jnEdZWqjQ --type string
+gconftool-2 -s /apps/maemo/hermes/twitter_secret wBwyJsQIZFsKMhCA0S9C0b5KmWSz07T3z0KWpKs --type string
# Hacky fix for NB#136012
gconftool-2 -s /desktop/gnome/url-handlers/http/command 'dbus-send --type=method_call --dest=com.nokia.osso_browser /com/nokia/osso_browser com.nokia.osso_browser.load_url string:"%s"' --type string
def if_defined(data, key, callback):
if key in data and data[key]:
+ print key, data[key]
callback(data[key])
friends_data = self._get_friends_data()
Copyright (c) Fredrik Wendt <fredrik@wendt.se> 2010.
Copyright (c) Andrew Flegg <andrew@bleb.org> 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:(id,first-name,last-name,picture-url,site-standard-profile-request:(url),date-of-birth,main-address,location:(country:(code)),phone-numbers,member-url-resources)"
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"
- ACCESS_TOKEN_URL = LI_API_URL + "/uas/oauth/accessToken"
-
-
+
+
# -----------------------------------------------------------------------
- def __init__(self, gconf=None):
- """Initialize the LinkedIn service, finding LinkedIn API keys in gconf and
- having a gui_callback available."""
-
- if gconf: self._gc = gconf
- else: self._gc = gnome.gconf.client_get_default()
-
- 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.consumer = oauth.OAuthConsumer(api_key, secret_key)
- self.sig_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
-
+ def __init__(self, consumer, api_request):
+ """Instantiate with an OAuth consumer"""
+ self.consumer = consumer
+ self._make_api_request = api_request
+
# -----------------------------------------------------------------------
- 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(token, verifier)
+ def verify_verifier(self, access_token):
+ try:
+ xml = self._make_api_request(self.LI_PROFILE_API_URL)
+ dom = parseString(xml)
+ friends = self._parse_dom(dom)
+ return friends[0].get_name()
+ except Exception, e:
+ import traceback
+ traceback.print_exc()
+ raise Exception("LinkedIn authorization failed, try again")
+
# -----------------------------------------------------------------------
connection.request(oauth_request.http_method, url, headers=headers)
data = connection.getresponse().read()
return data
-
-
- # -----------------------------------------------------------------------
- def _make_api_request(self, 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())
- xml = connection.getresponse().read()
- return xml
- except:
- raise Exception("Failed to contact LinkedIn at " + url)
# -----------------------------------------------------------------------
country = extract(get_first_tag(node, 'country'), 'code')
tag = get_first_tag(node, 'phone-numbers')
numbers = []
- for element in tag.childNodes:
- if element.nodeName != 'phone-number':
- continue
-
- phone_type = extract(element, 'phone-type')
- device = phone_type == 'mobile' and phone_type or None
- type = phone_type in set(['home', 'work']) and phone_type or None
-
- number = PhoneNumber(extract(element, 'phone-number'), device = device, type = type, country = country)
- numbers.append(number)
+ if tag:
+ for element in tag.childNodes:
+ if element.nodeName != 'phone-number':
+ continue
+
+ phone_type = extract(element, 'phone-type')
+ device = phone_type == 'mobile' and phone_type or None
+ type = phone_type in set(['home', 'work']) and phone_type or None
+
+ number = PhoneNumber(extract(element, 'phone-number'), device = device, type = type, country = country)
+ numbers.append(number)
return numbers
def extract_urls(node):
tag = get_first_tag(node, 'member-url-resources')
urls = []
- for element in tag.getElementsByTagName('url'):
- urls.append(element.firstChild.nodeValue.replace("&", "&"))
+ if tag:
+ for element in tag.getElementsByTagName('url'):
+ urls.append(element.firstChild.nodeValue.replace("&", "&"))
return urls
def extract_birthday(node):
# look for errors
errors = dom.getElementsByTagName('error')
- if (len(errors) > 0):
+ if (errors and len(errors) > 0):
details = ""
try:
details = " (" + extract(errors[0], "message") + ")"
friends.append(friend)
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."""
-
- 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 = httplib.HTTPSConnection(self.LI_SERVER)
- 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 _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 _store_access_token_in_gconf(self, token_str):
- if "oauth_problem" in token_str:
- raise Exception("Authorization failure - access token reported OAuth problem")
-
- self._gc.set_string(LinkedInApi.GCONF_ACCESS_TOKEN, token_str)
-
-
- # -----------------------------------------------------------------------
- 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)
import gobject, gtk, hildon
import time, thread
import webbrowser
-import org.maemo.hermes.engine.provider
+from org.maemo.hermes.engine.provider_oauth import OAuthProvider
import org.maemo.hermes.engine.linkedin.service
from org.maemo.hermes.engine.linkedin.api import LinkedInApi
-class Provider(org.maemo.hermes.engine.provider.Provider):
+class Provider(OAuthProvider):
"""LinkedIn provider for Hermes.
Copyright (c) Andrew Flegg <andrew@bleb.org> 2010.
Released under the Artistic Licence."""
-
+
+ LI_API_URL = "https://api.linkedin.com"
+
+ REQUEST_TOKEN_URL = LI_API_URL + "/uas/oauth/requestToken"
+ AUTHORIZE_URL = LI_API_URL + "/uas/oauth/authorize"
+ ACCESS_TOKEN_URL = LI_API_URL + "/uas/oauth/accessToken"
+
+
# -----------------------------------------------------------------------
def __init__(self):
- """Initialise the provider, and ensure the environment is going to work."""
-
- self._gc = gnome.gconf.client_get_default()
- self._api = LinkedInApi(gconf = self._gc)
+ OAuthProvider.__init__(self)
+ self._api = LinkedInApi(self.consumer, self.make_api_request)
# -----------------------------------------------------------------------
# -----------------------------------------------------------------------
- def get_account_detail(self):
- """Return the name of the linked LinkedIn account."""
-
- return self._gc.get_string(LinkedInApi.GCONF_USER)
-
-
- # -----------------------------------------------------------------------
- def has_preferences(self):
- """Whether or not this provider has any preferences. If it does not,
- open_preferences must NOT be called; as the behaviour is undetermined."""
+ def get_urls(self):
+ """Return the various URLs needed for OAuth."""
- return True
-
-
- # -----------------------------------------------------------------------
- def open_preferences(self, parent):
- """Open the preferences for this provider as a child of the 'parent' widget."""
-
- self.main_window = parent
- dialog = gtk.Dialog(self.get_name(), parent)
- dialog.add_button(_('Disable'), gtk.RESPONSE_NO)
- enable = dialog.add_button(_('Enable'), gtk.RESPONSE_YES)
-
- button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT,
- hildon.BUTTON_ARRANGEMENT_VERTICAL)
- self._handle_button(None, button, enable)
- button.connect('clicked', self._handle_button, button, enable)
-
- dialog.vbox.add(gtk.Label(""))
- dialog.vbox.add(button)
- dialog.vbox.add(gtk.Label(""))
-
- dialog.show_all()
- result = dialog.run()
- dialog.hide()
- if result == gtk.RESPONSE_CANCEL or result == gtk.RESPONSE_DELETE_EVENT:
- return None
-
- return result == gtk.RESPONSE_YES
-
-
- # -----------------------------------------------------------------------
- def _handle_button(self, e, button, enable):
- """Ensure the button state is correct."""
-
- authenticated = self._api.get_access_token_from_gconf() is not None
- if e is not None:
- if authenticated:
- self._api.remove_access_token_from_gconf()
- else:
- self._api.authenticate(lambda: None, self.block_for_auth)
-
- authenticated = self._api.get_access_token_from_gconf() is not None
-
- button.set_title(authenticated and _("Clear authorisation") or _("Authorise"))
- enable.set_sensitive(authenticated)
-
-
- # -----------------------------------------------------------------------
- def block_for_auth(self, url):
- """Part of the GUI callback API."""
-
- webbrowser.open(url)
- time.sleep(3)
- note = gtk.Dialog(_('Service authorisation'), self.main_window)
- note.add_button(_("Validate"), gtk.RESPONSE_OK)
- input = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
- input.set_property('is-focus', False)
- input.set_property('hildon-input-mode', gtk.HILDON_GTK_INPUT_MODE_NUMERIC)
- note.set_title(_("Verification code from web browser"))
- note.vbox.add(input)
-
- note.show_all()
- result = note.run()
- note.hide()
- if result == gtk.RESPONSE_OK:
- return input.get_text()
- else:
- return None
+ return (self.REQUEST_TOKEN_URL, self.ACCESS_TOKEN_URL, self.AUTHORIZE_URL)
# -----------------------------------------------------------------------
"""Return the service backend."""
return org.maemo.hermes.engine.linkedin.service.Service(self.get_id(), self._api)
+
+
+ # -----------------------------------------------------------------------
+ def verify_verifier(self, access_token):
+ return self._api.verify_verifier(access_token)
--- /dev/null
+from oauth import oauth
+import gnome.gconf
+import gobject
+import gtk
+import hildon
+import org.maemo.hermes.engine.provider
+import time
+import thread
+import httplib
+import re
+import webbrowser
+
+class OAuthProvider(org.maemo.hermes.engine.provider.Provider):
+ """Basis for OAuth services for Hermes. Sub-classes of this should
+ install keys to '<id>_...', and implement the following
+ methods:
+
+ * get_name
+ * get_urls (tuple of request token URL, access token URL & authorize URL)
+ * verify_verifier (returns name of authenticated user)
+ * additional_prefs [optional]
+ * handle_prefs_button [optional]
+
+ Copyright (c) Andrew Flegg <andrew@bleb.org> 2010.
+ Released under the Artistic Licence."""
+
+ GCONF_API_KEY = '/apps/maemo/hermes/%s_key'
+ GCONF_API_SECRET = '/apps/maemo/hermes/%s_secret'
+ GCONF_ACCESS_TOKEN = '/apps/maemo/hermes/%s_access_token'
+ GCONF_USER = '/apps/maemo/hermes/%s_user'
+
+ # -----------------------------------------------------------------------
+ def __init__(self):
+ """Initialise the provider, and ensure the environment is going to work."""
+
+ self._gc = gnome.gconf.client_get_default()
+
+ api_key = self._gc.get_string(self.GCONF_API_KEY % (self.get_id()))
+ secret_key = self._gc.get_string(self.GCONF_API_SECRET % (self.get_id()))
+ if api_key is None or secret_key is None:
+ raise Exception('No application keys found for %s. Installation error.' % (self.get_id()))
+
+ self.access_token = self._get_access_token_from_gconf()
+ self.consumer = oauth.OAuthConsumer(api_key, secret_key)
+ self.sig_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
+
+
+ # -----------------------------------------------------------------------
+ def get_urls(self):
+ """Return a tuple containing request token, access token & authorize URLs."""
+
+ return (None, None, None)
+
+
+ # -----------------------------------------------------------------------
+ def get_account_detail(self):
+ """Return the name of the linked LinkedIn account."""
+
+ return self._gc.get_string(self.GCONF_USER % (self.get_id()))
+
+
+ # -----------------------------------------------------------------------
+ def has_preferences(self):
+ """Whether or not this provider has any preferences. If it does not,
+ open_preferences must NOT be called; as the behaviour is undetermined."""
+
+ return True
+
+
+ # -----------------------------------------------------------------------
+ def open_preferences(self, parent):
+ """Open the preferences for this provider as a child of the 'parent' widget."""
+
+ self.main_window = parent
+ dialog = gtk.Dialog(self.get_name(), parent)
+ dialog.add_button(_('Disable'), gtk.RESPONSE_NO)
+ enable = dialog.add_button(_('Enable'), gtk.RESPONSE_YES)
+
+ button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT,
+ hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ self._handle_button(None, button, enable)
+ button.connect('clicked', self._handle_button, button, enable)
+
+ dialog.vbox.add(button)
+ self.additional_prefs(dialog)
+ dialog.vbox.add(gtk.Label(""))
+
+ dialog.show_all()
+ result = dialog.run()
+ dialog.hide()
+
+ self.handle_prefs_response(result)
+ if result == gtk.RESPONSE_CANCEL or result == gtk.RESPONSE_DELETE_EVENT:
+ return None
+
+ return result == gtk.RESPONSE_YES
+
+
+ # -----------------------------------------------------------------------
+ def additional_prefs(self, dialog):
+ """Override to add additional controls to the authentication dialogue."""
+
+ dialog.vbox.add(gtk.Label(""))
+ dialog.vbox.add(gtk.Label(""))
+
+
+ # -----------------------------------------------------------------------
+ def handle_prefs_response(self, result):
+ """Override to handle the response from a dialogue button."""
+
+ None
+
+
+ # -----------------------------------------------------------------------
+ def _handle_button(self, e, button, enable):
+ """Ensure the button state is correct."""
+
+ authenticated = self._get_access_token_from_gconf() is not None
+ if e is not None:
+ if authenticated:
+ self._remove_access_token_from_gconf()
+ else:
+ self.authenticate(lambda: None, self.block_for_auth)
+
+ authenticated = self._get_access_token_from_gconf() is not None
+
+ button.set_title(authenticated and _("Clear authorisation") or _("Authorise"))
+ enable.set_sensitive(authenticated)
+
+
+ # -----------------------------------------------------------------------
+ def block_for_auth(self, url):
+ """Part of the GUI callback API."""
+
+ webbrowser.open(url)
+ time.sleep(3)
+ note = gtk.Dialog(_('Service authorisation'), self.main_window)
+ note.add_button(_("Validate"), gtk.RESPONSE_OK)
+ input = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
+ input.set_property('is-focus', False)
+ input.set_property('hildon-input-mode', gtk.HILDON_GTK_INPUT_MODE_NUMERIC)
+ note.set_title(_("Verification code from web browser"))
+ note.vbox.add(input)
+
+ note.show_all()
+ result = note.run()
+ note.hide()
+ if result == gtk.RESPONSE_OK:
+ return input.get_text()
+ else:
+ return None
+
+
+ # -----------------------------------------------------------------------
+ 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.access_token = self._get_access_token(token, verifier)
+ name = self.verify_verifier(self.access_token)
+ self._gc.set_string(self.GCONF_USER % (self.get_id()), name)
+
+
+ # -----------------------------------------------------------------------
+ def _store_access_token_in_gconf(self, token_str):
+ if "oauth_problem" in token_str:
+ raise Exception("Authorization failure - access token reported OAuth problem")
+
+ self._gc.set_string(self.GCONF_ACCESS_TOKEN % (self.get_id()), token_str)
+
+
+ # -----------------------------------------------------------------------
+ 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(self.GCONF_ACCESS_TOKEN % (self.get_id()))
+ print token_str
+ if not token_str or len(token_str) < 8:
+ 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(self.GCONF_ACCESS_TOKEN % (self.get_id()))
+ self._gc.unset(self.GCONF_USER % (self.get_id()))
+
+
+ # -----------------------------------------------------------------------
+ def _get_request_token(self):
+ """Get a request token from OAuth provider."""
+
+ url = self.get_urls()[0]
+ hostname = self._get_hostname_from_url(url)
+
+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, callback="oob", http_url=url)
+ oauth_request.sign_request(self.sig_method, self.consumer, None)
+
+ connection = httplib.HTTPSConnection(hostname)
+ connection.request(oauth_request.http_method, 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.get_urls()[2])
+ 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."""
+
+ url = self.get_urls()[1]
+ hostname = self._get_hostname_from_url(url)
+
+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, verifier=verifier, http_url=url)
+ oauth_request.sign_request(self.sig_method, self.consumer, token)
+
+ connection = httplib.HTTPSConnection(hostname)
+ connection.request(oauth_request.http_method, 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 make_api_request(self, url):
+ hostname = self._get_hostname_from_url(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(hostname)
+ try:
+ connection.request(oauth_request.http_method, url, headers=oauth_request.to_header())
+ xml = connection.getresponse().read()
+ return xml
+ except:
+ raise Exception("Failed to contact LinkedIn at " + url)
+
+
+ # -----------------------------------------------------------------------
+ def _get_hostname_from_url(self, url):
+ """Extract the hostname from a URL."""
+ return re.sub(r'^\w+://(.+?)[/:].*$', r'\1', url)
+
\ No newline at end of file
+++ /dev/null
-#!/usr/bin/env python
-
-class SyncJob():
-
- def __init__(self, services, original_contacts, progress_callback):
- self._services = services
- self._original_contacts = original_contacts
-
-
- def run(self):
- self._pre_process_contacts()
- self._process_friends()
- self._process_contacts()
-
-
- def create_contact_for_friend(self, friend):
- """Creates a contact for the friend:
- FIXME: if we do it NOW, then that contact can be re-used and mapped to other unmatched friends
- FIXME: should this really take place here ..."""
-
- pass
-
-
- def register_match(self, contact, friend):
- """Makes sure that when save is clicked,"""
-
-
- def get_unmatched_friends(self):
- pass
-
-
- def get_updated_contacts(self):
- pass
-
-
- def get_matched_contacts(self):
- return self._original_contacts
-
-
- def _pre_process_contacts(self):
- for contact in self._original_contacts:
- for service in self._services:
- service.pre_process_contact(contact)
-
-
- def _process_friends(self):
- for service in self._services:
- service.process_friends()
-
-
- def _process_contacts(self):
- for contact in self._original_contacts:
- for service in self._services:
- service.process_contact(contact)
# -----------------------------------------------------------------------
- def __init__(self, username, password):
- self._username = username
- self._password = password
+ def __init__(self, make_api_request):
+ self._make_api_request = make_api_request
# -----------------------------------------------------------------------
def get_friends(self):
'''Return the full list of people being followed by 'username'.'''
- url = 'https://twitter.com/statuses/friends.json'
+ url = 'https://twitter.com/statuses/friends.json?cursor=%d'
cursor = -1
users = []
while True:
- json = self._FetchUrl(url, parameters = { 'cursor': cursor})
+ json = self._make_api_request(url % (cursor))
data = simplejson.loads(json)
if 'error' in data:
raise Exception(data['error'])
break
return users
-
-
- # -----------------------------------------------------------------------
- def _FetchUrl(self,
- url,
- parameters=None):
- '''Fetch a URL, optionally caching for a specified time.
-
- Args:
- url:
- The URL to retrieve
- parameters:
- A dict whose key/value pairs should encoded and added
- to the query string. [optional]
-
- Returns:
- A string containing the body of the response.
- '''
-
- # Build the extra parameters dict
- extra_params = {}
- if parameters:
- extra_params.update(parameters)
-
- # Add key/value parameters to the query string of the url
- url = self._BuildUrl(url, extra_params=extra_params)
-
- # Get a url opener that can handle basic auth
- basic_auth = base64.encodestring('%s:%s' % (self._username, self._password))[:-1]
-
- handler = urllib2.HTTPBasicAuthHandler()
- (scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url)
- handler.add_password('Twitter API', netloc, self._username, self._password)
- opener = urllib2.build_opener(handler)
- opener.addheaders = {'Authorization': 'Basic %s' % basic_auth}.items()
- url_data = opener.open(url, None).read()
- opener.close()
- return url_data
-
-
- # -----------------------------------------------------------------------
- def _BuildUrl(self, url, path_elements=None, extra_params=None):
- # Break url into consituent parts
- (scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url)
-
- # Add any additional path elements to the path
- if path_elements:
- # Filter out the path elements that have a value of None
- p = [i for i in path_elements if i]
- if not path.endswith('/'):
- path += '/'
- path += '/'.join(p)
-
- # Add any additional query parameters to the query string
- if extra_params and len(extra_params) > 0:
- extra_query = self._EncodeParameters(extra_params)
- # Add it to the existing query
- if query:
- query += '&' + extra_query
- else:
- query = extra_query
-
- # Return the rebuilt URL
- return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
-
-
-
+
# -----------------------------------------------------------------------
- def _EncodeParameters(self, parameters):
- '''Return a string in key=value&key=value form
-
- Values of None are not included in the output string.
-
- Args:
- parameters:
- A dict of (key, value) tuples, where value is encoded as
- specified by self._encoding
- Returns:
- A URL-encoded string in "key=value&key=value" form
- '''
- if parameters is None:
- return None
- else:
- return urllib.urlencode(dict([(k, unicode(v).encode('utf-8')) for k, v in parameters.items() if v is not None]))
-
+ def get_user(self):
+ """Return the name of the authenticated user."""
+
+ url = 'https://api.twitter.com/1/account/verify_credentials.json'
+ json = self._make_api_request(url)
+ user = simplejson.loads(json)
+# if 'name' in user and user['name']:
+# return user['name']
+ return user['screen_name']
import gnome.gconf
import gtk, hildon
-import org.maemo.hermes.engine.provider
import org.maemo.hermes.engine.twitter.service
+from org.maemo.hermes.engine.provider_oauth import OAuthProvider
from org.maemo.hermes.engine.twitter.api import TwitterApi
-class Provider(org.maemo.hermes.engine.provider.Provider):
+class Provider(OAuthProvider):
"""Twitter provider for Hermes.
Copyright (c) Andrew Flegg <andrew@bleb.org> 2010.
# -----------------------------------------------------------------------
def __init__(self):
- self._gconf = gnome.gconf.client_get_default()
+ OAuthProvider.__init__(self)
+ self._api = TwitterApi(self.make_api_request)
# -----------------------------------------------------------------------
all-alphabetic version of this name is expected to be provided."""
return 'Twitter'
-
-
- # -----------------------------------------------------------------------
- def has_preferences(self):
- """Whether or not this provider has any preferences. If it does not,
- open_preferences must NOT be called; as the behaviour is undetermined."""
-
- return True
-
-
- # -----------------------------------------------------------------------
- def open_preferences(self, parent):
- """Show the username/password dialogue."""
-
- dialog = gtk.Dialog(self.get_name(), parent)
- dialog.add_button(_('Disable'), gtk.RESPONSE_NO)
- enable = dialog.add_button(_('Enable'), gtk.RESPONSE_YES)
-
- # -- Username...
- #
- hbox = gtk.HBox()
- hbox.pack_start(gtk.Label(_("Username")))
-
- username = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
- username.set_property('is-focus', True)
- username.set_property('hildon-input-mode', gtk.HILDON_GTK_INPUT_MODE_FULL)
- username.set_text(self._gconf.get_string("/apps/maemo/hermes/twitter_user") or '')
-
- hbox.pack_start(username)
- dialog.vbox.add(hbox)
-
- # -- Password...
- #
- hbox = gtk.HBox()
- hbox.pack_start(gtk.Label(_("Password")))
-
- password = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
- password.set_property('hildon-input-mode', gtk.HILDON_GTK_INPUT_MODE_FULL | gtk.HILDON_GTK_INPUT_MODE_INVISIBLE)
- password.set_text(self._gconf.get_string("/apps/maemo/hermes/twitter_pwd") or '')
- hbox.pack_start(password)
- dialog.vbox.add(hbox)
-
- # -- Enable is only available if both populated...
- #
- def _check_fields(e, p):
- enable.set_sensitive(e.get_text() != '' and p.get_text() != '')
- username.connect('changed', _check_fields, password)
- password.connect('changed', _check_fields, username)
- _check_fields(username, password)
-
- # -- Run the dialogue...
- #
- dialog.show_all()
- result = dialog.run()
- dialog.hide()
- if result == gtk.RESPONSE_CANCEL or result == gtk.RESPONSE_DELETE_EVENT:
- return None
-
- self._gconf.set_string("/apps/maemo/hermes/twitter_user", username.get_text())
- self._gconf.set_string("/apps/maemo/hermes/twitter_pwd", password.get_text())
- return result == gtk.RESPONSE_YES
# -----------------------------------------------------------------------
- def get_account_detail(self):
- """Return the Twitter username."""
+ def get_urls(self):
+ """Return the various URLs needed for OAuth."""
+
+ return ('https://api.twitter.com/oauth/request_token',
+ 'https://api.twitter.com/oauth/access_token',
+ 'https://api.twitter.com/oauth/authorize')
- return self._gconf.get_string("/apps/maemo/hermes/twitter_user")
-
# -----------------------------------------------------------------------
def service(self, gui_callback):
See Service for more details."""
- username = self._gconf.get_string("/apps/maemo/hermes/twitter_user") or ''
- password = self._gconf.get_string("/apps/maemo/hermes/twitter_pwd") or ''
-
- api = TwitterApi(username, password)
+ return org.maemo.hermes.engine.twitter.service.Service(self.get_id(), self._api)
- return org.maemo.hermes.engine.twitter.service.Service(self.get_id(), api)
+
+ # -----------------------------------------------------------------------
+ def verify_verifier(self, access_token):
+ return self._api.get_user()