diff options
author | Krzysztof Klinikowski <kkszysiu@gmail.com> | 2010-02-01 02:50:00 +0100 |
---|---|---|
committer | Krzysztof Klinikowski <kkszysiu@gmail.com> | 2010-02-01 02:50:00 +0100 |
commit | 38f5ee3ba62625d5329949d2bb5d06cf783dc1c0 (patch) | |
tree | ee0684b64af1a8c5dcd153ae7bb5f92c005a909d /sunshine | |
parent | 326b528df377d646360febeee2866dde9c29dbcf (diff) |
Avatar uploading implemented.
Diffstat (limited to 'sunshine')
-rw-r--r-- | sunshine/avatars.py | 14 | ||||
-rw-r--r-- | sunshine/connection.py | 136 | ||||
-rw-r--r-- | sunshine/lqsoft/gaduapi.py | 348 |
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() |