iability to show captcha when joining a groupchat with captcha. (TODO: download image when it's not in the message itself)

This commit is contained in:
Yann Leboulanger 2010-06-27 23:09:07 +02:00
parent 90411cad12
commit f9e8b46e6c
8 changed files with 294 additions and 126 deletions

View File

@ -5,6 +5,7 @@
<object class="GtkVBox" id="groupchat_control_vbox">
<property name="can_focus">True</property>
<property name="border_width">3</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkAlignment" id="alignment">
<property name="visible">True</property>
@ -34,6 +35,7 @@
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">5</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel" id="banner_name_label">
<property name="visible">True</property>
@ -73,6 +75,85 @@
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHPaned" id="hpaned">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">3</property>
<property name="position">495</property>
<property name="position_set">True</property>
<child>
<object class="GtkVBox" id="gc_textviews_vbox">
<property name="width_request">0</property>
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkScrolledWindow" id="conversation_scrolledwindow">
<property name="width_request">200</property>
<property name="height_request">60</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<property name="shadow_type">in</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="message_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<property name="vscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="resize">False</property>
<property name="shrink">False</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="list_scrolledwindow">
<property name="width_request">100</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<property name="vscrollbar_policy">automatic</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="list_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">1</property>
<property name="headers_visible">False</property>
</object>
</child>
</object>
<packing>
<property name="resize">False</property>
<property name="shrink">False</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="actions_hbox">
<property name="visible">True</property>
@ -339,92 +420,5 @@
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkHPaned" id="hpaned">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">3</property>
<property name="position">495</property>
<property name="position_set">True</property>
<child>
<object class="GtkVBox" id="vbox108">
<property name="width_request">0</property>
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<object class="GtkVBox" id="vbox109">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<object class="GtkScrolledWindow" id="conversation_scrolledwindow">
<property name="width_request">200</property>
<property name="height_request">60</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<property name="shadow_type">in</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="message_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<property name="vscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="resize">False</property>
<property name="shrink">False</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="list_scrolledwindow">
<property name="width_request">100</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<property name="vscrollbar_policy">automatic</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="list_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">1</property>
<property name="headers_visible">False</property>
</object>
</child>
</object>
<packing>
<property name="resize">False</property>
<property name="shrink">False</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
</interface>

View File

@ -288,33 +288,37 @@ class CommonConnection:
gajim.thread_interface(encrypt_thread, [msg, keyID,
True], _on_encrypted, [])
else:
self._message_encrypted_cb(output, type_, msg, msgtxt,
original_message, fjid, resource, jid, xhtml,
subject, chatstate, composing_xep, label, forward_from,
delayed, session, form_node, user_nick, keyID,
callback)
self._message_encrypted_cb(output, type_, msg,
msgtxt, original_message, fjid, resource,
jid, xhtml, subject, chatstate, msg_id,
composing_xep, label, forward_from, delayed,
session, form_node, user_nick, keyID,
callback)
self.dispatch('GPG_ALWAYS_TRUST', _on_always_trust)
else:
self._message_encrypted_cb(output, type_, msg, msgtxt,
original_message, fjid, resource, jid, xhtml, subject,
chatstate, composing_xep, label, forward_from, delayed, session,
form_node, user_nick, keyID, callback)
original_message, fjid, resource, jid, xhtml,
subject, chatstate, msg_id, composing_xep, label,
forward_from, delayed, session, form_node,
user_nick, keyID, callback)
gajim.thread_interface(encrypt_thread, [msg, keyID, False],
_on_encrypted, [])
return
self._message_encrypted_cb(('', error), type_, msg, msgtxt,
original_message, fjid, resource, jid, xhtml, subject, chatstate,
composing_xep, label, forward_from, delayed, session, form_node, user_nick,
keyID, callback)
original_message, fjid, resource, jid, xhtml, subject,
chatstate, msg_id, composing_xep, label, forward_from, delayed,
session, form_node, user_nick, keyID, callback)
self._on_continue_message(type_, msg, msgtxt, original_message, fjid,
resource, jid, xhtml, subject, msgenc, keyID, chatstate, composing_xep,
label, forward_from, delayed, session, form_node, user_nick, callback)
resource, jid, xhtml, subject, msgenc, keyID, chatstate, msg_id,
composing_xep, label, forward_from, delayed, session, form_node,
user_nick, callback)
def _message_encrypted_cb(self, output, type_, msg, msgtxt, original_message,
fjid, resource, jid, xhtml, subject, chatstate, composing_xep, label, forward_from,
delayed, session, form_node, user_nick, keyID, callback):
def _message_encrypted_cb(self, output, type_, msg, msgtxt,
original_message, fjid, resource, jid, xhtml, subject, chatstate, msg_id,
composing_xep, label, forward_from, delayed, session, form_node, user_nick,
keyID, callback):
msgenc, error = output
if msgenc and not error:
@ -324,19 +328,19 @@ class CommonConnection:
# one in locale and one en
msgtxt = _('[This message is *encrypted* (See :XEP:`27`]') + \
' (' + msgtxt + ')'
self._on_continue_message(type_, msg, msgtxt, original_message, fjid,
resource, jid, xhtml, subject, msgenc, keyID, chatstate,
composing_xep, label, forward_from, delayed, session, form_node, user_nick,
callback)
self._on_continue_message(type_, msg, msgtxt, original_message,
fjid, resource, jid, xhtml, subject, msgenc, keyID,
chatstate, msg_id, composing_xep, label, forward_from, delayed,
session, form_node, user_nick, callback)
return
# Encryption failed, do not send message
tim = localtime()
self.dispatch('MSGNOTSENT', (jid, error, msgtxt, tim, session))
def _on_continue_message(self, type_, msg, msgtxt, original_message, fjid,
resource, jid, xhtml, subject, msgenc, keyID, chatstate, composing_xep,
label,
forward_from, delayed, session, form_node, user_nick, callback):
resource, jid, xhtml, subject, msgenc, keyID, chatstate, msg_id,
composing_xep, label, forward_from, delayed, session, form_node, user_nick,
callback):
if type_ == 'chat':
msg_iq = common.xmpp.Message(to=fjid, body=msgtxt, typ=type_,
xhtml=xhtml)
@ -347,6 +351,10 @@ class CommonConnection:
else:
msg_iq = common.xmpp.Message(to=fjid, body=msgtxt, typ='normal',
xhtml=xhtml)
if msg_id:
msg_iq.setID(msg_id)
if msgenc:
msg_iq.setTag(common.xmpp.NS_ENCRYPTED + ' x').setData(msgenc)
@ -1607,11 +1615,11 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(msg_iq)
def send_message(self, jid, msg, keyID, type_='chat', subject='',
def send_message(self, jid, msg, keyID=None, type_='chat', subject='',
chatstate=None, msg_id=None, composing_xep=None, resource=None,
user_nick=None, xhtml=None, label=None, session=None, forward_from=None, form_node=None,
original_message=None, delayed=None, callback=None, callback_args=[],
now=False):
user_nick=None, xhtml=None, label=None, session=None, forward_from=None,
form_node=None, original_message=None, delayed=None, callback=None,
callback_args=[], now=False):
def cb(jid, msg, keyID, forward_from, session, original_message,
subject, type_, msg_iq):
@ -1625,11 +1633,10 @@ class Connection(CommonConnection, ConnectionHandlers):
subject, type_)
self._prepare_message(jid, msg, keyID, type_=type_, subject=subject,
chatstate=chatstate, msg_id=msg_id, composing_xep=composing_xep,
resource=resource, user_nick=user_nick, xhtml=xhtml, label=label,
session=session,
forward_from=forward_from, form_node=form_node,
original_message=original_message, delayed=delayed, callback=cb)
chatstate=chatstate, msg_id=msg_id, composing_xep=composing_xep,
resource=resource, user_nick=user_nick, xhtml=xhtml, label=label,
session=session, forward_from=forward_from, form_node=form_node,
original_message=original_message, delayed=delayed, callback=cb)
def send_contacts(self, contacts, jid):
"""
@ -1987,6 +1994,14 @@ class Connection(CommonConnection, ConnectionHandlers):
p = self.add_sha(p, ptype != 'unavailable')
self.connection.send(p)
def send_captcha(self, jid, form_node):
if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Iq(typ='set', to=jid)
captcha = iq.addChild(name='captcha', namespace=common.xmpp.NS_CAPTCHA)
captcha.addChild(node=form_node)
self.connection.send(iq)
def check_unique_room_id_support(self, server, instance):
if not gajim.account_is_connected(self.name):
return

View File

@ -1667,8 +1667,21 @@ ConnectionCaps, ConnectionHandlersBase, ConnectionJingle):
if jid not in self.last_history_time:
return
self.dispatch('GC_MSG', (frm, msgtxt, tim, has_timestamp, msg.getXHTML(),
statusCode, displaymarking))
captcha = msg.getTag('captcha', namespace=common.xmpp.NS_CAPTCHA)
if captcha:
captcha = captcha.getTag('x', namespace=common.xmpp.NS_DATA)
for field in captcha.getTags('field'):
for media in field.getTags('media'):
for uri in media.getTags('uri'):
uri_data = uri.getData()
if uri_data.startswith('cid:'):
uri_data = uri_data[4:]
for data in msg.getTags('data',
namespace=common.xmpp.NS_BOB):
if data.getAttr('cid') == uri_data:
uri.setData(data.getData())
self.dispatch('GC_MSG', (frm, msgtxt, tim, has_timestamp,
msg.getXHTML(), statusCode, displaymarking, captcha))
tim_int = int(float(mktime(tim)))
if gajim.config.should_log(self.name, jid) and not \

View File

@ -227,6 +227,93 @@ class DataField(ExtendedNode):
return locals()
@nested_property
def media():
"""
Media data
"""
def fget(self):
media = self.getTag('media', namespace=xmpp.NS_DATA_MEDIA)
if media:
return Media(media)
return None
def fset(self, value):
assert isinstance(value, Media)
fdel(self)
self.addChild(node=value)
def fdel(self):
t = self.getTag('media')
if t is not None:
self.delChild(t)
return locals()
class Uri(xmpp.Node):
def __init__(self, uri_tag):
xmpp.Node.__init__(self, node=uri_tag)
@nested_property
def type_():
"""
uri type
"""
def fget(self):
return self.getAttr('type')
def fset(self, value):
assert isinstance(value, basestring)
self.setAttr('type', value)
def fdel(self):
self.delAttr('type')
return locals()
@nested_property
def uri_data():
"""
uri data
"""
def fget(self):
return self.getData()
def fset(self, value):
assert isinstance(value, basestring)
self.setData(value)
def fdel(self):
self.setData(None)
return locals()
class Media(xmpp.Node):
def __init__(self, media_tag):
xmpp.Node.__init__(self, node=media_tag)
@nested_property
def uris():
"""
URIs of the media element.
"""
def fget(self):
uris = []
for element in self.getTags('uri'):
uris.append(Uri(element))
return uris
def fset(self, value):
fdel(self)
for uri in values:
self.addChild(node=uri)
def fdel(self, value):
for element in self.getTags('uri'):
self.delChild(element)
return locals()
class BooleanField(DataField):
@nested_property
def value():
@ -543,12 +630,13 @@ class SimpleDataForm(DataForm, DataRecord):
if f.required:
# Keep all required fields
continue
if (hasattr(f, 'value') and not f.value) or (hasattr(f, 'values') and \
len(f.values) == 0):
if (hasattr(f, 'value') and not f.value) or (hasattr(f, 'values') \
and len(f.values) == 0):
to_be_removed.append(f)
else:
del f.label
del f.description
del f.media
for f in to_be_removed:
c.delChild(f)
return c

View File

@ -32,10 +32,12 @@ NS_ATOM ='http://www.w3.org/2005/Atom'
NS_AUTH ='jabber:iq:auth'
NS_AVATAR ='http://www.xmpp.org/extensions/xep-0084.html#ns-metadata'
NS_BIND ='urn:ietf:params:xml:ns:xmpp-bind'
NS_BOB ='urn:xmpp:bob' #XEP-0231
NS_BROWSE ='jabber:iq:browse'
NS_BROWSING ='http://jabber.org/protocol/browsing' # XEP-0195
NS_BYTESTREAM ='http://jabber.org/protocol/bytestreams' # JEP-0065
NS_CAPS ='http://jabber.org/protocol/caps' # JEP-0115
NS_CAPTCHA ='urn:xmpp:captcha' # XEP-0158
NS_CHATSTATES ='http://jabber.org/protocol/chatstates' # JEP-0085
NS_CHATTING ='http://jabber.org/protocol/chatting' # XEP-0194
NS_CLIENT ='jabber:client'
@ -45,6 +47,7 @@ NS_COMPONENT_1 ='http://jabberd.jabberstudio.org/ns/component/1.0'
NS_COMPRESS ='http://jabber.org/protocol/compress' # XEP-0138
NS_CONFERENCE ='jabber:x:conference'
NS_DATA ='jabber:x:data' # XEP-0004
NS_DATA_MEDIA ='urn:xmpp:media-element' # XEP-0221
NS_DELAY ='jabber:x:delay'
NS_DELAY2 ='urn:xmpp:delay'
NS_DIALBACK ='jabber:server:dialback'

View File

@ -27,6 +27,7 @@ multiple - these which may contain more data (with <reported/> element).'''
import gtk
import gobject
import base64
import gtkgui_helpers
import dialogs
@ -529,6 +530,25 @@ class SingleForm(gtk.Table, object):
self.attach(label, 0, 1, linecounter, linecounter+1,
xoptions=gtk.FILL, yoptions=gtk.FILL)
if field.media is not None:
for uri in field.media.uris:
if uri.type_.startswith('image/'):
try:
img_data = base64.decodestring(uri.uri_data)
pixbuf_l = gtk.gdk.PixbufLoader()
pixbuf_l.write(img_data)
pixbuf_l.close()
media = gtk.image_new_from_pixbuf(pixbuf_l.\
get_pixbuf())
except Exception:
media = gtk.Label(_('Unable to load image'))
else:
media = gtk.Label(_('Media type not supported: %s') % \
uri.type_)
linecounter += 1
self.attach(media, 0, 1, linecounter, linecounter+1,
xoptions=gtk.FILL, yoptions=gtk.FILL)
if commonwidget:
assert widget is not None
widget.set_sensitive(readwrite)

View File

@ -40,9 +40,11 @@ import dialogs
import config
import vcard
import cell_renderer_image
import dataforms_widget
from common import gajim
from common import helpers
from common import dataforms
from chat_control import ChatControl
from chat_control import ChatControlBase
@ -787,7 +789,39 @@ class GroupchatControl(ChatControlBase):
menu.destroy()
def on_message(self, nick, msg, tim, has_timestamp=False, xhtml=None,
status_code=[], displaymarking=None):
status_code=[], displaymarking=None, captcha=None):
if captcha:
dataform = dataforms.ExtendForm(node=captcha)
self.form_widget = dataforms_widget.DataFormWidget(dataform)
self.form_widget.show_all()
vbox = self.xml.get_object('gc_textviews_vbox')
vbox.pack_start(self.form_widget, expand=False, fill=False)
def on_send_dataform_clicked(widget):
if not self.form_widget:
return
form_node = self.form_widget.data_form.get_purged()
form_node.type = 'submit'
gajim.connections[self.account].send_captcha(self.room_jid,
form_node)
self.form_widget.hide()
self.form_widget.destroy()
self.btn_box.destroy()
del self.form_widget
del self.btn_box
valid_button = gtk.Button(stock=gtk.STOCK_OK)
valid_button.connect('clicked', on_send_dataform_clicked)
self.btn_box = gtk.HButtonBox()
self.btn_box.set_layout(gtk.BUTTONBOX_END)
self.btn_box.pack_start(valid_button)
self.btn_box.show_all()
vbox.pack_start(self.btn_box, expand=False, fill=False)
if self.parent_win:
self.parent_win.redraw_tab(self, 'attention')
else:
self.attention_flag = True
helpers.play_sound('muc_message_received')
if '100' in status_code:
# Room is not anonymous
self.is_anonymous = False

View File

@ -956,7 +956,7 @@ class Interface:
def handle_event_gc_msg(self, account, array):
# ('GC_MSG', account, (jid, msg, time, has_timestamp, htmlmsg,
# [status_codes], displaymarking))
# [status_codes], displaymarking, captcha))
jids = array[0].split('/', 1)
room_jid = jids[0]
@ -980,7 +980,8 @@ class Interface:
# message from someone
nick = jids[1]
gc_control.on_message(nick, msg, array[2], array[3], xhtml, array[5], displaymarking=array[6])
gc_control.on_message(nick, msg, array[2], array[3], xhtml, array[5],
displaymarking=array[6], captcha=array[7])
if self.remote_ctrl:
highlight = gc_control.needs_visual_notification(msg)