Fix pep8 support in src/plugins.
This commit is contained in:
parent
e51a8a650b
commit
0dca274360
27 changed files with 1052 additions and 1079 deletions
|
|
@ -40,20 +40,20 @@ import webbrowser
|
|||
from BeautifulSoup import BeautifulSoup
|
||||
import re
|
||||
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "CouchSurfing plugin",
|
||||
"import_name": "CS",
|
||||
"type": "Misc",
|
||||
"protocols": [],
|
||||
"dependencies": [],
|
||||
"main": "CS_Plugin",
|
||||
"handler": "no",
|
||||
"description": _(u"""This plugin allow to manage your CouchSurfing account throught your SàT frontend""")
|
||||
"name": "CouchSurfing plugin",
|
||||
"import_name": "CS",
|
||||
"type": "Misc",
|
||||
"protocols": [],
|
||||
"dependencies": [],
|
||||
"main": "CS_Plugin",
|
||||
"handler": "no",
|
||||
"description": _(u"""This plugin allow to manage your CouchSurfing account throught your SàT frontend""")
|
||||
}
|
||||
|
||||
AGENT = 'Salut à Toi XMPP/CS Plugin'
|
||||
|
||||
|
||||
class CS_Plugin(object):
|
||||
|
||||
params = """
|
||||
|
|
@ -73,16 +73,17 @@ class CS_Plugin(object):
|
|||
#parameters
|
||||
host.memory.importParams(CS_Plugin.params)
|
||||
#menu
|
||||
host.importMenu(_("Plugin"), "CouchSurfing", self.menuSelected, help_string = _("Launch CoushSurfing management interface"))
|
||||
self.data={} #TODO: delete cookies/data after a while
|
||||
host.importMenu(_("Plugin"), "CouchSurfing", self.menuSelected, help_string=_("Launch CoushSurfing management interface"))
|
||||
self.data = {} # TODO: delete cookies/data after a while
|
||||
self.host.registerGeneralCB("plugin_CS_sendMessage", self.sendMessage)
|
||||
self.host.registerGeneralCB("plugin_CS_showUnreadMessages", self.showUnreadMessages)
|
||||
|
||||
def profileConnected(self, profile):
|
||||
self.data[profile] = PersistentBinaryDict("plugin_CS", profile)
|
||||
|
||||
def dataLoaded(ignore):
|
||||
if not self.data[profile]:
|
||||
self.data[profile] = {'cookies':{}}
|
||||
self.data[profile] = {'cookies': {}}
|
||||
|
||||
self.data[profile].load().addCallback(dataLoaded)
|
||||
|
||||
|
|
@ -92,7 +93,7 @@ class CS_Plugin(object):
|
|||
def erroCB(self, e, id):
|
||||
"""Called when something is going wrong when contacting CS website"""
|
||||
#pdb.set_trace()
|
||||
message_data={"reason": "connection error", "message":_(u"Impossible to contact CS website, please check your login/password, connection or try again later")}
|
||||
message_data = {"reason": "connection error", "message": _(u"Impossible to contact CS website, please check your login/password, connection or try again later")}
|
||||
self.host.bridge.actionResult("ERROR", id, message_data)
|
||||
|
||||
def menuSelected(self, id, profile):
|
||||
|
|
@ -100,24 +101,24 @@ class CS_Plugin(object):
|
|||
login = self.host.memory.getParamA("Login", "CouchSurfing", profile_key=profile)
|
||||
password = self.host.memory.getParamA("Password", "CouchSurfing", profile_key=profile)
|
||||
if not login or not password:
|
||||
message_data={"reason": "uncomplete", "message":_(u"You have to fill your CouchSurfing login & password in parameters before using this interface")}
|
||||
message_data = {"reason": "uncomplete", "message": _(u"You have to fill your CouchSurfing login & password in parameters before using this interface")}
|
||||
self.host.bridge.actionResult("ERROR", id, message_data)
|
||||
return
|
||||
|
||||
post_data = urllib.urlencode({'auth_login[un]':login,'auth_login[pw]':password,'auth_login[action]':'Login...'})
|
||||
post_data = urllib.urlencode({'auth_login[un]': login, 'auth_login[pw]': password, 'auth_login[action]': 'Login...'})
|
||||
|
||||
self.data[profile]['cookies'] = {}
|
||||
|
||||
d = getPage('http://www.couchsurfing.org/login.html', method='POST', postdata=post_data, headers={'Content-Type':'application/x-www-form-urlencoded'} , agent=AGENT, cookies=self.data[profile]['cookies'])
|
||||
d = getPage('http://www.couchsurfing.org/login.html', method='POST', postdata=post_data, headers={'Content-Type': 'application/x-www-form-urlencoded'}, agent=AGENT, cookies=self.data[profile]['cookies'])
|
||||
d.addCallback(self.__connectionCB, id, profile)
|
||||
d.addErrback(self.erroCB, id)
|
||||
|
||||
#self.host.bridge.actionResult("SUPPRESS", id, {})
|
||||
|
||||
|
||||
#pages parsing callbacks
|
||||
|
||||
def savePage(self, name, html):
|
||||
f = open ('/tmp/CS_'+name+'.html','w')
|
||||
f = open('/tmp/CS_' + name + '.html', 'w')
|
||||
f.write(html)
|
||||
f.close()
|
||||
print "page [%s] sauvee" % name
|
||||
|
|
@ -127,27 +128,27 @@ class CS_Plugin(object):
|
|||
print 'Response received'
|
||||
#self.savePage('principale',html)
|
||||
soup = BeautifulSoup(html)
|
||||
self.data[profile]['user_nick'] = soup.find('a','item_link',href='/home.html').contents[0]
|
||||
self.data[profile]['user_nick'] = soup.find('a', 'item_link', href='/home.html').contents[0]
|
||||
self.data[profile]['user_name'] = soup.html.head.title.string.split(' - ')[1]
|
||||
#unread messages
|
||||
try:
|
||||
self.data[profile]['unread_messages'] = int(soup.find(lambda tag: tag.name=='div' and ('class','item_bubble') in tag.attrs and tag.find('a', href="/messages.html?message_status=inbox")).find(text=True))
|
||||
self.data[profile]['unread_messages'] = int(soup.find(lambda tag: tag.name == 'div' and ('class', 'item_bubble') in tag.attrs and tag.find('a', href="/messages.html?message_status=inbox")).find(text=True))
|
||||
except:
|
||||
self.data[profile]['unread_messages'] = 0
|
||||
#unread couchrequest messages
|
||||
try:
|
||||
self.data[profile]['unread_CR_messages'] = int(soup.find(lambda tag: tag.name=='div' and ('class','item_bubble') in tag.attrs and tag.find('a', href="/couchmanager")).find(text=True))
|
||||
self.data[profile]['unread_CR_messages'] = int(soup.find(lambda tag: tag.name == 'div' and ('class', 'item_bubble') in tag.attrs and tag.find('a', href="/couchmanager")).find(text=True))
|
||||
except:
|
||||
self.data[profile]['unread_CR_messages'] = 0
|
||||
|
||||
#if we have already the list of friend, no need to make new requests
|
||||
if not self.data[profile].has_key('friends'):
|
||||
if 'friends' not in self.data[profile]:
|
||||
self.data[profile]['friends'] = {}
|
||||
d = getPage('http://www.couchsurfing.org/connections.html?type=myfriends&show=10000', agent=AGENT, cookies=self.data[profile]['cookies'])
|
||||
d.addCallback(self.__friendsPageCB, id=id, profile=profile)
|
||||
d.addErrback(self.erroCB, id)
|
||||
else:
|
||||
self.host.bridge.actionResult("XMLUI", id, {"type":"window", "xml":self.__buildUI(self.data[profile])})
|
||||
self.host.bridge.actionResult("XMLUI", id, {"type": "window", "xml": self.__buildUI(self.data[profile])})
|
||||
|
||||
def __buildUI(self, data):
|
||||
"""Build the XML UI of the plugin
|
||||
|
|
@ -158,11 +159,11 @@ class CS_Plugin(object):
|
|||
unread_CR_mess = data['unread_CR_messages']
|
||||
friends_list = data['friends'].keys()
|
||||
friends_list.sort()
|
||||
interface = XMLUI('window','tabs', title='CouchSurfing management')
|
||||
interface = XMLUI('window', 'tabs', title='CouchSurfing management')
|
||||
interface.addCategory(_("Messages"), "vertical")
|
||||
interface.addText(_("G'day %(name)s, you have %(nb_message)i unread message%(plural_mess)s and %(unread_CR_mess)s unread couch request message%(plural_CR)s\nIf you want to send a message, select the recipient(s) in the list below") % {'name':user_name, 'nb_message':unread_mess, 'plural_mess':'s' if unread_mess>1 else '', 'unread_CR_mess': unread_CR_mess, 'plural_CR':'s' if unread_CR_mess>1 else ''})
|
||||
interface.addText(_("G'day %(name)s, you have %(nb_message)i unread message%(plural_mess)s and %(unread_CR_mess)s unread couch request message%(plural_CR)s\nIf you want to send a message, select the recipient(s) in the list below") % {'name': user_name, 'nb_message': unread_mess, 'plural_mess': 's' if unread_mess > 1 else '', 'unread_CR_mess': unread_CR_mess, 'plural_CR': 's' if unread_CR_mess > 1 else ''})
|
||||
if unread_mess:
|
||||
interface.addButton('plugin_CS_showUnreadMessages', 'showUnreadMessages', _('Show unread message%(plural)s in external web browser') % {'plural':'s' if unread_mess>1 else ''})
|
||||
interface.addButton('plugin_CS_showUnreadMessages', 'showUnreadMessages', _('Show unread message%(plural)s in external web browser') % {'plural': 's' if unread_mess > 1 else ''})
|
||||
interface.addList(friends_list, 'friends', style=['multi'])
|
||||
interface.changeLayout('pairs')
|
||||
interface.addLabel(_("Subject"))
|
||||
|
|
@ -171,7 +172,7 @@ class CS_Plugin(object):
|
|||
interface.addLabel(_("Message"))
|
||||
interface.addText("(use %name% for contact name and %firstname% for guessed first name)")
|
||||
interface.addTextBox('message')
|
||||
interface.addButton('plugin_CS_sendMessage', 'sendMessage', _('send'), fields_back=['friends','subject','message'])
|
||||
interface.addButton('plugin_CS_sendMessage', 'sendMessage', _('send'), fields_back=['friends', 'subject', 'message'])
|
||||
#interface.addCategory(_("Events"), "vertical") #TODO: coming soon, hopefuly :)
|
||||
#interface.addCategory(_("Couch search"), "vertical")
|
||||
return interface.toXml()
|
||||
|
|
@ -181,28 +182,28 @@ class CS_Plugin(object):
|
|||
|
||||
def __friendsPageCB(self, html, id, profile):
|
||||
"""Called when the friends list page has been received"""
|
||||
self.savePage('friends',html)
|
||||
soup = BeautifulSoup(html.replace('"formtable width="400','"formtable" width="400"')) #CS html fix #TODO: report the bug to CS dev team
|
||||
self.savePage('friends', html)
|
||||
soup = BeautifulSoup(html.replace('"formtable width="400', '"formtable" width="400"')) # CS html fix #TODO: report the bug to CS dev team
|
||||
friends = self.data[profile]['friends']
|
||||
for _tr in soup.findAll('tr', {'class':re.compile("^msgRow*")}): #we parse the row with friends infos
|
||||
_nobr = _tr.find('nobr') #contain the friend name
|
||||
for _tr in soup.findAll('tr', {'class': re.compile("^msgRow*")}): # we parse the row with friends infos
|
||||
_nobr = _tr.find('nobr') # contain the friend name
|
||||
friend_name = unicode(_nobr.string)
|
||||
friend_link = u'http://www.couchsurfing.org'+_nobr.parent['href']
|
||||
friend_link = u'http://www.couchsurfing.org' + _nobr.parent['href']
|
||||
regex_href = re.compile(r'/connections\.html\?id=([^&]+)')
|
||||
a_tag = _tr.find('a',href=regex_href)
|
||||
a_tag = _tr.find('a', href=regex_href)
|
||||
friend_id = regex_href.search(unicode(a_tag)).groups()[0]
|
||||
|
||||
debug(_("CS friend found: %(friend_name)s (id: %(friend_id)s, link: %(friend_link)s)") % {'friend_name':friend_name, 'friend_id':friend_id, 'friend_link':friend_link})
|
||||
friends[friend_name] = {'link':friend_link,'id':friend_id}
|
||||
a = soup.find('td','barmiddle next').a #is there several pages ?
|
||||
debug(_("CS friend found: %(friend_name)s (id: %(friend_id)s, link: %(friend_link)s)") % {'friend_name': friend_name, 'friend_id': friend_id, 'friend_link': friend_link})
|
||||
friends[friend_name] = {'link': friend_link, 'id': friend_id}
|
||||
a = soup.find('td', 'barmiddle next').a # is there several pages ?
|
||||
if a:
|
||||
#yes, we parse the next page
|
||||
d = getPage('http://www.couchsurfing.org/'+str(a['href']), agent=AGENT, cookies=self.data[profile]['cookies'])
|
||||
d = getPage('http://www.couchsurfing.org/' + str(a['href']), agent=AGENT, cookies=self.data[profile]['cookies'])
|
||||
d.addCallback(self.__friendsPageCB, id=id, profile=profile)
|
||||
d.addErrback(self.erroCB, id)
|
||||
else:
|
||||
#no, we show the result
|
||||
self.host.bridge.actionResult("XMLUI", id, {"type":"window", "xml":self.__buildUI(self.data[profile])})
|
||||
self.host.bridge.actionResult("XMLUI", id, {"type": "window", "xml": self.__buildUI(self.data[profile])})
|
||||
|
||||
def __sendMessage(self, answer, subject, message, data, recipient_list, id, profile):
|
||||
"""Send actually the message
|
||||
|
|
@ -224,18 +225,18 @@ class CS_Plugin(object):
|
|||
friend_id = data['friends'][recipient]['id']
|
||||
except KeyError:
|
||||
error('INTERNAL ERROR: unknown friend')
|
||||
return #send an error to the frontend
|
||||
mess = message.replace('%name%',recipient).replace('%firstname%',recipient.split(' ')[0])
|
||||
return # send an error to the frontend
|
||||
mess = message.replace('%name%', recipient).replace('%firstname%', recipient.split(' ')[0])
|
||||
info(_('Sending message to %s') % recipient)
|
||||
debug(_("\nsubject: %(subject)s\nmessage: \n---\n%(message)s\n---\n\n") % {'subject':subject,'message':mess})
|
||||
post_data = urllib.urlencode({'email[subject]':subject.encode('utf-8'),'email[body]':mess.encode('utf-8'),'email[id]':friend_id,'email[action]':'Send Message','email[replied_id]':'','email[couchsurf]':'','email[directions_to_add]':''})
|
||||
d = getPage("http://www.couchsurfing.org/send_message.html", method='POST', postdata=post_data, headers={'Content-Type':'application/x-www-form-urlencoded'} , agent=AGENT, cookies=data['cookies'])
|
||||
debug(_("\nsubject: %(subject)s\nmessage: \n---\n%(message)s\n---\n\n") % {'subject': subject, 'message': mess})
|
||||
post_data = urllib.urlencode({'email[subject]': subject.encode('utf-8'), 'email[body]': mess.encode('utf-8'), 'email[id]': friend_id, 'email[action]': 'Send Message', 'email[replied_id]': '', 'email[couchsurf]': '', 'email[directions_to_add]': ''})
|
||||
d = getPage("http://www.couchsurfing.org/send_message.html", method='POST', postdata=post_data, headers={'Content-Type': 'application/x-www-form-urlencoded'}, agent=AGENT, cookies=data['cookies'])
|
||||
d.addCallback(self.__sendMessage, subject, message, data, recipient_list, id, profile)
|
||||
d.addErrback(self.erroCB, id)
|
||||
else:
|
||||
interface = XMLUI('window', title=_('Message sent')) #TODO: create particular actionResult for alerts ?
|
||||
interface = XMLUI('window', title=_('Message sent')) # TODO: create particular actionResult for alerts ?
|
||||
interface.addText(_('The message has been sent to every recipients'))
|
||||
self.host.bridge.actionResult("XMLUI", id, {"type":"window", "xml":interface.toXml()})
|
||||
self.host.bridge.actionResult("XMLUI", id, {"type": "window", "xml": interface.toXml()})
|
||||
|
||||
def sendMessage(self, id, data, profile):
|
||||
"""Called to send a message to a friend
|
||||
|
|
@ -247,26 +248,25 @@ class CS_Plugin(object):
|
|||
- %firstname%: guessed first name of the friend (currently the first part of the name)
|
||||
"""
|
||||
if not data['friends']:
|
||||
message_data={"reason": "bad data", "message":_(u"There is not recipient selected for this message !")}
|
||||
message_data = {"reason": "bad data", "message": _(u"There is not recipient selected for this message !")}
|
||||
self.host.bridge.actionResult("ERROR", id, message_data)
|
||||
return
|
||||
friends = data['friends'].split('\t')
|
||||
subject = data['subject']
|
||||
message = data['message']
|
||||
info(_("sending message to %(friends)s with subject [%(subject)s]" % {'friends':friends, 'subject':subject}))
|
||||
info(_("sending message to %(friends)s with subject [%(subject)s]" % {'friends': friends, 'subject': subject}))
|
||||
self.__sendMessage(None, subject, message, self.data[profile], friends, id, profile)
|
||||
|
||||
def __showUnreadMessages2(self, html, id, profile):
|
||||
"""Called when the inbox page has been received"""
|
||||
#FIXME: that's really too fragile, only works if the unread messages are in the first page, and it would be too resources consuming for the website to DL each time all pages. In addition, the show attribute doesn't work as expected.
|
||||
soup = BeautifulSoup(html)
|
||||
for tag in soup.findAll(lambda tag: tag.name=='strong' and tag.a and tag.a['href'].startswith('messages.html?message_status=inbox')):
|
||||
link = "http://www.couchsurfing.org/"+str(tag.a['href'])
|
||||
webbrowser.open_new_tab(link) #TODO: the web browser need to already have CS cookies (i.e. already be opened & logged on CS, or be permanently loggued), a warning to the user should be sent/or a balloon-tip
|
||||
for tag in soup.findAll(lambda tag: tag.name == 'strong' and tag.a and tag.a['href'].startswith('messages.html?message_status=inbox')):
|
||||
link = "http://www.couchsurfing.org/" + str(tag.a['href'])
|
||||
webbrowser.open_new_tab(link) # TODO: the web browser need to already have CS cookies (i.e. already be opened & logged on CS, or be permanently loggued), a warning to the user should be sent/or a balloon-tip
|
||||
|
||||
def showUnreadMessages(self, id, data, profile):
|
||||
"""Called when user want to see all unread messages in the external browser"""
|
||||
d = getPage("http://www.couchsurfing.org/messages.html?message_status=inbox&show=10000", agent=AGENT, cookies=self.data[profile]['cookies'])
|
||||
d.addCallback(self.__showUnreadMessages2, id, profile)
|
||||
d.addErrback(self.erroCB, id)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,21 +22,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
from logging import debug, info, warning, error
|
||||
from twisted.words.protocols.jabber import jid
|
||||
|
||||
|
||||
from sat.core.exceptions import UnknownEntityError
|
||||
from sat.tools.misc import SkipOtherTriggers
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "Parrot Plugin",
|
||||
"import_name": "EXP-PARROT",
|
||||
"type": "EXP",
|
||||
"protocols": [],
|
||||
"dependencies": ["XEP-0045"],
|
||||
"main": "Exp_Parrot",
|
||||
"handler": "no",
|
||||
"description": _("""Implementation of parrot mode (repeat messages between 2 entities)""")
|
||||
"name": "Parrot Plugin",
|
||||
"import_name": "EXP-PARROT",
|
||||
"type": "EXP",
|
||||
"protocols": [],
|
||||
"dependencies": ["XEP-0045"],
|
||||
"main": "Exp_Parrot",
|
||||
"handler": "no",
|
||||
"description": _("""Implementation of parrot mode (repeat messages between 2 entities)""")
|
||||
}
|
||||
|
||||
|
||||
class Exp_Parrot(object):
|
||||
"""Parrot mode plugin: repeat messages from one entity or MUC room to another one"""
|
||||
#XXX: This plugin can be potentially dangerous if we don't trust entities linked
|
||||
|
|
@ -123,5 +123,3 @@ class Exp_Parrot(object):
|
|||
del client.parrot_links[source_jid.userhostJID()]
|
||||
except (AttributeError, KeyError):
|
||||
pass
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ from logging import debug, info, warning, error
|
|||
from twisted.words.xish import domish
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from twisted.words.protocols.jabber import error as jab_error
|
||||
import os, os.path
|
||||
import os
|
||||
from twisted.internet import reactor
|
||||
from sat.core.exceptions import ProfileNotInCacheError
|
||||
|
||||
|
|
@ -34,16 +34,17 @@ PROFILE_NAME = "pipe-transfer"
|
|||
PROFILE = "http://jabber.org/protocol/si/profile/" + PROFILE_NAME
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "Pipe Plugin",
|
||||
"import_name": "EXP-PIPE",
|
||||
"type": "EXP",
|
||||
"protocols": ["EXP-PIPE"],
|
||||
"dependencies": ["XEP-0020", "XEP-0095", "XEP-0065", "XEP-0047"],
|
||||
"main": "Exp_Pipe",
|
||||
"handler": "no",
|
||||
"description": _("""Implementation of SI Pipe Transfer""")
|
||||
"name": "Pipe Plugin",
|
||||
"import_name": "EXP-PIPE",
|
||||
"type": "EXP",
|
||||
"protocols": ["EXP-PIPE"],
|
||||
"dependencies": ["XEP-0020", "XEP-0095", "XEP-0065", "XEP-0047"],
|
||||
"main": "Exp_Pipe",
|
||||
"handler": "no",
|
||||
"description": _("""Implementation of SI Pipe Transfer""")
|
||||
}
|
||||
|
||||
|
||||
class Exp_Pipe(object):
|
||||
"""This is a modified version of XEP-0096 to work with named pipes instead of files"""
|
||||
|
||||
|
|
@ -51,13 +52,13 @@ class Exp_Pipe(object):
|
|||
info(_("Plugin Pipe initialization"))
|
||||
self.host = host
|
||||
self.managed_stream_m = [self.host.plugins["XEP-0065"].NAMESPACE,
|
||||
self.host.plugins["XEP-0047"].NAMESPACE] #Stream methods managed
|
||||
self.host.plugins["XEP-0047"].NAMESPACE] # Stream methods managed
|
||||
self.host.plugins["XEP-0095"].registerSIProfile(PROFILE_NAME, self.transferRequest)
|
||||
host.bridge.addMethod("pipeOut", ".plugin", in_sign='ssa{ss}s', out_sign='s', method=self.pipeOut)
|
||||
|
||||
def profileConnected(self, profile):
|
||||
client = self.host.getClient(profile)
|
||||
client._pipe_waiting_for_approval = {} #key = id, value = [transfer data, IdelayedCall Reactor timeout,
|
||||
client._pipe_waiting_for_approval = {} # key = id, value = [transfer data, IdelayedCall Reactor timeout,
|
||||
# current stream method, [failed stream methods], profile]
|
||||
|
||||
def _kill_id(self, approval_id, profile):
|
||||
|
|
@ -78,7 +79,7 @@ class Exp_Pipe(object):
|
|||
@param si_mime_type: Mime type of the pipe (or default "application/octet-stream" if unknown)
|
||||
@param si_el: domish.Element of the request
|
||||
@param profile: %(doc_profile)s"""
|
||||
info (_("EXP-PIPE file transfer requested"))
|
||||
info(_("EXP-PIPE file transfer requested"))
|
||||
debug(si_el.toXml())
|
||||
client = self.host.getClient(profile)
|
||||
if not client:
|
||||
|
|
@ -95,7 +96,7 @@ class Exp_Pipe(object):
|
|||
feature_el = feature_elts[0]
|
||||
form = data_form.Form.fromElement(feature_el.firstChildElement())
|
||||
try:
|
||||
stream_method = self.host.plugins["XEP-0020"].negociate(feature_el, 'stream-method',self.managed_stream_m)
|
||||
stream_method = self.host.plugins["XEP-0020"].negociate(feature_el, 'stream-method', self.managed_stream_m)
|
||||
except KeyError:
|
||||
warning(_("No stream method found"))
|
||||
self.host.plugins["XEP-0095"].sendBadRequestError(iq_id, from_jid, profile)
|
||||
|
|
@ -110,12 +111,11 @@ class Exp_Pipe(object):
|
|||
return
|
||||
|
||||
#if we are here, the transfer can start, we just need user's agreement
|
||||
data={ "id": iq_id, "from":from_jid }
|
||||
data = {"id": iq_id, "from": from_jid}
|
||||
client._pipe_waiting_for_approval[si_id] = [data, reactor.callLater(300, self._kill_id, si_id), stream_method, [], profile]
|
||||
|
||||
self.host.askConfirmation(si_id, "PIPE_TRANSFER", data, self.confirmationCB, profile)
|
||||
|
||||
|
||||
def confirmationCB(self, sid, accepted, frontend_data, profile):
|
||||
"""Called on confirmation answer
|
||||
@param sid: file transfer session id
|
||||
|
|
@ -146,13 +146,13 @@ class Exp_Pipe(object):
|
|||
return
|
||||
|
||||
#we can send the iq result
|
||||
feature_elt = self.host.plugins["XEP-0020"].chooseOption({'stream-method':stream_method})
|
||||
feature_elt = self.host.plugins["XEP-0020"].chooseOption({'stream-method': stream_method})
|
||||
misc_elts = []
|
||||
misc_elts.append(domish.Element((PROFILE, "file")))
|
||||
self.host.plugins["XEP-0095"].acceptStream(data["id"], data['from'], feature_elt, misc_elts, profile)
|
||||
else:
|
||||
debug (_("Transfer [%s] refused"), sid)
|
||||
self.host.plugins["XEP-0095"].sendRejectedError (data["id"], data['from'], profile=profile)
|
||||
debug(_("Transfer [%s] refused"), sid)
|
||||
self.host.plugins["XEP-0095"].sendRejectedError(data["id"], data['from'], profile=profile)
|
||||
del(client._pipe_waiting_for_approval[sid])
|
||||
|
||||
def _transferSucceeded(self, sid, file_obj, stream_method, profile):
|
||||
|
|
@ -173,8 +173,8 @@ class Exp_Pipe(object):
|
|||
if not client:
|
||||
raise ProfileNotInCacheError
|
||||
data, timeout, stream_method, failed_methods, profile = client._pipe_waiting_for_approval[sid]
|
||||
warning(_('Transfer %(id)s failed with stream method %(s_method)s') % { 'id': sid,
|
||||
's_method': stream_method })
|
||||
warning(_('Transfer %(id)s failed with stream method %(s_method)s') % {'id': sid,
|
||||
's_method': stream_method})
|
||||
filepath = file_obj.name
|
||||
file_obj.close()
|
||||
#TODO: session remenber (within a time limit) when a stream method fail, and avoid that stream method with full jid for the rest of the session
|
||||
|
|
@ -211,15 +211,15 @@ class Exp_Pipe(object):
|
|||
return
|
||||
|
||||
if stream_method == self.host.plugins["XEP-0065"].NAMESPACE:
|
||||
#fd = os.open(filepath, os.O_RDONLY | os.O_NONBLOCK) #XXX: non blocking openingl cause issues with XEP-0065's FileSender
|
||||
#file_obj = os.fdopen(fd, 'r')
|
||||
file_obj = open(filepath, 'r') #XXX: we have to be sure that filepath is well opened, as reading can block it
|
||||
self.host.plugins["XEP-0065"].startStream(file_obj, jid.JID(IQ['from']), sid, None, self.sendSuccessCb, self.sendFailureCb, None, profile)
|
||||
#fd = os.open(filepath, os.O_RDONLY | os.O_NONBLOCK) #XXX: non blocking openingl cause issues with XEP-0065's FileSender
|
||||
#file_obj = os.fdopen(fd, 'r')
|
||||
file_obj = open(filepath, 'r') # XXX: we have to be sure that filepath is well opened, as reading can block it
|
||||
self.host.plugins["XEP-0065"].startStream(file_obj, jid.JID(IQ['from']), sid, None, self.sendSuccessCb, self.sendFailureCb, None, profile)
|
||||
elif stream_method == self.host.plugins["XEP-0047"].NAMESPACE:
|
||||
#fd = os.open(filepath, os.O_RDONLY | os.O_NONBLOCK) #XXX: non blocking openingl cause issues with XEP-0065's FileSender
|
||||
#file_obj = os.fdopen(fd, 'r')
|
||||
file_obj = open(filepath, 'r') #XXX: we have to be sure that filepath is well opened, as reading can block it
|
||||
self.host.plugins["XEP-0047"].startStream(file_obj, jid.JID(IQ['from']), sid, None, self.sendSuccessCb, self.sendFailureCb, None, profile)
|
||||
#fd = os.open(filepath, os.O_RDONLY | os.O_NONBLOCK) #XXX: non blocking openingl cause issues with XEP-0065's FileSender
|
||||
#file_obj = os.fdopen(fd, 'r')
|
||||
file_obj = open(filepath, 'r') # XXX: we have to be sure that filepath is well opened, as reading can block it
|
||||
self.host.plugins["XEP-0047"].startStream(file_obj, jid.JID(IQ['from']), sid, None, self.sendSuccessCb, self.sendFailureCb, None, profile)
|
||||
else:
|
||||
warning(_("Invalid stream method received"))
|
||||
|
||||
|
|
@ -242,7 +242,7 @@ class Exp_Pipe(object):
|
|||
pipe_elt = domish.Element((PROFILE, 'pipe'))
|
||||
pipe_transfer_elts.append(pipe_elt)
|
||||
|
||||
sid, offer = self.host.plugins["XEP-0095"].proposeStream(jid.JID(to_jid), PROFILE, feature_elt, pipe_transfer_elts, profile_key = profile)
|
||||
sid, offer = self.host.plugins["XEP-0095"].proposeStream(jid.JID(to_jid), PROFILE, feature_elt, pipe_transfer_elts, profile_key=profile)
|
||||
offer.addCallback(self.pipeCb, filepath, sid, profile)
|
||||
return sid
|
||||
|
||||
|
|
@ -252,5 +252,4 @@ class Exp_Pipe(object):
|
|||
|
||||
def sendFailureCb(self, sid, file_obj, stream_method, reason, profile):
|
||||
file_obj.close()
|
||||
warning(_('Transfer %(id)s failed with stream method %(s_method)s %(profile)s') % { 'id': sid, "s_method": stream_method, "profile": profile })
|
||||
|
||||
warning(_('Transfer %(id)s failed with stream method %(s_method)s %(profile)s') % {'id': sid, "s_method": stream_method, "profile": profile})
|
||||
|
|
|
|||
|
|
@ -28,14 +28,14 @@ from email.mime.text import MIMEText
|
|||
from twisted.mail.smtp import sendmail
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "Account Plugin",
|
||||
"import_name": "MISC-ACCOUNT",
|
||||
"type": "MISC",
|
||||
"protocols": [],
|
||||
"dependencies": [],
|
||||
"main": "MiscAccount",
|
||||
"handler": "no",
|
||||
"description": _(u"""SàT account creation""")
|
||||
"name": "Account Plugin",
|
||||
"import_name": "MISC-ACCOUNT",
|
||||
"type": "MISC",
|
||||
"protocols": [],
|
||||
"dependencies": [],
|
||||
"main": "MiscAccount",
|
||||
"handler": "no",
|
||||
"description": _(u"""SàT account creation""")
|
||||
}
|
||||
|
||||
#You need do adapt the following consts to your server
|
||||
|
|
@ -45,24 +45,24 @@ _REG_ADMIN_EMAIL = "admin@example.net"
|
|||
_NEW_ACCOUNT_SERVER = "localhost"
|
||||
_NEW_ACCOUNT_DOMAIN = "example.net"
|
||||
_NEW_ACCOUNT_RESOURCE = "libervia"
|
||||
_PROSODY_PATH = None #prosody path (where prosodyctl will be executed from), or None to automaticaly find it
|
||||
_PROSODY_PATH = None # prosody path (where prosodyctl will be executed from), or None to automaticaly find it
|
||||
_PROSODYCTL = "prosodyctl"
|
||||
|
||||
RESERVED = ['libervia']
|
||||
|
||||
|
||||
class ProsodyRegisterProtocol(protocol.ProcessProtocol):
|
||||
""" Try to register an account with prosody """
|
||||
|
||||
def __init__(self, password, deferred = None):
|
||||
def __init__(self, password, deferred=None):
|
||||
self.password = password
|
||||
self.deferred = deferred
|
||||
self.data = ''
|
||||
|
||||
def connectionMade(self):
|
||||
self.transport.write("%s\n%s" % ((self.password.encode('utf-8'),)*2))
|
||||
self.transport.write("%s\n%s" % ((self.password.encode('utf-8'), ) * 2))
|
||||
self.transport.closeStdin()
|
||||
|
||||
|
||||
def outReceived(self, data):
|
||||
self.data += data
|
||||
|
||||
|
|
@ -87,14 +87,14 @@ class MiscAccount(object):
|
|||
def __init__(self, host):
|
||||
info(_(u"Plugin Account initialization"))
|
||||
self.host = host
|
||||
host.bridge.addMethod("registerSatAccount", ".plugin", in_sign='sss', out_sign='', method=self._registerAccount, async = True)
|
||||
host.bridge.addMethod("registerSatAccount", ".plugin", in_sign='sss', out_sign='', method=self._registerAccount, async=True)
|
||||
if not self._prosody_path:
|
||||
paths = which(_PROSODYCTL)
|
||||
if not paths:
|
||||
error(_("Can't find %s") % (_PROSODYCTL,))
|
||||
error(_("Can't find %s") % (_PROSODYCTL, ))
|
||||
else:
|
||||
self._prosody_path = dirname(paths[0])
|
||||
info(_('Prosody path found: %s') % (self._prosody_path,))
|
||||
info(_('Prosody path found: %s') % (self._prosody_path, ))
|
||||
|
||||
def _registerAccount(self, email, password, profile):
|
||||
|
||||
|
|
@ -128,7 +128,7 @@ class MiscAccount(object):
|
|||
# and just change the password if the account already exists
|
||||
d = defer.Deferred()
|
||||
prosody_reg = ProsodyRegisterProtocol(password, d)
|
||||
prosody_exe = join (self._prosody_path, _PROSODYCTL)
|
||||
prosody_exe = join(self._prosody_path, _PROSODYCTL)
|
||||
reactor.spawnProcess(prosody_reg, prosody_exe, [prosody_exe, 'adduser', "%s@%s" % (profile, _NEW_ACCOUNT_DOMAIN)], path=self._prosody_path)
|
||||
|
||||
d.addCallback(self._accountCreated, profile, email, password)
|
||||
|
|
@ -145,7 +145,7 @@ class MiscAccount(object):
|
|||
|
||||
def email_ko(ignore):
|
||||
#TODO: return error code to user
|
||||
error ("Failed to send email to %s" % email)
|
||||
error("Failed to send email to %s" % email)
|
||||
|
||||
body = (u"""Welcome to Libervia, a Salut à Toi project part
|
||||
|
||||
|
|
@ -165,7 +165,7 @@ follow SàT news: http://www.goffi.org
|
|||
Any feedback welcome
|
||||
|
||||
Cheers
|
||||
Goffi""" % { 'login': login, 'password': password, 'jid':"%s@%s" % (login, _NEW_ACCOUNT_DOMAIN) }).encode('utf-8')
|
||||
Goffi""" % {'login': login, 'password': password, 'jid': "%s@%s" % (login, _NEW_ACCOUNT_DOMAIN)}).encode('utf-8')
|
||||
msg = MIMEText(body, 'plain', 'UTF-8')
|
||||
msg['Subject'] = 'Libervia account created'
|
||||
msg['From'] = _email_from
|
||||
|
|
@ -173,10 +173,9 @@ Goffi""" % { 'login': login, 'password': password, 'jid':"%s@%s" % (login, _NEW_
|
|||
|
||||
d = sendmail(_email_host, _email_from, email, msg.as_string())
|
||||
d.addCallbacks(email_ok, email_ko)
|
||||
|
||||
#email to the administrator
|
||||
|
||||
body = (u"""New account created: %(login)s [%(email)s]""" % { 'login': login, 'email': email }).encode('utf-8')
|
||||
body = (u"""New account created: %(login)s [%(email)s]""" % {'login': login, 'email': email}).encode('utf-8')
|
||||
msg = MIMEText(body, 'plain', 'UTF-8')
|
||||
msg['Subject'] = 'Libervia new account created'
|
||||
msg['From'] = _email_from
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ NS_PUBSUB = 'http://jabber.org/protocol/pubsub'
|
|||
NS_GROUPBLOG = 'http://goffi.org/protocol/groupblog'
|
||||
NS_NODE_PREFIX = 'urn:xmpp:groupblog:'
|
||||
#NS_PUBSUB_EXP = 'http://goffi.org/protocol/pubsub' #for non official features
|
||||
NS_PUBSUB_EXP = NS_PUBSUB #XXX: we can't use custom namespace as Wokkel's PubSubService use official NS
|
||||
NS_PUBSUB_EXP = NS_PUBSUB # XXX: we can't use custom namespace as Wokkel's PubSubService use official NS
|
||||
NS_PUBSUB_ITEM_ACCESS = NS_PUBSUB_EXP + "#item-access"
|
||||
NS_PUBSUB_CREATOR_JID_CHECK = NS_PUBSUB_EXP + "#creator-jid-check"
|
||||
NS_PUBSUB_ITEM_CONFIG = NS_PUBSUB_EXP + "#item-config"
|
||||
|
|
@ -51,28 +51,33 @@ OPT_SUBSCRIPTION_DEPTH = 'pubsub#subscription_depth'
|
|||
TYPE_COLLECTION = 'collection'
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "Group blogging throught collections",
|
||||
"import_name": "groupblog",
|
||||
"type": "MISC",
|
||||
"protocols": [],
|
||||
"dependencies": ["XEP-0277"],
|
||||
"main": "GroupBlog",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of microblogging with roster access""")
|
||||
"name": "Group blogging throught collections",
|
||||
"import_name": "groupblog",
|
||||
"type": "MISC",
|
||||
"protocols": [],
|
||||
"dependencies": ["XEP-0277"],
|
||||
"main": "GroupBlog",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of microblogging with roster access""")
|
||||
}
|
||||
|
||||
|
||||
class NoCompatiblePubSubServerFound(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class BadAccessTypeError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class BadAccessListError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class UnknownType(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class GroupBlog(object):
|
||||
"""This class use a SàT PubSub Service to manage access on microblog"""
|
||||
|
||||
|
|
@ -81,29 +86,28 @@ class GroupBlog(object):
|
|||
self.host = host
|
||||
|
||||
host.bridge.addMethod("sendGroupBlog", ".plugin", in_sign='sasss', out_sign='',
|
||||
method=self.sendGroupBlog)
|
||||
method=self.sendGroupBlog)
|
||||
|
||||
host.bridge.addMethod("getLastGroupBlogs", ".plugin",
|
||||
in_sign='sis', out_sign='aa{ss}',
|
||||
method=self.getLastGroupBlogs,
|
||||
async = True)
|
||||
async=True)
|
||||
|
||||
host.bridge.addMethod("getMassiveLastGroupBlogs", ".plugin",
|
||||
in_sign='sasis', out_sign='a{saa{ss}}',
|
||||
method=self.getMassiveLastGroupBlogs,
|
||||
async = True)
|
||||
async=True)
|
||||
|
||||
host.bridge.addMethod("subscribeGroupBlog", ".plugin", in_sign='ss', out_sign='',
|
||||
method=self.subscribeGroupBlog,
|
||||
async = True)
|
||||
method=self.subscribeGroupBlog,
|
||||
async=True)
|
||||
|
||||
host.bridge.addMethod("massiveSubscribeGroupBlogs", ".plugin", in_sign='sass', out_sign='',
|
||||
method=self.massiveSubscribeGroupBlogs,
|
||||
async = True)
|
||||
method=self.massiveSubscribeGroupBlogs,
|
||||
async=True)
|
||||
|
||||
host.trigger.add("PubSubItemsReceived", self.pubSubItemsReceivedTrigger)
|
||||
|
||||
|
||||
def getHandler(self, profile):
|
||||
return GroupBlog_handler()
|
||||
|
||||
|
|
@ -121,10 +125,10 @@ class GroupBlog(object):
|
|||
if not client:
|
||||
error(_('No client for this profile key: %s') % profile_key)
|
||||
raise Exception("Unknown profile")
|
||||
yield client.client_initialized #we want to be sure that the client is initialized
|
||||
yield client.client_initialized # we want to be sure that the client is initialized
|
||||
|
||||
#we first check that we have a item-access pubsub server
|
||||
if not hasattr(client,"item_access_pubsub"):
|
||||
if not hasattr(client, "item_access_pubsub"):
|
||||
debug(_('Looking for item-access power pubsub server'))
|
||||
#we don't have any pubsub server featuring item access yet
|
||||
client.item_access_pubsub = None
|
||||
|
|
@ -137,7 +141,7 @@ class GroupBlog(object):
|
|||
client.item_access_pubsub = entity
|
||||
client._item_access_pubsub_pending.callback(None)
|
||||
|
||||
if hasattr(client,"_item_access_pubsub_pending"):
|
||||
if hasattr(client, "_item_access_pubsub_pending"):
|
||||
#XXX: we need to wait for item access pubsub service check
|
||||
yield client._item_access_pubsub_pending
|
||||
del client._item_access_pubsub_pending
|
||||
|
|
@ -157,8 +161,8 @@ class GroupBlog(object):
|
|||
#FIXME: basic origin check, must be improved
|
||||
#TODO: automatic security test
|
||||
if (not (origin_host)
|
||||
or len(event_host) < len(origin_host)
|
||||
or event_host[-len(origin_host):] != origin_host):
|
||||
or len(event_host) < len(origin_host)
|
||||
or event_host[-len(origin_host):] != origin_host):
|
||||
warning("Host incoherence between %s and %s (hack attempt ?)" % (unicode(event.sender),
|
||||
unicode(publisher)))
|
||||
return
|
||||
|
|
@ -169,7 +173,6 @@ class GroupBlog(object):
|
|||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _parseAccessData(self, microblog_data, item):
|
||||
form_elts = filter(lambda elt: elt.name == "x", item.children)
|
||||
for form_elt in form_elts:
|
||||
|
|
@ -192,15 +195,12 @@ class GroupBlog(object):
|
|||
self._parseAccessData(microblog_data, item)
|
||||
return microblog_data
|
||||
|
||||
|
||||
def getNodeName(self, publisher):
|
||||
"""Retrieve the name of publisher's node
|
||||
@param publisher: publisher's jid
|
||||
@return: node's name (string)"""
|
||||
return NS_NODE_PREFIX + publisher.userhost()
|
||||
|
||||
|
||||
|
||||
def _publishMblog(self, service, client, access_type, access_list, message):
|
||||
"""Actually publish the message on the group blog
|
||||
@param service: jid of the item-access pubsub service
|
||||
|
|
@ -209,7 +209,7 @@ class GroupBlog(object):
|
|||
@param access_list: set of entities (empty list for all, groups or jids) allowed to see the item
|
||||
@param message: message to publish
|
||||
"""
|
||||
mblog_item = self.host.plugins["XEP-0277"].data2entry({'content':message}, client.profile)
|
||||
mblog_item = self.host.plugins["XEP-0277"].data2entry({'content': message}, client.profile)
|
||||
form = data_form.Form('submit', formNamespace=NS_PUBSUB_ITEM_CONFIG)
|
||||
if access_type == "PUBLIC":
|
||||
if access_list:
|
||||
|
|
@ -243,6 +243,7 @@ class GroupBlog(object):
|
|||
@profile_key: %(doc_profile)s
|
||||
"""
|
||||
print "sendGroupBlog"
|
||||
|
||||
def initialised(result):
|
||||
profile, client = result
|
||||
if access_type == "PUBLIC":
|
||||
|
|
@ -250,7 +251,7 @@ class GroupBlog(object):
|
|||
raise Exception("Publishers list must be empty when getting microblogs for all contacts")
|
||||
self._publishMblog(client.item_access_pubsub, client, "PUBLIC", [], message)
|
||||
elif access_type == "GROUP":
|
||||
_groups = set(access_list).intersection(client.roster.getGroups()) #We only keep group which actually exist
|
||||
_groups = set(access_list).intersection(client.roster.getGroups()) # We only keep group which actually exist
|
||||
if not _groups:
|
||||
raise BadAccessListError("No valid group")
|
||||
self._publishMblog(client.item_access_pubsub, client, "GROUP", _groups, message)
|
||||
|
|
@ -262,8 +263,6 @@ class GroupBlog(object):
|
|||
|
||||
self.initialise(profile_key).addCallback(initialised)
|
||||
|
||||
|
||||
|
||||
def getLastGroupBlogs(self, pub_jid, max_items=10, profile_key='@DEFAULT@'):
|
||||
"""Get the last published microblogs
|
||||
@param pub_jid: jid of the publisher
|
||||
|
|
@ -277,7 +276,7 @@ class GroupBlog(object):
|
|||
d = self.host.plugins["XEP-0060"].getItems(client.item_access_pubsub, self.getNodeName(jid.JID(pub_jid)),
|
||||
max_items=max_items, profile_key=profile_key)
|
||||
d.addCallback(lambda items: map(self.item2gbdata, items))
|
||||
d.addErrback(lambda ignore: {}) #TODO: more complete error management (log !)
|
||||
d.addErrback(lambda ignore: {}) # TODO: more complete error management (log !)
|
||||
return d
|
||||
|
||||
#TODO: we need to use the server corresponding the the host of the jid
|
||||
|
|
@ -290,6 +289,7 @@ class GroupBlog(object):
|
|||
@param max_items: how many microblogs we want to get
|
||||
@param profile_key: profile key
|
||||
"""
|
||||
|
||||
def sendResult(result):
|
||||
"""send result of DeferredList (list of microblogs to the calling method"""
|
||||
|
||||
|
|
@ -329,11 +329,10 @@ class GroupBlog(object):
|
|||
|
||||
return dlist
|
||||
|
||||
|
||||
#TODO: custom exception
|
||||
if publishers_type not in ["GROUP", "JID", "ALL"]:
|
||||
raise Exception("Bad call, unknown publishers_type")
|
||||
if publishers_type=="ALL" and publishers:
|
||||
if publishers_type == "ALL" and publishers:
|
||||
raise Exception("Publishers list must be empty when getting microblogs for all contacts")
|
||||
return self.initialise(profile_key).addCallback(initialised)
|
||||
#TODO: we need to use the server corresponding the the host of the jid
|
||||
|
|
@ -348,13 +347,13 @@ class GroupBlog(object):
|
|||
#TODO: we need to use the server corresponding the the host of the jid
|
||||
return self.initialise(profile_key).addCallback(initialised)
|
||||
|
||||
|
||||
def massiveSubscribeGroupBlogs(self, publishers_type, publishers, profile_key='@DEFAULT@'):
|
||||
"""Subscribe microblogs for a list of groups or jids
|
||||
@param publishers_type: type of the list of publishers (one of "GROUP" or "JID" or "ALL")
|
||||
@param publishers: list of publishers, according to "publishers_type" (list of groups or list of jids)
|
||||
@param profile_key: profile key
|
||||
"""
|
||||
|
||||
def initialised(result):
|
||||
profile, client = result
|
||||
|
||||
|
|
@ -378,17 +377,15 @@ class GroupBlog(object):
|
|||
dlist = defer.DeferredList(mblogs)
|
||||
return dlist
|
||||
|
||||
|
||||
#TODO: custom exception
|
||||
if publishers_type not in ["GROUP", "JID", "ALL"]:
|
||||
raise Exception("Bad call, unknown publishers_type")
|
||||
if publishers_type=="ALL" and publishers:
|
||||
if publishers_type == "ALL" and publishers:
|
||||
raise Exception("Publishers list must be empty when getting microblogs for all contacts")
|
||||
return self.initialise(profile_key).addCallback(initialised)
|
||||
#TODO: we need to use the server corresponding the the host of the jid
|
||||
|
||||
|
||||
|
||||
class GroupBlog_handler(XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
|
||||
|
|
@ -397,4 +394,3 @@ class GroupBlog_handler(XMPPHandler):
|
|||
|
||||
def getDiscoItems(self, requestor, target, nodeIdentifier=''):
|
||||
return []
|
||||
|
||||
|
|
|
|||
|
|
@ -21,34 +21,33 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
from logging import debug, info, error
|
||||
import warnings
|
||||
from twisted.internet import protocol,defer
|
||||
from twisted.internet import protocol, defer
|
||||
from twisted.words.protocols.jabber import error as jab_error
|
||||
from twisted.cred import portal,checkers,credentials
|
||||
from twisted.cred import portal, checkers, credentials
|
||||
from twisted.cred import error as cred_error
|
||||
from twisted.mail import imap4
|
||||
from twisted.python import failure
|
||||
from email.parser import Parser
|
||||
import email.message
|
||||
import os,os.path
|
||||
import os
|
||||
from cStringIO import StringIO
|
||||
from twisted.internet import reactor
|
||||
import pdb
|
||||
|
||||
|
||||
from zope.interface import implements
|
||||
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "IMAP server Plugin",
|
||||
"import_name": "IMAP",
|
||||
"type": "Misc",
|
||||
"protocols": [],
|
||||
"dependencies": ["Maildir"],
|
||||
"main": "IMAP_server",
|
||||
"handler": "no",
|
||||
"description": _("""Create an Imap server that you can use to read your "normal" type messages""")
|
||||
"name": "IMAP server Plugin",
|
||||
"import_name": "IMAP",
|
||||
"type": "Misc",
|
||||
"protocols": [],
|
||||
"dependencies": ["Maildir"],
|
||||
"main": "IMAP_server",
|
||||
"handler": "no",
|
||||
"description": _("""Create an Imap server that you can use to read your "normal" type messages""")
|
||||
}
|
||||
|
||||
|
||||
class IMAP_server(object):
|
||||
#TODO: connect profile on mailbox request, once password is accepted
|
||||
|
||||
|
|
@ -75,15 +74,16 @@ class IMAP_server(object):
|
|||
self.server_factory = ImapServerFactory(self.host)
|
||||
reactor.listenTCP(port, self.server_factory)
|
||||
|
||||
|
||||
class Message(object):
|
||||
implements(imap4.IMessage)
|
||||
|
||||
def __init__(self, uid, flags, mess_fp):
|
||||
debug('Message Init')
|
||||
self.uid=uid
|
||||
self.flags=flags
|
||||
self.mess_fp=mess_fp
|
||||
self.message=Parser().parse(mess_fp)
|
||||
self.uid = uid
|
||||
self.flags = flags
|
||||
self.mess_fp = mess_fp
|
||||
self.message = Parser().parse(mess_fp)
|
||||
|
||||
def getUID(self):
|
||||
"""Retrieve the unique identifier associated with this message.
|
||||
|
|
@ -105,7 +105,6 @@ class Message(object):
|
|||
debug('getInternalDate')
|
||||
return self.message['Date']
|
||||
|
||||
|
||||
def getHeaders(self, negate, *names):
|
||||
"""Retrieve a group of message headers.
|
||||
@param names: The names of the headers to retrieve or omit.
|
||||
|
|
@ -114,12 +113,12 @@ class Message(object):
|
|||
@return: A mapping of header field names to header field values
|
||||
"""
|
||||
debug('getHeaders %s - %s' % (negate, names))
|
||||
final_dict={}
|
||||
to_check=[name.lower() for name in names]
|
||||
final_dict = {}
|
||||
to_check = [name.lower() for name in names]
|
||||
for header in self.message.keys():
|
||||
if (negate and not header.lower() in to_check) or \
|
||||
(not negate and header.lower() in to_check):
|
||||
final_dict[header]=self.message[header]
|
||||
(not negate and header.lower() in to_check):
|
||||
final_dict[header] = self.message[header]
|
||||
return final_dict
|
||||
|
||||
def getBodyFile(self):
|
||||
|
|
@ -132,17 +131,16 @@ class Message(object):
|
|||
"""Retrieve the total size, in octets, of this message.
|
||||
"""
|
||||
debug('getSize')
|
||||
self.mess_fp.seek(0,os.SEEK_END)
|
||||
self.mess_fp.seek(0, os.SEEK_END)
|
||||
return self.mess_fp.tell()
|
||||
|
||||
|
||||
def isMultipart(self):
|
||||
"""Indicate whether this message has subparts.
|
||||
"""
|
||||
debug('isMultipart')
|
||||
return False
|
||||
|
||||
def getSubPart(self,part):
|
||||
def getSubPart(self, part):
|
||||
"""Retrieve a MIME sub-message
|
||||
@param part: The number of the part to retrieve, indexed from 0.
|
||||
@return: The specified sub-part.
|
||||
|
|
@ -154,39 +152,39 @@ class Message(object):
|
|||
class SatMailbox(object):
|
||||
implements(imap4.IMailbox)
|
||||
|
||||
def __init__(self,host,name,profile):
|
||||
def __init__(self, host, name, profile):
|
||||
self.host = host
|
||||
self.listeners=set()
|
||||
debug ('Mailbox init (%s)', name)
|
||||
if name!="INBOX":
|
||||
self.listeners = set()
|
||||
debug('Mailbox init (%s)', name)
|
||||
if name != "INBOX":
|
||||
raise imap4.MailboxException("Only INBOX is managed for the moment")
|
||||
self.mailbox=self.host.plugins["Maildir"].accessMessageBox(name,self.newMessage, profile)
|
||||
self.mailbox = self.host.plugins["Maildir"].accessMessageBox(name, self.newMessage, profile)
|
||||
|
||||
def newMessage(self):
|
||||
"""Called when a new message is in the mailbox"""
|
||||
debug ("newMessage signal received")
|
||||
nb_messages=self.getMessageCount()
|
||||
debug("newMessage signal received")
|
||||
nb_messages = self.getMessageCount()
|
||||
for listener in self.listeners:
|
||||
listener.newMessages(nb_messages,None)
|
||||
listener.newMessages(nb_messages, None)
|
||||
|
||||
def getUIDValidity(self):
|
||||
"""Return the unique validity identifier for this mailbox.
|
||||
"""
|
||||
debug ('getUIDValidity')
|
||||
debug('getUIDValidity')
|
||||
return 0
|
||||
|
||||
def getUIDNext(self):
|
||||
"""Return the likely UID for the next message added to this mailbox.
|
||||
"""
|
||||
debug ('getUIDNext')
|
||||
debug('getUIDNext')
|
||||
return self.mailbox.getNextUid()
|
||||
|
||||
def getUID(self,message):
|
||||
def getUID(self, message):
|
||||
"""Return the UID of a message in the mailbox
|
||||
@param message: The message sequence number
|
||||
@return: The UID of the message.
|
||||
"""
|
||||
debug ('getUID (%i)' % message)
|
||||
debug('getUID (%i)' % message)
|
||||
#return self.mailbox.getUid(message-1) #XXX: it seems that this method get uid and not message sequence number
|
||||
return message
|
||||
|
||||
|
|
@ -208,7 +206,7 @@ class SatMailbox(object):
|
|||
"""Return the number of messages with the 'Unseen' flag.
|
||||
"""
|
||||
debug('getUnseenCount')
|
||||
return self.getMessageCount()-len(self.mailbox.getMessageIdsWithFlag('\\SEEN'))
|
||||
return self.getMessageCount() - len(self.mailbox.getMessageIdsWithFlag('\\SEEN'))
|
||||
|
||||
def isWriteable(self):
|
||||
"""Get the read/write status of the mailbox.
|
||||
|
|
@ -222,7 +220,6 @@ class SatMailbox(object):
|
|||
"""
|
||||
debug('destroy')
|
||||
|
||||
|
||||
def requestStatus(self, names):
|
||||
"""Return status information about this mailbox.
|
||||
@param names: The status names to return information regarding.
|
||||
|
|
@ -262,7 +259,7 @@ class SatMailbox(object):
|
|||
else:
|
||||
raise imap4.MailboxException('Trying to remove an unknown listener')
|
||||
|
||||
def addMessage(self, message, flags = (), date = None):
|
||||
def addMessage(self, message, flags=(), date=None):
|
||||
"""Add the given message to this mailbox.
|
||||
@param message: The RFC822 formatted message
|
||||
@param flags: The flags to associate with this message
|
||||
|
|
@ -288,24 +285,24 @@ class SatMailbox(object):
|
|||
about
|
||||
@param uid: If true, the IDs specified in the query are UIDs;
|
||||
"""
|
||||
debug('fetch (%s, %s)'%(messages,uid))
|
||||
debug('fetch (%s, %s)' % (messages, uid))
|
||||
if uid:
|
||||
messages.last = self.mailbox.getMaxUid()
|
||||
messages.getnext = self.mailbox.getNextExistingUid
|
||||
for mess_uid in messages:
|
||||
if mess_uid == None:
|
||||
debug ('stopping iteration')
|
||||
if mess_uid is None:
|
||||
debug('stopping iteration')
|
||||
raise StopIteration
|
||||
try:
|
||||
yield (mess_uid,Message(mess_uid,self.mailbox.getFlagsUid(mess_uid), self.mailbox.getMessageUid(mess_uid)))
|
||||
yield (mess_uid, Message(mess_uid, self.mailbox.getFlagsUid(mess_uid), self.mailbox.getMessageUid(mess_uid)))
|
||||
except IndexError:
|
||||
continue
|
||||
else:
|
||||
messages.last = self.getMessageCount()
|
||||
for mess_idx in messages:
|
||||
if mess_idx>self.getMessageCount():
|
||||
if mess_idx > self.getMessageCount():
|
||||
raise StopIteration
|
||||
yield (mess_idx,Message(mess_idx,self.mailbox.getFlags(mess_idx),self.mailbox.getMessage(mess_idx-1)))
|
||||
yield (mess_idx, Message(mess_idx, self.mailbox.getFlags(mess_idx), self.mailbox.getMessage(mess_idx - 1)))
|
||||
|
||||
def store(self, messages, flags, mode, uid):
|
||||
"""Set the flags of one or more messages.
|
||||
|
|
@ -324,19 +321,19 @@ class SatMailbox(object):
|
|||
"""
|
||||
debug('store')
|
||||
|
||||
flags=[flag.upper() for flag in flags]
|
||||
flags = [flag.upper() for flag in flags]
|
||||
|
||||
def updateFlags(getF,setF):
|
||||
def updateFlags(getF, setF):
|
||||
ret = {}
|
||||
for mess_id in messages:
|
||||
if (uid and mess_id == None) or (not uid and mess_id>self.getMessageCount()):
|
||||
if (uid and mess_id is None) or (not uid and mess_id > self.getMessageCount()):
|
||||
break
|
||||
_flags=set(getF(mess_id) if mode else [])
|
||||
if mode==-1:
|
||||
_flags = set(getF(mess_id) if mode else [])
|
||||
if mode == -1:
|
||||
_flags.difference_update(set(flags))
|
||||
else:
|
||||
_flags.update(set(flags))
|
||||
new_flags=list(_flags)
|
||||
new_flags = list(_flags)
|
||||
setF(mess_id, new_flags)
|
||||
ret[mess_id] = tuple(new_flags)
|
||||
return ret
|
||||
|
|
@ -344,15 +341,15 @@ class SatMailbox(object):
|
|||
if uid:
|
||||
messages.last = self.mailbox.getMaxUid()
|
||||
messages.getnext = self.mailbox.getNextExistingUid
|
||||
ret = updateFlags(self.mailbox.getFlagsUid,self.mailbox.setFlagsUid)
|
||||
ret = updateFlags(self.mailbox.getFlagsUid, self.mailbox.setFlagsUid)
|
||||
for listener in self.listeners:
|
||||
listener.flagsChanged(ret)
|
||||
return ret
|
||||
|
||||
else:
|
||||
messages.last = self.getMessageCount()
|
||||
ret = updateFlags(self.mailbox.getFlags,self.mailbox.setFlags)
|
||||
newFlags={}
|
||||
ret = updateFlags(self.mailbox.getFlags, self.mailbox.setFlags)
|
||||
newFlags = {}
|
||||
for idx in ret:
|
||||
#we have to convert idx to uid for the listeners
|
||||
newFlags[self.mailbox.getUid(idx)] = ret[idx]
|
||||
|
|
@ -366,7 +363,7 @@ class SatMailbox(object):
|
|||
@return: A list of the flags that can be set on messages in this mailbox.
|
||||
"""
|
||||
debug('getFlags')
|
||||
return ['\\SEEN','\\ANSWERED','\\FLAGGED','\\DELETED','\\DRAFT'] #TODO: add '\\RECENT'
|
||||
return ['\\SEEN', '\\ANSWERED', '\\FLAGGED', '\\DELETED', '\\DRAFT'] # TODO: add '\\RECENT'
|
||||
|
||||
def getHierarchicalDelimiter(self):
|
||||
"""Get the character which delimits namespaces for in this mailbox.
|
||||
|
|
@ -374,33 +371,35 @@ class SatMailbox(object):
|
|||
debug('getHierarchicalDelimiter')
|
||||
return '.'
|
||||
|
||||
|
||||
class ImapSatAccount(imap4.MemoryAccount):
|
||||
#implements(imap4.IAccount)
|
||||
|
||||
def __init__(self, host, profile):
|
||||
debug("ImapAccount init")
|
||||
self.host=host
|
||||
self.profile=profile
|
||||
imap4.MemoryAccount.__init__(self,profile)
|
||||
self.addMailbox("Inbox") #We only manage Inbox for the moment
|
||||
debug ('INBOX added')
|
||||
self.host = host
|
||||
self.profile = profile
|
||||
imap4.MemoryAccount.__init__(self, profile)
|
||||
self.addMailbox("Inbox") # We only manage Inbox for the moment
|
||||
debug('INBOX added')
|
||||
|
||||
def _emptyMailbox(self, name, id):
|
||||
return SatMailbox(self.host,name,self.profile)
|
||||
return SatMailbox(self.host, name, self.profile)
|
||||
|
||||
|
||||
class ImapRealm(object):
|
||||
implements(portal.IRealm)
|
||||
|
||||
def __init__(self,host):
|
||||
def __init__(self, host):
|
||||
self.host = host
|
||||
|
||||
def requestAvatar(self, avatarID, mind, *interfaces):
|
||||
debug('requestAvatar')
|
||||
profile=avatarID.decode('utf-8')
|
||||
profile = avatarID.decode('utf-8')
|
||||
if imap4.IAccount not in interfaces:
|
||||
raise NotImplementedError
|
||||
return imap4.IAccount, ImapSatAccount(self.host,profile), lambda:None
|
||||
return imap4.IAccount, ImapSatAccount(self.host, profile), lambda: None
|
||||
|
||||
|
||||
class SatProfileCredentialChecker(object):
|
||||
"""
|
||||
|
|
@ -412,7 +411,6 @@ class SatProfileCredentialChecker(object):
|
|||
credentialInterfaces = (credentials.IUsernamePassword,
|
||||
credentials.IUsernameHashedPassword)
|
||||
|
||||
|
||||
def __init__(self, host):
|
||||
self.host = host
|
||||
|
||||
|
|
@ -431,20 +429,21 @@ class SatProfileCredentialChecker(object):
|
|||
d.addCallback(self._cbPasswordMatch, credentials.username)
|
||||
return d
|
||||
|
||||
|
||||
class ImapServerFactory(protocol.ServerFactory):
|
||||
protocol = imap4.IMAP4Server
|
||||
|
||||
def __init__(self, host):
|
||||
self.host=host
|
||||
self.host = host
|
||||
|
||||
def startedConnecting(self, connector):
|
||||
debug (_("IMAP server connection started"))
|
||||
debug(_("IMAP server connection started"))
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
debug (_("IMAP server connection lost (reason: %s)"), reason)
|
||||
debug(_("IMAP server connection lost (reason: %s)"), reason)
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
debug ("Building protocol")
|
||||
debug("Building protocol")
|
||||
prot = protocol.ServerFactory.buildProtocol(self, addr)
|
||||
prot.portal = portal.Portal(ImapRealm(self.host))
|
||||
prot.portal.registerChecker(SatProfileCredentialChecker(self.host))
|
||||
|
|
|
|||
|
|
@ -21,54 +21,54 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
from logging import debug, info, error
|
||||
import warnings
|
||||
warnings.filterwarnings('ignore','the MimeWriter',DeprecationWarning,'twisted' ) #FIXME: to be removed, see http://twistedmatrix.com/trac/ticket/4038
|
||||
warnings.filterwarnings('ignore', 'the MimeWriter', DeprecationWarning, 'twisted') # FIXME: to be removed, see http://twistedmatrix.com/trac/ticket/4038
|
||||
from twisted.internet import protocol
|
||||
from twisted.words.protocols.jabber import error as jab_error
|
||||
from twisted.cred import portal,checkers
|
||||
from twisted.mail import imap4,maildir
|
||||
from twisted.cred import portal, checkers
|
||||
from twisted.mail import imap4, maildir
|
||||
from email.parser import Parser
|
||||
import email.message
|
||||
from email.charset import Charset
|
||||
import os,os.path
|
||||
import os
|
||||
from cStringIO import StringIO
|
||||
from twisted.internet import reactor
|
||||
import pdb
|
||||
from sat.core.exceptions import ProfileUnknownError
|
||||
from sat.memory.persistent import PersistentBinaryDict
|
||||
|
||||
|
||||
from zope.interface import implements
|
||||
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "Maildir Plugin",
|
||||
"import_name": "Maildir",
|
||||
"type": "Misc",
|
||||
"protocols": [],
|
||||
"dependencies": [],
|
||||
"main": "MaildirBox",
|
||||
"handler": "no",
|
||||
"description": _("""Intercept "normal" type messages, and put them in a Maildir type box""")
|
||||
"name": "Maildir Plugin",
|
||||
"import_name": "Maildir",
|
||||
"type": "Misc",
|
||||
"protocols": [],
|
||||
"dependencies": [],
|
||||
"main": "MaildirBox",
|
||||
"handler": "no",
|
||||
"description": _("""Intercept "normal" type messages, and put them in a Maildir type box""")
|
||||
}
|
||||
|
||||
MAILDIR_PATH = "Maildir"
|
||||
|
||||
|
||||
class MaildirError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class MaildirBox(object):
|
||||
|
||||
def __init__(self, host):
|
||||
info(_("Plugin Maildir initialization"))
|
||||
self.host = host
|
||||
|
||||
self.__observed={}
|
||||
self.data={} #list of profile spectific data. key = profile, value = PersistentBinaryDict where key=mailbox name,
|
||||
self.__observed = {}
|
||||
self.data = {} # list of profile spectific data. key = profile, value = PersistentBinaryDict where key=mailbox name,
|
||||
# and value is a dictionnary with the following value
|
||||
# - cur_idx: value of the current unique integer increment (UID)
|
||||
# - message_id (as returned by MaildirMailbox): a tuple of (UID, [flag1, flag2, ...])
|
||||
pList=host.memory.getProfilesList #shorter :)
|
||||
self.__mailboxes={} #key: profile, value: {boxname: MailboxUser instance}
|
||||
pList = host.memory.getProfilesList # shorter :)
|
||||
self.__mailboxes = {} # key: profile, value: {boxname: MailboxUser instance}
|
||||
|
||||
#the triggers
|
||||
host.trigger.add("MessageReceived", self.messageReceivedTrigger)
|
||||
|
|
@ -76,11 +76,12 @@ class MaildirBox(object):
|
|||
def profileConnected(self, profile):
|
||||
"""Called on profile connection, create profile data"""
|
||||
self.data[profile] = PersistentBinaryDict("plugin_maildir", profile)
|
||||
self.__mailboxes[profile]={}
|
||||
self.__mailboxes[profile] = {}
|
||||
|
||||
def dataLoaded(ignore):
|
||||
if not self.data[profile]:
|
||||
#the mailbox is new, we initiate the data
|
||||
self.data[profile]["INBOX"] = {"cur_idx":0}
|
||||
self.data[profile]["INBOX"] = {"cur_idx": 0}
|
||||
self.data[profile].load().addCallback(dataLoaded)
|
||||
|
||||
def profileDisconnected(self, profile):
|
||||
|
|
@ -108,8 +109,8 @@ class MaildirBox(object):
|
|||
profile = self.host.memory.getProfileName(profile_key)
|
||||
if not profile:
|
||||
raise ProfileUnknownError(profile_key)
|
||||
if not self.__mailboxes[profile].has_key(boxname):
|
||||
self.__mailboxes[profile][boxname]=MailboxUser(self, boxname, observer, profile=profile)
|
||||
if boxname not in self.__mailboxes[profile]:
|
||||
self.__mailboxes[profile][boxname] = MailboxUser(self, boxname, observer, profile=profile)
|
||||
else:
|
||||
if observer:
|
||||
self.addObserver(observer, profile, boxname)
|
||||
|
|
@ -118,33 +119,33 @@ class MaildirBox(object):
|
|||
def _getProfilePath(self, profile):
|
||||
"""Return a unique path for profile's mailbox
|
||||
The path must be unique, usable as a dir name, and bijectional"""
|
||||
return profile.replace('/','_').replace('..','_') #FIXME: this is too naive to work well, must be improved
|
||||
return profile.replace('/', '_').replace('..', '_') # FIXME: this is too naive to work well, must be improved
|
||||
|
||||
def _removeBoxAccess(self, boxname, mailboxUser, profile):
|
||||
"""Remove a reference to a box
|
||||
@param name: name of the box
|
||||
@param mailboxUser: MailboxUser instance"""
|
||||
if not self.__mailboxes.has_key(boxname):
|
||||
err_msg=_("Trying to remove an mailboxUser not referenced")
|
||||
if boxname not in self.__mailboxes:
|
||||
err_msg = _("Trying to remove an mailboxUser not referenced")
|
||||
error(_("INTERNAL ERROR: ") + err_msg)
|
||||
raise MaildirError(err_msg)
|
||||
assert self.__mailboxes[profile][boxname]==mailboxUser
|
||||
assert self.__mailboxes[profile][boxname] == mailboxUser
|
||||
del __mailboxes[profile][boxname]
|
||||
|
||||
def _checkBoxReference(self, boxname, profile):
|
||||
"""Check if there is a reference on a box, and return it
|
||||
@param boxname: name of the box to check
|
||||
@return: MailboxUser instance or None"""
|
||||
if self.__mailboxes.has_key(profile):
|
||||
if self.__mailboxes[profile].has_key(boxname):
|
||||
if profile in self.__mailboxes:
|
||||
if boxname in self.__mailboxes[profile]:
|
||||
return self.__mailboxes[profile][boxname]
|
||||
|
||||
def __getBoxData(self, boxname, profile):
|
||||
"""Return the date of a box"""
|
||||
try:
|
||||
return self.data[profile][boxname] #the boxname MUST exist in the data
|
||||
return self.data[profile][boxname] # the boxname MUST exist in the data
|
||||
except KeyError:
|
||||
err_msg=_("Boxname doesn't exist in internal data")
|
||||
err_msg = _("Boxname doesn't exist in internal data")
|
||||
error(_("INTERNAL ERROR: ") + err_msg)
|
||||
raise MaildirError(err_msg)
|
||||
|
||||
|
|
@ -155,11 +156,11 @@ class MaildirBox(object):
|
|||
@param message_id: unique id of the message as given by MaildirMailbox
|
||||
@return: Integer UID"""
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
if box_data.has_key(message_id):
|
||||
if message_id in box_data:
|
||||
ret = box_data[message_id][0]
|
||||
else:
|
||||
box_data['cur_idx']+=1
|
||||
box_data[message_id]=[box_data['cur_idx'],[]]
|
||||
box_data['cur_idx'] += 1
|
||||
box_data[message_id] = [box_data['cur_idx'], []]
|
||||
ret = box_data[message_id]
|
||||
self.data[profile].force(boxname)
|
||||
return ret
|
||||
|
|
@ -170,7 +171,7 @@ class MaildirBox(object):
|
|||
@param boxname: name of the box where the message is
|
||||
@return: Integer UID"""
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
return box_data['cur_idx']+1
|
||||
return box_data['cur_idx'] + 1
|
||||
|
||||
def getNextExistingUid(self, boxname, uid, profile):
|
||||
"""Give the next uid of existing message
|
||||
|
|
@ -178,10 +179,10 @@ class MaildirBox(object):
|
|||
@param uid: uid to start from
|
||||
@return: uid or None if the is no more message"""
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
idx=uid+1
|
||||
while self.getIdFromUid(boxname, idx, profile) == None: #TODO: this is highly inefficient because getIdfromUid is inefficient, fix this
|
||||
idx+=1
|
||||
if idx>box_data['cur_idx']:
|
||||
idx = uid + 1
|
||||
while self.getIdFromUid(boxname, idx, profile) is None: # TODO: this is highly inefficient because getIdfromUid is inefficient, fix this
|
||||
idx += 1
|
||||
if idx > box_data['cur_idx']:
|
||||
return None
|
||||
return idx
|
||||
|
||||
|
|
@ -198,7 +199,7 @@ class MaildirBox(object):
|
|||
@param message_uid: unique integer identifier
|
||||
@return: unique id of the message as given by MaildirMailbox or None if not found"""
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
for message_id in box_data.keys(): #TODO: this is highly inefficient on big mailbox, must be replaced in the future
|
||||
for message_id in box_data.keys(): # TODO: this is highly inefficient on big mailbox, must be replaced in the future
|
||||
if message_id == 'cur_idx':
|
||||
continue
|
||||
if box_data[message_id][0] == message_uid:
|
||||
|
|
@ -211,7 +212,7 @@ class MaildirBox(object):
|
|||
@param message_idx: message id as given by MaildirMailbox
|
||||
@return: list of strings"""
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
if not box_data.has_key(mess_id):
|
||||
if mess_id not in box_data:
|
||||
raise MailboxException("Trying to get flags from an unexisting message")
|
||||
return box_data[mess_id][1]
|
||||
|
||||
|
|
@ -222,11 +223,11 @@ class MaildirBox(object):
|
|||
@param flags: list of strings
|
||||
"""
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
assert(type(flags)==list)
|
||||
flags=[flag.upper() for flag in flags] #we store every flag UPPERCASE
|
||||
if not box_data.has_key(mess_id):
|
||||
assert(type(flags) == list)
|
||||
flags = [flag.upper() for flag in flags] # we store every flag UPPERCASE
|
||||
if mess_id not in box_data:
|
||||
raise MailboxException("Trying to set flags for an unexisting message")
|
||||
box_data[mess_id][1]=flags
|
||||
box_data[mess_id][1] = flags
|
||||
self.data[profile].force(boxname)
|
||||
|
||||
def getMessageIdsWithFlag(self, boxname, flag, profile):
|
||||
|
|
@ -235,11 +236,11 @@ class MaildirBox(object):
|
|||
@param flag: flag to check
|
||||
@return: list of id (as given by MaildirMailbox)"""
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
assert(isinstance(flag,basestring))
|
||||
flag=flag.upper()
|
||||
assert(isinstance(flag, basestring))
|
||||
flag = flag.upper()
|
||||
result = []
|
||||
for key in box_data:
|
||||
if key=='cur_idx':
|
||||
if key == 'cur_idx':
|
||||
continue
|
||||
if flag in box_data[key][1]:
|
||||
result.append(key)
|
||||
|
|
@ -250,8 +251,8 @@ class MaildirBox(object):
|
|||
@param boxname: name of the box where the message is
|
||||
"""
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
for mess_id in self.getMessageIdsWithFlag(boxname,"\\Deleted", profile):
|
||||
del(box_data[mess_id])
|
||||
for mess_id in self.getMessageIdsWithFlag(boxname, "\\Deleted", profile):
|
||||
del(box_data[mess_id])
|
||||
self.data[profile].force(boxname)
|
||||
|
||||
def cleanTable(self, boxname, existant_id, profile):
|
||||
|
|
@ -259,9 +260,9 @@ class MaildirBox(object):
|
|||
@param boxname: name of the box to clean
|
||||
@param existant_id: list of id which actually exist"""
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
to_remove=[]
|
||||
to_remove = []
|
||||
for key in box_data:
|
||||
if key not in existant_id and key!="cur_idx":
|
||||
if key not in existant_id and key != "cur_idx":
|
||||
to_remove.append(key)
|
||||
for key in to_remove:
|
||||
del box_data[key]
|
||||
|
|
@ -271,34 +272,34 @@ class MaildirBox(object):
|
|||
@param callback: method to call when the the box is updated
|
||||
@param boxname: name of the box to observe
|
||||
@param signal: which signal is observed by the caller"""
|
||||
if not self.__observed.has_key((profile,boxname)):
|
||||
self.__observed[(profile,boxname)]={}
|
||||
if not self.__observed[(profile,boxname)].has_key(signal):
|
||||
self.__observed[(profile,boxname)][signal]=set()
|
||||
self.__observed[(profile,boxname)][signal].add(callback)
|
||||
if (profile, boxname) not in self.__observed:
|
||||
self.__observed[(profile, boxname)] = {}
|
||||
if signal not in self.__observed[(profile, boxname)]:
|
||||
self.__observed[(profile, boxname)][signal] = set()
|
||||
self.__observed[(profile, boxname)][signal].add(callback)
|
||||
|
||||
def removeObserver(self, callback, profile, boxname, signal="NEW_MESSAGE"):
|
||||
"""Remove an observer of maildir box changes
|
||||
@param callback: method to remove from obervers
|
||||
@param boxname: name of the box which was observed
|
||||
@param signal: which signal was observed by the caller"""
|
||||
if not self.__observed.has_key((profile,boxname)):
|
||||
err_msg=_("Trying to remove an observer for an inexistant mailbox")
|
||||
if (profile, boxname) not in self.__observed:
|
||||
err_msg = _("Trying to remove an observer for an inexistant mailbox")
|
||||
error(_("INTERNAL ERROR: ") + err_msg)
|
||||
raise MaildirError(err_msg)
|
||||
if not self.__observed[(profile,boxname)].has_key(signal):
|
||||
err_msg=_("Trying to remove an inexistant observer, no observer for this signal")
|
||||
if signal not in self.__observed[(profile, boxname)]:
|
||||
err_msg = _("Trying to remove an inexistant observer, no observer for this signal")
|
||||
error(_("INTERNAL ERROR: ") + err_msg)
|
||||
raise MaildirError(err_msg)
|
||||
if not callback in self.__observed[(profile,boxname)][signal]:
|
||||
err_msg=_("Trying to remove an inexistant observer")
|
||||
if not callback in self.__observed[(profile, boxname)][signal]:
|
||||
err_msg = _("Trying to remove an inexistant observer")
|
||||
error(_("INTERNAL ERROR: ") + err_msg)
|
||||
raise MaildirError(err_msg)
|
||||
self.__observed[(profile,boxname)][signal].remove(callback)
|
||||
self.__observed[(profile, boxname)][signal].remove(callback)
|
||||
|
||||
def emitSignal(self, profile, boxname, signal_name):
|
||||
"""Emit the signal to observer"""
|
||||
debug('emitSignal %s %s %s' %(profile, boxname, signal_name))
|
||||
debug('emitSignal %s %s %s' % (profile, boxname, signal_name))
|
||||
try:
|
||||
for observer_cb in self.__observed[(profile, boxname)][signal_name]:
|
||||
observer_cb()
|
||||
|
|
@ -325,7 +326,7 @@ class MailboxUser(object):
|
|||
if e.name == "body":
|
||||
mail.set_payload(e.children[0].encode('utf-8'))
|
||||
elif e.name == "subject":
|
||||
mail['Subject'] = e.children[0].encode('utf-8')
|
||||
mail['Subject'] = e.children[0].encode('utf-8')
|
||||
return mail.as_string()
|
||||
|
||||
def __init__(self, _maildir, name, observer=None, profile="@NONE@"):
|
||||
|
|
@ -334,36 +335,35 @@ class MailboxUser(object):
|
|||
@param profile: real profile (ie not a profile_key)
|
||||
THIS OBJECT MUST NOT BE USED DIRECTLY: use MaildirBox.accessMessageBox instead"""
|
||||
if _maildir._checkBoxReference(name, profile):
|
||||
error ("INTERNAL ERROR: MailboxUser MUST NOT be instancied directly")
|
||||
error("INTERNAL ERROR: MailboxUser MUST NOT be instancied directly")
|
||||
raise MailboxException('double MailboxUser instanciation')
|
||||
if name!="INBOX":
|
||||
if name != "INBOX":
|
||||
raise NotImplementedError
|
||||
self.name=name
|
||||
self.profile=profile
|
||||
self.maildir=_maildir
|
||||
self.name = name
|
||||
self.profile = profile
|
||||
self.maildir = _maildir
|
||||
profile_path = self.maildir._getProfilePath(profile)
|
||||
full_profile_path = os.path.join(self.maildir.host.memory.getConfig('','local_dir'), profile_path)
|
||||
full_profile_path = os.path.join(self.maildir.host.memory.getConfig('', 'local_dir'), profile_path)
|
||||
if not os.path.exists(full_profile_path):
|
||||
os.makedirs(full_profile_path,0700)
|
||||
os.makedirs(full_profile_path, 0700)
|
||||
mailbox_path = os.path.join(full_profile_path, MAILDIR_PATH)
|
||||
self.mailbox_path=mailbox_path
|
||||
self.mailbox_path = mailbox_path
|
||||
self.mailbox = maildir.MaildirMailbox(mailbox_path)
|
||||
self.observer=observer
|
||||
self.observer = observer
|
||||
self.__uid_table_update()
|
||||
|
||||
if observer:
|
||||
debug("adding observer for %s (%s)" % (name,profile))
|
||||
debug("adding observer for %s (%s)" % (name, profile))
|
||||
self.maildir.addObserver(observer, profile, name, "NEW_MESSAGE")
|
||||
|
||||
def __uid_table_update(self):
|
||||
existant_id=[]
|
||||
for mess_idx in range (self.getMessageCount()):
|
||||
existant_id = []
|
||||
for mess_idx in range(self.getMessageCount()):
|
||||
#we update the uid table
|
||||
existant_id.append(self.getId(mess_idx))
|
||||
self.getUid(mess_idx)
|
||||
self.maildir.cleanTable(self.name, existant_id, profile=self.profile)
|
||||
|
||||
|
||||
def __del__(self):
|
||||
if observer:
|
||||
debug("removing observer for %s" % self.name)
|
||||
|
|
@ -377,8 +377,8 @@ class MailboxUser(object):
|
|||
|
||||
def emitSignal(self, ignore, signal):
|
||||
"""Emit the signal to the observers"""
|
||||
if signal=="NEW_MESSAGE":
|
||||
self.getUid(self.getMessageCount()-1) #XXX: we make an uid for the last message added
|
||||
if signal == "NEW_MESSAGE":
|
||||
self.getUid(self.getMessageCount() - 1) # XXX: we make an uid for the last message added
|
||||
self.maildir.emitSignal(self.profile, self.name, signal)
|
||||
|
||||
def getId(self, mess_idx):
|
||||
|
|
@ -388,8 +388,8 @@ class MailboxUser(object):
|
|||
|
||||
def getUid(self, mess_idx):
|
||||
"""Return a unique interger id for the message, always ascending"""
|
||||
mess_id=self.getId(mess_idx)
|
||||
return self.maildir.getUid(self.name,mess_id, profile=self.profile)
|
||||
mess_id = self.getId(mess_idx)
|
||||
return self.maildir.getUid(self.name, mess_id, profile=self.profile)
|
||||
|
||||
def getNextUid(self):
|
||||
return self.maildir.getNextUid(self.name, profile=self.profile)
|
||||
|
|
@ -413,7 +413,7 @@ class MailboxUser(object):
|
|||
"""Return the message index from the uid
|
||||
@param mess_uid: message unique identifier
|
||||
@return: message index, as managed by MaildirMailbox"""
|
||||
for mess_idx in range (self.getMessageCount()):
|
||||
for mess_idx in range(self.getMessageCount()):
|
||||
if self.getUid(mess_idx) == mess_uid:
|
||||
return mess_idx
|
||||
raise IndexError
|
||||
|
|
@ -422,7 +422,7 @@ class MailboxUser(object):
|
|||
"""Return the message index from the unique index
|
||||
@param mess_id: message unique index as given by MaildirMailbox
|
||||
@return: message sequence index"""
|
||||
for mess_idx in range (self.getMessageCount()):
|
||||
for mess_idx in range(self.getMessageCount()):
|
||||
if self.mailbox.getUidl(mess_idx) == mess_id:
|
||||
return mess_idx
|
||||
raise IndexError
|
||||
|
|
@ -448,7 +448,7 @@ class MailboxUser(object):
|
|||
"""Return the flags of the message
|
||||
@param mess_uid: message unique identifier
|
||||
@return: list of strings"""
|
||||
id = self.maildir.getIdFromUid(self.name,mess_uid, profile=self.profile)
|
||||
id = self.maildir.getIdFromUid(self.name, mess_uid, profile=self.profile)
|
||||
return self.maildir.getFlags(self.name, id, profile=self.profile)
|
||||
|
||||
def setFlags(self, mess_idx, flags):
|
||||
|
|
@ -464,14 +464,14 @@ class MailboxUser(object):
|
|||
@param mess_uid: message unique identifier
|
||||
@param flags: list of strings
|
||||
"""
|
||||
id = self.maildir.getIdFromUid(self.name,mess_uid, profile=self.profile)
|
||||
id = self.maildir.getIdFromUid(self.name, mess_uid, profile=self.profile)
|
||||
return self.maildir.setFlags(self.name, id, flags, profile=self.profile)
|
||||
|
||||
def getMessageIdsWithFlag(self, flag):
|
||||
"""Return ids of messages where a flag is set
|
||||
@param flag: flag to check
|
||||
@return: list of id (as given by MaildirMailbox)"""
|
||||
return self.maildir.getMessageIdsWithFlag(self.name,flag, profile=self.profile)
|
||||
return self.maildir.getMessageIdsWithFlag(self.name, flag, profile=self.profile)
|
||||
|
||||
def removeDeleted(self):
|
||||
"""Actually delete message flagged "\\Deleted"
|
||||
|
|
@ -480,11 +480,10 @@ class MailboxUser(object):
|
|||
for mess_id in self.getMessageIdsWithFlag("\\Deleted"):
|
||||
print ("Deleting %s" % mess_id)
|
||||
self.mailbox.deleteMessage(self.getIdxFromId(mess_id))
|
||||
self.mailbox = maildir.MaildirMailbox(self.mailbox_path) #We need to reparse the dir to have coherent indexing
|
||||
self.mailbox = maildir.MaildirMailbox(self.mailbox_path) # We need to reparse the dir to have coherent indexing
|
||||
self.maildir.purgeDeleted(self.name, profile=self.profile)
|
||||
|
||||
def emptyTrash(self):
|
||||
"""Delete everything in the .Trash dir"""
|
||||
import shutils
|
||||
pdb.set_trace()
|
||||
|
||||
|
|
|
|||
|
|
@ -46,14 +46,14 @@ QG_TAG = 'quiz'
|
|||
QG_REQUEST = MESSAGE + '/' + QG_TAG + '[@xmlns="' + NS_QG + '"]'
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "Quiz game plugin",
|
||||
"import_name": "Quiz",
|
||||
"type": "Game",
|
||||
"protocols": [],
|
||||
"dependencies": ["XEP-0045", "XEP-0249"],
|
||||
"main": "Quiz",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of Quiz game""")
|
||||
"name": "Quiz game plugin",
|
||||
"import_name": "Quiz",
|
||||
"type": "Game",
|
||||
"protocols": [],
|
||||
"dependencies": ["XEP-0045", "XEP-0249"],
|
||||
"main": "Quiz",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of Quiz game""")
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -62,66 +62,66 @@ class Quiz(object):
|
|||
def __init__(self, host):
|
||||
info(_("Plugin Quiz initialization"))
|
||||
self.host = host
|
||||
self.games={}
|
||||
self.waiting_inv = {} #Invitation waiting for people to join to launch a game
|
||||
host.bridge.addMethod("quizGameLaunch", ".plugin", in_sign='ass', out_sign='', method=self.quizGameLaunch) #args: players, profile
|
||||
host.bridge.addMethod("quizGameCreate", ".plugin", in_sign='sass', out_sign='', method=self.quizGameCreate) #args: room_jid, players, profile
|
||||
host.bridge.addMethod("quizGameReady", ".plugin", in_sign='sss', out_sign='', method=self.newPlayerReady) #args: player, referee, profile
|
||||
self.games = {}
|
||||
self.waiting_inv = {} # Invitation waiting for people to join to launch a game
|
||||
host.bridge.addMethod("quizGameLaunch", ".plugin", in_sign='ass', out_sign='', method=self.quizGameLaunch) # args: players, profile
|
||||
host.bridge.addMethod("quizGameCreate", ".plugin", in_sign='sass', out_sign='', method=self.quizGameCreate) # args: room_jid, players, profile
|
||||
host.bridge.addMethod("quizGameReady", ".plugin", in_sign='sss', out_sign='', method=self.newPlayerReady) # args: player, referee, profile
|
||||
host.bridge.addMethod("quizGameAnswer", ".plugin", in_sign='ssss', out_sign='', method=self.playerAnswer)
|
||||
host.bridge.addSignal("quizGameStarted", ".plugin", signature='ssass') #args: room_jid, referee, players, profile
|
||||
host.bridge.addSignal("quizGameStarted", ".plugin", signature='ssass') # args: room_jid, referee, players, profile
|
||||
host.bridge.addSignal("quizGameNew", ".plugin",
|
||||
signature='sa{ss}s',
|
||||
doc = { 'summary': 'Start a new game',
|
||||
'param_0': "room_jid: jid of game's room",
|
||||
'param_1': "game_data: data of the game",
|
||||
'param_2': '%(doc_profile)s'})
|
||||
doc={'summary': 'Start a new game',
|
||||
'param_0': "room_jid: jid of game's room",
|
||||
'param_1': "game_data: data of the game",
|
||||
'param_2': '%(doc_profile)s'})
|
||||
host.bridge.addSignal("quizGameQuestion", ".plugin",
|
||||
signature = 'sssis',
|
||||
doc = { 'summary': "Send the current question",
|
||||
'param_0': "room_jid: jid of game's room",
|
||||
'param_1': "question_id: question id",
|
||||
'param_2': "question: question to ask",
|
||||
'param_3': "timer: timer",
|
||||
'param_4': '%(doc_profile)s'})
|
||||
signature='sssis',
|
||||
doc={'summary': "Send the current question",
|
||||
'param_0': "room_jid: jid of game's room",
|
||||
'param_1': "question_id: question id",
|
||||
'param_2': "question: question to ask",
|
||||
'param_3': "timer: timer",
|
||||
'param_4': '%(doc_profile)s'})
|
||||
host.bridge.addSignal("quizGamePlayerBuzzed", ".plugin",
|
||||
signature = 'ssbs',
|
||||
doc = { 'summary': "A player just pressed the buzzer",
|
||||
'param_0': "room_jid: jid of game's room",
|
||||
'param_1': "player: player who pushed the buzzer",
|
||||
'param_2': "pause: should the game be paused ?",
|
||||
'param_3': '%(doc_profile)s'})
|
||||
signature='ssbs',
|
||||
doc={'summary': "A player just pressed the buzzer",
|
||||
'param_0': "room_jid: jid of game's room",
|
||||
'param_1': "player: player who pushed the buzzer",
|
||||
'param_2': "pause: should the game be paused ?",
|
||||
'param_3': '%(doc_profile)s'})
|
||||
host.bridge.addSignal("quizGamePlayerSays", ".plugin",
|
||||
signature = 'sssis',
|
||||
doc = { 'summary': "A player just pressed the buzzer",
|
||||
'param_0': "room_jid: jid of game's room",
|
||||
'param_1': "player: player who pushed the buzzer",
|
||||
'param_2': "text: what the player say",
|
||||
'param_3': "delay: how long, in seconds, the text must appear",
|
||||
'param_4': '%(doc_profile)s'})
|
||||
signature='sssis',
|
||||
doc={'summary': "A player just pressed the buzzer",
|
||||
'param_0': "room_jid: jid of game's room",
|
||||
'param_1': "player: player who pushed the buzzer",
|
||||
'param_2': "text: what the player say",
|
||||
'param_3': "delay: how long, in seconds, the text must appear",
|
||||
'param_4': '%(doc_profile)s'})
|
||||
host.bridge.addSignal("quizGameAnswerResult", ".plugin",
|
||||
signature = 'ssba{si}s',
|
||||
doc = { 'summary': "Result of the just given answer",
|
||||
'param_0': "room_jid: jid of game's room",
|
||||
'param_1': "player: player who gave the answer",
|
||||
'param_2': "good_answer: True if the answer is right",
|
||||
'param_3': "score: dict of score with player as key",
|
||||
'param_4': '%(doc_profile)s'})
|
||||
signature='ssba{si}s',
|
||||
doc={'summary': "Result of the just given answer",
|
||||
'param_0': "room_jid: jid of game's room",
|
||||
'param_1': "player: player who gave the answer",
|
||||
'param_2': "good_answer: True if the answer is right",
|
||||
'param_3': "score: dict of score with player as key",
|
||||
'param_4': '%(doc_profile)s'})
|
||||
host.bridge.addSignal("quizGameTimerExpired", ".plugin",
|
||||
signature = 'ss',
|
||||
doc = { 'summary': "Nobody answered the question in time",
|
||||
'param_0': "room_jid: jid of game's room",
|
||||
'param_1': '%(doc_profile)s'})
|
||||
signature='ss',
|
||||
doc={'summary': "Nobody answered the question in time",
|
||||
'param_0': "room_jid: jid of game's room",
|
||||
'param_1': '%(doc_profile)s'})
|
||||
host.bridge.addSignal("quizGameTimerRestarted", ".plugin",
|
||||
signature = 'sis',
|
||||
doc = { 'summary': "Nobody answered the question in time",
|
||||
'param_0': "room_jid: jid of game's room",
|
||||
'param_1': "time_left: time left before timer expiration",
|
||||
'param_2': '%(doc_profile)s'})
|
||||
signature='sis',
|
||||
doc={'summary': "Nobody answered the question in time",
|
||||
'param_0': "room_jid: jid of game's room",
|
||||
'param_1': "time_left: time left before timer expiration",
|
||||
'param_2': '%(doc_profile)s'})
|
||||
host.trigger.add("MUC user joined", self.userJoinedTrigger)
|
||||
|
||||
def createGameElt(self, to_jid, type="normal"):
|
||||
type = "normal" if to_jid.resource else "groupchat"
|
||||
elt = domish.Element((None,'message'))
|
||||
elt = domish.Element((None, 'message'))
|
||||
elt["to"] = to_jid.full()
|
||||
elt["type"] = type
|
||||
elt.addElement((NS_QG, QG_TAG))
|
||||
|
|
@ -129,9 +129,9 @@ class Quiz(object):
|
|||
|
||||
def __game_data_to_xml(self, game_data):
|
||||
"""Convert a game data dict to domish element"""
|
||||
game_data_elt = domish.Element((None,'game_data'))
|
||||
game_data_elt = domish.Element((None, 'game_data'))
|
||||
for data in game_data:
|
||||
data_elt = domish.Element((None,data))
|
||||
data_elt = domish.Element((None, data))
|
||||
data_elt.addContent(game_data[data])
|
||||
game_data_elt.addChild(data_elt)
|
||||
return game_data_elt
|
||||
|
|
@ -162,12 +162,12 @@ class Quiz(object):
|
|||
for player in game_data['players']:
|
||||
score[player] = players_data[player]['score']
|
||||
|
||||
answer_result_elt = domish.Element((None,'answer_result'))
|
||||
answer_result_elt = domish.Element((None, 'answer_result'))
|
||||
answer_result_elt['player'] = player_answering
|
||||
answer_result_elt['good_answer'] = str(good_answer)
|
||||
|
||||
for player in score:
|
||||
score_elt = domish.Element((None,"score"))
|
||||
score_elt = domish.Element((None, "score"))
|
||||
score_elt['player'] = player
|
||||
score_elt['score'] = str(score[player])
|
||||
answer_result_elt.addChild(score_elt)
|
||||
|
|
@ -176,19 +176,19 @@ class Quiz(object):
|
|||
|
||||
def __create_started_elt(self, players):
|
||||
"""Create a game_started domish element"""
|
||||
started_elt = domish.Element((None,'started'))
|
||||
started_elt = domish.Element((None, 'started'))
|
||||
idx = 0
|
||||
for player in players:
|
||||
player_elt = domish.Element((None,'player'))
|
||||
player_elt = domish.Element((None, 'player'))
|
||||
player_elt.addContent(player)
|
||||
player_elt['index'] = str(idx)
|
||||
idx+=1
|
||||
idx += 1
|
||||
started_elt.addChild(player_elt)
|
||||
return started_elt
|
||||
|
||||
def __ask_question(self, question_id, question, timer):
|
||||
"""Create a element for asking a question"""
|
||||
question_elt = domish.Element((None,'question'))
|
||||
question_elt = domish.Element((None, 'question'))
|
||||
question_elt['id'] = question_id
|
||||
question_elt['timer'] = str(timer)
|
||||
question_elt.addContent(question)
|
||||
|
|
@ -197,14 +197,13 @@ class Quiz(object):
|
|||
def __start_play(self, room_jid, game_data, profile):
|
||||
"""Start the game (tell to the first player after dealer to play"""
|
||||
game_data['stage'] = "play"
|
||||
next_player_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(game_data['players']) #the player after the dealer start
|
||||
next_player_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(game_data['players']) # the player after the dealer start
|
||||
game_data['first_player'] = next_player = game_data['players'][next_player_idx]
|
||||
to_jid = jid.JID(room_jid.userhost()+"/"+next_player)
|
||||
to_jid = jid.JID(room_jid.userhost() + "/" + next_player)
|
||||
mess = self.createGameElt(to_jid)
|
||||
yourturn_elt = mess.firstChildElement().addElement('your_turn')
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
|
||||
|
||||
def userJoinedTrigger(self, room, user, profile):
|
||||
"""This trigger is used to check if we are waiting people in this room,
|
||||
and to create a game if everybody is here"""
|
||||
|
|
@ -229,8 +228,8 @@ class Quiz(object):
|
|||
def quizRoomJoined(room):
|
||||
_room = room.occupantJID.userhostJID()
|
||||
for player in players:
|
||||
self.host.plugins["XEP-0249"].invite(jid.JID(player), room.occupantJID.userhostJID(), {"game":"Quiz"}, profile)
|
||||
self.waiting_inv[_room] = (time(), players) #TODO: remove invitation waiting for too long, using the time data
|
||||
self.host.plugins["XEP-0249"].invite(jid.JID(player), room.occupantJID.userhostJID(), {"game": "Quiz"}, profile)
|
||||
self.waiting_inv[_room] = (time(), players) # TODO: remove invitation waiting for too long, using the time data
|
||||
|
||||
def after_init(ignore):
|
||||
room_name = "sat_quiz_%s" % self.host.plugins["XEP-0045"].getUniqueName(profile_key)
|
||||
|
|
@ -262,28 +261,28 @@ class Quiz(object):
|
|||
@param room_jid_param: jid of the room
|
||||
@param players: list of players nick (nick must exist in the room)
|
||||
@param profile_key: %(doc_profile_key)s"""
|
||||
debug (_("Creating Quiz game"))
|
||||
debug(_("Creating Quiz game"))
|
||||
room_jid = jid.JID(room_jid_param)
|
||||
profile = self.host.memory.getProfileName(profile_key)
|
||||
if not profile:
|
||||
error (_("profile %s is unknown") % profile_key)
|
||||
error(_("profile %s is unknown") % profile_key)
|
||||
return
|
||||
if self.games.has_key(room_jid):
|
||||
warning (_("Quiz game already started in room %s") % room_jid.userhost())
|
||||
if room_jid in self.games:
|
||||
warning(_("Quiz game already started in room %s") % room_jid.userhost())
|
||||
else:
|
||||
room_nick = self.host.plugins["XEP-0045"].getRoomNick(room_jid.userhost(), profile)
|
||||
if not room_nick:
|
||||
error ('Internal error')
|
||||
error('Internal error')
|
||||
return
|
||||
referee = room_jid.userhost() + '/' + room_nick
|
||||
status = {}
|
||||
players_data = {}
|
||||
for player in players:
|
||||
players_data[player] = {'score':0}
|
||||
players_data[player] = {'score': 0}
|
||||
status[player] = "init"
|
||||
self.games[room_jid.userhost()] = {'referee':referee, 'players':players, 'status':status, 'players_data':players_data, 'stage': None}
|
||||
self.games[room_jid.userhost()] = {'referee': referee, 'players': players, 'status': status, 'players_data': players_data, 'stage': None}
|
||||
for player in players:
|
||||
mess = self.createGameElt(jid.JID(room_jid.userhost()+'/'+player))
|
||||
mess = self.createGameElt(jid.JID(room_jid.userhost() + '/' + player))
|
||||
mess.firstChildElement().addChild(self.__create_started_elt(players))
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
|
||||
|
|
@ -291,9 +290,9 @@ class Quiz(object):
|
|||
"""Must be called when player is ready to start a new game"""
|
||||
profile = self.host.memory.getProfileName(profile_key)
|
||||
if not profile:
|
||||
error (_("profile %s is unknown") % profile_key)
|
||||
error(_("profile %s is unknown") % profile_key)
|
||||
return
|
||||
debug ('new player ready: %s' % profile)
|
||||
debug('new player ready: %s' % profile)
|
||||
mess = self.createGameElt(jid.JID(referee))
|
||||
ready_elt = mess.firstChildElement().addElement('player_ready')
|
||||
ready_elt['player'] = player
|
||||
|
|
@ -303,9 +302,9 @@ class Quiz(object):
|
|||
"""Called when a player give an answer"""
|
||||
profile = self.host.memory.getProfileName(profile_key)
|
||||
if not profile:
|
||||
error (_("profile %s is unknown") % profile_key)
|
||||
error(_("profile %s is unknown") % profile_key)
|
||||
return
|
||||
debug ('new player answer (%(profile)s): %(answer)s' % {'profile':profile, 'answer':answer})
|
||||
debug('new player answer (%(profile)s): %(answer)s' % {'profile': profile, 'answer': answer})
|
||||
mess = self.createGameElt(jid.JID(referee))
|
||||
answer_elt = mess.firstChildElement().addElement('player_answer')
|
||||
answer_elt['player'] = player
|
||||
|
|
@ -333,7 +332,7 @@ class Quiz(object):
|
|||
def restartTimer(self, room_jid, profile):
|
||||
"""Restart a timer with the saved time"""
|
||||
game_data = self.games[room_jid.userhost()]
|
||||
assert(game_data['time_left'] != None)
|
||||
assert game_data['time_left'] is not None
|
||||
mess = self.createGameElt(room_jid)
|
||||
restarted_elt = mess.firstChildElement().addElement('timer_restarted')
|
||||
restarted_elt["time_left"] = str(game_data['time_left'])
|
||||
|
|
@ -359,7 +358,7 @@ class Quiz(object):
|
|||
"""Check if the answer given is right"""
|
||||
game_data = self.games[room_jid.userhost()]
|
||||
players_data = game_data['players_data']
|
||||
good_answer = game_data['question_id'] == "1" and answer=="42"
|
||||
good_answer = game_data['question_id'] == "1" and answer == "42"
|
||||
players_data[player]['score'] += 1 if good_answer else -1
|
||||
players_data[player]['score'] = min(9, max(0, players_data[player]['score']))
|
||||
|
||||
|
|
@ -374,7 +373,7 @@ class Quiz(object):
|
|||
|
||||
def newGame(self, room_jid, profile):
|
||||
"""Launch a new round"""
|
||||
debug (_('new Quiz game'))
|
||||
debug(_('new Quiz game'))
|
||||
game_data = self.games[room_jid.userhost()]
|
||||
players = game_data['players']
|
||||
players_data = game_data['players_data']
|
||||
|
|
@ -401,30 +400,30 @@ Attention, tu es prêt ?""")}
|
|||
|
||||
for elt in game_elt.elements():
|
||||
|
||||
if elt.name == 'started': #new game created
|
||||
if elt.name == 'started': # new game created
|
||||
players = []
|
||||
for player in elt.elements():
|
||||
players.append(unicode(player))
|
||||
self.host.bridge.quizGameStarted(room_jid.userhost(), from_jid.full(), players, profile)
|
||||
|
||||
elif elt.name == 'player_ready': #ready to play
|
||||
elif elt.name == 'player_ready': # ready to play
|
||||
player = elt['player']
|
||||
status = self.games[room_jid.userhost()]['status']
|
||||
nb_players = len(self.games[room_jid.userhost()]['players'])
|
||||
status[player] = 'ready'
|
||||
debug (_('Player %(player)s is ready to start [status: %(status)s]') % {'player':player, 'status':status})
|
||||
if status.values().count('ready') == nb_players: #everybody is ready, we can start the game
|
||||
debug(_('Player %(player)s is ready to start [status: %(status)s]') % {'player': player, 'status': status})
|
||||
if status.values().count('ready') == nb_players: # everybody is ready, we can start the game
|
||||
self.newGame(room_jid, profile)
|
||||
|
||||
elif elt.name == 'game_data':
|
||||
self.host.bridge.quizGameNew(room_jid.userhost(), self.__xml_to_game_data(elt), profile)
|
||||
|
||||
elif elt.name == 'question': #A question is asked
|
||||
self.host.bridge.quizGameQuestion(room_jid.userhost(), elt["id"], unicode(elt), int(elt["timer"]), profile )
|
||||
elif elt.name == 'question': # A question is asked
|
||||
self.host.bridge.quizGameQuestion(room_jid.userhost(), elt["id"], unicode(elt), int(elt["timer"]), profile)
|
||||
|
||||
elif elt.name == 'player_answer':
|
||||
player = elt['player']
|
||||
pause = game_data['stage'] == 'question' #we pause the game only if we are have a question at the moment
|
||||
pause = game_data['stage'] == 'question' # we pause the game only if we are have a question at the moment
|
||||
#we first send a buzzer message
|
||||
mess = self.createGameElt(room_jid)
|
||||
buzzer_elt = mess.firstChildElement().addElement('player_buzzed')
|
||||
|
|
@ -460,10 +459,11 @@ Attention, tu es prêt ?""")}
|
|||
self.host.bridge.quizGameTimerRestarted(room_jid.userhost(), int(elt['time_left']), profile)
|
||||
|
||||
else:
|
||||
error (_('Unmanaged game element: %s') % elt.name)
|
||||
error(_('Unmanaged game element: %s') % elt.name)
|
||||
|
||||
def getHandler(self, profile):
|
||||
return QuizGameHandler(self)
|
||||
return QuizGameHandler(self)
|
||||
|
||||
|
||||
class QuizGameHandler (XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
|
|
@ -473,11 +473,10 @@ class QuizGameHandler (XMPPHandler):
|
|||
self.host = plugin_parent.host
|
||||
|
||||
def connectionInitialized(self):
|
||||
self.xmlstream.addObserver(QG_REQUEST, self.plugin_parent.quiz_game_cmd, profile = self.parent.profile)
|
||||
self.xmlstream.addObserver(QG_REQUEST, self.plugin_parent.quiz_game_cmd, profile=self.parent.profile)
|
||||
|
||||
def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
|
||||
return [disco.DiscoFeature(NS_QG)]
|
||||
|
||||
def getDiscoItems(self, requestor, target, nodeIdentifier=''):
|
||||
return []
|
||||
|
||||
|
|
|
|||
|
|
@ -43,14 +43,14 @@ RADIOC_TAG = 'radiocol'
|
|||
RADIOC_REQUEST = MESSAGE + '/' + RADIOC_TAG + '[@xmlns="' + NC_RADIOCOL + '"]'
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "Radio collective plugin",
|
||||
"import_name": "Radiocol",
|
||||
"type": "Exp",
|
||||
"protocols": [],
|
||||
"dependencies": ["XEP-0045", "XEP-0249"],
|
||||
"main": "Radiocol",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of radio collective""")
|
||||
"name": "Radio collective plugin",
|
||||
"import_name": "Radiocol",
|
||||
"type": "Exp",
|
||||
"protocols": [],
|
||||
"dependencies": ["XEP-0045", "XEP-0249"],
|
||||
"main": "Radiocol",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of radio collective""")
|
||||
}
|
||||
|
||||
QUEUE_LIMIT = 2
|
||||
|
|
@ -61,21 +61,21 @@ class Radiocol(object):
|
|||
def __init__(self, host):
|
||||
info(_("Radio collective initialization"))
|
||||
self.host = host
|
||||
self.radios={}
|
||||
self.radios = {}
|
||||
host.bridge.addMethod("radiocolLaunch", ".plugin", in_sign='ass', out_sign='', method=self.radiocolLaunch)
|
||||
host.bridge.addMethod("radiocolCreate", ".plugin", in_sign='ss', out_sign='', method=self.radiocolCreate)
|
||||
host.bridge.addMethod("radiocolSongAdded", ".plugin", in_sign='sss', out_sign='', method=self.radiocolSongAdded)
|
||||
host.bridge.addSignal("radiocolStarted", ".plugin", signature='sss') #room_jid, referee, profile
|
||||
host.bridge.addSignal("radiocolSongRejected", ".plugin", signature='sss') #room_jid, reason, profile
|
||||
host.bridge.addSignal("radiocolPreload", ".plugin", signature='ssssss') #room_jid, filename, title, artist, album, profile
|
||||
host.bridge.addSignal("radiocolPlay", ".plugin", signature='sss') #room_jid, filename, profile
|
||||
host.bridge.addSignal("radiocolNoUpload", ".plugin", signature='ss') #room_jid, profile
|
||||
host.bridge.addSignal("radiocolUploadOk", ".plugin", signature='ss') #room_jid, profile
|
||||
host.bridge.addSignal("radiocolStarted", ".plugin", signature='sss') # room_jid, referee, profile
|
||||
host.bridge.addSignal("radiocolSongRejected", ".plugin", signature='sss') # room_jid, reason, profile
|
||||
host.bridge.addSignal("radiocolPreload", ".plugin", signature='ssssss') # room_jid, filename, title, artist, album, profile
|
||||
host.bridge.addSignal("radiocolPlay", ".plugin", signature='sss') # room_jid, filename, profile
|
||||
host.bridge.addSignal("radiocolNoUpload", ".plugin", signature='ss') # room_jid, profile
|
||||
host.bridge.addSignal("radiocolUploadOk", ".plugin", signature='ss') # room_jid, profile
|
||||
host.trigger.add("MUC user joined", self.userJoinedTrigger)
|
||||
|
||||
def createRadiocolElt(self, to_jid, type="normal"):
|
||||
type = "normal" if to_jid.resource else "groupchat"
|
||||
elt = domish.Element((None,'message'))
|
||||
elt = domish.Element((None, 'message'))
|
||||
elt["to"] = to_jid.full()
|
||||
elt["type"] = type
|
||||
elt.addElement((NC_RADIOCOL, RADIOC_TAG))
|
||||
|
|
@ -83,26 +83,25 @@ class Radiocol(object):
|
|||
|
||||
def __create_started_elt(self):
|
||||
"""Create a game_started domish element"""
|
||||
started_elt = domish.Element((None,'started'))
|
||||
started_elt = domish.Element((None, 'started'))
|
||||
return started_elt
|
||||
|
||||
def __create_preload_elt(self, sender, filename, title, artist, album):
|
||||
preload_elt = domish.Element((None,'preload'))
|
||||
preload_elt = domish.Element((None, 'preload'))
|
||||
preload_elt['sender'] = sender
|
||||
preload_elt['filename'] = filename #XXX: the frontend should know the temporary directory where file is put
|
||||
preload_elt['filename'] = filename # XXX: the frontend should know the temporary directory where file is put
|
||||
preload_elt['title'] = title
|
||||
preload_elt['artist'] = artist
|
||||
preload_elt['album'] = album
|
||||
return preload_elt
|
||||
|
||||
|
||||
def userJoinedTrigger(self, room, user, profile):
|
||||
"""This trigger is used to check if we are waiting people in this room,
|
||||
and to create a game if everybody is here"""
|
||||
room_jid = room.occupantJID.userhost()
|
||||
if self.radios.has_key(room_jid) and self.radios[room_jid]["referee"] == room.occupantJID.full():
|
||||
if room_jid in self.radios and self.radios[room_jid]["referee"] == room.occupantJID.full():
|
||||
#we are in a radiocol room, let's start the party !
|
||||
mess = self.createRadiocolElt(jid.JID(room_jid+'/'+user.nick))
|
||||
mess = self.createRadiocolElt(jid.JID(room_jid + '/' + user.nick))
|
||||
mess.firstChildElement().addChild(self.__create_started_elt())
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
return True
|
||||
|
|
@ -121,7 +120,7 @@ class Radiocol(object):
|
|||
_room_jid = room.occupantJID.userhostJID()
|
||||
self.radiocolCreate(_room_jid.userhost(), profile_key=profile)
|
||||
for occupant in occupants:
|
||||
self.host.plugins["XEP-0249"].invite(jid.JID(occupant), room.occupantJID.userhostJID(), {"game":"Radiocol"}, profile)
|
||||
self.host.plugins["XEP-0249"].invite(jid.JID(occupant), room.occupantJID.userhostJID(), {"game": "Radiocol"}, profile)
|
||||
|
||||
def after_init(ignore):
|
||||
room_name = "sat_radiocol_%s" % self.host.plugins["XEP-0045"].getUniqueName(profile_key)
|
||||
|
|
@ -153,21 +152,21 @@ class Radiocol(object):
|
|||
"""Create a new game
|
||||
@param room_jid_param: jid of the room
|
||||
@param profile_key: %(doc_profile_key)s"""
|
||||
debug (_("Creating Radiocol"))
|
||||
debug(_("Creating Radiocol"))
|
||||
room_jid = jid.JID(room_jid_param)
|
||||
profile = self.host.memory.getProfileName(profile_key)
|
||||
if not profile:
|
||||
error (_("profile %s is unknown") % profile_key)
|
||||
error(_("profile %s is unknown") % profile_key)
|
||||
return
|
||||
if self.radios.has_key(room_jid):
|
||||
warning (_("Radiocol already started in room %s") % room_jid.userhost())
|
||||
if room_jid in self.radios:
|
||||
warning(_("Radiocol already started in room %s") % room_jid.userhost())
|
||||
else:
|
||||
room_nick = self.host.plugins["XEP-0045"].getRoomNick(room_jid.userhost(), profile)
|
||||
if not room_nick:
|
||||
error ('Internal error')
|
||||
error('Internal error')
|
||||
return
|
||||
referee = room_jid.userhost() + '/' + room_nick
|
||||
self.radios[room_jid.userhost()] = {'referee':referee, 'queue':[], 'upload':True, 'playing': False, 'occupants_data':{}, 'to_delete':{}}
|
||||
self.radios[room_jid.userhost()] = {'referee': referee, 'queue': [], 'upload': True, 'playing': False, 'occupants_data': {}, 'to_delete': {}}
|
||||
mess = self.createRadiocolElt(jid.JID(room_jid.userhost()))
|
||||
mess.firstChildElement().addChild(self.__create_started_elt())
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
|
|
@ -189,8 +188,8 @@ class Radiocol(object):
|
|||
song = OggVorbis(song_path)
|
||||
except OggVorbisHeaderError:
|
||||
#this file is not ogg vorbis, we reject it
|
||||
unlink(song_path) # FIXME: same host trick (see note above)
|
||||
self.host.bridge.radiocolSongRejected(jid.JID(referee).userhost(), \
|
||||
unlink(song_path) # FIXME: same host trick (see note above)
|
||||
self.host.bridge.radiocolSongRejected(jid.JID(referee).userhost(),
|
||||
"Uploaded file is not Ogg Vorbis song, only Ogg Vorbis songs are acceptable", profile)
|
||||
"""mess = self.createRadiocolElt(jid.JID(referee))
|
||||
reject_elt = mess.firstChildElement().addElement(('','song_rejected'))
|
||||
|
|
@ -204,7 +203,7 @@ class Radiocol(object):
|
|||
album = song.get("album", ["Unknown"])[0]
|
||||
length = song.info.length
|
||||
mess = self.createRadiocolElt(jid.JID(referee))
|
||||
added_elt = mess.firstChildElement().addElement(('','song_added'))
|
||||
added_elt = mess.firstChildElement().addElement(('', 'song_added'))
|
||||
added_elt['filename'] = filename = os.path.basename(song_path)
|
||||
added_elt['title'] = title
|
||||
added_elt['artist'] = artist
|
||||
|
|
@ -212,8 +211,8 @@ class Radiocol(object):
|
|||
added_elt['length'] = str(length)
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
|
||||
radio_data = self.radios[jid.JID(referee).userhost()] #FIXME: referee comes from Libervia's client side, it's unsecure
|
||||
radio_data['to_delete'][filename] = song_path #FIXME: works only because of the same host trick, see the note under the docstring
|
||||
radio_data = self.radios[jid.JID(referee).userhost()] # FIXME: referee comes from Libervia's client side, it's unsecure
|
||||
radio_data['to_delete'][filename] = song_path # FIXME: works only because of the same host trick, see the note under the docstring
|
||||
|
||||
def playNext(self, room_jid, profile):
|
||||
""""Play next sont in queue if exists, and put a timer
|
||||
|
|
@ -231,14 +230,14 @@ class Radiocol(object):
|
|||
|
||||
filename, length = queue.pop(0)
|
||||
mess = self.createRadiocolElt(room_jid)
|
||||
play_elt = mess.firstChildElement().addElement(('','play'))
|
||||
play_elt = mess.firstChildElement().addElement(('', 'play'))
|
||||
play_elt['filename'] = filename
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
|
||||
if not radio_data['upload'] and len(queue) < QUEUE_LIMIT:
|
||||
#upload is blocked and we now have resources to get more, we reactivate it
|
||||
mess = self.createRadiocolElt(room_jid)
|
||||
no_upload_elt = mess.firstChildElement().addElement(('','upload_ok'))
|
||||
no_upload_elt = mess.firstChildElement().addElement(('', 'upload_ok'))
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
radio_data['upload'] = True
|
||||
|
||||
|
|
@ -250,8 +249,7 @@ class Radiocol(object):
|
|||
return
|
||||
|
||||
#we wait more than the song length to delete the file, to manage poorly reactive networks/clients
|
||||
reactor.callLater(length + 90, unlink, file_to_delete) #FIXME: same host trick (see above)
|
||||
|
||||
reactor.callLater(length + 90, unlink, file_to_delete) # FIXME: same host trick (see above)
|
||||
|
||||
def radiocol_game_cmd(self, mess_elt, profile):
|
||||
#FIXME: we should check sender (is it referee ?) here before accepting commands
|
||||
|
|
@ -264,26 +262,26 @@ class Radiocol(object):
|
|||
|
||||
for elt in radio_elt.elements():
|
||||
|
||||
if elt.name == 'started': #new game created
|
||||
if elt.name == 'started': # new game created
|
||||
self.host.bridge.radiocolStarted(room_jid.userhost(), from_jid.full(), profile)
|
||||
elif elt.name == 'preload': #a song is in queue and must be preloaded
|
||||
elif elt.name == 'preload': # a song is in queue and must be preloaded
|
||||
self.host.bridge.radiocolPreload(room_jid.userhost(), elt['filename'], elt['title'], elt['artist'], elt['album'], profile)
|
||||
elif elt.name == 'play':
|
||||
self.host.bridge.radiocolPlay(room_jid.userhost(), elt['filename'], profile)
|
||||
elif elt.name == 'song_rejected': #a song has been refused
|
||||
elif elt.name == 'song_rejected': # a song has been refused
|
||||
self.host.bridge.radiocolSongRejected(room_jid.userhost(), elt['reason'], profile)
|
||||
elif elt.name == 'no_upload':
|
||||
self.host.bridge.radiocolNoUpload(room_jid.userhost(), profile)
|
||||
elif elt.name == 'upload_ok':
|
||||
self.host.bridge.radiocolUploadOk(room_jid.userhost(), profile)
|
||||
elif elt.name == 'song_added': #a song has been added
|
||||
elif elt.name == 'song_added': # a song has been added
|
||||
#FIXME: we are KISS for the proof of concept: every song is added, to a limit of 3 in queue.
|
||||
# Need to manage some sort of rules to allow peoples to send songs
|
||||
|
||||
if len(queue) >= QUEUE_LIMIT:
|
||||
#there are already too many songs in queue, we reject this one
|
||||
mess = self.createRadiocolElt(room_jid)
|
||||
reject_elt = mess.firstChildElement().addElement(('','song_rejected'))
|
||||
reject_elt = mess.firstChildElement().addElement(('', 'song_rejected'))
|
||||
reject_elt['sender'] = from_jid.resource
|
||||
reject_elt['reason'] = "Too many songs in queue"
|
||||
#FIXME: add an error code
|
||||
|
|
@ -296,12 +294,11 @@ class Radiocol(object):
|
|||
if len(queue) >= QUEUE_LIMIT:
|
||||
#We are at the limit, we refuse new upload until next play
|
||||
mess = self.createRadiocolElt(room_jid)
|
||||
no_upload_elt = mess.firstChildElement().addElement(('','no_upload'))
|
||||
no_upload_elt = mess.firstChildElement().addElement(('', 'no_upload'))
|
||||
#FIXME: add an error code
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
radio_data['upload'] = False
|
||||
|
||||
|
||||
mess = self.createRadiocolElt(room_jid)
|
||||
preload_elt = self.__create_preload_elt(from_jid.resource,
|
||||
elt['filename'],
|
||||
|
|
@ -316,10 +313,11 @@ class Radiocol(object):
|
|||
radio_data['playing'] = True
|
||||
self.playNext(room_jid, profile)
|
||||
else:
|
||||
error (_('Unmanaged game element: %s') % elt.name)
|
||||
error(_('Unmanaged game element: %s') % elt.name)
|
||||
|
||||
def getHandler(self, profile):
|
||||
return RadiocolHandler(self)
|
||||
return RadiocolHandler(self)
|
||||
|
||||
|
||||
class RadiocolHandler (XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
|
|
@ -329,11 +327,10 @@ class RadiocolHandler (XMPPHandler):
|
|||
self.host = plugin_parent.host
|
||||
|
||||
def connectionInitialized(self):
|
||||
self.xmlstream.addObserver(RADIOC_REQUEST, self.plugin_parent.radiocol_game_cmd, profile = self.parent.profile)
|
||||
self.xmlstream.addObserver(RADIOC_REQUEST, self.plugin_parent.radiocol_game_cmd, profile=self.parent.profile)
|
||||
|
||||
def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
|
||||
return [disco.DiscoFeature(NC_RADIOCOL)]
|
||||
|
||||
def getDiscoItems(self, requestor, target, nodeIdentifier=''):
|
||||
return []
|
||||
|
||||
|
|
|
|||
|
|
@ -21,34 +21,33 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
from logging import debug, info, error
|
||||
import warnings
|
||||
from twisted.internet import protocol,defer
|
||||
from twisted.internet import protocol, defer
|
||||
from twisted.words.protocols.jabber import error as jab_error
|
||||
from twisted.cred import portal,checkers,credentials
|
||||
from twisted.cred import portal, checkers, credentials
|
||||
from twisted.cred import error as cred_error
|
||||
from twisted.mail import smtp
|
||||
from twisted.python import failure
|
||||
from email.parser import Parser
|
||||
from email.utils import parseaddr
|
||||
from twisted.mail.imap4 import LOGINCredentials, PLAINCredentials
|
||||
import os,os.path
|
||||
import os
|
||||
from twisted.internet import reactor
|
||||
import sys
|
||||
|
||||
|
||||
from zope.interface import implements
|
||||
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "SMTP server Plugin",
|
||||
"import_name": "SMTP",
|
||||
"type": "Misc",
|
||||
"protocols": [],
|
||||
"dependencies": ["Maildir"],
|
||||
"main": "SMTP_server",
|
||||
"handler": "no",
|
||||
"description": _("""Create a SMTP server that you can use to send your "normal" type messages""")
|
||||
"name": "SMTP server Plugin",
|
||||
"import_name": "SMTP",
|
||||
"type": "Misc",
|
||||
"protocols": [],
|
||||
"dependencies": ["Maildir"],
|
||||
"main": "SMTP_server",
|
||||
"handler": "no",
|
||||
"description": _("""Create a SMTP server that you can use to send your "normal" type messages""")
|
||||
}
|
||||
|
||||
|
||||
class SMTP_server(object):
|
||||
|
||||
params = """
|
||||
|
|
@ -74,13 +73,14 @@ class SMTP_server(object):
|
|||
self.server_factory = SmtpServerFactory(self.host)
|
||||
reactor.listenTCP(port, self.server_factory)
|
||||
|
||||
|
||||
class SatSmtpMessage(object):
|
||||
implements(smtp.IMessage)
|
||||
|
||||
def __init__(self, host, profile):
|
||||
self.host=host
|
||||
self.profile=profile
|
||||
self.message=[]
|
||||
self.host = host
|
||||
self.profile = profile
|
||||
self.message = []
|
||||
|
||||
def lineReceived(self, line):
|
||||
"""handle another line"""
|
||||
|
|
@ -90,25 +90,26 @@ class SatSmtpMessage(object):
|
|||
"""handle end of message"""
|
||||
mail = Parser().parsestr("\n".join(self.message))
|
||||
try:
|
||||
self.host.sendMessage(parseaddr(mail['to'].decode('utf-8','replace'))[1], mail.get_payload().decode('utf-8','replace'), #TODO: manage other charsets
|
||||
subject=mail['subject'].decode('utf-8','replace'), mess_type='normal', profile_key=self.profile)
|
||||
self.host.sendMessage(parseaddr(mail['to'].decode('utf-8', 'replace'))[1], mail.get_payload().decode('utf-8', 'replace'), # TODO: manage other charsets
|
||||
subject=mail['subject'].decode('utf-8', 'replace'), mess_type='normal', profile_key=self.profile)
|
||||
except:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
error(_("Can't send message: %s") % exc_value) #The email is invalid or incorreclty parsed
|
||||
error(_("Can't send message: %s") % exc_value) # The email is invalid or incorreclty parsed
|
||||
return defer.fail()
|
||||
self.message=None
|
||||
self.message = None
|
||||
return defer.succeed(None)
|
||||
|
||||
def connectionLost(self):
|
||||
"""handle message truncated"""
|
||||
raise smtp.SMTPError
|
||||
|
||||
|
||||
class SatSmtpDelivery(object):
|
||||
implements(smtp.IMessageDelivery)
|
||||
|
||||
def __init__(self, host, profile):
|
||||
self.host=host
|
||||
self.profile=profile
|
||||
self.host = host
|
||||
self.profile = profile
|
||||
|
||||
def receivedHeader(self, helo, origin, recipients):
|
||||
"""
|
||||
|
|
@ -144,18 +145,20 @@ class SatSmtpDelivery(object):
|
|||
"""
|
||||
return origin
|
||||
|
||||
|
||||
class SmtpRealm(object):
|
||||
implements(portal.IRealm)
|
||||
|
||||
def __init__(self,host):
|
||||
def __init__(self, host):
|
||||
self.host = host
|
||||
|
||||
def requestAvatar(self, avatarID, mind, *interfaces):
|
||||
debug('requestAvatar')
|
||||
profile=avatarID.decode('utf-8')
|
||||
profile = avatarID.decode('utf-8')
|
||||
if smtp.IMessageDelivery not in interfaces:
|
||||
raise NotImplementedError
|
||||
return smtp.IMessageDelivery, SatSmtpDelivery(self.host,profile), lambda:None
|
||||
return smtp.IMessageDelivery, SatSmtpDelivery(self.host, profile), lambda: None
|
||||
|
||||
|
||||
class SatProfileCredentialChecker(object):
|
||||
"""
|
||||
|
|
@ -167,7 +170,6 @@ class SatProfileCredentialChecker(object):
|
|||
credentialInterfaces = (credentials.IUsernamePassword,
|
||||
credentials.IUsernameHashedPassword)
|
||||
|
||||
|
||||
def __init__(self, host):
|
||||
self.host = host
|
||||
|
||||
|
|
@ -182,26 +184,26 @@ class SatProfileCredentialChecker(object):
|
|||
if not credentials.username in profiles:
|
||||
return defer.fail(cred_error.UnauthorizedLogin())
|
||||
password = self.host.memory.getParamA("Password", "Connection", profile_key=credentials.username)
|
||||
return defer.maybeDeferred(
|
||||
credentials.checkPassword,
|
||||
password).addCallback(
|
||||
self._cbPasswordMatch, credentials.username)
|
||||
return defer.maybeDeferred(credentials.checkPassword,
|
||||
password).addCallback(self._cbPasswordMatch,
|
||||
credentials.username)
|
||||
|
||||
|
||||
class SmtpServerFactory(smtp.SMTPFactory):
|
||||
|
||||
def __init__(self, host):
|
||||
self.protocol = smtp.ESMTP
|
||||
self.host=host
|
||||
self.host = host
|
||||
_portal = portal.Portal(SmtpRealm(self.host))
|
||||
_portal.registerChecker(SatProfileCredentialChecker(self.host))
|
||||
smtp.SMTPFactory.__init__(self, _portal)
|
||||
|
||||
def startedConnecting(self, connector):
|
||||
debug (_("SMTP server connection started"))
|
||||
debug(_("SMTP server connection started"))
|
||||
smtp.SMTPFactory.startedConnecting(self, connector)
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
debug (_("SMTP server connection lost (reason: %s)"), reason)
|
||||
debug(_("SMTP server connection lost (reason: %s)"), reason)
|
||||
smtp.SMTPFactory.clientConnectionLost(self, connector, reason)
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
|
|
@ -209,4 +211,3 @@ class SmtpServerFactory(smtp.SMTPFactory):
|
|||
# add the challengers from imap4, more secure and complicated challengers are available
|
||||
p.challengers = {"LOGIN": LOGINCredentials, "PLAIN": PLAINCredentials}
|
||||
return p
|
||||
|
||||
|
|
|
|||
|
|
@ -43,14 +43,14 @@ CG_TAG = 'card_game'
|
|||
CG_REQUEST = MESSAGE + '/' + CG_TAG + '[@xmlns="' + NS_CG + '"]'
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "Tarot cards plugin",
|
||||
"import_name": "Tarot",
|
||||
"type": "Misc",
|
||||
"protocols": [],
|
||||
"dependencies": ["XEP-0045", "XEP-0249"],
|
||||
"main": "Tarot",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of Tarot card game""")
|
||||
"name": "Tarot cards plugin",
|
||||
"import_name": "Tarot",
|
||||
"type": "Misc",
|
||||
"protocols": [],
|
||||
"dependencies": ["XEP-0045", "XEP-0249"],
|
||||
"main": "Tarot",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of Tarot card game""")
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -59,33 +59,33 @@ class Tarot(object):
|
|||
def __init__(self, host):
|
||||
info(_("Plugin Tarot initialization"))
|
||||
self.host = host
|
||||
self.games={}
|
||||
self.waiting_inv = {} #Invitation waiting for people to join to launch a game
|
||||
self.games = {}
|
||||
self.waiting_inv = {} # Invitation waiting for people to join to launch a game
|
||||
self.contrats = [_('Passe'), _('Petite'), _('Garde'), _('Garde Sans'), _('Garde Contre')]
|
||||
host.bridge.addMethod("tarotGameLaunch", ".plugin", in_sign='ass', out_sign='', method=self.launchGame) #args: room_jid, players, profile
|
||||
host.bridge.addMethod("tarotGameCreate", ".plugin", in_sign='sass', out_sign='', method=self.createGame) #args: room_jid, players, profile
|
||||
host.bridge.addMethod("tarotGameReady", ".plugin", in_sign='sss', out_sign='', method=self.newPlayerReady) #args: player, referee, profile
|
||||
host.bridge.addMethod("tarotGameContratChoosed", ".plugin", in_sign='ssss', out_sign='', method=self.contratChoosed) #args: player, referee, contrat, profile
|
||||
host.bridge.addMethod("tarotGamePlayCards", ".plugin", in_sign='ssa(ss)s', out_sign='', method=self.play_cards) #args: player, referee, cards, profile
|
||||
host.bridge.addSignal("tarotGameStarted", ".plugin", signature='ssass') #args: room_jid, referee, players, profile
|
||||
host.bridge.addSignal("tarotGameNew", ".plugin", signature='sa(ss)s') #args: room_jid, hand, profile
|
||||
host.bridge.addSignal("tarotGameChooseContrat", ".plugin", signature='sss') #args: room_jid, xml_data, profile
|
||||
host.bridge.addSignal("tarotGameShowCards", ".plugin", signature='ssa(ss)a{ss}s') #args: room_jid, type ["chien", "poignée",...], cards, data[dict], profile
|
||||
host.bridge.addSignal("tarotGameCardsPlayed", ".plugin", signature='ssa(ss)s') #args: room_jid, player, type ["chien", "poignée",...], cards, data[dict], profile
|
||||
host.bridge.addSignal("tarotGameYourTurn", ".plugin", signature='ss') #args: room_jid, profile
|
||||
host.bridge.addSignal("tarotGameScore", ".plugin", signature='ssasass') #args: room_jid, xml_data, winners (list of nicks), loosers (list of nicks), profile
|
||||
host.bridge.addSignal("tarotGameInvalidCards", ".plugin", signature='ssa(ss)a(ss)s') #args: room_jid, game phase, played_cards, invalid_cards, profile
|
||||
host.bridge.addMethod("tarotGameLaunch", ".plugin", in_sign='ass', out_sign='', method=self.launchGame) # args: room_jid, players, profile
|
||||
host.bridge.addMethod("tarotGameCreate", ".plugin", in_sign='sass', out_sign='', method=self.createGame) # args: room_jid, players, profile
|
||||
host.bridge.addMethod("tarotGameReady", ".plugin", in_sign='sss', out_sign='', method=self.newPlayerReady) # args: player, referee, profile
|
||||
host.bridge.addMethod("tarotGameContratChoosed", ".plugin", in_sign='ssss', out_sign='', method=self.contratChoosed) # args: player, referee, contrat, profile
|
||||
host.bridge.addMethod("tarotGamePlayCards", ".plugin", in_sign='ssa(ss)s', out_sign='', method=self.play_cards) # args: player, referee, cards, profile
|
||||
host.bridge.addSignal("tarotGameStarted", ".plugin", signature='ssass') # args: room_jid, referee, players, profile
|
||||
host.bridge.addSignal("tarotGameNew", ".plugin", signature='sa(ss)s') # args: room_jid, hand, profile
|
||||
host.bridge.addSignal("tarotGameChooseContrat", ".plugin", signature='sss') # args: room_jid, xml_data, profile
|
||||
host.bridge.addSignal("tarotGameShowCards", ".plugin", signature='ssa(ss)a{ss}s') # args: room_jid, type ["chien", "poignée",...], cards, data[dict], profile
|
||||
host.bridge.addSignal("tarotGameCardsPlayed", ".plugin", signature='ssa(ss)s') # args: room_jid, player, type ["chien", "poignée",...], cards, data[dict], profile
|
||||
host.bridge.addSignal("tarotGameYourTurn", ".plugin", signature='ss') # args: room_jid, profile
|
||||
host.bridge.addSignal("tarotGameScore", ".plugin", signature='ssasass') # args: room_jid, xml_data, winners (list of nicks), loosers (list of nicks), profile
|
||||
host.bridge.addSignal("tarotGameInvalidCards", ".plugin", signature='ssa(ss)a(ss)s') # args: room_jid, game phase, played_cards, invalid_cards, profile
|
||||
host.trigger.add("MUC user joined", self.userJoinedTrigger)
|
||||
self.deck_ordered = []
|
||||
for value in ['excuse']+map(str,range(1,22)):
|
||||
self.deck_ordered.append(TarotCard(("atout",value)))
|
||||
for value in ['excuse'] + map(str, range(1, 22)):
|
||||
self.deck_ordered.append(TarotCard(("atout", value)))
|
||||
for suit in ["pique", "coeur", "carreau", "trefle"]:
|
||||
for value in map(str,range(1,11))+["valet","cavalier","dame","roi"]:
|
||||
for value in map(str, range(1, 11)) + ["valet", "cavalier", "dame", "roi"]:
|
||||
self.deck_ordered.append(TarotCard((suit, value)))
|
||||
|
||||
def createGameElt(self, to_jid, type="normal"):
|
||||
type = "normal" if to_jid.resource else "groupchat"
|
||||
elt = domish.Element((None,'message'))
|
||||
elt = domish.Element((None, 'message'))
|
||||
elt["to"] = to_jid.full()
|
||||
elt["type"] = type
|
||||
elt.addElement((NS_CG, CG_TAG))
|
||||
|
|
@ -93,9 +93,9 @@ class Tarot(object):
|
|||
|
||||
def __card_list_to_xml(self, cards_list, elt_name):
|
||||
"""Convert a card list to domish element"""
|
||||
cards_list_elt = domish.Element((None,elt_name))
|
||||
cards_list_elt = domish.Element((None, elt_name))
|
||||
for card in cards_list:
|
||||
card_elt = domish.Element((None,'card'))
|
||||
card_elt = domish.Element((None, 'card'))
|
||||
card_elt['suit'] = card.suit
|
||||
card_elt['value'] = card.value
|
||||
cards_list_elt.addChild(card_elt)
|
||||
|
|
@ -108,22 +108,21 @@ class Tarot(object):
|
|||
cards_list.append((card['suit'], card['value']))
|
||||
return cards_list
|
||||
|
||||
|
||||
def __create_started_elt(self, players):
|
||||
"""Create a game_started domish element"""
|
||||
started_elt = domish.Element((None,'started'))
|
||||
started_elt = domish.Element((None, 'started'))
|
||||
idx = 0
|
||||
for player in players:
|
||||
player_elt = domish.Element((None,'player'))
|
||||
player_elt = domish.Element((None, 'player'))
|
||||
player_elt.addContent(player)
|
||||
player_elt['index'] = str(idx)
|
||||
idx+=1
|
||||
idx += 1
|
||||
started_elt.addChild(player_elt)
|
||||
return started_elt
|
||||
|
||||
def __ask_contrat(self):
|
||||
"""Create a element for asking contrat"""
|
||||
contrat_elt = domish.Element((None,'contrat'))
|
||||
contrat_elt = domish.Element((None, 'contrat'))
|
||||
form = data_form.Form('form', title=_('contrat selection'))
|
||||
field = data_form.Field('list-single', 'contrat', options=map(data_form.Option, self.contrats), required=True)
|
||||
form.addField(field)
|
||||
|
|
@ -136,18 +135,18 @@ class Tarot(object):
|
|||
@param winners: list of unicode nicks of winners
|
||||
@param loosers: list of unicode nicks of loosers"""
|
||||
|
||||
score_elt = domish.Element((None,'score'))
|
||||
score_elt = domish.Element((None, 'score'))
|
||||
form = data_form.Form('form', title=_('scores'))
|
||||
for line in scores.split('\n'):
|
||||
field = data_form.Field('fixed', value = line)
|
||||
field = data_form.Field('fixed', value=line)
|
||||
form.addField(field)
|
||||
score_elt.addChild(form.toElement())
|
||||
for winner in winners:
|
||||
winner_elt = domish.Element((None,'winner'))
|
||||
winner_elt = domish.Element((None, 'winner'))
|
||||
winner_elt.addContent(winner)
|
||||
score_elt.addChild(winner_elt)
|
||||
for looser in loosers:
|
||||
looser_elt = domish.Element((None,'looser'))
|
||||
looser_elt = domish.Element((None, 'looser'))
|
||||
looser_elt.addContent(looser)
|
||||
score_elt.addChild(looser_elt)
|
||||
return score_elt
|
||||
|
|
@ -156,7 +155,7 @@ class Tarot(object):
|
|||
"""Create a element for invalid_cards error
|
||||
@param list_cards: list of Card
|
||||
@param game_phase: phase of the game ['ecart', 'play']"""
|
||||
error_elt = domish.Element((None,'error'))
|
||||
error_elt = domish.Element((None, 'error'))
|
||||
played_elt = self.__card_list_to_xml(played_cards, 'played')
|
||||
invalid_elt = self.__card_list_to_xml(invalid_cards, 'invalid')
|
||||
error_elt['type'] = 'invalid_cards'
|
||||
|
|
@ -165,7 +164,7 @@ class Tarot(object):
|
|||
error_elt.addChild(invalid_elt)
|
||||
return error_elt
|
||||
|
||||
def __next_player(self, game_data, next_pl = None):
|
||||
def __next_player(self, game_data, next_pl=None):
|
||||
"""Increment player number & return player name
|
||||
@param next_pl: if given, then next_player is forced to this one
|
||||
"""
|
||||
|
|
@ -189,7 +188,7 @@ class Tarot(object):
|
|||
card = players_data[player]['played']
|
||||
if card.value == "excuse":
|
||||
continue
|
||||
if suit_asked == None:
|
||||
if suit_asked is None:
|
||||
suit_asked = card.suit
|
||||
if (card.suit == suit_asked or card.suit == "atout") and card > strongest:
|
||||
strongest = card
|
||||
|
|
@ -204,7 +203,7 @@ class Tarot(object):
|
|||
@param winner: nick of the trick winner"""
|
||||
#TODO: manage the case where excuse is played on the last trick (and lost)
|
||||
players_data = game_data['players_data']
|
||||
excuse = TarotCard(("atout","excuse"))
|
||||
excuse = TarotCard(("atout", "excuse"))
|
||||
|
||||
#we first check if the Excuse was already played
|
||||
#and if somebody is waiting for a card
|
||||
|
|
@ -218,7 +217,7 @@ class Tarot(object):
|
|||
pl_waiting = players_data[player]['wait_for_low']
|
||||
played.remove(card)
|
||||
players_data[pl_waiting]['levees'].append(card)
|
||||
debug (_('Player %(excuse_owner)s give %(card_waited)s to %(player_waiting)s for Excuse compensation') % {"excuse_owner":player, "card_waited": card, "player_waiting":pl_waiting})
|
||||
debug(_('Player %(excuse_owner)s give %(card_waited)s to %(player_waiting)s for Excuse compensation') % {"excuse_owner": player, "card_waited": card, "player_waiting": pl_waiting})
|
||||
return
|
||||
return
|
||||
|
||||
|
|
@ -226,14 +225,14 @@ class Tarot(object):
|
|||
#the Excuse is not on the table, nothing to do
|
||||
return
|
||||
|
||||
excuse_player = None #Who has played the Excuse ?
|
||||
excuse_player = None # Who has played the Excuse ?
|
||||
for player in game_data['players']:
|
||||
if players_data[player]['played'] == excuse:
|
||||
excuse_player = player
|
||||
break
|
||||
|
||||
if excuse_player == winner:
|
||||
return #the excuse player win the trick, nothing to do
|
||||
return # the excuse player win the trick, nothing to do
|
||||
|
||||
#first we remove the excuse from played cards
|
||||
played.remove(excuse)
|
||||
|
|
@ -244,18 +243,17 @@ class Tarot(object):
|
|||
low_card = None
|
||||
#We look backward in cards won by the Excuse owner to
|
||||
#find a low value card
|
||||
for card_idx in range(len(owner_levees)-1, -1, -1):
|
||||
for card_idx in range(len(owner_levees) - 1, -1, -1):
|
||||
if owner_levees[card_idx].points == 0.5:
|
||||
low_card = owner_levees[card_idx]
|
||||
del owner_levees[card_idx]
|
||||
players_data[winner]['levees'].append(low_card)
|
||||
debug (_('Player %(excuse_owner)s give %(card_waited)s to %(player_waiting)s for Excuse compensation') % {"excuse_owner":excuse_player, "card_waited": low_card, "player_waiting":winner})
|
||||
debug(_('Player %(excuse_owner)s give %(card_waited)s to %(player_waiting)s for Excuse compensation') % {"excuse_owner": excuse_player, "card_waited": low_card, "player_waiting": winner})
|
||||
break
|
||||
if not low_card: #The player has no low card yet
|
||||
if not low_card: # The player has no low card yet
|
||||
#TODO: manage case when player never win a trick with low card
|
||||
players_data[excuse_player]['wait_for_low'] = winner
|
||||
debug(_("%(excuse_owner)s keep the Excuse but has not card to give, %(winner)s is waiting for one") % {'excuse_owner':excuse_player, 'winner':winner})
|
||||
|
||||
debug(_("%(excuse_owner)s keep the Excuse but has not card to give, %(winner)s is waiting for one") % {'excuse_owner': excuse_player, 'winner': winner})
|
||||
|
||||
def __draw_game(self, game_data):
|
||||
"""The game is draw, no score change
|
||||
|
|
@ -263,14 +261,13 @@ class Tarot(object):
|
|||
@return: tuple with (string victory message, list of winners, list of loosers)"""
|
||||
players_data = game_data['players_data']
|
||||
scores_str = _('Draw game')
|
||||
scores_str+='\n'
|
||||
scores_str += '\n'
|
||||
for player in game_data['players']:
|
||||
scores_str+=_("\n--\n%(player)s:\nscore for this game ==> %(score_game)i\ntotal score ==> %(total_score)i") % {'player':player, 'score_game':0, 'total_score': players_data[player]['score']}
|
||||
scores_str += _("\n--\n%(player)s:\nscore for this game ==> %(score_game)i\ntotal score ==> %(total_score)i") % {'player': player, 'score_game': 0, 'total_score': players_data[player]['score']}
|
||||
debug(scores_str)
|
||||
|
||||
return (scores_str, [], [])
|
||||
|
||||
|
||||
def __calculate_scores(self, game_data):
|
||||
"""The game is finished, time to know who won :)
|
||||
@param game_data: data of the game
|
||||
|
|
@ -282,7 +279,7 @@ class Tarot(object):
|
|||
bouts = []
|
||||
for card in levees:
|
||||
if card.bout:
|
||||
nb_bouts +=1
|
||||
nb_bouts += 1
|
||||
bouts.append(card.value)
|
||||
score += card.points
|
||||
|
||||
|
|
@ -292,10 +289,10 @@ class Tarot(object):
|
|||
defenseurs.remove(game_data['attaquant'])
|
||||
for defenseur in defenseurs:
|
||||
for card in players_data[defenseur]['levees']:
|
||||
check_score+=card.points
|
||||
check_score += card.points
|
||||
if game_data['contrat'] == "Garde Contre":
|
||||
for card in game_data['chien']:
|
||||
check_score+=card.points
|
||||
check_score += card.points
|
||||
assert (score + check_score == 91)
|
||||
|
||||
point_limit = None
|
||||
|
|
@ -329,16 +326,16 @@ class Tarot(object):
|
|||
#TODO: adjust this for 3 and 5 players variants
|
||||
#TODO: manage bonuses (petit au bout, poignée, chelem)
|
||||
player_score[player] = points_defenseur if player != game_data['attaquant'] else points_defenseur * -3
|
||||
players_data[player]['score'] += player_score[player] #we add score of this game to the global score
|
||||
players_data[player]['score'] += player_score[player] # we add score of this game to the global score
|
||||
if player_score[player] > 0:
|
||||
winners.append(player)
|
||||
else:
|
||||
loosers.append(player)
|
||||
|
||||
scores_str = _('The attacker (%(attaquant)s) makes %(points)i and needs to make %(point_limit)i (%(nb_bouts)s oulder%(plural)s%(separator)s%(bouts)s): he %(victory)s') % {'attaquant':game_data['attaquant'], 'points':score, 'point_limit':point_limit, 'nb_bouts': nb_bouts, 'plural': 's' if nb_bouts>1 else '', 'separator':': ' if nb_bouts != 0 else '', 'bouts':','.join(map(str,bouts)), 'victory': 'win' if victory else 'loose'}
|
||||
scores_str+='\n'
|
||||
scores_str = _('The attacker (%(attaquant)s) makes %(points)i and needs to make %(point_limit)i (%(nb_bouts)s oulder%(plural)s%(separator)s%(bouts)s): he %(victory)s') % {'attaquant': game_data['attaquant'], 'points': score, 'point_limit': point_limit, 'nb_bouts': nb_bouts, 'plural': 's' if nb_bouts > 1 else '', 'separator': ': ' if nb_bouts != 0 else '', 'bouts': ','.join(map(str, bouts)), 'victory': 'win' if victory else 'loose'}
|
||||
scores_str += '\n'
|
||||
for player in game_data['players']:
|
||||
scores_str+=_("\n--\n%(player)s:\nscore for this game ==> %(score_game)i\ntotal score ==> %(total_score)i") % {'player':player, 'score_game':player_score[player], 'total_score': players_data[player]['score']}
|
||||
scores_str += _("\n--\n%(player)s:\nscore for this game ==> %(score_game)i\ntotal score ==> %(total_score)i") % {'player': player, 'score_game': player_score[player], 'total_score': players_data[player]['score']}
|
||||
debug(scores_str)
|
||||
|
||||
return (scores_str, winners, loosers)
|
||||
|
|
@ -351,7 +348,7 @@ class Tarot(object):
|
|||
forbidden_cards = []
|
||||
if game_data['stage'] == 'ecart':
|
||||
for card in cards:
|
||||
if card.bout or card.value=="roi":
|
||||
if card.bout or card.value == "roi":
|
||||
forbidden_cards.append(card)
|
||||
#TODO: manage case where atouts (trumps) are in the dog
|
||||
elif game_data['stage'] == 'play':
|
||||
|
|
@ -373,7 +370,7 @@ class Tarot(object):
|
|||
if played_card.suit == "atout" and played_card > biggest_atout:
|
||||
biggest_atout = played_card
|
||||
idx = (idx + 1) % len(players)
|
||||
has_suit = False #True if there is one card of the asked suit in the hand of the player
|
||||
has_suit = False # True if there is one card of the asked suit in the hand of the player
|
||||
has_atout = False
|
||||
biggest_hand_atout = None
|
||||
|
||||
|
|
@ -399,18 +396,16 @@ class Tarot(object):
|
|||
error(_('Internal error: unmanaged game stage'))
|
||||
return forbidden_cards
|
||||
|
||||
|
||||
def __start_play(self, room_jid, game_data, profile):
|
||||
"""Start the game (tell to the first player after dealer to play"""
|
||||
game_data['stage'] = "play"
|
||||
next_player_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(game_data['players']) #the player after the dealer start
|
||||
next_player_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(game_data['players']) # the player after the dealer start
|
||||
game_data['first_player'] = next_player = game_data['players'][next_player_idx]
|
||||
to_jid = jid.JID(room_jid.userhost()+"/"+next_player) #FIXME: gof:
|
||||
to_jid = jid.JID(room_jid.userhost() + "/" + next_player) # FIXME: gof:
|
||||
mess = self.createGameElt(to_jid)
|
||||
yourturn_elt = mess.firstChildElement().addElement('your_turn')
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
|
||||
|
||||
def userJoinedTrigger(self, room, user, profile):
|
||||
"""This trigger is used to check if we are waiting people in this room,
|
||||
and to create a game if everybody is here"""
|
||||
|
|
@ -435,8 +430,8 @@ class Tarot(object):
|
|||
def tarotRoomJoined(room):
|
||||
_room = room.occupantJID.userhostJID()
|
||||
for player in players:
|
||||
self.host.plugins["XEP-0249"].invite(jid.JID(player), room.occupantJID.userhostJID(), {"game":"Tarot"}, profile)
|
||||
self.waiting_inv[_room] = (time(), players) #TODO: remove invitation waiting for too long, using the time data
|
||||
self.host.plugins["XEP-0249"].invite(jid.JID(player), room.occupantJID.userhostJID(), {"game": "Tarot"}, profile)
|
||||
self.waiting_inv[_room] = (time(), players) # TODO: remove invitation waiting for too long, using the time data
|
||||
|
||||
def after_init(ignore):
|
||||
room_name = "sat_tarot_%s" % self.host.plugins["XEP-0045"].getUniqueName(profile_key)
|
||||
|
|
@ -464,36 +459,33 @@ class Tarot(object):
|
|||
return
|
||||
client.client_initialized.addCallback(after_init)
|
||||
|
||||
|
||||
|
||||
|
||||
def createGame(self, room_jid_param, players, profile_key='@NONE@'):
|
||||
"""Create a new game
|
||||
@param room_jid_param: jid of the room
|
||||
@param players: list of players nick (nick must exist in the room)
|
||||
@param profile_key: %(doc_profile_key)s"""
|
||||
debug (_("Creating Tarot game"))
|
||||
debug(_("Creating Tarot game"))
|
||||
room_jid = jid.JID(room_jid_param)
|
||||
profile = self.host.memory.getProfileName(profile_key)
|
||||
if not profile:
|
||||
error (_("profile %s is unknown") % profile_key)
|
||||
error(_("profile %s is unknown") % profile_key)
|
||||
return
|
||||
if self.games.has_key(room_jid):
|
||||
warning (_("Tarot game already started in room %s") % room_jid.userhost())
|
||||
if room_jid in self.games:
|
||||
warning(_("Tarot game already started in room %s") % room_jid.userhost())
|
||||
else:
|
||||
room_nick = self.host.plugins["XEP-0045"].getRoomNick(room_jid.userhost(), profile)
|
||||
if not room_nick:
|
||||
error ('Internal error')
|
||||
error('Internal error')
|
||||
return
|
||||
referee = room_jid.userhost() + '/' + room_nick
|
||||
status = {}
|
||||
players_data = {}
|
||||
for player in players:
|
||||
players_data[player] = {'score':0}
|
||||
players_data[player] = {'score': 0}
|
||||
status[player] = "init"
|
||||
self.games[room_jid.userhost()] = {'referee':referee, 'players':players, 'status':status, 'players_data':players_data, 'hand_size':18, 'init_player':0, 'current_player': None, 'contrat': None, 'stage': None}
|
||||
self.games[room_jid.userhost()] = {'referee': referee, 'players': players, 'status': status, 'players_data': players_data, 'hand_size': 18, 'init_player': 0, 'current_player': None, 'contrat': None, 'stage': None}
|
||||
for player in players:
|
||||
mess = self.createGameElt(jid.JID(room_jid.userhost()+'/'+player))
|
||||
mess = self.createGameElt(jid.JID(room_jid.userhost() + '/' + player))
|
||||
mess.firstChildElement().addChild(self.__create_started_elt(players))
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
|
||||
|
|
@ -501,9 +493,9 @@ class Tarot(object):
|
|||
"""Must be called when player is ready to start a new game"""
|
||||
profile = self.host.memory.getProfileName(profile_key)
|
||||
if not profile:
|
||||
error (_("profile %s is unknown") % profile_key)
|
||||
error(_("profile %s is unknown") % profile_key)
|
||||
return
|
||||
debug ('new player ready: %s' % profile)
|
||||
debug('new player ready: %s' % profile)
|
||||
mess = self.createGameElt(jid.JID(referee))
|
||||
ready_elt = mess.firstChildElement().addElement('player_ready')
|
||||
ready_elt['player'] = player
|
||||
|
|
@ -518,11 +510,11 @@ class Tarot(object):
|
|||
"""
|
||||
profile = self.host.memory.getProfileName(profile_key)
|
||||
if not profile:
|
||||
error (_("profile %s is unknown") % profile_key)
|
||||
error(_("profile %s is unknown") % profile_key)
|
||||
return
|
||||
debug (_('contrat [%(contrat)s] choosed by %(profile)s') % {'contrat':contrat, 'profile':profile})
|
||||
debug(_('contrat [%(contrat)s] choosed by %(profile)s') % {'contrat': contrat, 'profile': profile})
|
||||
mess = self.createGameElt(jid.JID(referee))
|
||||
contrat_elt = mess.firstChildElement().addElement(('','contrat_choosed'), content=contrat)
|
||||
contrat_elt = mess.firstChildElement().addElement(('', 'contrat_choosed'), content=contrat)
|
||||
contrat_elt['player'] = player
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
|
||||
|
|
@ -535,9 +527,9 @@ class Tarot(object):
|
|||
"""
|
||||
profile = self.host.memory.getProfileName(profile_key)
|
||||
if not profile:
|
||||
error (_("profile %s is unknown") % profile_key)
|
||||
error(_("profile %s is unknown") % profile_key)
|
||||
return
|
||||
debug (_('Cards played by %(profile)s: [%(cards)s]') % {'profile':profile,'cards':cards})
|
||||
debug(_('Cards played by %(profile)s: [%(cards)s]') % {'profile': profile, 'cards': cards})
|
||||
mess = self.createGameElt(jid.JID(referee))
|
||||
playcard_elt = mess.firstChildElement().addChild(self.__card_list_to_xml(TarotCard.from_tuples(cards), 'cards_played'))
|
||||
playcard_elt['player'] = player
|
||||
|
|
@ -545,7 +537,7 @@ class Tarot(object):
|
|||
|
||||
def newGame(self, room_jid, profile):
|
||||
"""Launch a new round"""
|
||||
debug (_('new Tarot game'))
|
||||
debug(_('new Tarot game'))
|
||||
deck = self.deck_ordered[:]
|
||||
random.shuffle(deck)
|
||||
game_data = self.games[room_jid.userhost()]
|
||||
|
|
@ -553,7 +545,7 @@ class Tarot(object):
|
|||
players_data = game_data['players_data']
|
||||
current_player = game_data['current_player']
|
||||
game_data['stage'] = "init"
|
||||
game_data['first_player'] = None #first player for the current trick
|
||||
game_data['first_player'] = None # first player for the current trick
|
||||
game_data['contrat'] = None
|
||||
hand = game_data['hand'] = {}
|
||||
hand_size = game_data['hand_size']
|
||||
|
|
@ -565,23 +557,22 @@ class Tarot(object):
|
|||
del(deck[:])
|
||||
|
||||
for player in players:
|
||||
to_jid = jid.JID(room_jid.userhost()+"/"+player) #FIXME: gof:
|
||||
to_jid = jid.JID(room_jid.userhost() + "/" + player) # FIXME: gof:
|
||||
mess = self.createGameElt(to_jid)
|
||||
mess.firstChildElement().addChild(self.__card_list_to_xml(hand[player], 'hand'))
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
players_data[player]['contrat'] = None
|
||||
players_data[player]['levees'] = [] #cards won
|
||||
players_data[player]['played'] = None #card on the table
|
||||
players_data[player]['wait_for_low'] = None #Used when a player wait for a low card because of excuse
|
||||
players_data[player]['levees'] = [] # cards won
|
||||
players_data[player]['played'] = None # card on the table
|
||||
players_data[player]['wait_for_low'] = None # Used when a player wait for a low card because of excuse
|
||||
|
||||
pl_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(players) #the player after the dealer start
|
||||
pl_idx = game_data['current_player'] = (game_data['init_player'] + 1) % len(players) # the player after the dealer start
|
||||
player = players[pl_idx]
|
||||
to_jid = jid.JID(room_jid.userhost()+"/"+player) #FIXME: gof:
|
||||
to_jid = jid.JID(room_jid.userhost() + "/" + player) # FIXME: gof:
|
||||
mess = self.createGameElt(to_jid)
|
||||
mess.firstChildElement().addChild(self.__ask_contrat())
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
|
||||
|
||||
def card_game_cmd(self, mess_elt, profile):
|
||||
from_jid = jid.JID(mess_elt['from'])
|
||||
room_jid = jid.JID(from_jid.userhost())
|
||||
|
|
@ -591,25 +582,25 @@ class Tarot(object):
|
|||
|
||||
for elt in game_elt.elements():
|
||||
|
||||
if elt.name == 'started': #new game created
|
||||
if elt.name == 'started': # new game created
|
||||
players = []
|
||||
for player in elt.elements():
|
||||
players.append(unicode(player))
|
||||
self.host.bridge.tarotGameStarted(room_jid.userhost(), from_jid.full(), players, profile)
|
||||
|
||||
elif elt.name == 'player_ready': #ready to play
|
||||
elif elt.name == 'player_ready': # ready to play
|
||||
player = elt['player']
|
||||
status = self.games[room_jid.userhost()]['status']
|
||||
nb_players = len(self.games[room_jid.userhost()]['players'])
|
||||
status[player] = 'ready'
|
||||
debug (_('Player %(player)s is ready to start [status: %(status)s]') % {'player':player, 'status':status})
|
||||
if status.values().count('ready') == nb_players: #everybody is ready, we can start the game
|
||||
debug(_('Player %(player)s is ready to start [status: %(status)s]') % {'player': player, 'status': status})
|
||||
if status.values().count('ready') == nb_players: # everybody is ready, we can start the game
|
||||
self.newGame(room_jid, profile)
|
||||
|
||||
elif elt.name == 'hand': #a new hand has been received
|
||||
elif elt.name == 'hand': # a new hand has been received
|
||||
self.host.bridge.tarotGameNew(room_jid.userhost(), self.__xml_to_list(elt), profile)
|
||||
|
||||
elif elt.name == 'contrat': #it's time to choose contrat
|
||||
elif elt.name == 'contrat': # it's time to choose contrat
|
||||
form = data_form.Form.fromElement(elt.firstChildElement())
|
||||
xml_data = dataForm2xml(form)
|
||||
self.host.bridge.tarotGameChooseContrat(room_jid.userhost(), xml_data, profile)
|
||||
|
|
@ -623,7 +614,7 @@ class Tarot(object):
|
|||
if contrats.count(None):
|
||||
#not everybody has choosed his contrat, it's next one turn
|
||||
player = self.__next_player(game_data)
|
||||
to_jid = jid.JID(room_jid.userhost()+"/"+player) #FIXME: gof:
|
||||
to_jid = jid.JID(room_jid.userhost() + "/" + player) # FIXME: gof:
|
||||
mess = self.createGameElt(to_jid)
|
||||
mess.firstChildElement().addChild(self.__ask_contrat())
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
|
|
@ -642,11 +633,11 @@ class Tarot(object):
|
|||
mess = self.createGameElt(to_jid)
|
||||
mess.firstChildElement().addChild(self.__give_scores(*self.__draw_game(game_data)))
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
game_data['init_player'] = (game_data['init_player'] + 1) % len(game_data['players']) #we change the dealer
|
||||
game_data['init_player'] = (game_data['init_player'] + 1) % len(game_data['players']) # we change the dealer
|
||||
for player in game_data['players']:
|
||||
game_data['status'][player] = "init"
|
||||
return
|
||||
debug (_("%(player)s win the bid with %(contrat)s") % {'player':best_contrat[0],'contrat':best_contrat[1]})
|
||||
debug(_("%(player)s win the bid with %(contrat)s") % {'player': best_contrat[0], 'contrat': best_contrat[1]})
|
||||
game_data['contrat'] = best_contrat[1]
|
||||
|
||||
if game_data['contrat'] == "Garde Sans" or game_data['contrat'] == "Garde Contre":
|
||||
|
|
@ -654,7 +645,7 @@ class Tarot(object):
|
|||
game_data['attaquant'] = best_contrat[0]
|
||||
else:
|
||||
#Time to show the chien to everybody
|
||||
to_jid = jid.JID(room_jid.userhost()) #FIXME: gof:
|
||||
to_jid = jid.JID(room_jid.userhost()) # FIXME: gof:
|
||||
mess = self.createGameElt(to_jid)
|
||||
chien_elt = mess.firstChildElement().addChild(self.__card_list_to_xml(game_data['chien'], 'chien'))
|
||||
chien_elt['attaquant'] = best_contrat[0]
|
||||
|
|
@ -668,10 +659,9 @@ class Tarot(object):
|
|||
players_data[best_contrat[0]]['levees'].extend(game_data['chien'])
|
||||
del game_data['chien'][:]
|
||||
|
||||
|
||||
elif elt.name == 'chien': #we have received the chien
|
||||
debug (_("tarot: chien received"))
|
||||
data = {"attaquant":elt['attaquant']}
|
||||
elif elt.name == 'chien': # we have received the chien
|
||||
debug(_("tarot: chien received"))
|
||||
data = {"attaquant": elt['attaquant']}
|
||||
game_data['stage'] = "ecart"
|
||||
game_data['attaquant'] = elt['attaquant']
|
||||
self.host.bridge.tarotGameShowCards(room_jid.userhost(), "chien", self.__xml_to_list(elt), data, profile)
|
||||
|
|
@ -679,18 +669,18 @@ class Tarot(object):
|
|||
elif elt.name == 'cards_played':
|
||||
if game_data['stage'] == "ecart":
|
||||
#TODO: show atouts (trumps) if player put some in écart
|
||||
assert (game_data['attaquant'] == elt['player']) #TODO: throw an xml error here
|
||||
assert (game_data['attaquant'] == elt['player']) # TODO: throw an xml error here
|
||||
list_cards = TarotCard.from_tuples(self.__xml_to_list(elt))
|
||||
#we now check validity of card
|
||||
invalid_cards = self.__invalid_cards(game_data, list_cards)
|
||||
if invalid_cards:
|
||||
mess = self.createGameElt(jid.JID(room_jid.userhost()+'/'+elt['player']))
|
||||
mess = self.createGameElt(jid.JID(room_jid.userhost() + '/' + elt['player']))
|
||||
mess.firstChildElement().addChild(self.__invalid_cards_elt(list_cards, invalid_cards, game_data['stage']))
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
return
|
||||
|
||||
#FIXME: gof: manage Garde Sans & Garde Contre cases
|
||||
players_data[elt['player']]['levees'].extend(list_cards) #we add the chien to attaquant's levées
|
||||
players_data[elt['player']]['levees'].extend(list_cards) # we add the chien to attaquant's levées
|
||||
for card in list_cards:
|
||||
game_data['hand'][elt['player']].remove(card)
|
||||
|
||||
|
|
@ -706,7 +696,7 @@ class Tarot(object):
|
|||
#we first check validity of card
|
||||
invalid_cards = self.__invalid_cards(game_data, cards)
|
||||
if invalid_cards:
|
||||
mess = self.createGameElt(jid.JID(room_jid.userhost()+'/'+current_player))
|
||||
mess = self.createGameElt(jid.JID(room_jid.userhost() + '/' + current_player))
|
||||
mess.firstChildElement().addChild(self.__invalid_cards_elt(cards, invalid_cards, game_data['stage']))
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
return
|
||||
|
|
@ -725,7 +715,7 @@ class Tarot(object):
|
|||
if all(played):
|
||||
#everybody has played
|
||||
winner = self.__winner(game_data)
|
||||
debug (_('The winner of this trick is %s') % winner)
|
||||
debug(_('The winner of this trick is %s') % winner)
|
||||
#the winner win the trick
|
||||
self.__excuse_hack(game_data, played, winner)
|
||||
players_data[elt['player']]['levees'].extend(played)
|
||||
|
|
@ -738,7 +728,7 @@ class Tarot(object):
|
|||
mess = self.createGameElt(to_jid)
|
||||
chien_elt = mess.firstChildElement().addChild(self.__give_scores(*self.__calculate_scores(game_data)))
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
game_data['init_player'] = (game_data['init_player'] + 1) % len(game_data['players']) #we change the dealer
|
||||
game_data['init_player'] = (game_data['init_player'] + 1) % len(game_data['players']) # we change the dealer
|
||||
for player in game_data['players']:
|
||||
game_data['status'][player] = "init"
|
||||
return
|
||||
|
|
@ -748,7 +738,7 @@ class Tarot(object):
|
|||
next_player = self.__next_player(game_data)
|
||||
|
||||
#finally, we tell to the next player to play
|
||||
to_jid = jid.JID(room_jid.userhost()+"/"+next_player)
|
||||
to_jid = jid.JID(room_jid.userhost() + "/" + next_player)
|
||||
mess = self.createGameElt(to_jid)
|
||||
yourturn_elt = mess.firstChildElement().addElement('your_turn')
|
||||
self.host.profiles[profile].xmlstream.send(mess)
|
||||
|
|
@ -757,7 +747,7 @@ class Tarot(object):
|
|||
self.host.bridge.tarotGameYourTurn(room_jid.userhost(), profile)
|
||||
|
||||
elif elt.name == 'score':
|
||||
form_elt = elt.elements(name='x',uri='jabber:x:data').next()
|
||||
form_elt = elt.elements(name='x', uri='jabber:x:data').next()
|
||||
winners = []
|
||||
loosers = []
|
||||
for winner in elt.elements(name='winner', uri=NS_CG):
|
||||
|
|
@ -769,16 +759,17 @@ class Tarot(object):
|
|||
self.host.bridge.tarotGameScore(room_jid.userhost(), xml_data, winners, loosers, profile)
|
||||
elif elt.name == 'error':
|
||||
if elt['type'] == 'invalid_cards':
|
||||
played_cards = self.__xml_to_list(elt.elements(name='played',uri=NS_CG).next())
|
||||
invalid_cards = self.__xml_to_list(elt.elements(name='invalid',uri=NS_CG).next())
|
||||
played_cards = self.__xml_to_list(elt.elements(name='played', uri=NS_CG).next())
|
||||
invalid_cards = self.__xml_to_list(elt.elements(name='invalid', uri=NS_CG).next())
|
||||
self.host.bridge.tarotGameInvalidCards(room_jid.userhost(), elt['phase'], played_cards, invalid_cards, profile)
|
||||
else:
|
||||
error (_('Unmanaged error type: %s') % elt['type'])
|
||||
error(_('Unmanaged error type: %s') % elt['type'])
|
||||
else:
|
||||
error (_('Unmanaged card game element: %s') % elt.name)
|
||||
error(_('Unmanaged card game element: %s') % elt.name)
|
||||
|
||||
def getHandler(self, profile):
|
||||
return CardGameHandler(self)
|
||||
return CardGameHandler(self)
|
||||
|
||||
|
||||
class CardGameHandler (XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
|
|
@ -788,11 +779,10 @@ class CardGameHandler (XMPPHandler):
|
|||
self.host = plugin_parent.host
|
||||
|
||||
def connectionInitialized(self):
|
||||
self.xmlstream.addObserver(CG_REQUEST, self.plugin_parent.card_game_cmd, profile = self.parent.profile)
|
||||
self.xmlstream.addObserver(CG_REQUEST, self.plugin_parent.card_game_cmd, profile=self.parent.profile)
|
||||
|
||||
def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
|
||||
return [disco.DiscoFeature(NS_CG)]
|
||||
|
||||
def getDiscoItems(self, requestor, target, nodeIdentifier=''):
|
||||
return []
|
||||
|
||||
|
|
|
|||
|
|
@ -23,16 +23,17 @@ from twisted.words.protocols.jabber import jid
|
|||
from logging import debug, info, warning, error
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "Text commands",
|
||||
"import_name": "text-commands",
|
||||
"type": "Misc",
|
||||
"protocols": [],
|
||||
"dependencies": ["XEP-0045", "EXP-PARROT"],
|
||||
"main": "TextCommands",
|
||||
"handler": "no",
|
||||
"description": _("""IRC like text commands""")
|
||||
"name": "Text commands",
|
||||
"import_name": "text-commands",
|
||||
"type": "Misc",
|
||||
"protocols": [],
|
||||
"dependencies": ["XEP-0045", "EXP-PARROT"],
|
||||
"main": "TextCommands",
|
||||
"handler": "no",
|
||||
"description": _("""IRC like text commands""")
|
||||
}
|
||||
|
||||
|
||||
class TextCommands(object):
|
||||
#FIXME: doc strings for commands have to be translatable
|
||||
# plugins need a dynamic translation system (translation
|
||||
|
|
@ -52,13 +53,13 @@ class TextCommands(object):
|
|||
if command.isalpha():
|
||||
# looks like an actual command, we try to call the corresponding method
|
||||
try:
|
||||
mess_data["unparsed"] = msg[1+len(command):] #part not yet parsed of the message
|
||||
return getattr(self,"cmd_%s" % command)(mess_data,profile)
|
||||
mess_data["unparsed"] = msg[1 + len(command):] # part not yet parsed of the message
|
||||
return getattr(self, "cmd_%s" % command)(mess_data, profile)
|
||||
except AttributeError:
|
||||
pass
|
||||
elif msg[0] == '\\': #we have escape char
|
||||
elif msg[0] == '\\': # we have escape char
|
||||
try:
|
||||
if msg[1] in ('/','\\'): # we have '\/' or '\\', we escape to '/' or '\'
|
||||
if msg[1] in ('/', '\\'): # we have '\/' or '\\', we escape to '/' or '\'
|
||||
mess_data["message"] = msg[1:]
|
||||
except IndexError:
|
||||
pass
|
||||
|
|
@ -72,9 +73,9 @@ class TextCommands(object):
|
|||
"""
|
||||
nb_arobas = arg.count('@')
|
||||
if nb_arobas == 1:
|
||||
if arg[-1] !='@':
|
||||
if arg[-1] != '@':
|
||||
return jid.JID(arg)
|
||||
return jid.JID(arg+service_jid)
|
||||
return jid.JID(arg + service_jid)
|
||||
return jid.JID(u"%s@%s" % (arg, service_jid))
|
||||
|
||||
def _feedBack(self, message, mess_data, profile):
|
||||
|
|
@ -99,7 +100,7 @@ class TextCommands(object):
|
|||
nick = mess_data["unparsed"].strip()
|
||||
room = mess_data["to"]
|
||||
|
||||
self.host.plugins["XEP-0045"].nick(room,nick,profile)
|
||||
self.host.plugins["XEP-0045"].nick(room, nick, profile)
|
||||
|
||||
return False
|
||||
|
||||
|
|
@ -134,7 +135,7 @@ class TextCommands(object):
|
|||
else:
|
||||
room = mess_data["to"]
|
||||
|
||||
self.host.plugins["XEP-0045"].leave(room,profile)
|
||||
self.host.plugins["XEP-0045"].leave(room, profile)
|
||||
|
||||
return False
|
||||
|
||||
|
|
@ -183,7 +184,7 @@ class TextCommands(object):
|
|||
self.host.plugins["EXP-PARROT"].addParrot(link_left_jid, link_right_jid, profile)
|
||||
self.host.plugins["EXP-PARROT"].addParrot(link_right_jid, link_left_jid, profile)
|
||||
|
||||
self._feedBack("Parrot mode activated for %s" % (unicode(link_left_jid),), mess_data, profile)
|
||||
self._feedBack("Parrot mode activated for %s" % (unicode(link_left_jid), ), mess_data, profile)
|
||||
|
||||
return False
|
||||
|
||||
|
|
@ -210,7 +211,7 @@ class TextCommands(object):
|
|||
|
||||
def cmd_help(self, mess_data, profile):
|
||||
"""show help on available commands"""
|
||||
commands=filter(lambda method: method.startswith('cmd_'), dir(self))
|
||||
commands = filter(lambda method: method.startswith('cmd_'), dir(self))
|
||||
longuest = max([len(command) for command in commands])
|
||||
help_cmds = []
|
||||
|
||||
|
|
@ -223,7 +224,5 @@ class TextCommands(object):
|
|||
spaces = (longuest - len(command)) * ' '
|
||||
help_cmds.append(" /%s: %s %s" % (command[4:], spaces, help_str))
|
||||
|
||||
help_mess = _(u"Text commands available:\n%s") % (u'\n'.join(help_cmds),)
|
||||
help_mess = _(u"Text commands available:\n%s") % (u'\n'.join(help_cmds), )
|
||||
self._feedBack(help_mess, mess_data, profile)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -24,24 +24,25 @@ from twisted.words.protocols.jabber.xmlstream import XmlStream
|
|||
from twisted.words.xish import domish
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "Raw XML log Plugin",
|
||||
"import_name": "XmlLog",
|
||||
"type": "Misc",
|
||||
"protocols": [],
|
||||
"dependencies": [],
|
||||
"main": "XmlLog",
|
||||
"handler": "no",
|
||||
"description": _("""Send raw XML logs to bridge""")
|
||||
"name": "Raw XML log Plugin",
|
||||
"import_name": "XmlLog",
|
||||
"type": "Misc",
|
||||
"protocols": [],
|
||||
"dependencies": [],
|
||||
"main": "XmlLog",
|
||||
"handler": "no",
|
||||
"description": _("""Send raw XML logs to bridge""")
|
||||
}
|
||||
|
||||
|
||||
class LoggingXmlStream(XmlStream):
|
||||
"""This class send the raw XML to the Bridge, for logging purpose"""
|
||||
|
||||
def send(self, obj):
|
||||
if isinstance(obj,basestring):
|
||||
log=unicode(obj)
|
||||
if isinstance(obj, basestring):
|
||||
log = unicode(obj)
|
||||
elif isinstance(obj, domish.Element):
|
||||
log=obj.toXml()
|
||||
log = obj.toXml()
|
||||
else:
|
||||
error(_('INTERNAL ERROR: Unmanaged XML type'))
|
||||
self._host.bridge.xmlLog("OUT", log, self._profile)
|
||||
|
|
@ -72,7 +73,7 @@ class XmlLog(object):
|
|||
host.memory.importParams(self.params)
|
||||
|
||||
#bridge
|
||||
host.bridge.addSignal("xmlLog", ".plugin", signature='sss') #args: direction("IN" or "OUT"), xml_data, profile
|
||||
host.bridge.addSignal("xmlLog", ".plugin", signature='sss') # args: direction("IN" or "OUT"), xml_data, profile
|
||||
|
||||
do_log = self.host.memory.getParamA("Xml log", "Debug")
|
||||
if do_log:
|
||||
|
|
|
|||
|
|
@ -35,15 +35,16 @@ from wokkel import disco, iwokkel, data_form
|
|||
NS_FEATURE_NEG = 'http://jabber.org/protocol/feature-neg'
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "XEP 0020 Plugin",
|
||||
"import_name": "XEP-0020",
|
||||
"type": "XEP",
|
||||
"protocols": ["XEP-0020"],
|
||||
"main": "XEP_0020",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of Feature Negotiation""")
|
||||
"name": "XEP 0020 Plugin",
|
||||
"import_name": "XEP-0020",
|
||||
"type": "XEP",
|
||||
"protocols": ["XEP-0020"],
|
||||
"main": "XEP_0020",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of Feature Negotiation""")
|
||||
}
|
||||
|
||||
|
||||
class XEP_0020(object):
|
||||
|
||||
def __init__(self, host):
|
||||
|
|
@ -67,7 +68,7 @@ class XEP_0020(object):
|
|||
for field in form.fields:
|
||||
values = form.fields[field].values
|
||||
result[field] = values[0] if values else None
|
||||
if len(values)>1:
|
||||
if len(values) > 1:
|
||||
warning(_("More than one value choosed for %s, keeping the first one") % field)
|
||||
return result
|
||||
|
||||
|
|
@ -92,7 +93,7 @@ class XEP_0020(object):
|
|||
feature_elt.addChild(x_form.toElement())
|
||||
return feature_elt
|
||||
|
||||
def proposeFeatures(self, options_dict, namespace = None):
|
||||
def proposeFeatures(self, options_dict, namespace=None):
|
||||
"""Build a feature element with options to propose
|
||||
@param options_dict: dict with feature as key and list of acceptable options as value
|
||||
@param namespace: feature namespace"""
|
||||
|
|
@ -104,6 +105,7 @@ class XEP_0020(object):
|
|||
feature_elt.addChild(x_form.toElement())
|
||||
return feature_elt
|
||||
|
||||
|
||||
class XEP_0020_handler(XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
|
||||
|
|
@ -112,4 +114,3 @@ class XEP_0020_handler(XMPPHandler):
|
|||
|
||||
def getDiscoItems(self, requestor, target, nodeIdentifier=''):
|
||||
return []
|
||||
|
||||
|
|
|
|||
|
|
@ -36,53 +36,55 @@ except ImportError:
|
|||
from wokkel.subprotocols import XMPPHandler
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "XEP 0045 Plugin",
|
||||
"import_name": "XEP-0045",
|
||||
"type": "XEP",
|
||||
"protocols": ["XEP-0045"],
|
||||
"dependencies": [],
|
||||
"main": "XEP_0045",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of Multi-User Chat""")
|
||||
"name": "XEP 0045 Plugin",
|
||||
"import_name": "XEP-0045",
|
||||
"type": "XEP",
|
||||
"protocols": ["XEP-0045"],
|
||||
"dependencies": [],
|
||||
"main": "XEP_0045",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of Multi-User Chat""")
|
||||
}
|
||||
|
||||
|
||||
class UnknownRoom(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class XEP_0045(object):
|
||||
|
||||
def __init__(self, host):
|
||||
info(_("Plugin XEP_0045 initialization"))
|
||||
self.host = host
|
||||
self.clients={}
|
||||
self.clients = {}
|
||||
host.bridge.addMethod("joinMUC", ".plugin", in_sign='ssa{ss}s', out_sign='', method=self._join)
|
||||
host.bridge.addMethod("mucNick", ".plugin", in_sign='sss', out_sign='', method=self.mucNick)
|
||||
host.bridge.addMethod("mucLeave", ".plugin", in_sign='ss', out_sign='', method=self.leave)
|
||||
host.bridge.addMethod("getRoomsJoined", ".plugin", in_sign='s', out_sign='a(sass)', method=self.getRoomsJoined)
|
||||
host.bridge.addMethod("getRoomsSubjects", ".plugin", in_sign='s', out_sign='a(ss)', method=self.getRoomsSubjects)
|
||||
host.bridge.addMethod("getUniqueRoomName", ".plugin", in_sign='s', out_sign='s', method=self.getUniqueName)
|
||||
host.bridge.addSignal("roomJoined", ".plugin", signature='sasss') #args: room_jid, room_nicks, user_nick, profile
|
||||
host.bridge.addSignal("roomLeft", ".plugin", signature='ss') #args: room_jid, profile
|
||||
host.bridge.addSignal("roomUserJoined", ".plugin", signature='ssa{ss}s') #args: room_jid, user_nick, user_data, profile
|
||||
host.bridge.addSignal("roomUserLeft", ".plugin", signature='ssa{ss}s') #args: room_jid, user_nick, user_data, profile
|
||||
host.bridge.addSignal("roomUserChangedNick", ".plugin", signature='ssss') #args: room_jid, old_nick, new_nick, profile
|
||||
host.bridge.addSignal("roomNewSubject", ".plugin", signature='sss') #args: room_jid, subject, profile
|
||||
|
||||
host.bridge.addSignal("roomJoined", ".plugin", signature='sasss') # args: room_jid, room_nicks, user_nick, profile
|
||||
host.bridge.addSignal("roomLeft", ".plugin", signature='ss') # args: room_jid, profile
|
||||
host.bridge.addSignal("roomUserJoined", ".plugin", signature='ssa{ss}s') # args: room_jid, user_nick, user_data, profile
|
||||
host.bridge.addSignal("roomUserLeft", ".plugin", signature='ssa{ss}s') # args: room_jid, user_nick, user_data, profile
|
||||
host.bridge.addSignal("roomUserChangedNick", ".plugin", signature='ssss') # args: room_jid, old_nick, new_nick, profile
|
||||
host.bridge.addSignal("roomNewSubject", ".plugin", signature='sss') # args: room_jid, subject, profile
|
||||
|
||||
def __check_profile(self, profile):
|
||||
"""check if profile is used and connected
|
||||
if profile known but disconnected, remove it from known profiles
|
||||
@param profile: profile to check
|
||||
@return: True if the profile is known and connected, else False"""
|
||||
if not profile or not self.clients.has_key(profile) or not self.host.isConnected(profile):
|
||||
error (_('Unknown or disconnected profile (%s)') % profile)
|
||||
if self.clients.has_key(profile):
|
||||
if not profile or profile not in self.clients or not self.host.isConnected(profile):
|
||||
error(_('Unknown or disconnected profile (%s)') % profile)
|
||||
if profile in self.clients:
|
||||
del self.clients[profile]
|
||||
return False
|
||||
return True
|
||||
|
||||
def __room_joined(self, room, profile):
|
||||
"""Called when the user is in the requested room"""
|
||||
|
||||
def _sendBridgeSignal(ignore=None):
|
||||
self.host.bridge.roomJoined(room.roomJID.userhost(), [user.nick for user in room.roster.values()], room.nick, profile)
|
||||
|
||||
|
|
@ -99,16 +101,15 @@ class XEP_0045(object):
|
|||
_sendBridgeSignal()
|
||||
return room
|
||||
|
||||
|
||||
def __err_joining_room(self, failure, room_jid, nick, history_options, password, profile):
|
||||
"""Called when something is going wrong when joining the room"""
|
||||
if failure.value.condition == 'conflict':
|
||||
# we have a nickname conflict, we try again with "_" suffixed to current nickname
|
||||
nick += '_'
|
||||
return self.clients[profile].join(room_jid, nick, history_options, password).addCallbacks(self.__room_joined, self.__err_joining_room, callbackKeywords={'profile':profile}, errbackArgs=[room_jid, nick, history_options, password, profile])
|
||||
return self.clients[profile].join(room_jid, nick, history_options, password).addCallbacks(self.__room_joined, self.__err_joining_room, callbackKeywords={'profile': profile}, errbackArgs=[room_jid, nick, history_options, password, profile])
|
||||
|
||||
mess = _("Error when joining the room")
|
||||
error (mess)
|
||||
error(mess)
|
||||
self.host.bridge.newAlert(mess, _("Group chat error"), "ERROR", profile)
|
||||
raise failure
|
||||
|
||||
|
|
@ -128,7 +129,7 @@ class XEP_0045(object):
|
|||
@profile_key: profile
|
||||
@return: nick or empty string in case of error"""
|
||||
profile = self.host.memory.getProfileName(profile_key)
|
||||
if not self.__check_profile(profile) or not self.clients[profile].joined_rooms.has_key(room_jid_s):
|
||||
if not self.__check_profile(profile) or room_jid_s not in self.clients[profile].joined_rooms:
|
||||
return ''
|
||||
return self.clients[profile].joined_rooms[room_jid_s].nick
|
||||
|
||||
|
|
@ -137,7 +138,7 @@ class XEP_0045(object):
|
|||
profile = self.host.memory.getProfileName(profile)
|
||||
if not self.__check_profile(profile):
|
||||
raise exceptions.UnknownProfileError("Unknown or disconnected profile")
|
||||
if not self.clients[profile].joined_rooms.has_key(room_jid.userhost()):
|
||||
if room_jid.userhost() not in self.clients[profile].joined_rooms:
|
||||
raise UnknownRoom("This room has not been joined")
|
||||
return self.clients[profile].joined_rooms[room_jid.userhost()].inRoster(nick)
|
||||
|
||||
|
|
@ -155,7 +156,7 @@ class XEP_0045(object):
|
|||
return uuid.uuid1()
|
||||
|
||||
def join(self, room_jid, nick, options, profile_key='@DEFAULT@'):
|
||||
def _errDeferred(exc_obj = Exception, txt='Error while joining room'):
|
||||
def _errDeferred(exc_obj=Exception, txt='Error while joining room'):
|
||||
d = defer.Deferred()
|
||||
d.errback(exc_obj(txt))
|
||||
return d
|
||||
|
|
@ -163,15 +164,15 @@ class XEP_0045(object):
|
|||
profile = self.host.memory.getProfileName(profile_key)
|
||||
if not self.__check_profile(profile):
|
||||
return _errDeferred()
|
||||
if self.clients[profile].joined_rooms.has_key(room_jid.userhost()):
|
||||
warning(_('%(profile)s is already in room %(room_jid)s') % {'profile':profile, 'room_jid':room_jid.userhost()})
|
||||
if room_jid.userhost() in self.clients[profile].joined_rooms:
|
||||
warning(_('%(profile)s is already in room %(room_jid)s') % {'profile': profile, 'room_jid': room_jid.userhost()})
|
||||
return _errDeferred()
|
||||
info (_("[%(profile)s] is joining room %(room)s with nick %(nick)s") % {'profile':profile,'room':room_jid.userhost(), 'nick':nick})
|
||||
info(_("[%(profile)s] is joining room %(room)s with nick %(nick)s") % {'profile': profile, 'room': room_jid.userhost(), 'nick': nick})
|
||||
|
||||
history_options = options["history"] == "True" if options.has_key("history") else None
|
||||
password = options["password"] if options.has_key("password") else None
|
||||
history_options = options["history"] == "True" if "history" in options else None
|
||||
password = options["password"] if "password" in options else None
|
||||
|
||||
return self.clients[profile].join(room_jid, nick, history_options, password).addCallbacks(self.__room_joined, self.__err_joining_room, callbackKeywords={'profile':profile}, errbackArgs=[room_jid, nick, history_options, password, profile])
|
||||
return self.clients[profile].join(room_jid, nick, history_options, password).addCallbacks(self.__room_joined, self.__err_joining_room, callbackKeywords={'profile': profile}, errbackArgs=[room_jid, nick, history_options, password, profile])
|
||||
|
||||
def _join(self, room_jid_s, nick, options={}, profile_key='@DEFAULT@'):
|
||||
"""join method used by bridge: use the _join method, but doesn't return any deferred"""
|
||||
|
|
@ -186,13 +187,13 @@ class XEP_0045(object):
|
|||
self.host.bridge.newAlert(mess, _("Group chat error"), "ERROR", profile)
|
||||
return
|
||||
d = self.join(room_jid, nick, options, profile)
|
||||
d.addErrback(lambda x: warning(_('Error while joining room'))) #TODO: error management + signal in bridge
|
||||
d.addErrback(lambda x: warning(_('Error while joining room'))) # TODO: error management + signal in bridge
|
||||
|
||||
def nick(self, room_jid, nick, profile_key):
|
||||
profile = self.host.memory.getProfileName(profile_key)
|
||||
if not self.__check_profile(profile):
|
||||
raise exceptions.UnknownProfileError("Unknown or disconnected profile")
|
||||
if not self.clients[profile].joined_rooms.has_key(room_jid.userhost()):
|
||||
if room_jid.userhost() not in self.clients[profile].joined_rooms:
|
||||
raise UnknownRoom("This room has not been joined")
|
||||
return self.clients[profile].nick(room_jid, nick)
|
||||
|
||||
|
|
@ -200,7 +201,7 @@ class XEP_0045(object):
|
|||
profile = self.host.memory.getProfileName(profile_key)
|
||||
if not self.__check_profile(profile):
|
||||
raise exceptions.UnknownProfileError("Unknown or disconnected profile")
|
||||
if not self.clients[profile].joined_rooms.has_key(room_jid.userhost()):
|
||||
if room_jid.userhost() not in self.clients[profile].joined_rooms:
|
||||
raise UnknownRoom("This room has not been joined")
|
||||
return self.clients[profile].leave(room_jid)
|
||||
|
||||
|
|
@ -208,7 +209,7 @@ class XEP_0045(object):
|
|||
profile = self.host.memory.getProfileName(profile_key)
|
||||
if not self.__check_profile(profile):
|
||||
raise exceptions.UnknownProfileError("Unknown or disconnected profile")
|
||||
if not self.clients[profile].joined_rooms.has_key(room_jid.userhost()):
|
||||
if room_jid.userhost() not in self.clients[profile].joined_rooms:
|
||||
raise UnknownRoom("This room has not been joined")
|
||||
return self.clients[profile].subject(room_jid, subject)
|
||||
|
||||
|
|
@ -232,9 +233,9 @@ class SatMUCClient (muc.MUCClient):
|
|||
self.plugin_parent = plugin_parent
|
||||
self.host = plugin_parent.host
|
||||
muc.MUCClient.__init__(self)
|
||||
self.joined_rooms = {} #FIXME: seem to do the same thing as MUCClient's _rooms attribute, must be removed
|
||||
self.joined_rooms = {} # FIXME: seem to do the same thing as MUCClient's _rooms attribute, must be removed
|
||||
self.rec_subjects = {}
|
||||
self.__changing_nicks = set() # used to keep trace of who is changing nick,
|
||||
self.__changing_nicks = set() # used to keep trace of who is changing nick,
|
||||
# and to discard userJoinedRoom signal in this case
|
||||
print "init SatMUCClient OK"
|
||||
|
||||
|
|
@ -271,35 +272,35 @@ class SatMUCClient (muc.MUCClient):
|
|||
if user.nick in self.__changing_nicks:
|
||||
self.__changing_nicks.remove(user.nick)
|
||||
else:
|
||||
debug (_("user %(nick)s has joined room (%(room_id)s)") % {'nick':user.nick, 'room_id':room.occupantJID.userhost()})
|
||||
debug(_("user %(nick)s has joined room (%(room_id)s)") % {'nick': user.nick, 'room_id': room.occupantJID.userhost()})
|
||||
if not self.host.trigger.point("MUC user joined", room, user, self.parent.profile):
|
||||
return
|
||||
user_data={'entity':user.entity.full() if user.entity else '', 'affiliation':user.affiliation, 'role':user.role}
|
||||
user_data = {'entity': user.entity.full() if user.entity else '', 'affiliation': user.affiliation, 'role': user.role}
|
||||
self.host.bridge.roomUserJoined(room.roomJID.userhost(), user.nick, user_data, self.parent.profile)
|
||||
|
||||
def userLeftRoom(self, room, user):
|
||||
if user.nick == room.nick:
|
||||
# we left the room
|
||||
room_jid_s = room.roomJID.userhost()
|
||||
info (_("Room [%(room)s] left (%(profile)s))") % { "room": room_jid_s,
|
||||
"profile": self.parent.profile })
|
||||
info(_("Room [%(room)s] left (%(profile)s))") % {"room": room_jid_s,
|
||||
"profile": self.parent.profile})
|
||||
self.host.memory.delEntityCache(room.roomJID, self.parent.profile)
|
||||
del self.plugin_parent.clients[self.parent.profile].joined_rooms[room_jid_s]
|
||||
self.host.bridge.roomLeft(room.roomJID.userhost(), self.parent.profile)
|
||||
else:
|
||||
debug (_("user %(nick)s left room (%(room_id)s)") % {'nick':user.nick, 'room_id':room.occupantJID.userhost()})
|
||||
user_data={'entity':user.entity.full() if user.entity else '', 'affiliation':user.affiliation, 'role':user.role}
|
||||
debug(_("user %(nick)s left room (%(room_id)s)") % {'nick': user.nick, 'room_id': room.occupantJID.userhost()})
|
||||
user_data = {'entity': user.entity.full() if user.entity else '', 'affiliation': user.affiliation, 'role': user.role}
|
||||
self.host.bridge.roomUserLeft(room.roomJID.userhost(), user.nick, user_data, self.parent.profile)
|
||||
|
||||
def userChangedNick(self, room, user, new_nick):
|
||||
self.host.bridge.roomUserChangedNick(room.roomJID.userhost(), user.nick, new_nick, self.parent.profile)
|
||||
self.host.bridge.roomUserChangedNick(room.roomJID.userhost(), user.nick, new_nick, self.parent.profile)
|
||||
|
||||
def userUpdatedStatus(self, room, user, show, status):
|
||||
print("FIXME: MUC status not managed yet")
|
||||
#FIXME:
|
||||
|
||||
def receivedSubject(self, room, user, subject):
|
||||
debug (_("New subject for room (%(room_id)s): %(subject)s") % {'room_id':room.roomJID.full(),'subject':subject})
|
||||
debug(_("New subject for room (%(room_id)s): %(subject)s") % {'room_id': room.roomJID.full(), 'subject': subject})
|
||||
self.rec_subjects[room.roomJID.userhost()] = (room.roomJID.userhost(), subject)
|
||||
self.host.bridge.roomNewSubject(room.roomJID.userhost(), subject, self.parent.profile)
|
||||
|
||||
|
|
@ -311,4 +312,3 @@ class SatMUCClient (muc.MUCClient):
|
|||
|
||||
#def getDiscoItems(self, requestor, target, nodeIdentifier=''):
|
||||
#return []
|
||||
|
||||
|
|
|
|||
|
|
@ -44,19 +44,20 @@ IBB_OPEN = IQ_SET + '/open[@xmlns="' + NS_IBB + '"]'
|
|||
IBB_CLOSE = IQ_SET + '/close[@xmlns="' + NS_IBB + '" and @sid="%s"]'
|
||||
IBB_IQ_DATA = IQ_SET + '/data[@xmlns="' + NS_IBB + '" and @sid="%s"]'
|
||||
IBB_MESSAGE_DATA = MESSAGE + '/data[@xmlns="' + NS_IBB + '" and @sid="%s"]'
|
||||
TIMEOUT = 60 #timeout for workflow
|
||||
TIMEOUT = 60 # timeout for workflow
|
||||
BLOCK_SIZE = 4096
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "In-Band Bytestream Plugin",
|
||||
"import_name": "XEP-0047",
|
||||
"type": "XEP",
|
||||
"protocols": ["XEP-0047"],
|
||||
"main": "XEP_0047",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of In-Band Bytestreams""")
|
||||
"name": "In-Band Bytestream Plugin",
|
||||
"import_name": "XEP-0047",
|
||||
"type": "XEP",
|
||||
"protocols": ["XEP-0047"],
|
||||
"main": "XEP_0047",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of In-Band Bytestreams""")
|
||||
}
|
||||
|
||||
|
||||
class XEP_0047(object):
|
||||
NAMESPACE = NS_IBB
|
||||
|
||||
|
|
@ -71,7 +72,7 @@ class XEP_0047(object):
|
|||
client = self.host.getClient(profile)
|
||||
if not client:
|
||||
raise ProfileNotInCacheError
|
||||
client.xep_0047_current_stream = {} #key: stream_id, value: data(dict)
|
||||
client.xep_0047_current_stream = {} # key: stream_id, value: data(dict)
|
||||
|
||||
def _timeOut(self, sid, profile):
|
||||
"""Delecte current_stream id, called after timeout
|
||||
|
|
@ -87,14 +88,14 @@ class XEP_0047(object):
|
|||
if not client:
|
||||
warning(_("Client no more in cache"))
|
||||
return
|
||||
if not client.xep_0047_current_stream.has_key(sid):
|
||||
if sid not in client.xep_0047_current_stream:
|
||||
warning(_("kill id called on a non existant id"))
|
||||
return
|
||||
if client.xep_0047_current_stream[sid].has_key("observer_cb"):
|
||||
if "observer_cb" in client.xep_0047_current_stream[sid]:
|
||||
client.xmlstream.removeObserver(client.xep_0047_current_stream[sid]["event_data"], client.xep_0047_current_stream[sid]["observer_cb"])
|
||||
if client.xep_0047_current_stream[sid]['timer'].active():
|
||||
client.xep_0047_current_stream[sid]['timer'].cancel()
|
||||
if client.xep_0047_current_stream[sid].has_key("size"):
|
||||
if "size" in client.xep_0047_current_stream[sid]:
|
||||
self.host.removeProgressCB(sid, profile)
|
||||
|
||||
file_obj = client.xep_0047_current_stream[sid]['file_obj']
|
||||
|
|
@ -145,7 +146,7 @@ class XEP_0047(object):
|
|||
|
||||
def streamOpening(self, IQ, profile):
|
||||
debug(_("IBB stream opening"))
|
||||
IQ.handled=True
|
||||
IQ.handled = True
|
||||
client = self.host.getClient(profile)
|
||||
if not client:
|
||||
raise ProfileNotInCacheError
|
||||
|
|
@ -153,7 +154,7 @@ class XEP_0047(object):
|
|||
block_size = open_elt.getAttribute('block-size')
|
||||
sid = open_elt.getAttribute('sid')
|
||||
stanza = open_elt.getAttribute('stanza', 'iq')
|
||||
if not sid or not block_size or int(block_size)>65535:
|
||||
if not sid or not block_size or int(block_size) > 65535:
|
||||
warning(_("malformed IBB transfer: %s" % IQ['id']))
|
||||
self.sendNotAcceptableError(IQ['id'], IQ['from'], client.xmlstream)
|
||||
return
|
||||
|
|
@ -173,12 +174,12 @@ class XEP_0047(object):
|
|||
client.xep_0047_current_stream[sid]["timer"].reset(TIMEOUT)
|
||||
|
||||
#we save the xmlstream, events and observer data to allow observer removal
|
||||
client.xep_0047_current_stream[sid]["event_data"] = event_data = (IBB_MESSAGE_DATA if stanza=='message' else IBB_IQ_DATA) % sid
|
||||
client.xep_0047_current_stream[sid]["observer_cb"] = observer_cb = self.messageData if stanza=='message' else self.iqData
|
||||
client.xep_0047_current_stream[sid]["event_data"] = event_data = (IBB_MESSAGE_DATA if stanza == 'message' else IBB_IQ_DATA) % sid
|
||||
client.xep_0047_current_stream[sid]["observer_cb"] = observer_cb = self.messageData if stanza == 'message' else self.iqData
|
||||
event_close = IBB_CLOSE % sid
|
||||
#we now set the stream observer to look after data packet
|
||||
client.xmlstream.addObserver(event_data, observer_cb, profile = profile)
|
||||
client.xmlstream.addOnetimeObserver(event_close, self.streamClosing, profile = profile)
|
||||
client.xmlstream.addObserver(event_data, observer_cb, profile=profile)
|
||||
client.xmlstream.addOnetimeObserver(event_close, self.streamClosing, profile=profile)
|
||||
#finally, we send the accept stanza
|
||||
result = domish.Element((None, 'iq'))
|
||||
result['type'] = 'result'
|
||||
|
|
@ -187,7 +188,7 @@ class XEP_0047(object):
|
|||
client.xmlstream.send(result)
|
||||
|
||||
def streamClosing(self, IQ, profile):
|
||||
IQ.handled=True
|
||||
IQ.handled = True
|
||||
client = self.host.getClient(profile)
|
||||
if not client:
|
||||
raise ProfileNotInCacheError
|
||||
|
|
@ -202,7 +203,7 @@ class XEP_0047(object):
|
|||
self._killId(sid, success=True, profile=profile)
|
||||
|
||||
def iqData(self, IQ, profile):
|
||||
IQ.handled=True
|
||||
IQ.handled = True
|
||||
client = self.host.getClient(profile)
|
||||
if not client:
|
||||
raise ProfileNotInCacheError
|
||||
|
|
@ -219,7 +220,7 @@ class XEP_0047(object):
|
|||
|
||||
def messageData(self, message_elt, profile):
|
||||
data_elt = message_elt.firstChildElement()
|
||||
sid = message_elt.getAttribute('id','')
|
||||
sid = message_elt.getAttribute('id', '')
|
||||
self._manageDataElt(message_elt, 'message', sid, jid.JID(message_elt['from']), profile)
|
||||
|
||||
def _manageDataElt(self, data_elt, stanza, sid, stanza_from_jid, profile):
|
||||
|
|
@ -239,14 +240,14 @@ class XEP_0047(object):
|
|||
|
||||
if stanza_from_jid != from_jid:
|
||||
warning(_("sended jid inconsistency (man in the middle attack attempt ?)"))
|
||||
if stanza=='iq':
|
||||
if stanza == 'iq':
|
||||
self.sendNotAcceptableError(sid, from_jid, client.xmlstream)
|
||||
return False
|
||||
|
||||
client.xep_0047_current_stream[sid]["seq"]+=1
|
||||
if int(data_elt.getAttribute("seq",-1)) != client.xep_0047_current_stream[sid]["seq"]:
|
||||
client.xep_0047_current_stream[sid]["seq"] += 1
|
||||
if int(data_elt.getAttribute("seq", -1)) != client.xep_0047_current_stream[sid]["seq"]:
|
||||
warning(_("Sequence error"))
|
||||
if stanza=='iq':
|
||||
if stanza == 'iq':
|
||||
self.sendNotAcceptableError(data_elt["id"], from_jid, client.xmlstream)
|
||||
return False
|
||||
|
||||
|
|
@ -259,7 +260,7 @@ class XEP_0047(object):
|
|||
except TypeError:
|
||||
#The base64 data is invalid
|
||||
warning(_("Invalid base64 data"))
|
||||
if stanza=='iq':
|
||||
if stanza == 'iq':
|
||||
self.sendNotAcceptableError(data_elt["id"], from_jid, client.xmlstream)
|
||||
return False
|
||||
return True
|
||||
|
|
@ -275,10 +276,10 @@ class XEP_0047(object):
|
|||
result['to'] = to_jid
|
||||
error_el = result.addElement('error')
|
||||
error_el['type'] = 'cancel'
|
||||
error_el.addElement(('urn:ietf:params:xml:ns:xmpp-stanzas','not-acceptable'))
|
||||
error_el.addElement(('urn:ietf:params:xml:ns:xmpp-stanzas', 'not-acceptable'))
|
||||
xmlstream.send(result)
|
||||
|
||||
def startStream(self, file_obj, to_jid, sid, length, successCb, failureCb, size = None, profile=None):
|
||||
def startStream(self, file_obj, to_jid, sid, length, successCb, failureCb, size=None, profile=None):
|
||||
"""Launch the stream workflow
|
||||
@param file_obj: file_obj to send
|
||||
@param to_jid: JID of the recipient
|
||||
|
|
@ -290,7 +291,7 @@ class XEP_0047(object):
|
|||
client = self.host.getClient(profile)
|
||||
if not client:
|
||||
raise ProfileNotInCacheError
|
||||
if length != None:
|
||||
if length is not None:
|
||||
error(_('stream length not managed yet'))
|
||||
return
|
||||
data = client.xep_0047_current_stream[sid] = {}
|
||||
|
|
@ -303,10 +304,10 @@ class XEP_0047(object):
|
|||
if size:
|
||||
data["size"] = size
|
||||
self.host.registerProgressCB(sid, self.getProgress, profile)
|
||||
iq_elt = jabber_client.IQ(client.xmlstream,'set')
|
||||
iq_elt = jabber_client.IQ(client.xmlstream, 'set')
|
||||
iq_elt['from'] = client.jid.full()
|
||||
iq_elt['to'] = to_jid.full()
|
||||
open_elt = iq_elt.addElement('open',NS_IBB)
|
||||
open_elt = iq_elt.addElement('open', NS_IBB)
|
||||
open_elt['block-size'] = str(BLOCK_SIZE)
|
||||
open_elt['sid'] = sid
|
||||
open_elt['stanza'] = 'iq'
|
||||
|
|
@ -329,18 +330,18 @@ class XEP_0047(object):
|
|||
|
||||
buffer = data["file_obj"].read(data["block_size"])
|
||||
if buffer:
|
||||
next_iq_elt = jabber_client.IQ(client.xmlstream,'set')
|
||||
next_iq_elt = jabber_client.IQ(client.xmlstream, 'set')
|
||||
next_iq_elt['to'] = data["to"].full()
|
||||
data_elt = next_iq_elt.addElement('data', NS_IBB)
|
||||
data_elt['seq'] = str(seq)
|
||||
data_elt['sid'] = sid
|
||||
data_elt.addContent(base64.b64encode(buffer))
|
||||
next_iq_elt.addCallback(self.iqResult, sid, seq+1, length, profile)
|
||||
next_iq_elt.addCallback(self.iqResult, sid, seq + 1, length, profile)
|
||||
next_iq_elt.send()
|
||||
else:
|
||||
self.terminateStream(sid, profile=profile)
|
||||
|
||||
def terminateStream(self, sid, failure_reason = None, profile=None):
|
||||
def terminateStream(self, sid, failure_reason=None, profile=None):
|
||||
"""Terminate the stream session
|
||||
@param to_jid: recipient
|
||||
@param sid: Session id
|
||||
|
|
@ -353,9 +354,9 @@ class XEP_0047(object):
|
|||
if not client:
|
||||
raise ProfileNotInCacheError
|
||||
data = client.xep_0047_current_stream[sid]
|
||||
iq_elt = jabber_client.IQ(client.xmlstream,'set')
|
||||
iq_elt = jabber_client.IQ(client.xmlstream, 'set')
|
||||
iq_elt['to'] = data["to"].full()
|
||||
close_elt = iq_elt.addElement('close',NS_IBB)
|
||||
close_elt = iq_elt.addElement('close', NS_IBB)
|
||||
close_elt['sid'] = sid
|
||||
iq_elt.send()
|
||||
self.host.removeProgressCB(sid, profile)
|
||||
|
|
@ -364,14 +365,15 @@ class XEP_0047(object):
|
|||
else:
|
||||
self._killId(sid, True, profile=profile)
|
||||
|
||||
|
||||
class XEP_0047_handler(XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
|
||||
def __init__(self,parent):
|
||||
def __init__(self, parent):
|
||||
self.plugin_parent = parent
|
||||
|
||||
def connectionInitialized(self):
|
||||
self.xmlstream.addObserver(IBB_OPEN, self.plugin_parent.streamOpening, profile = self.parent.profile)
|
||||
self.xmlstream.addObserver(IBB_OPEN, self.plugin_parent.streamOpening, profile=self.parent.profile)
|
||||
|
||||
def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
|
||||
return [disco.DiscoFeature(NS_IBB)]
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ from zope.interface import implements
|
|||
|
||||
from wokkel import disco, iwokkel
|
||||
|
||||
from base64 import b64decode,b64encode
|
||||
from base64 import b64decode, b64encode
|
||||
from hashlib import sha1
|
||||
from sat.core import exceptions
|
||||
from sat.memory.persistent import PersistentDict
|
||||
|
|
@ -47,23 +47,24 @@ AVATAR_PATH = "avatars"
|
|||
|
||||
IQ_GET = '/iq[@type="get"]'
|
||||
NS_VCARD = 'vcard-temp'
|
||||
VCARD_REQUEST = IQ_GET + '/vCard[@xmlns="' + NS_VCARD + '"]' #TODO: manage requests
|
||||
VCARD_REQUEST = IQ_GET + '/vCard[@xmlns="' + NS_VCARD + '"]' # TODO: manage requests
|
||||
|
||||
PRESENCE = '/presence'
|
||||
NS_VCARD_UPDATE = 'vcard-temp:x:update'
|
||||
VCARD_UPDATE = PRESENCE + '/x[@xmlns="' + NS_VCARD_UPDATE + '"]'
|
||||
VCARD_UPDATE = PRESENCE + '/x[@xmlns="' + NS_VCARD_UPDATE + '"]'
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "XEP 0054 Plugin",
|
||||
"import_name": "XEP-0054",
|
||||
"type": "XEP",
|
||||
"protocols": ["XEP-0054", "XEP-0153"],
|
||||
"dependencies": [],
|
||||
"main": "XEP_0054",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of vcard-temp""")
|
||||
"name": "XEP 0054 Plugin",
|
||||
"import_name": "XEP-0054",
|
||||
"type": "XEP",
|
||||
"protocols": ["XEP-0054", "XEP-0153"],
|
||||
"dependencies": [],
|
||||
"main": "XEP_0054",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of vcard-temp""")
|
||||
}
|
||||
|
||||
|
||||
class XEP_0054(object):
|
||||
#TODO: - check that nickname is ok
|
||||
# - refactor the code/better use of Wokkel
|
||||
|
|
@ -76,10 +77,10 @@ class XEP_0054(object):
|
|||
if not os.path.exists(self.avatar_path):
|
||||
os.makedirs(self.avatar_path)
|
||||
self.avatars_cache = PersistentDict(NS_VCARD)
|
||||
self.avatars_cache.load() #FIXME: resulting deferred must be correctly managed
|
||||
self.avatars_cache.load() # FIXME: resulting deferred must be correctly managed
|
||||
host.bridge.addMethod("getCard", ".plugin", in_sign='ss', out_sign='s', method=self.getCard)
|
||||
host.bridge.addMethod("getAvatarFile", ".plugin", in_sign='s', out_sign='s', method=self.getAvatarFile)
|
||||
host.bridge.addMethod("setAvatar", ".plugin", in_sign='ss', out_sign='', method=self.setAvatar, async = True)
|
||||
host.bridge.addMethod("setAvatar", ".plugin", in_sign='ss', out_sign='', method=self.setAvatar, async=True)
|
||||
host.trigger.add("presence_available", self.presenceTrigger)
|
||||
|
||||
def getHandler(self, profile):
|
||||
|
|
@ -144,9 +145,9 @@ class XEP_0054(object):
|
|||
debug(_('Decoding binary'))
|
||||
decoded = b64decode(str(elem))
|
||||
image_hash = sha1(decoded).hexdigest()
|
||||
filename = self.avatar_path+'/'+image_hash
|
||||
filename = self.avatar_path + '/' + image_hash
|
||||
if not os.path.exists(filename):
|
||||
with open(filename,'wb') as file:
|
||||
with open(filename, 'wb') as file:
|
||||
file.write(decoded)
|
||||
debug(_("file saved to %s") % image_hash)
|
||||
else:
|
||||
|
|
@ -156,7 +157,7 @@ class XEP_0054(object):
|
|||
@inlineCallbacks
|
||||
def vCard2Dict(self, vcard, target, profile):
|
||||
"""Convert a VCard to a dict, and save binaries"""
|
||||
debug (_("parsing vcard"))
|
||||
debug(_("parsing vcard"))
|
||||
dictionary = {}
|
||||
|
||||
for elem in vcard.elements():
|
||||
|
|
@ -173,18 +174,18 @@ class XEP_0054(object):
|
|||
dictionary['birthday'] = unicode(elem)
|
||||
elif elem.name == 'PHOTO':
|
||||
dictionary["avatar"] = yield threads.deferToThread(self.save_photo, elem)
|
||||
if not dictionary["avatar"]: #can happen in case of e.g. empty photo elem
|
||||
if not dictionary["avatar"]: # can happen in case of e.g. empty photo elem
|
||||
del dictionary['avatar']
|
||||
else:
|
||||
self.update_cache(target, 'avatar', dictionary['avatar'], profile)
|
||||
else:
|
||||
info (_('FIXME: [%s] VCard tag is not managed yet') % elem.name)
|
||||
info(_('FIXME: [%s] VCard tag is not managed yet') % elem.name)
|
||||
|
||||
returnValue(dictionary)
|
||||
|
||||
def vcard_ok(self, answer, profile):
|
||||
"""Called after the first get IQ"""
|
||||
debug (_("VCard found"))
|
||||
debug(_("VCard found"))
|
||||
|
||||
if answer.firstChildElement().name == "vCard":
|
||||
_jid, steam = self.host.getJidNStream(profile)
|
||||
|
|
@ -195,13 +196,13 @@ class XEP_0054(object):
|
|||
d = self.vCard2Dict(answer.firstChildElement(), from_jid, profile)
|
||||
d.addCallback(lambda data: self.host.bridge.actionResult("RESULT", answer['id'], data, profile))
|
||||
else:
|
||||
error (_("FIXME: vCard not found as first child element"))
|
||||
self.host.bridge.actionResult("SUPPRESS", answer['id'], {}, profile) #FIXME: maybe an error message would be better
|
||||
error(_("FIXME: vCard not found as first child element"))
|
||||
self.host.bridge.actionResult("SUPPRESS", answer['id'], {}, profile) # FIXME: maybe an error message would be better
|
||||
|
||||
def vcard_err(self, failure, profile):
|
||||
"""Called when something is wrong with registration"""
|
||||
error (_("Can't find VCard of %s") % failure.value.stanza['from'])
|
||||
self.host.bridge.actionResult("SUPPRESS", failure.value.stanza['id'], {}, profile) #FIXME: maybe an error message would be better
|
||||
error(_("Can't find VCard of %s") % failure.value.stanza['from'])
|
||||
self.host.bridge.actionResult("SUPPRESS", failure.value.stanza['id'], {}, profile) # FIXME: maybe an error message would be better
|
||||
|
||||
def getCard(self, target_s, profile_key='@DEFAULT@'):
|
||||
"""Ask server for VCard
|
||||
|
|
@ -209,13 +210,13 @@ class XEP_0054(object):
|
|||
@result: id to retrieve the profile"""
|
||||
current_jid, xmlstream = self.host.getJidNStream(profile_key)
|
||||
if not xmlstream:
|
||||
error (_('Asking vcard for a non-existant or not connected profile'))
|
||||
error(_('Asking vcard for a non-existant or not connected profile'))
|
||||
return ""
|
||||
profile = self.host.memory.getProfileName(profile_key)
|
||||
to_jid = jid.JID(target_s)
|
||||
debug(_("Asking for %s's VCard") % to_jid.userhost())
|
||||
reg_request=IQ(xmlstream,'get')
|
||||
reg_request["from"]=current_jid.full()
|
||||
reg_request = IQ(xmlstream, 'get')
|
||||
reg_request["from"] = current_jid.full()
|
||||
reg_request["to"] = to_jid.userhost()
|
||||
reg_request.addElement('vCard', NS_VCARD)
|
||||
reg_request.send(to_jid.userhost()).addCallbacks(self.vcard_ok, self.vcard_err, callbackArgs=[profile], errbackArgs=[profile])
|
||||
|
|
@ -226,9 +227,9 @@ class XEP_0054(object):
|
|||
@param hash: SHA1 hash
|
||||
@return full_path
|
||||
"""
|
||||
filename = self.avatar_path+'/'+avatar_hash
|
||||
filename = self.avatar_path + '/' + avatar_hash
|
||||
if not os.path.exists(filename):
|
||||
error (_("Asking for an uncached avatar [%s]") % avatar_hash)
|
||||
error(_("Asking for an uncached avatar [%s]") % avatar_hash)
|
||||
return ""
|
||||
return filename
|
||||
|
||||
|
|
@ -259,13 +260,13 @@ class XEP_0054(object):
|
|||
if not client:
|
||||
raise exceptions.NotConnectedProfileError(_('Trying to set avatar for a non-existant or not connected profile'))
|
||||
|
||||
vcard_set = IQ(client.xmlstream,'set')
|
||||
vcard_set = IQ(client.xmlstream, 'set')
|
||||
d = threads.deferToThread(self._buildSetAvatar, vcard_set, filepath)
|
||||
|
||||
def elementBuilt(result):
|
||||
"""Called once the image is at the right size/format, and the vcard set element is build"""
|
||||
set_avatar_elt, img_hash = result
|
||||
self.avatars_cache[client.jid.userhost()] = img_hash # we need to update the hash, so we can send a new presence
|
||||
self.avatars_cache[client.jid.userhost()] = img_hash # we need to update the hash, so we can send a new presence
|
||||
# element with the right hash
|
||||
return set_avatar_elt.send().addCallback(lambda ignore: client.presence.available())
|
||||
|
||||
|
|
@ -296,7 +297,7 @@ class XEP_0054_handler(XMPPHandler):
|
|||
"""
|
||||
from_jid = jid.JID(presence['from'])
|
||||
#FIXME: wokkel's data_form should be used here
|
||||
x_elem = filter (lambda x:x.name == "x", presence.elements())[0] #We only want the "x" element
|
||||
x_elem = filter(lambda x: x.name == "x", presence.elements())[0] # We only want the "x" element
|
||||
for elem in x_elem.elements():
|
||||
if elem.name == 'photo':
|
||||
_hash = str(elem)
|
||||
|
|
|
|||
|
|
@ -30,23 +30,24 @@ from wokkel import disco, iwokkel, pubsub
|
|||
from zope.interface import implements
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "Publish-Subscribe",
|
||||
"import_name": "XEP-0060",
|
||||
"type": "XEP",
|
||||
"protocols": ["XEP-0060"],
|
||||
"dependencies": [],
|
||||
"main": "XEP_0060",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of PubSub Protocol""")
|
||||
"name": "Publish-Subscribe",
|
||||
"import_name": "XEP-0060",
|
||||
"type": "XEP",
|
||||
"protocols": ["XEP-0060"],
|
||||
"dependencies": [],
|
||||
"main": "XEP_0060",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of PubSub Protocol""")
|
||||
}
|
||||
|
||||
|
||||
class XEP_0060(object):
|
||||
|
||||
def __init__(self, host):
|
||||
info(_("PubSub plugin initialization"))
|
||||
self.host = host
|
||||
self.managedNodes=[]
|
||||
self.clients={}
|
||||
self.managedNodes = []
|
||||
self.clients = {}
|
||||
"""host.bridge.addMethod("getItems", ".plugin", in_sign='ssa{ss}s', out_sign='as', method=self.getItems,
|
||||
async = True,
|
||||
doc = { 'summary':'retrieve items',
|
||||
|
|
@ -78,8 +79,8 @@ class XEP_0060(object):
|
|||
profile = self.host.memory.getProfileName(profile_key)
|
||||
if not profile:
|
||||
err_mess = _('Trying to %(action)s with an unknown profile key [%(profile_key)s]') % {
|
||||
'action':action,
|
||||
'profile_key':profile_key}
|
||||
'action': action,
|
||||
'profile_key': profile_key}
|
||||
error(err_mess)
|
||||
raise Exception(err_mess)
|
||||
try:
|
||||
|
|
@ -91,31 +92,31 @@ class XEP_0060(object):
|
|||
return profile, client
|
||||
|
||||
def publish(self, service, nodeIdentifier, items=None, profile_key='@DEFAULT@'):
|
||||
profile,client = self.__getClientNProfile(profile_key, 'publish item')
|
||||
profile, client = self.__getClientNProfile(profile_key, 'publish item')
|
||||
return client.publish(service, nodeIdentifier, items, client.parent.jid)
|
||||
|
||||
def getItems(self, service, node, max_items=None, sub_id=None, profile_key='@DEFAULT@'):
|
||||
profile,client = self.__getClientNProfile(profile_key, 'get items')
|
||||
profile, client = self.__getClientNProfile(profile_key, 'get items')
|
||||
return client.items(service, node, max_items, sub_id, client.parent.jid)
|
||||
|
||||
def getOptions(self, service, nodeIdentifier, subscriber, subscriptionIdentifier=None, profile_key='@DEFAULT@'):
|
||||
profile,client = self.__getClientNProfile(profile_key, 'get options')
|
||||
profile, client = self.__getClientNProfile(profile_key, 'get options')
|
||||
return client.getOptions(service, nodeIdentifier, subscriber, subscriptionIdentifier)
|
||||
|
||||
def setOptions(self, service, nodeIdentifier, subscriber, options, subscriptionIdentifier=None, profile_key='@DEFAULT@'):
|
||||
profile,client = self.__getClientNProfile(profile_key, 'set options')
|
||||
profile, client = self.__getClientNProfile(profile_key, 'set options')
|
||||
return client.setOptions(service, nodeIdentifier, subscriber, options, subscriptionIdentifier)
|
||||
|
||||
def createNode(self, service, nodeIdentifier, options, profile_key='@DEFAULT@'):
|
||||
profile,client = self.__getClientNProfile(profile_key, 'create node')
|
||||
profile, client = self.__getClientNProfile(profile_key, 'create node')
|
||||
return client.createNode(service, nodeIdentifier, options)
|
||||
|
||||
def deleteNode(self, service, nodeIdentifier, profile_key='@DEFAULT@'):
|
||||
profile,client = self.__getClientNProfile(profile_key, 'delete node')
|
||||
profile, client = self.__getClientNProfile(profile_key, 'delete node')
|
||||
return client.deleteNode(service, nodeIdentifier)
|
||||
|
||||
def subscribe(self, service, nodeIdentifier, sub_jid = None, options=None, profile_key='@DEFAULT@'):
|
||||
profile,client = self.__getClientNProfile(profile_key, 'subscribe node')
|
||||
def subscribe(self, service, nodeIdentifier, sub_jid=None, options=None, profile_key='@DEFAULT@'):
|
||||
profile, client = self.__getClientNProfile(profile_key, 'subscribe node')
|
||||
return client.subscribe(service, nodeIdentifier, sub_jid or client.parent.jid.userhostJID(), options=options)
|
||||
|
||||
|
||||
|
|
@ -123,7 +124,7 @@ class SatPubSubClient(pubsub.PubSubClient):
|
|||
implements(disco.IDisco)
|
||||
|
||||
def __init__(self, host, parent_plugin):
|
||||
self.host=host
|
||||
self.host = host
|
||||
self.parent_plugin = parent_plugin
|
||||
pubsub.PubSubClient.__init__(self)
|
||||
|
||||
|
|
@ -152,4 +153,3 @@ class SatPubSubClient(pubsub.PubSubClient):
|
|||
|
||||
def getDiscoItems(self, requestor, target, nodeIdentifier=''):
|
||||
return []
|
||||
|
||||
|
|
|
|||
|
|
@ -78,30 +78,28 @@ from wokkel import disco, iwokkel
|
|||
IQ_SET = '/iq[@type="set"]'
|
||||
NS_BS = 'http://jabber.org/protocol/bytestreams'
|
||||
BS_REQUEST = IQ_SET + '/query[@xmlns="' + NS_BS + '"]'
|
||||
TIMEOUT = 60 #timeout for workflow
|
||||
|
||||
|
||||
TIMEOUT = 60 # timeout for workflow
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "XEP 0065 Plugin",
|
||||
"import_name": "XEP-0065",
|
||||
"type": "XEP",
|
||||
"protocols": ["XEP-0065"],
|
||||
"main": "XEP_0065",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of SOCKS5 Bytestreams""")
|
||||
"name": "XEP 0065 Plugin",
|
||||
"import_name": "XEP-0065",
|
||||
"type": "XEP",
|
||||
"protocols": ["XEP-0065"],
|
||||
"main": "XEP_0065",
|
||||
"handler": "yes",
|
||||
"description": _("""Implementation of SOCKS5 Bytestreams""")
|
||||
}
|
||||
|
||||
STATE_INITIAL = 0
|
||||
STATE_AUTH = 1
|
||||
STATE_AUTH = 1
|
||||
STATE_REQUEST = 2
|
||||
STATE_READY = 3
|
||||
STATE_READY = 3
|
||||
STATE_AUTH_USERPASS = 4
|
||||
STATE_TARGET_INITIAL = 5
|
||||
STATE_TARGET_AUTH = 6
|
||||
STATE_TARGET_AUTH = 6
|
||||
STATE_TARGET_REQUEST = 7
|
||||
STATE_TARGET_READY = 8
|
||||
STATE_LAST = 9
|
||||
STATE_TARGET_READY = 8
|
||||
STATE_LAST = 9
|
||||
|
||||
STATE_CONNECT_PENDING = STATE_LAST + 1
|
||||
|
||||
|
|
@ -139,15 +137,14 @@ def calculateHash(from_jid, to_jid, sid):
|
|||
return hashlib.sha1((sid + from_jid.full() + to_jid.full()).encode('utf-8')).hexdigest()
|
||||
|
||||
|
||||
|
||||
class SOCKSv5(protocol.Protocol, FileSender):
|
||||
def __init__(self):
|
||||
debug(_("Protocol init"))
|
||||
self.state = STATE_INITIAL
|
||||
self.buf = ""
|
||||
self.supportedAuthMechs = [ AUTHMECH_ANON ]
|
||||
self.supportedAddrs = [ ADDR_DOMAINNAME ]
|
||||
self.enabledCommands = [ CMD_CONNECT ]
|
||||
self.supportedAuthMechs = [AUTHMECH_ANON]
|
||||
self.supportedAddrs = [ADDR_DOMAINNAME]
|
||||
self.enabledCommands = [CMD_CONNECT]
|
||||
self.peersock = None
|
||||
self.addressType = 0
|
||||
self.requestType = 0
|
||||
|
|
@ -162,7 +159,7 @@ class SOCKSv5(protocol.Protocol, FileSender):
|
|||
try:
|
||||
# Parse out data
|
||||
ver, nmethod = struct.unpack('!BB', self.buf[:2])
|
||||
methods = struct.unpack('%dB' % nmethod, self.buf[2:nmethod+2])
|
||||
methods = struct.unpack('%dB' % nmethod, self.buf[2:nmethod + 2])
|
||||
|
||||
# Ensure version is correct
|
||||
if ver != 5:
|
||||
|
|
@ -171,7 +168,7 @@ class SOCKSv5(protocol.Protocol, FileSender):
|
|||
return
|
||||
|
||||
# Trim off front of the buffer
|
||||
self.buf = self.buf[nmethod+2:]
|
||||
self.buf = self.buf[nmethod + 2:]
|
||||
|
||||
# Check for supported auth mechs
|
||||
for m in self.supportedAuthMechs:
|
||||
|
|
@ -320,7 +317,7 @@ class SOCKSv5(protocol.Protocol, FileSender):
|
|||
debug("connectRequested")
|
||||
|
||||
# Check that this session is expected
|
||||
if not self.factory.hash_sid_map.has_key(addr):
|
||||
if addr not in self.factory.hash_sid_map:
|
||||
#no: we refuse it
|
||||
self.sendErrorReply(REPLY_CONN_REFUSED)
|
||||
return
|
||||
|
|
@ -374,14 +371,13 @@ class SOCKSv5(protocol.Protocol, FileSender):
|
|||
if self.state == STATE_TARGET_AUTH:
|
||||
ver, method = struct.unpack('!BB', buf)
|
||||
self.buf = self.buf[2:]
|
||||
if ver!=SOCKS5_VER or method!=AUTHMECH_ANON:
|
||||
if ver != SOCKS5_VER or method != AUTHMECH_ANON:
|
||||
self.transport.loseConnection()
|
||||
else:
|
||||
self._makeRequest()
|
||||
if self.state == STATE_TARGET_REQUEST:
|
||||
self._parseRequestReply()
|
||||
|
||||
|
||||
def clientConnectionLost(self, reason):
|
||||
debug("clientConnectionLost")
|
||||
self.transport.loseConnection()
|
||||
|
|
@ -390,7 +386,7 @@ class SOCKSv5(protocol.Protocol, FileSender):
|
|||
debug("connectionLost")
|
||||
if self.state != STATE_CONNECT_PENDING:
|
||||
self.transport.unregisterProducer()
|
||||
if self.peersock != None:
|
||||
if self.peersock is not None:
|
||||
self.peersock.peersock = None
|
||||
self.peersock.transport.unregisterProducer()
|
||||
self.peersock = None
|
||||
|
|
@ -405,10 +401,11 @@ class Socks5ServerFactory(protocol.ServerFactory):
|
|||
self.finishedCb = finishedCb
|
||||
|
||||
def startedConnecting(self, connector):
|
||||
debug (_("Socks 5 server connection started"))
|
||||
debug(_("Socks 5 server connection started"))
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
debug (_("Socks 5 server connection lost (reason: %s)"), reason)
|
||||
debug(_("Socks 5 server connection lost (reason: %s)"), reason)
|
||||
|
||||
|
||||
class Socks5ClientFactory(protocol.ClientFactory):
|
||||
protocol = SOCKSv5
|
||||
|
|
@ -432,11 +429,11 @@ class Socks5ClientFactory(protocol.ClientFactory):
|
|||
self.profile = profile
|
||||
|
||||
def startedConnecting(self, connector):
|
||||
debug (_("Socks 5 client connection started"))
|
||||
debug(_("Socks 5 client connection started"))
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
debug (_("Socks 5 client connection lost (reason: %s)"), reason)
|
||||
self.finishedCb(self.sid, reason.type == jab_error.ConnectionDone, self.profile) #TODO: really check if the state is actually successful
|
||||
debug(_("Socks 5 client connection lost (reason: %s)"), reason)
|
||||
self.finishedCb(self.sid, reason.type == jab_error.ConnectionDone, self.profile) # TODO: really check if the state is actually successful
|
||||
|
||||
|
||||
class XEP_0065(object):
|
||||
|
|
@ -465,7 +462,7 @@ class XEP_0065(object):
|
|||
info(_("Plugin XEP_0065 initialization"))
|
||||
|
||||
#session data
|
||||
self.hash_sid_map = {} #key: hash of the transfer session, value: (session id, profile)
|
||||
self.hash_sid_map = {} # key: hash of the transfer session, value: (session id, profile)
|
||||
|
||||
self.host = host
|
||||
debug(_("registering"))
|
||||
|
|
@ -486,7 +483,7 @@ class XEP_0065(object):
|
|||
client = self.host.getClient(profile)
|
||||
if not client:
|
||||
raise ProfileNotInCacheError
|
||||
client.xep_0065_current_stream = {} #key: stream_id, value: data(dict)
|
||||
client.xep_0065_current_stream = {} # key: stream_id, value: data(dict)
|
||||
|
||||
def getExternalIP(self):
|
||||
"""Return IP visible from outside, by asking to a website"""
|
||||
|
|
@ -518,15 +515,15 @@ class XEP_0065(object):
|
|||
if not client:
|
||||
warning(_("Client no more in cache"))
|
||||
return
|
||||
if not client.xep_0065_current_stream.has_key(sid):
|
||||
if sid not in client.xep_0065_current_stream:
|
||||
warning(_("kill id called on a non existant id"))
|
||||
return
|
||||
if client.xep_0065_current_stream[sid].has_key("observer_cb"):
|
||||
if "observer_cb" in client.xep_0065_current_stream[sid]:
|
||||
xmlstream = client.xep_0065_current_stream[sid]["xmlstream"]
|
||||
xmlstream.removeObserver(client.xep_0065_current_stream[sid]["event_data"], client.xep_0065_current_stream[sid]["observer_cb"])
|
||||
if client.xep_0065_current_stream[sid]['timer'].active():
|
||||
client.xep_0065_current_stream[sid]['timer'].cancel()
|
||||
if client.xep_0065_current_stream[sid].has_key("size"):
|
||||
if "size" in client.xep_0065_current_stream[sid]:
|
||||
self.host.removeProgressCB(sid, profile)
|
||||
|
||||
file_obj = client.xep_0065_current_stream[sid]['file_obj']
|
||||
|
|
@ -544,7 +541,7 @@ class XEP_0065(object):
|
|||
else:
|
||||
failure_cb(sid, file_obj, NS_BS, failure_reason, profile)
|
||||
|
||||
def startStream(self, file_obj, to_jid, sid, length, successCb, failureCb, size = None, profile=None):
|
||||
def startStream(self, file_obj, to_jid, sid, length, successCb, failureCb, size=None, profile=None):
|
||||
"""Launch the stream workflow
|
||||
@param file_obj: file_obj to send
|
||||
@param to_jid: JID of the recipient
|
||||
|
|
@ -559,7 +556,7 @@ class XEP_0065(object):
|
|||
error(_("Unknown profile, this should not happen"))
|
||||
raise ProfileNotInCacheError
|
||||
|
||||
if length != None:
|
||||
if length is not None:
|
||||
error(_('stream length not managed yet'))
|
||||
return
|
||||
|
||||
|
|
@ -579,7 +576,7 @@ class XEP_0065(object):
|
|||
if size:
|
||||
data["size"] = size
|
||||
self.host.registerProgressCB(sid, self.getProgress, profile)
|
||||
iq_elt = jabber_client.IQ(xmlstream,'set')
|
||||
iq_elt = jabber_client.IQ(xmlstream, 'set')
|
||||
iq_elt["from"] = profile_jid.full()
|
||||
iq_elt["to"] = to_jid.full()
|
||||
query_elt = iq_elt.addElement('query', NS_BS)
|
||||
|
|
@ -638,7 +635,7 @@ class XEP_0065(object):
|
|||
factory = Socks5ClientFactory(client.xep_0065_current_stream, sid, None, self.activateProxyStream, lambda sid, success, profile: self._killId(sid, success, profile=profile), True, profile)
|
||||
reactor.connectTCP(proxy_host, int(proxy_port), factory)
|
||||
else:
|
||||
data["start_transfer_cb"](file_obj) #We now activate the stream
|
||||
data["start_transfer_cb"](file_obj) # We now activate the stream
|
||||
|
||||
def activateProxyStream(self, sid, iq_id, start_transfer_cb, profile):
|
||||
debug(_("activating stream"))
|
||||
|
|
@ -648,7 +645,7 @@ class XEP_0065(object):
|
|||
data = client.xep_0065_current_stream[sid]
|
||||
profile_jid, xmlstream = self.host.getJidNStream(profile)
|
||||
|
||||
iq_elt = client.IQ(xmlstream,'set')
|
||||
iq_elt = client.IQ(xmlstream, 'set')
|
||||
iq_elt["from"] = profile_jid.full()
|
||||
iq_elt["to"] = self.host.memory.getParamA("Proxy", "File Transfer", profile_key=profile)
|
||||
query_elt = iq_elt.addElement('query', NS_BS)
|
||||
|
|
@ -687,7 +684,6 @@ class XEP_0065(object):
|
|||
data["success_cb"] = success_cb
|
||||
data["failure_cb"] = failure_cb
|
||||
|
||||
|
||||
def streamQuery(self, iq_elt, profile):
|
||||
"""Get file using byte stream"""
|
||||
debug(_("BS stream query"))
|
||||
|
|
@ -717,7 +713,7 @@ class XEP_0065(object):
|
|||
self.sendBadRequestError(iq_elt['id'], iq_elt['from'], xmlstream)
|
||||
return
|
||||
|
||||
streamhost_elt = streamhost_elts[0] #TODO: manage several streamhost elements case
|
||||
streamhost_elt = streamhost_elts[0] # TODO: manage several streamhost elements case
|
||||
sh_host = streamhost_elt.getAttribute("host")
|
||||
sh_port = streamhost_elt.getAttribute("port")
|
||||
sh_jid = streamhost_elt.getAttribute("jid")
|
||||
|
|
@ -728,7 +724,7 @@ class XEP_0065(object):
|
|||
|
||||
client.xep_0065_current_stream[sid]["streamhost"] = (sh_host, sh_port, sh_jid)
|
||||
|
||||
info (_("Stream proposed: host=[%(host)s] port=[%(port)s]") % {'host':sh_host, 'port':sh_port})
|
||||
info(_("Stream proposed: host=[%(host)s] port=[%(port)s]") % {'host': sh_host, 'port': sh_port})
|
||||
factory = Socks5ClientFactory(client.xep_0065_current_stream, sid, iq_elt["id"], self.activateStream, lambda sid, success, profile: self._killId(sid, success, profile=profile), profile=profile)
|
||||
reactor.connectTCP(sh_host, int(sh_port), factory)
|
||||
|
||||
|
|
@ -760,7 +756,7 @@ class XEP_0065(object):
|
|||
result['to'] = to_jid
|
||||
error_el = result.addElement('error')
|
||||
error_el['type'] = 'modify'
|
||||
error_el.addElement(('urn:ietf:params:xml:ns:xmpp-stanzas','not-acceptable'))
|
||||
error_el.addElement(('urn:ietf:params:xml:ns:xmpp-stanzas', 'not-acceptable'))
|
||||
xmlstream.send(result)
|
||||
|
||||
def sendBadRequestError(self, iq_id, to_jid, xmlstream):
|
||||
|
|
@ -774,9 +770,10 @@ class XEP_0065(object):
|
|||
result['to'] = to_jid
|
||||
error_el = result.addElement('error')
|
||||
error_el['type'] = 'cancel'
|
||||
error_el.addElement(('urn:ietf:params:xml:ns:xmpp-stanzas','bad-request'))
|
||||
error_el.addElement(('urn:ietf:params:xml:ns:xmpp-stanzas', 'bad-request'))
|
||||
xmlstream.send(result)
|
||||
|
||||
|
||||
class XEP_0065_handler(XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
|
||||
|
|
@ -801,10 +798,9 @@ class XEP_0065_handler(XMPPHandler):
|
|||
if len(streamhost_elts) != 1:
|
||||
warning(_("Multiple streamhost elements in proxy not managed, keeping only the first one"))
|
||||
streamhost_elt = streamhost_elts[0]
|
||||
proxy = self.host.memory.setParam("Proxy", streamhost_elt.getAttribute("jid",""), "File Transfer", self.parent.profile)
|
||||
proxy = self.host.memory.setParam("Proxy host", streamhost_elt.getAttribute("host",""), "File Transfer", self.parent.profile)
|
||||
proxy = self.host.memory.setParam("Proxy port", streamhost_elt.getAttribute("port",""), "File Transfer", self.parent.profile)
|
||||
|
||||
proxy = self.host.memory.setParam("Proxy", streamhost_elt.getAttribute("jid", ""), "File Transfer", self.parent.profile)
|
||||
proxy = self.host.memory.setParam("Proxy host", streamhost_elt.getAttribute("host", ""), "File Transfer", self.parent.profile)
|
||||
proxy = self.host.memory.setParam("Proxy port", streamhost_elt.getAttribute("port", ""), "File Transfer", self.parent.profile)
|
||||
|
||||
def connectionInitialized(self):
|
||||
def after_init(ignore):
|
||||
|
|
@ -812,20 +808,17 @@ class XEP_0065_handler(XMPPHandler):
|
|||
if not proxy_ent:
|
||||
debug(_("No proxy found on this server"))
|
||||
return
|
||||
iq_elt = jabber_client.IQ(self.parent.xmlstream,'get')
|
||||
iq_elt = jabber_client.IQ(self.parent.xmlstream, 'get')
|
||||
iq_elt["to"] = proxy_ent.full()
|
||||
query_elt = iq_elt.addElement('query', NS_BS)
|
||||
iq_elt.addCallback(self._proxyDataResult)
|
||||
iq_elt.send()
|
||||
|
||||
|
||||
self.xmlstream.addObserver(BS_REQUEST, self.plugin_parent.streamQuery, profile = self.parent.profile)
|
||||
proxy = self.host.memory.getParamA("Proxy", "File Transfer", profile_key = self.parent.profile)
|
||||
self.xmlstream.addObserver(BS_REQUEST, self.plugin_parent.streamQuery, profile=self.parent.profile)
|
||||
proxy = self.host.memory.getParamA("Proxy", "File Transfer", profile_key=self.parent.profile)
|
||||
if not proxy:
|
||||
self.parent.client_initialized.addCallback(after_init)
|
||||
|
||||
|
||||
|
||||
def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
|
||||
return [disco.DiscoFeature(NS_BS)]
|
||||
|
||||
|
|
|
|||
|
|
@ -29,21 +29,22 @@ from wokkel import data_form
|
|||
NS_REG = 'jabber:iq:register'
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "XEP 0077 Plugin",
|
||||
"import_name": "XEP-0077",
|
||||
"type": "XEP",
|
||||
"protocols": ["XEP-0077"],
|
||||
"dependencies": [],
|
||||
"main": "XEP_0077",
|
||||
"description": _("""Implementation of in-band registration""")
|
||||
"name": "XEP 0077 Plugin",
|
||||
"import_name": "XEP-0077",
|
||||
"type": "XEP",
|
||||
"protocols": ["XEP-0077"],
|
||||
"dependencies": [],
|
||||
"main": "XEP_0077",
|
||||
"description": _("""Implementation of in-band registration""")
|
||||
}
|
||||
|
||||
|
||||
class XEP_0077(object):
|
||||
|
||||
def __init__(self, host):
|
||||
info(_("Plugin XEP_0077 initialization"))
|
||||
self.host = host
|
||||
self.triggers = {} #used by other protocol (e.g. XEP-0100) to finish registration. key = target_jid
|
||||
self.triggers = {} # used by other protocol (e.g. XEP-0100) to finish registration. key = target_jid
|
||||
host.bridge.addMethod("in_band_register", ".plugin", in_sign='ss', out_sign='s', method=self.in_band_register)
|
||||
host.bridge.addMethod("in_band_submit", ".plugin", in_sign='ssa(ss)s', out_sign='s', method=self.in_band_submit)
|
||||
|
||||
|
|
@ -54,65 +55,65 @@ class XEP_0077(object):
|
|||
def reg_ok(self, answer, profile):
|
||||
"""Called after the first get IQ"""
|
||||
try:
|
||||
x_elem = filter (lambda x:x.name == "x", answer.firstChildElement().elements())[0] #We only want the "x" element (data form)
|
||||
x_elem = filter(lambda x: x.name == "x", answer.firstChildElement().elements())[0] # We only want the "x" element (data form)
|
||||
except IndexError:
|
||||
info(_("No data form found"))
|
||||
#TODO: manage registration without data form
|
||||
answer_data={"reason": "unmanaged", "message":_("This gateway can't be managed by SàT, sorry :(")}
|
||||
answer_data = {"reason": "unmanaged", "message": _("This gateway can't be managed by SàT, sorry :(")}
|
||||
answer_type = "ERROR"
|
||||
self.host.bridge.actionResult(answer_type, answer['id'], answer_data, profile)
|
||||
return
|
||||
|
||||
form = data_form.Form.fromElement(x_elem)
|
||||
xml_data = dataForm2xml(form)
|
||||
self.host.bridge.actionResult("XMLUI", answer['id'], {"target":answer["from"], "type":"registration", "xml":xml_data}, profile)
|
||||
self.host.bridge.actionResult("XMLUI", answer['id'], {"target": answer["from"], "type": "registration", "xml": xml_data}, profile)
|
||||
|
||||
def reg_err(self, failure, profile):
|
||||
"""Called when something is wrong with registration"""
|
||||
info (_("Registration failure: %s") % str(failure.value))
|
||||
info(_("Registration failure: %s") % str(failure.value))
|
||||
answer_data = {}
|
||||
answer_data['reason'] = 'unknown'
|
||||
answer_data={"message":"%s [code: %s]" % (failure.value.condition, unicode(failure.value))}
|
||||
answer_data = {"message": "%s [code: %s]" % (failure.value.condition, unicode(failure.value))}
|
||||
answer_type = "ERROR"
|
||||
self.host.bridge.actionResult(answer_type, failure.value.stanza['id'], answer_data, profile)
|
||||
|
||||
def unregistrationAnswer(self, answer, profile):
|
||||
debug (_("registration answer: %s") % answer.toXml())
|
||||
debug(_("registration answer: %s") % answer.toXml())
|
||||
answer_type = "SUCCESS"
|
||||
answer_data={"message":_("Your are now unregistred")}
|
||||
answer_data = {"message": _("Your are now unregistred")}
|
||||
self.host.bridge.actionResult(answer_type, answer['id'], answer_data, profile)
|
||||
|
||||
def unregistrationFailure(self, failure, profile):
|
||||
info (_("Unregistration failure: %s") % str(failure.value))
|
||||
info(_("Unregistration failure: %s") % str(failure.value))
|
||||
answer_type = "ERROR"
|
||||
answer_data = {}
|
||||
answer_data['reason'] = 'unknown'
|
||||
answer_data={"message":_("Unregistration failed: %s") % failure.value.condition}
|
||||
answer_data = {"message": _("Unregistration failed: %s") % failure.value.condition}
|
||||
self.host.bridge.actionResult(answer_type, failure.value.stanza['id'], answer_data, profile)
|
||||
|
||||
def registrationAnswer(self, answer, profile):
|
||||
debug (_("registration answer: %s") % answer.toXml())
|
||||
debug(_("registration answer: %s") % answer.toXml())
|
||||
answer_type = "SUCCESS"
|
||||
answer_data={"message":_("Registration successfull")}
|
||||
answer_data = {"message": _("Registration successfull")}
|
||||
self.host.bridge.actionResult(answer_type, answer['id'], answer_data, profile)
|
||||
if self.triggers.has_key(answer["from"]):
|
||||
callback,profile = self.triggers[answer["from"]]
|
||||
if answer["from"] in self.triggers:
|
||||
callback, profile = self.triggers[answer["from"]]
|
||||
callback(answer["from"], profile)
|
||||
del self.triggers[answer["from"]]
|
||||
|
||||
def registrationFailure(self, failure, profile):
|
||||
info (_("Registration failure: %s") % str(failure.value))
|
||||
info(_("Registration failure: %s") % str(failure.value))
|
||||
print failure.value.stanza.toXml()
|
||||
answer_type = "ERROR"
|
||||
answer_data = {}
|
||||
if failure.value.condition == 'conflict':
|
||||
answer_data['reason'] = 'conflict'
|
||||
answer_data={"message":_("Username already exists, please choose an other one")}
|
||||
answer_data = {"message": _("Username already exists, please choose an other one")}
|
||||
else:
|
||||
answer_data['reason'] = 'unknown'
|
||||
answer_data={"message":_("Registration failed")}
|
||||
answer_data = {"message": _("Registration failed")}
|
||||
self.host.bridge.actionResult(answer_type, failure.value.stanza['id'], answer_data, profile)
|
||||
if failure.value.stanza["from"] in self.triggers.has_key:
|
||||
if failure.value.stanza["from"] in self.triggers:
|
||||
del self.triggers[failure.value.stanza["from"]]
|
||||
|
||||
def in_band_submit(self, action, target, fields, profile):
|
||||
|
|
@ -128,12 +129,12 @@ class XEP_0077(object):
|
|||
"""register to a target JID"""
|
||||
client = self.host.getClient(profile_key)
|
||||
if not client:
|
||||
error (_('Asking for an non-existant or not connected profile'))
|
||||
error(_('Asking for an non-existant or not connected profile'))
|
||||
return ""
|
||||
to_jid = jid.JID(target)
|
||||
debug(_("Asking registration for [%s]") % to_jid.full())
|
||||
reg_request=IQ(client.xmlstream,'get')
|
||||
reg_request["from"]=client.jid.full()
|
||||
reg_request = IQ(client.xmlstream, 'get')
|
||||
reg_request["from"] = client.jid.full()
|
||||
reg_request["to"] = to_jid.full()
|
||||
reg_request.addElement('query', NS_REG)
|
||||
reg_request.send(to_jid.full()).addCallbacks(self.reg_ok, self.reg_err, callbackArgs=[client.profile], errbackArgs=[client.profile])
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue