summaryrefslogtreecommitdiff
path: root/sunshine
diff options
context:
space:
mode:
authorKrzysztof Klinikowski <kkszysiu@gmail.com>2010-02-01 02:50:00 +0100
committerKrzysztof Klinikowski <kkszysiu@gmail.com>2010-02-01 02:50:00 +0100
commit38f5ee3ba62625d5329949d2bb5d06cf783dc1c0 (patch)
treeee0684b64af1a8c5dcd153ae7bb5f92c005a909d /sunshine
parent326b528df377d646360febeee2866dde9c29dbcf (diff)
Avatar uploading implemented.
Diffstat (limited to 'sunshine')
-rw-r--r--sunshine/avatars.py14
-rw-r--r--sunshine/connection.py136
-rw-r--r--sunshine/lqsoft/gaduapi.py348
3 files changed, 378 insertions, 120 deletions
diff --git a/sunshine/avatars.py b/sunshine/avatars.py
index 485cd37..365e31d 100644
--- a/sunshine/avatars.py
+++ b/sunshine/avatars.py
@@ -21,6 +21,8 @@ import logging
import imghdr
import hashlib
import dbus
+import StringIO
+from lqsoft.gaduapi import *
import telepathy
@@ -45,7 +47,7 @@ class SunshineAvatars(telepathy.server.ConnectionInterfaceAvatars):
def GetAvatarRequirements(self):
mime_types = ("image/png","image/jpeg","image/gif")
- return (mime_types, 96, 96, 192, 192, 500 * 1024)
+ return (mime_types, 0, 0, 0, 0, 0)
def GetKnownAvatarTokens(self, contacts):
result = {}
@@ -55,6 +57,7 @@ class SunshineAvatars(telepathy.server.ConnectionInterfaceAvatars):
#tutaj kiedys trzeba napisac kod odp za naszego avatara
contact = None
result[handle] = handle.name
+
else:
contact = handle.contact
@@ -79,7 +82,14 @@ class SunshineAvatars(telepathy.server.ConnectionInterfaceAvatars):
d.addErrback(self.on_fetch_avatars_file_failed, url, handle_id)
def SetAvatar(self, avatar, mime_type):
- pass
+ if check_requirements() == True:
+ if not isinstance(avatar, str):
+ avatar = "".join([chr(b) for b in avatar])
+ data = StringIO.StringIO(avatar).getvalue()
+ gg = GG_Oauth(self.profile.uin, self.password)
+ ext = gg.getExtByType(mime_type)
+ gg.uploadAvatar(data, ext)
+ return str(self.profile.uin).encode("hex")
# self._avatar_known = True
# if not isinstance(avatar, str):
# avatar = "".join([chr(b) for b in avatar])
diff --git a/sunshine/connection.py b/sunshine/connection.py
index 46c7952..26dfcc9 100644
--- a/sunshine/connection.py
+++ b/sunshine/connection.py
@@ -91,18 +91,6 @@ class SunshineConfig(object):
return self.roster
- def get_self_alias(self):
- if os.path.exists(self.path2):
- file = open(self.path2, "r")
- alias = file.read()
- file.close()
- return alias
-
- def save_self_alias(self, alias):
- file = open(self.path2, "w")
- file.write(alias)
- file.close()
-
def make_contacts_file(self, groups, contacts):
contactbook_xml = ET.Element("ContactBook")
@@ -135,6 +123,19 @@ class SunshineConfig(object):
def get_contacts_count(self):
return self.contacts_count
+ # alias config
+ def get_self_alias(self):
+ if os.path.exists(self.path2):
+ file = open(self.path2, "r")
+ alias = file.read()
+ file.close()
+ return alias
+
+ def save_self_alias(self, alias):
+ file = open(self.path2, "w")
+ file.write(alias)
+ file.close()
+
#class GaduClientFactory(protocol.ClientFactory, protocol.ReconnectingClientFactory):
class GaduClientFactory(protocol.ClientFactory):
def __init__(self, config):
@@ -203,6 +204,8 @@ class SunshineConnection(telepathy.server.Connection,
self.profile.onMessageReceived = self.on_messageReceived
#self.profile.onStatusNoticiesRecv = self.on_StatusNoticiesRecv
+ self.password = str(parameters['password'])
+
#lets try to make file with contacts etc ^^
self.configfile = SunshineConfig(int(parameters['account']))
self.configfile.check_dirs()
@@ -424,7 +427,7 @@ class SunshineConnection(telepathy.server.Connection,
def updateContactsFile(self):
"""Method that updates contact file when it changes and in loop every 5 seconds."""
self.configfile.make_contacts_file(self.profile.groups, self.profile.contacts)
- reactor.callLater(5, self.updateContactsFile)
+ reactor.callLater(50, self.updateContactsFile)
@async
def makeTelepathyContactsChannel(self):
@@ -477,7 +480,7 @@ class SunshineConnection(telepathy.server.Connection,
logger.info("No contacts in the XML contacts file yet. Contacts imported.")
self.configfile.make_contacts_file(self.profile.groups, self.profile.contacts)
- reactor.callLater(5, self.updateContactsFile)
+ reactor.callLater(50, self.updateContactsFile)
self.makeTelepathyContactsChannel()
self.makeTelepathyGroupChannels()
@@ -496,7 +499,7 @@ class SunshineConnection(telepathy.server.Connection,
self.profile.importContacts(self.on_contactsImported)
else:
self.configfile.make_contacts_file(self.profile.groups, self.profile.contacts)
- reactor.callLater(5, self.updateContactsFile)
+ reactor.callLater(50, self.updateContactsFile)
self.makeTelepathyContactsChannel()
self.makeTelepathyGroupChannels()
@@ -551,106 +554,3 @@ class SunshineConnection(telepathy.server.Connection,
#print 'message: ', message
channel.Received(self._recv_id, timestamp, handle, type, 0, message)
self._recv_id += 1
-
-
- # papyon.event.ClientEventInterface
- def on_client_state_changed(self, state):
- if state == papyon.event.ClientState.CONNECTING:
- self.StatusChanged(telepathy.CONNECTION_STATUS_CONNECTING,
- telepathy.CONNECTION_STATUS_REASON_REQUESTED)
- elif state == papyon.event.ClientState.SYNCHRONIZED:
- handle = ButterflyHandleFactory(self, 'list', 'subscribe')
-# props = self._generate_props(telepathy.CHANNEL_TYPE_CONTACT_LIST,
-# handle, False)
-# self._channel_manager.channel_for_props(props, signal=True)
-#
-# handle = ButterflyHandleFactory(self, 'list', 'publish')
-# props = self._generate_props(telepathy.CHANNEL_TYPE_CONTACT_LIST,
-# handle, False)
-# self._channel_manager.channel_for_props(props, signal=True)
-
- #handle = ButterflyHandleFactory(self, 'list', 'hide')
- #props = self._generate_props(telepathy.CHANNEL_TYPE_CONTACT_LIST,
- # handle, False)
- #self._channel_manager.channel_for_props(props, signal=True)
-
- #handle = ButterflyHandleFactory(self, 'list', 'allow')
- #props = self._generate_propstelepathy.CHANNEL_TYPE_CONTACT_LIST,
- # handle, False)
- #self._channel_manager.channel_for_props(props, signal=True)
-
- #handle = ButterflyHandleFactory(self, 'list', 'deny')
- #props = self._generate_props(telepathy.CHANNEL_TYPE_CONTACT_LIST,
- # handle, False)
- #self._channel_manager.channel_for_props(props, signal=True)
-
- for group in self.msn_client.address_book.groups:
- handle = ButterflyHandleFactory(self, 'group',
- group.name.decode("utf-8"))
- props = self._generate_props(
- telepathy.CHANNEL_TYPE_CONTACT_LIST, handle, False)
- self._channel_manager.channel_for_props(props, signal=True)
- elif state == papyon.event.ClientState.OPEN:
- self.StatusChanged(telepathy.CONNECTION_STATUS_CONNECTED,
- telepathy.CONNECTION_STATUS_REASON_REQUESTED)
- presence = self._initial_presence
- message = self._initial_personal_message
- if presence is not None:
- self._client.profile.presence = presence
- if message is not None:
- self._client.profile.personal_message = message
- self._client.profile.end_point_name = "PAPYON"
-
- if (presence is not None) or (message is not None):
- self._presence_changed(ButterflyHandleFactory(self, 'self'),
- self._client.profile.presence,
- self._client.profile.personal_message)
- elif state == papyon.event.ClientState.CLOSED:
- self.StatusChanged(telepathy.CONNECTION_STATUS_DISCONNECTED,
- self.__disconnect_reason)
- #FIXME
- self._channel_manager.close()
- self._advertise_disconnected()
-
- # papyon.event.ClientEventInterface
- def on_client_error(self, type, error):
- if type == papyon.event.ClientErrorType.NETWORK:
- self.__disconnect_reason = telepathy.CONNECTION_STATUS_REASON_NETWORK_ERROR
- elif type == papyon.event.ClientErrorType.AUTHENTICATION:
- self.__disconnect_reason = telepathy.CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED
- elif type == papyon.event.ClientErrorType.PROTOCOL and \
- error == papyon.event.ProtocolError.OTHER_CLIENT:
- self.__disconnect_reason = telepathy.CONNECTION_STATUS_REASON_NAME_IN_USE
- else:
- self.__disconnect_reason = telepathy.CONNECTION_STATUS_REASON_NONE_SPECIFIED
-
- # papyon.event.InviteEventInterface
- def on_invite_conversation(self, conversation):
- logger.debug("Conversation invite")
- #FIXME: get rid of this crap and implement group support
- participants = conversation.participants
- for p in participants:
- participant = p
- break
- handle = ButterflyHandleFactory(self, 'contact',
- participant.account, participant.network_id)
-
- props = self._generate_props(telepathy.CHANNEL_TYPE_TEXT,
- handle, False, initiator_handle=handle)
- channel = self._channel_manager.channel_for_props(props,
- signal=True, conversation=conversation)
-
- # papyon.event.InviteEventInterface
- def on_invite_conference(self, call):
- logger.debug("Call invite")
- handle = ButterflyHandleFactory(self, 'contact', call.peer.account,
- call.peer.network_id)
-
- props = self._generate_props(telepathy.CHANNEL_TYPE_STREAMED_MEDIA,
- handle, False, initiator_handle=handle)
-
- channel = self._channel_manager.channel_for_props(props,
- signal=True, call=call)
-
- def _advertise_disconnected(self):
- self._manager.disconnected(self)
diff --git a/sunshine/lqsoft/gaduapi.py b/sunshine/lqsoft/gaduapi.py
new file mode 100644
index 0000000..33c612b
--- /dev/null
+++ b/sunshine/lqsoft/gaduapi.py
@@ -0,0 +1,348 @@
+from twisted.internet import reactor
+from pprint import pformat
+
+from twisted.internet.defer import Deferred
+from twisted.internet.protocol import Protocol
+from twisted.web.http_headers import Headers
+from twisted.internet.defer import succeed
+
+from twisted.internet import task
+
+from twisted.web.client import getPage
+
+from zope.interface import implements
+
+import sys
+
+import urllib
+import logging
+
+#__all__ = ['SunshineGaduAPI']
+
+logger = logging.getLogger('Sunshine.GaduAPI')
+
+try:
+ proper_twisted = True
+ from twisted.web.iweb import IBodyProducer
+ from twisted.web.client import Agent
+except ImportError:
+ logger.info("Twisted version is too old.")
+ proper_twisted = False
+
+try:
+ import oauth as oauth
+ oauth_loaded = True
+except ImportError:
+ logger.info("oAuth module can't be loaded")
+ oauth_loaded = False
+import json
+import mimetools
+import mimetypes
+import time
+
+REQUEST_TOKEN_URL = 'http://api.gadu-gadu.pl/request_token'
+ACCESS_TOKEN_URL = 'http://api.gadu-gadu.pl/access_token'
+AUTHORIZE_TOKEN_URL = 'http://login.gadu-gadu.pl/authorize'
+PUT_AVATAR_URL = 'http://api.gadu-gadu.pl/avatars/%s/0.xml'
+
+def check_requirements():
+ if proper_twisted == True and oauth_loaded == True:
+ return True
+ else:
+ return False
+ logger.info("Requirements related with Gadu-Gadu oAuth API support not fullfilled. You need twisted-core, twisted-web in version 9.0.0 or greater and python-oauth.")
+
+
+class StringProducer(object):
+ if check_requirements() == True:
+ implements(IBodyProducer)
+
+ def __init__(self, body):
+ self.body = body
+ self.length = len(body)
+
+ def startProducing(self, consumer):
+ consumer.write(self.body)
+ return succeed(None)
+
+ def pauseProducing(self):
+ pass
+
+ def stopProducing(self):
+ pass
+
+class BeginningPrinter(Protocol):
+ def __init__(self, finished):
+ self.finished = finished
+ self.remaining = 1024 * 10
+ self.body = ''
+
+ def dataReceived(self, bytes):
+ if self.remaining:
+ display = bytes[:self.remaining]
+ self.body = self.body+display
+ self.remaining -= len(display)
+
+ def connectionLost(self, reason):
+ #print 'Finished receiving body:', reason.getErrorMessage()
+ self.finished.callback(self.body)
+
+class GG_Oauth(object):
+ def __init__(self, uin, password):
+ self.uin = uin
+ self.password = password
+
+ self.timestamp = 0
+ self.expire_token = 0
+ self.access_token = None
+
+ self.__loopingcall = None
+
+ self.agent = Agent(reactor)
+
+ self.consumer = oauth.OAuthConsumer(self.uin, self.password)
+
+ self._signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
+
+ def getContentType(self, filename):
+ return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
+
+ def getExtByType(self, mime):
+ return mimetypes.guess_extension(mime)
+
+ def encodeMultipart(self, fields, files):
+ """
+ fields is a sequence of (name, value) elements for regular form fields.
+ files is a sequence of (name, filename, value) elements for data to be uploaded as files
+ Return (content_type, body) ready for httplib.HTTP instance
+ """
+ boundary = mimetools.choose_boundary()
+ crlf = '\r\n'
+
+ l = []
+ for (k, v) in fields:
+ l.append('--' + boundary)
+ l.append('Content-Disposition: form-data; name="%s"' % k)
+ l.append('')
+ l.append(v)
+ for (k, f, v) in files:
+ l.append('--' + boundary)
+ l.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (k, f))
+ l.append('Content-Type: %s' % self.getContentType(f))
+ l.append('')
+ l.append(v)
+ l.append('--' + boundary + '--')
+ l.append('')
+ body = crlf.join(l)
+
+ return boundary, body
+
+ def putAvatar(self, data, ext):
+ url = str(PUT_AVATAR_URL % self.uin)
+ #print url
+
+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=self.access_token, http_method='PUT', http_url=url) # create an oauth request
+ oauth_request.sign_request(self._signature_method, self.consumer, self.access_token) # the request knows how to generate a signature
+ auth_header = oauth_request.to_header()
+
+ filename = str(self.uin)+ext
+
+ (boundary, body) = self.encodeMultipart(fields=(('_method', 'PUT'),), files=(('avatar', filename, data),))
+ body = StringProducer(str(body))
+
+ headers = {}
+ #headers['Connection'] = ['keep-alive']
+ headers['Authorization'] = [auth_header['Authorization']]
+ headers['User-Agent'] = ['Gadu-Gadu Client, build 8,0,0,4881']
+ headers['Accept'] = ['*/*']
+ headers['Content-Type'] = ['multipart/form-data; boundary=%s' % boundary]
+ headers = Headers(headers)
+
+ d = self.agent.request(
+ 'POST',
+ url,
+ headers,
+ body)
+
+ d.addCallback(self.putAvatarSuccess)
+ d.addErrback(self.cbShutdown)
+
+ def putAvatarSuccess(self, response):
+ #print 'putAvatarSuccess: ', response
+ #print 'Response version:', response.version
+ #print 'Response code:', response.code
+ #print 'Response phrase:', response.phrase
+ #print 'Response headers:'
+ #print pformat(list(response.headers.getAllRawHeaders()))
+ logger.info("New avatar should be uploaded now.")
+
+ """
+ def accessTokenReceived(self, result, oauth_token):
+ print 'accessTokenReceived: ', result
+ content = json.loads(result)['result']
+ oauth_access_token = oauth.OAuthToken(content['oauth_token'], content['oauth_token_secret'])
+
+ #url = str(PUT_AVATAR_URL % content['uin'])
+ url = 'http://api.gadu-gadu.pl/users/5120225.xml'
+ print url
+
+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=oauth_access_token, http_method='GET', http_url=url) # create an oauth request
+ oauth_request.sign_request(self._signature_method, self.consumer, oauth_access_token) # the request knows how to generate a signature
+ auth_header = oauth_request.to_header()
+
+ headers = {}
+ headers['Authorization'] = [auth_header['Authorization']]
+ headers['User-Agent'] = ['Gadu-Gadu Client, build 8,0,0,4881']
+ headers['Accept'] = ['*/*']
+ headers['Host'] = ['api.gadu-gadu.pl']
+ #headers['Content-Type'] = ['multipart/form-data; boundary=%s' % boundary]
+ headers['Content-Length'] = [0]
+ headers = Headers(headers)
+ d = self.agent.request(
+ 'GET',
+ 'http://api.gadu-gadu.pl/users/5120225.xml',
+ headers,
+ None)
+
+ d.addCallback(self.putAvatarSuccess)
+ d.addErrback(self.cbShutdown)
+ """
+
+ def accessTokenReceived(self, result, oauth_token):
+ #print 'accessTokenReceived: ', result
+ content = json.loads(result)['result']
+
+ self.access_token = oauth.OAuthToken(content['oauth_token'], content['oauth_token_secret'])
+ self.expire_token = time.time()+36000
+
+ def requestAccessToken(self, response, oauth_token):
+ #print 'Response version:', response.version
+ #print 'Response code:', response.code
+ #print 'Response phrase:', response.phrase
+ #print 'Response headers:'
+ #print pformat(list(response.headers.getAllRawHeaders()))
+ finished = Deferred()
+ response.deliverBody(BeginningPrinter(finished))
+ finished.addCallback(self.accessTokenReceived, oauth_token)
+ finished.addErrback(self.cbShutdown)
+ return finished
+
+ def cbTokenAuthorised(self, result, oauth_token):
+ #print 'tokenAuthorised: ', result
+ #print 'Response version:', result.version
+ #print 'Response code:', result.code
+ #print 'Response phrase:', result.phrase
+ #print 'Response headers:'
+ #print pformat(list(result.headers.getAllRawHeaders()))
+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=oauth_token, http_method='POST', http_url=ACCESS_TOKEN_URL) # create an oauth request
+ oauth_request.sign_request(self._signature_method, self.consumer, oauth_token) # the request knows how to generate a signature
+ auth_header = oauth_request.to_header()
+
+ headers = {}
+ headers['Authorization'] = [auth_header['Authorization']]
+ headers['User-Agent'] = ['Gadu-Gadu Client, build 8,0,0,4881']
+ headers['Accept'] = ['application/json']
+ #headers['Content-Type'] = ['application/x-www-form-urlencoded']
+ headers['Content-Length'] = [0]
+ headers = Headers(headers)
+
+ d = self.agent.request(
+ 'POST',
+ ACCESS_TOKEN_URL,
+ headers,
+ None)
+
+ d.addCallback(self.requestAccessToken, oauth_token)
+ d.addErrback(self.cbShutdown)
+
+ def cbRequestToken(self, response):
+ #print 'Response version:', response.version
+ #print 'Response code:', response.code
+ #print 'Response phrase:', response.phrase
+ #print 'Response headers:'
+ #print pformat(list(response.headers.getAllRawHeaders()))
+ finished = Deferred()
+ response.deliverBody(BeginningPrinter(finished))
+ finished.addCallback(self.cbRequestTokenSuccess)
+ finished.addErrback(self.cbShutdown)
+ return finished
+
+ def cbRequestTokenSuccess(self, result):
+ content = json.loads(result)['result']
+
+ oauth_token = oauth.OAuthToken(content['oauth_token'], content['oauth_token_secret'])
+
+ postvars = 'callback_url=http://www.mojageneracja.pl&request_token=%s&uin=%s&password=%s' % (oauth_token.key, self.uin, self.password)
+
+ headers = {}
+ headers['User-Agent'] = ['Gadu-Gadu Client, build 8,0,0,4881']
+ headers['Accept'] = ['*/*']
+ headers['Content-Type'] = ['application/x-www-form-urlencoded']
+
+ headers = Headers(headers)
+
+ body = StringProducer(str(postvars))
+
+ d = self.agent.request(
+ 'POST',
+ AUTHORIZE_TOKEN_URL,
+ headers,
+ body)
+
+ d.addCallback(self.cbTokenAuthorised, oauth_token)
+ d.addErrback(self.cbShutdown)
+
+ def cbShutdown(self, ignored):
+ #reactor.stop()
+ logger.info("Something went wrong.")
+ #print 'cbShutdown: ', ignored
+
+ def requestToken(self):
+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_method='POST', http_url=REQUEST_TOKEN_URL) # create an oauth request
+ #oauth_request.set_parameter('oauth_timestamp', int(time.time())-3600)
+ oauth_request.sign_request(self._signature_method, self.consumer, None) # the request knows how to generate a signature
+ auth_header = oauth_request.to_header()
+ headers = {}
+ headers['Authorization'] = [auth_header['Authorization']]
+ headers['Accept'] = ['application/json']
+ headers['User-Agent'] = ['Gadu-Gadu Client, build 8,0,0,4881']
+ headers['Host'] = ['api.gadu-gadu.pl']
+ headers['Content-Length'] = [0]
+ headers = Headers(headers)
+
+ url = REQUEST_TOKEN_URL
+
+ d = self.agent.request(
+ 'POST',
+ REQUEST_TOKEN_URL,
+ headers,
+ None)
+
+ d.addCallback(self.cbRequestToken)
+ d.addErrback(self.cbShutdown)
+
+ def checkTokenForAvatar(self, data, ext):
+ #print 'checkTokenForAvatar'
+ if int(time.time()) <= self.expire_token and self.access_token != None:
+ self.putAvatar(data, ext)
+ self.__loopingcall.stop()
+
+ def getToken(self):
+ self.requestToken()
+
+ def uploadAvatar(self, data, ext):
+ if int(time.time()) <= self.expire_token and self.access_token != None:
+ self.putAvatar(data, ext)
+ else:
+ self.requestToken()
+ self.__loopingcall = task.LoopingCall(self.checkTokenForAvatar, data, ext)
+ self.__loopingcall.start(5.0)
+
+#if check_requirements() == True:
+# gg = GG_Oauth(4634020, 'xxxxxx')
+# data = open('avatar.png', 'r').read()
+# ext = mimetypes.guess_extension(mimetypes.guess_type('avatar.png')[0])
+# gg.uploadAvatar(data, ext)
+#else:
+# print 'GG_oAuth_API: Requirements related with Gadu-Gadu oAuth API support not fullfilled. You need twisted-core, twisted-web in version 9.0.0 or greater and python-oauth.'
+#reactor.run()