Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dev.gajim.org/gajim/gajim-plugins.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Brötzmann <mailtrash@posteo.de>2020-05-01 11:18:49 +0300
committerlovetox <philipp@hoerist.com>2020-05-06 23:46:09 +0300
commit125c2096902bd888f7ec2cd20f0b264ffa37a242 (patch)
tree8f4704f74c9de68cb6f07a2f2d0c539b4e85c8d3
parentadb90182b43b6978cab5e57b29f83820bfb558b8 (diff)
[anti_spam] Rework plugin
-rw-r--r--anti_spam/CHANGELOG34
-rw-r--r--anti_spam/README.md22
-rw-r--r--anti_spam/anti_spam.py283
-rw-r--r--anti_spam/config_dialog.py99
-rw-r--r--anti_spam/config_dialog.ui245
-rw-r--r--anti_spam/modules/anti_spam.py151
6 files changed, 291 insertions, 543 deletions
diff --git a/anti_spam/CHANGELOG b/anti_spam/CHANGELOG
index 2e1ed32..af47fd4 100644
--- a/anti_spam/CHANGELOG
+++ b/anti_spam/CHANGELOG
@@ -1,24 +1,18 @@
-## [Unreleased]
-### Added
-- Sending confirmation message that anti spam check was passed. It can be disabled in plugin gui config.
+1.5.1 / 2020-05-03
+- Rework plugin
+- Adapt to upstream changes in Gajim and python-nbxmpp
+- Use new configuration dialog
+- Remove pubsub blocking (Gajim handles these messages differently now)
+- Remove domain blocking feature (Gajim handles this)
-## [1.4.3] - 2016-12-04
-### Added
-- Filtering 'normal' type messages
-
-### Changed
+1.4.3 / 2016-12-04
+- Added filtering 'normal' type messages
- User from private conference conversation permanently stored in file
- Switched to GTK3
+- Messages sent before the correct answer were marked as received
+- Fixed chat between the two antispam plugins
-### Fixed
-- Messages that was sent before correct answer was marked as received
-- Chat between the two antispam plugins
-
-## [0.4.2] - 2016-11-28
-### Added
-- Anti spam question functionality
-- This CHANGELOG
-- README with some explanation of functionality
-
-### Changed
-- homepage in manifest.ini
+0.4.2 / 2016-11-28
+- Added anti spam question functionality
+- Added README with some explanation of functionality
+- Added website in manifest.ini
diff --git a/anti_spam/README.md b/anti_spam/README.md
deleted file mode 100644
index 4ab998f..0000000
--- a/anti_spam/README.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# Anti_spam Plugin for Gajim
-
-This Plugin allows you to dissociate itself from the spam.
-
-## Installation
-
-Use special plugin, that manages automatic download and installation of others plugins, it is called Plugin Installer.
-
-## Options
-### Block pubsub
-
-Block incoming messages from pubsub
-
-### Message size limit
-
-Block incoming messages that have size more than configured. Default value -1 mean that any sized messages are coming.
-
-### Anti spam question
-
-Block incoming messages from users not in your roster. In response, the Plugin sends a question that you configured. After correct answer(also configurable) you will receive all new messages from user.
-**Attention!** All messages before correct answer will be lost.
-Also you can enable this function, in Plugin config, for conference private messages. In some servers, the question in conference private does not reach your interlocutor. This can lead to the fact that you will not receive any messages from him, and he will not know it. \ No newline at end of file
diff --git a/anti_spam/anti_spam.py b/anti_spam/anti_spam.py
index 7bcdb34..15f54b5 100644
--- a/anti_spam/anti_spam.py
+++ b/anti_spam/anti_spam.py
@@ -1,278 +1,49 @@
-# -*- coding: utf-8 -*-
-
-## This file is part of Gajim.
-##
-## Gajim is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published
-## by the Free Software Foundation; version 3 only.
-##
-## Gajim is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
-##
+# This file is part of Gajim.
+#
+# Gajim is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published
+# by the Free Software Foundation; version 3 only.
+#
+# Gajim is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
+#
'''
-Block some incoming messages
-
:author: Yann Leboulanger <asterix@lagaule.org>
:since: 16 August 2012
:copyright: Copyright (2012) Yann Leboulanger <asterix@lagaule.org>
:license: GPLv3
'''
-from gi.repository import Gtk
-import nbxmpp
-
-from gajim.common import app
-from gajim.common import ged
+from functools import partial
from gajim.plugins import GajimPlugin
-from gajim.plugins.helpers import log, log_calls
-from gajim.plugins.gui import GajimPluginConfigDialog
from gajim.plugins.plugins_i18n import _
+from anti_spam.modules import anti_spam
+from anti_spam.config_dialog import AntiSpamConfigDialog
-class AntiSpamPlugin(GajimPlugin):
- @log_calls('AntiSpamPlugin')
+class AntiSpamPlugin(GajimPlugin):
def init(self):
- self.description = _('Allows to block some kind of incoming messages')
- self.config_dialog = AntiSpamPluginConfigDialog(self)
-
- self.gui_extension_points = {
- }
-
- self.events_handlers = {
- 'atom-entry-received': (ged.POSTCORE,
- self._nec_atom_entry_received),
- 'message-received': (ged.PRECORE,
- self._nec_message_received_received),
- 'decrypted-message-received': (ged.PRECORE,
- self._nec_decrypted_message_received_received),
- 'subscribe-presence-received': (ged.POSTCORE,
- self._nec_subscribe_presence_received),
- 'message-outgoing': (ged.OUT_PRECORE,
- self._nec_message_outgoing)
- }
-
+ self.description = _('Allows you to block various kinds of incoming '
+ 'messages (Spam, XHTML formatting, etc.)')
+ self.config_dialog = partial(AntiSpamConfigDialog, self)
self.config_default_values = {
- 'block_pubsub_messages': (False, 'If True, Gajim will block incoming messages from pubsub.'),
'disable_xhtml_muc': (False, ''),
'disable_xhtml_pm': (False, ''),
'block_subscription_requests': (False, ''),
- 'msgtxt_limit': (-1, ''),
- 'msgtxt_question': ('Please answer: 12 x 12 =', ''),
+ 'msgtxt_limit': (0, ''),
+ 'msgtxt_question': ('12 x 12 = ?', ''),
'msgtxt_answer': ('', ''),
'antispam_for_conference': (False, ''),
- 'conference_white_list': ([], ''), # conference private chat jid's
- 'block_domains': ('', ''), # comma separated list of domain names to block
+ 'block_domains': ('', ''),
+ 'whitelist': ([], ''),
}
-
- # List of outgoing jid's
- # Needs to avoid chat of two anti spam plugins
- # Contain all jid's where are you initiate a chat
- self.outgoing_jids = []
-
- self.block_domains = [h.strip() for h in self.config['block_domains'].split(",") if len(h.strip())]
-
- @log_calls('AntiSpamPlugin')
- def _nec_atom_entry_received(self, obj):
- if self.config['block_pubsub_messages']:
- log.info('discarding pubdubd message')
- return True
-
- @log_calls('AntiSpamPlugin')
- def _nec_message_received_received(self, obj):
- if self.config['disable_xhtml_muc'] and obj.mtype == 'groupchat':
- self.remove_xhtml(obj)
- if self.config['disable_xhtml_pm'] and obj.gc_control and \
- obj.resource and obj.mtype == 'chat':
- self.remove_xhtml(obj)
-
- if obj.jid.split("@", 1)[1] in self.block_domains:
- log.info('discarding message from %s, domain is blocked', obj.jid)
- return True
-
- return False
-
- @log_calls('AntiSpamPlugin')
- def _nec_decrypted_message_received_received(self, obj):
- if not obj.msgtxt:
- return False
-
- if obj.jid.split("@", 1)[1] in self.block_domains:
- log.info('discarding message from %s, domain is blocked', obj.jid)
- return True
-
- if self._nec_decrypted_message_received_question(obj):
- return True
- limit = self.config['msgtxt_limit']
- if limit > -1 and len(obj.msgtxt) > limit:
- return True
- return False
-
- @log_calls('AntiSpamPlugin')
- def _nec_subscribe_presence_received(self, obj):
- if self.config['block_subscription_requests'] and \
- not app.contacts.get_contacts(obj.conn.name, obj.jid):
- log.info('discarding subscription request from %s', obj.jid)
- return True
-
- if obj.jid.split("@", 1)[1] in self.block_domains:
- log.info('discarding subscription request from %s, domain is blocked', obj.jid)
- return True
-
- @log_calls('AntiSpamPlugin')
- def _nec_decrypted_message_received_question(self, obj):
- if obj.mtype != 'chat' and obj.mtype != 'normal':
- return False
-
- tjid = obj.jid if obj.mtype == 'normal' else obj.fjid
- if tjid in self.outgoing_jids:
- return False
-
- answer = self.config['msgtxt_answer']
- if len(answer) == 0:
- return False
- block_conference = self.config['antispam_for_conference']
- is_conference = app.contacts.is_gc_contact(obj.conn.name, obj.fjid)
- if not block_conference and is_conference:
- return False
- jid = obj.jid if not is_conference else obj.fjid
- # If we receive conference privat message or direct message from unknown user than
- # anti spam question will send in background mode, without any notification for us
- # There are two methods to see who wrote you and not passed filter:
- # 1. Using XML console
- # 2. Running Gajim with log info messages and see logs (probably gajim.log file)
- if is_conference or not app.contacts.get_contacts(obj.conn.name, jid):
- if not self.contain_answer(obj.msgtxt, answer):
- if is_conference and jid in self.config['conference_white_list']:
- return False
- self.send_question(obj, jid)
- return True
- else:
- if is_conference and jid not in self.config['conference_white_list']:
- self.config['conference_white_list'].append(jid)
- # Need to save because 'append' method does not implement __setitem__ method
- self.config.save()
- return False
-
- @log_calls('AntiSpamPlugin')
- def _nec_message_outgoing(self, obj):
- if obj.type_ != 'chat' and obj.type_ != 'normal':
- return
-
- if isinstance(obj.jid, list):
- for i in obj.jid:
- if i not in self.outgoing_jids:
- self.outgoing_jids.append(i)
- else:
- if obj.jid not in self.outgoing_jids:
- self.outgoing_jids.append(obj.jid)
-
- def send_question(self, obj, jid):
- if obj.mtype != 'chat' and obj.mtype != 'normal':
- log.info('Anti_spam wrong message type: %s', obj.mtype)
- return
-
- question = self.config['msgtxt_question']
- log.info('Anti_spam enabled for %s, question: %s', jid, question)
- message = _('Antispam enabled. Please answer the question. The message must only ' + \
- 'contain the answer. (Messages sent before the correct answer, will be lost): ') \
- + question
-
- if obj.mtype == 'chat':
- stanza = nbxmpp.Message(to=jid, body=message, typ=obj.mtype)
- else: # for 'normal' type
- stanza = nbxmpp.Message(to=jid, body=message, subject='Antispam enabled', typ=obj.mtype)
-
- app.connections[obj.conn.name].connection.send(stanza)
-
- def contain_answer(self, msg, answer):
- return answer in msg.split('\n')
-
- def remove_xhtml(self, obj):
- html_node = obj.stanza.getTag('html')
- if html_node:
- obj.stanza.delChild(html_node)
-
-
-class AntiSpamPluginConfigDialog(GajimPluginConfigDialog):
- def init(self):
- self.GTK_BUILDER_FILE_PATH = self.plugin.local_file_path(
- 'config_dialog.ui')
- self.xml = Gtk.Builder()
- self.xml.set_translation_domain('gajim_plugins')
- self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH,
- ['anti_spam_config_vbox'])
- self.config_vbox = self.xml.get_object('anti_spam_config_vbox')
- self.get_child().pack_start(self.config_vbox, True, True, 0)
-
- self.block_pubsub_messages_checkbutton = self.xml.get_object(
- 'block_pubsub_messages_checkbutton')
-
- self.xml.connect_signals(self)
-
- def on_run(self):
- self.block_pubsub_messages_checkbutton.set_active(self.plugin.config[
- 'block_pubsub_messages'])
- widget = self.xml.get_object('disable_xhtml_muc_checkbutton')
- widget.set_active(self.plugin.config['disable_xhtml_muc'])
- widget = self.xml.get_object('disable_xhtml_pm_checkbutton')
- widget.set_active(self.plugin.config['disable_xhtml_pm'])
- widget = self.xml.get_object('block_subscription_requests_checkbutton')
- widget.set_active(self.plugin.config['block_subscription_requests'])
- widget = self.xml.get_object('message_size_limit_entry')
- widget.set_text(str(self.plugin.config['msgtxt_limit']))
- widget = self.xml.get_object('antispam_question')
- widget.set_text(str(self.plugin.config['msgtxt_question']))
- widget = self.xml.get_object('antispam_answer')
- widget.set_text(str(self.plugin.config['msgtxt_answer']))
- widget = self.xml.get_object('antispam_for_conference')
- widget.set_active(self.plugin.config['antispam_for_conference'])
- widget = self.xml.get_object('block_domains_entry')
- widget.set_text(str(self.plugin.config['block_domains']))
-
- def on_block_pubsub_messages_checkbutton_toggled(self, button):
- self.plugin.config['block_pubsub_messages'] = button.get_active()
-
- def on_disable_xhtml_muc_checkbutton_toggled(self, button):
- self.plugin.config['disable_xhtml_muc'] = button.get_active()
-
- def on_disable_xhtml_pm_checkbutton_toggled(self, button):
- self.plugin.config['disable_xhtml_pm'] = button.get_active()
-
- def on_block_subscription_requests_checkbutton_toggled(self, button):
- self.plugin.config['block_subscription_requests'] = button.get_active()
-
- def on_message_size_limit_entry_changed(self, entry):
- try:
- self.plugin.config['msgtxt_limit'] = int(entry.get_text())
- except Exception:
- pass
-
- def on_message_question_entry_changed(self, entry):
- try:
- self.plugin.config['msgtxt_question'] = entry.get_text()
- except Exception:
- pass
-
- def on_message_answer_entry_changed(self, entry):
- try:
- self.plugin.config['msgtxt_answer'] = entry.get_text()
- except Exception:
- pass
-
- def on_antispam_for_conference_checkbutton_toggled(self, button):
- self.plugin.config['antispam_for_conference'] = button.get_active()
-
- def on_block_domains_entry_changed(self, entry):
- try:
- block_domains = self.plugin.config['block_domains'] = entry.get_text()
- self.plugin.block_domains = [h.strip() for h in block_domains.split(",") if len(h.strip())]
- except Exception as e:
- log.debug(str(e))
+ self.gui_extension_points = {}
+ self.modules = [anti_spam]
diff --git a/anti_spam/config_dialog.py b/anti_spam/config_dialog.py
new file mode 100644
index 0000000..ae733ae
--- /dev/null
+++ b/anti_spam/config_dialog.py
@@ -0,0 +1,99 @@
+# This file is part of Gajim.
+#
+# Gajim is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Gajim is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
+
+from gi.repository import Gtk
+
+from gajim.gtk.settings import SettingsDialog
+from gajim.gtk.const import Setting
+from gajim.gtk.const import SettingKind
+from gajim.gtk.const import SettingType
+
+from gajim.plugins.plugins_i18n import _
+
+
+class AntiSpamConfigDialog(SettingsDialog):
+ def __init__(self, plugin, parent):
+ self.plugin = plugin
+ msgtxt_limit = self.plugin.config['msgtxt_limit']
+ max_length = '' if msgtxt_limit == 0 else msgtxt_limit
+
+ settings = [
+ Setting(SettingKind.ENTRY,
+ _('Limit Message Length'),
+ SettingType.VALUE,
+ max_length,
+ callback=self._on_length_setting,
+ data='msgtxt_limit',
+ desc=_('Limits maximum message length (leave empty to '
+ 'disable)')),
+ Setting(SettingKind.SWITCH,
+ _('Deny Subscription Requests'),
+ SettingType.VALUE,
+ self.plugin.config['block_subscription_requests'],
+ callback=self._on_setting,
+ data='block_subscription_requests'),
+ Setting(SettingKind.SWITCH,
+ _('Disable XHTML for Group Chats'),
+ SettingType.VALUE,
+ self.plugin.config['disable_xhtml_muc'],
+ callback=self._on_setting,
+ data='disable_xhtml_muc',
+ desc=_('Removes XHTML formatting from group chat '
+ 'messages')),
+ Setting(SettingKind.SWITCH,
+ _('Disable XHTML for PMs'),
+ SettingType.VALUE,
+ self.plugin.config['disable_xhtml_pm'],
+ callback=self._on_setting,
+ data='disable_xhtml_pm',
+ desc=_('Removes XHTML formatting from private messages '
+ 'in group chats')),
+ Setting(SettingKind.ENTRY,
+ _('Anti Spam Question'),
+ SettingType.VALUE,
+ self.plugin.config['msgtxt_question'],
+ callback=self._on_setting,
+ data='msgtxt_question',
+ desc=_('Question has to be answered in order to '
+ 'contact you')),
+ Setting(SettingKind.ENTRY,
+ _('Anti Spam Answer'),
+ SettingType.VALUE,
+ self.plugin.config['msgtxt_answer'],
+ callback=self._on_setting,
+ data='msgtxt_answer',
+ desc=_('Correct answer to your Anti Spam Question '
+ '(leave empty to disable question)')),
+ Setting(SettingKind.SWITCH,
+ _('Anti Spam Question in Group Chats'),
+ SettingType.VALUE,
+ self.plugin.config['antispam_for_conference'],
+ callback=self._on_setting,
+ data='antispam_for_conference',
+ desc=_('Enables anti spam question for private messages '
+ 'in group chats')),
+ ]
+
+ SettingsDialog.__init__(self, parent, _('Anti Spam Configuration'),
+ Gtk.DialogFlags.MODAL, settings, None)
+
+ def _on_setting(self, value, data):
+ self.plugin.config[data] = value
+
+ def _on_length_setting(self, value, data):
+ try:
+ self.plugin.config[data] = int(value)
+ except Exception:
+ self.plugin.config[data] = 0
diff --git a/anti_spam/config_dialog.ui b/anti_spam/config_dialog.ui
deleted file mode 100644
index 5d99c70..0000000
--- a/anti_spam/config_dialog.ui
+++ /dev/null
@@ -1,245 +0,0 @@
-<?xml version="1.0"?>
-<interface>
- <requires lib="gtk+" version="2.16"/>
- <!-- interface-naming-policy toplevel-contextual -->
- <object class="GtkWindow" id="window1">
- <child>
- <object class="GtkVBox" id="anti_spam_config_vbox">
- <property name="visible">True</property>
- <property name="border_width">9</property>
- <property name="orientation">vertical</property>
- <property name="spacing">4</property>
- <child>
- <object class="GtkCheckButton" id="block_pubsub_messages_checkbutton">
- <property name="label" translatable="yes">Block pubsub messages</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="receives_default">False</property>
- <property name="tooltip_text" translatable="yes">If checked, Gajim will block incoming messages from pubsub.</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_block_pubsub_messages_checkbutton_toggled"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="disable_xhtml_muc_checkbutton">
- <property name="label" translatable="yes">Disable xhtml in MUCs</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="receives_default">False</property>
- <property name="tooltip_text" translatable="yes">If checked, Gajim will remove XHTML from incoming multi-user chat messages</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_disable_xhtml_muc_checkbutton_toggled"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="disable_xhtml_pm_checkbutton">
- <property name="label" translatable="yes">Disable xhtml in private messages</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="receives_default">False</property>
- <property name="tooltip_text" translatable="yes">If checked, Gajim will remove XHTML from incoming private messages</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_disable_xhtml_pm_checkbutton_toggled"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="block_subscription_requests_checkbutton">
- <property name="label" translatable="yes">Block incoming subscription requests</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="receives_default">False</property>
- <property name="tooltip_text" translatable="yes">If checked, Gajim will block incoming subscription requests</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_block_subscription_requests_checkbutton_toggled"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkHBox" id="box1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkLabel" id="label1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Message size limit:</property>
- </object>
- <packing>
- <property name="expand">False</property>
-
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="message_size_limit_entry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <signal name="changed" handler="on_message_size_limit_entry_changed"/>
- </object>
- <packing>
- <property name="expand">False</property>
-
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
-
- <property name="position">4</property>
- </packing>
- </child>
- <child>
- <object class="GtkHSeparator" id="hseparator1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
-
- <property name="position">5</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_question">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Anti spam question (Leave an empty answer field to disable this function):</property>
- <property name="wrap">True</property>
- <attributes>
- <attribute name="style" value="italic"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
-
- <property name="position">6</property>
- <property name="padding">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkVBox" id="questionbox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkLabel" id="label2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Question:</property>
- </object>
- <packing>
- <property name="expand">False</property>
-
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="antispam_question">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <signal name="changed" handler="on_message_question_entry_changed"/>
- </object>
- <packing>
- <property name="expand">False</property>
-
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label3">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Answer:</property>
- </object>
- <packing>
- <property name="expand">False</property>
-
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="antispam_answer">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <signal name="changed" handler="on_message_answer_entry_changed"/>
- </object>
- <packing>
- <property name="expand">False</property>
-
- <property name="position">3</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
-
- <property name="position">7</property>
- </packing>
- </child>
- <child>
- <object class="GtkVBox" id="block_domains_box">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkLabel" id="block_domains_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Block Domains:</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="block_domains_entry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <signal name="changed" handler="on_block_domains_entry_changed"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkCheckButton" id="antispam_for_conference">
- <property name="label" translatable="yes">Enable for conferences</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="receives_default">False</property>
- <property name="tooltip_text" translatable="yes">If checked, anti spam question will send for conference private messages</property>
-
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_antispam_for_conference_checkbutton_toggled"/>
- </object>
- <packing>
- <property name="expand">False</property>
-
- <property name="position">8</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
-</interface>
diff --git a/anti_spam/modules/anti_spam.py b/anti_spam/modules/anti_spam.py
new file mode 100644
index 0000000..5453d32
--- /dev/null
+++ b/anti_spam/modules/anti_spam.py
@@ -0,0 +1,151 @@
+# This file is part of Gajim.
+#
+# Gajim is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published
+# by the Free Software Foundation; version 3 only.
+#
+# Gajim is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
+#
+
+from nbxmpp import NodeProcessed
+from nbxmpp.protocol import Message
+from nbxmpp.structs import StanzaHandler
+
+from gajim.common import app
+from gajim.common import ged
+from gajim.common.modules.base import BaseModule
+
+# Module name
+name = 'AntiSpam'
+zeroconf = False
+
+
+class AntiSpam(BaseModule):
+ def __init__(self, con):
+ BaseModule.__init__(self, con, plugin=True)
+
+ self.handlers = [
+ StanzaHandler(name='message',
+ callback=self._message_received,
+ priority=48),
+ StanzaHandler(name='presence',
+ callback=self._subscribe_received,
+ typ='subscribe',
+ priority=48),
+ ]
+
+ self.register_events([
+ ('message-sent', ged.OUT_PRECORE, self._on_message_sent),
+ ])
+
+ for plugin in app.plugin_manager.plugins:
+ if plugin.short_name == 'anti_spam':
+ self._config = plugin.config
+
+ self._contacted_jids = set()
+
+ def _on_message_sent(self, event):
+ if event.type_ not in ('chat', 'normal'):
+ return
+
+ # We need self._contacted_jids in order to prevent two
+ # Anti Spam Plugins from chatting with each other.
+ # This set contains JIDs of all outgoing chats.
+ if isinstance(event.jid, list):
+ for jid in event.jid:
+ self._contacted_jids.add(jid)
+ else:
+ self._contacted_jids.add(event.jid)
+
+ def _message_received(self, _con, _stanza, properties):
+ if properties.is_sent_carbon:
+ # Another device already sent a message
+ self._contacted_jids.add(properties.jid)
+ return
+
+ msg_body = properties.body
+ if not msg_body:
+ return
+
+ if self._ask_question(properties):
+ raise NodeProcessed
+
+ msg_from = properties.jid
+ limit = self._config['msgtxt_limit']
+ if limit > 0 and len(msg_body) > limit:
+ self._log.info('Discarded message from %s: message '
+ 'length exceeded' % msg_from)
+ raise NodeProcessed
+
+ if self._config['disable_xhtml_muc'] and properties.type.is_groupchat:
+ properties.xhtml = None
+ self._log.info('Stripped message from %s: message '
+ 'contained XHTML' % msg_from)
+
+ if self._config['disable_xhtml_pm'] and properties.is_muc_pm:
+ properties.xhtml = None
+ self._log.info('Stripped message from %s: message '
+ 'contained XHTML' % msg_from)
+
+ def _ask_question(self, properties):
+ answer = self._config['msgtxt_answer']
+ if len(answer) == 0:
+ return False
+
+ is_muc_pm = properties.is_muc_pm
+ if is_muc_pm and not self._config['antispam_for_conference']:
+ return False
+
+ if (properties.type.value not in ('chat', 'normal') or
+ properties.is_mam_message):
+ return False
+
+ msg_from = properties.jid if is_muc_pm else properties.jid.getBare()
+
+ if msg_from in self._contacted_jids:
+ return False
+
+ # If we receive a PM or a message from an unknown user, our anti spam
+ # question will silently be sent in the background
+ whitelist = self._config['whitelist']
+ if msg_from in whitelist:
+ return False
+
+ is_contact = app.contacts.get_contacts(self._account, msg_from)
+
+ if is_muc_pm or not is_contact:
+ if answer in properties.body.split('\n'):
+ if msg_from not in whitelist:
+ whitelist.append(msg_from)
+ # We need to explicitly save, because 'append' does not
+ # implement the __setitem__ method
+ self._config.save()
+ else:
+ self._send_question(properties, msg_from)
+ return True
+ return False
+
+ def _send_question(self, properties, jid):
+ message = 'Anti Spam Question: %s' % self._config['msgtxt_question']
+ stanza = Message(to=jid, body=message, typ=properties.type.value)
+ self._con.connection.send_stanza(stanza)
+ self._log.info('Anti spam question sent to %s', jid)
+
+ def _subscribe_received(self, _con, _stanza, properties):
+ msg_from = properties.jid
+ block_sub = self._config['block_subscription_requests']
+ is_contact = app.contacts.get_contacts(self._account, msg_from)
+ if block_sub and not is_contact:
+ self._con.get_module('Presence').unsubscribed(msg_from)
+ self._log.info('Denied subscription request from %s' % msg_from)
+ raise NodeProcessed
+
+
+def get_instance(*args, **kwargs):
+ return AntiSpam(*args, **kwargs), 'AntiSpam'