community.general/lib/ansible/modules/notification/jabber.py
Jonas Schäfer afb48414b6 jabber: Fix joining group chats (#50846)
The previous code was using the legacy Group Chat 1.0 (GC1.0)
protocol to join [XEP-0045 Multi User Chats][1]. The legacy
protocol is described in § 7.2.1 of that document, the current
protocol is described in § 7.2.2.

The legacy protocol has not been in active use for more than ten
years, and servers are fading out support for it because its
presence causes issues (see for example the lengthy discussion
in [2], particularly the part starting with "A MUC
misunderstanding a presence update for a GC1.0 join").

The effect of servers fading out GC1.0 is that jabber.py cannot
send messages to rooms on servers which have done that step any
more.

This commit implements the modern join protocol, restoring
functionality. The modern join protocol is, to my knowledge,
supported by all XMPP servers which are still in use.

Prosody 0.11 is an example of a server implementation which does
not support GC1.0 anymore.

   [1]: https://xmpp.org/extensions/xep-0045.html
   [2]: https://mail.jabber.org/pipermail/standards/2017-October/033501.html
2019-01-14 22:30:48 +01:00

163 lines
4.3 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, Brian Coca <bcoca@ansible.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
version_added: "1.2"
module: jabber
short_description: Send a message to jabber user or chat room
description:
- Send a message to jabber
options:
user:
description:
- User as which to connect
required: true
password:
description:
- password for user to connect
required: true
to:
description:
- user ID or name of the room, when using room use a slash to indicate your nick.
required: true
msg:
description:
- The message body.
required: true
host:
description:
- host to connect, overrides user info
port:
description:
- port to connect to, overrides default
default: 5222
encoding:
description:
- message encoding
# informational: requirements for nodes
requirements:
- python xmpp (xmpppy)
author: "Brian Coca (@bcoca)"
'''
EXAMPLES = '''
# send a message to a user
- jabber:
user: mybot@example.net
password: secret
to: friend@example.net
msg: Ansible task finished
# send a message to a room
- jabber:
user: mybot@example.net
password: secret
to: mychaps@conference.example.net/ansiblebot
msg: Ansible task finished
# send a message, specifying the host and port
- jabber:
user: mybot@example.net
host: talk.example.net
port: 5223
password: secret
to: mychaps@example.net
msg: Ansible task finished
'''
import time
import traceback
HAS_XMPP = True
try:
import xmpp
except ImportError:
HAS_XMPP = False
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
def main():
module = AnsibleModule(
argument_spec=dict(
user=dict(required=True),
password=dict(required=True, no_log=True),
to=dict(required=True),
msg=dict(required=True),
host=dict(required=False),
port=dict(required=False, default=5222),
encoding=dict(required=False),
),
supports_check_mode=True
)
if not HAS_XMPP:
module.fail_json(msg="The required python xmpp library (xmpppy) is not installed")
jid = xmpp.JID(module.params['user'])
user = jid.getNode()
server = jid.getDomain()
port = module.params['port']
password = module.params['password']
try:
to, nick = module.params['to'].split('/', 1)
except ValueError:
to, nick = module.params['to'], None
if module.params['host']:
host = module.params['host']
else:
host = server
if module.params['encoding']:
xmpp.simplexml.ENCODING = module.params['encoding']
msg = xmpp.protocol.Message(body=module.params['msg'])
try:
conn = xmpp.Client(server, debug=[])
if not conn.connect(server=(host, port)):
module.fail_json(rc=1, msg='Failed to connect to server: %s' % (server))
if not conn.auth(user, password, 'Ansible'):
module.fail_json(rc=1, msg='Failed to authorize %s on: %s' % (user, server))
# some old servers require this, also the sleep following send
conn.sendInitPresence(requestRoster=0)
if nick: # sending to room instead of user, need to join
msg.setType('groupchat')
msg.setTag('x', namespace='http://jabber.org/protocol/muc#user')
join = xmpp.Presence(to=module.params['to'])
join.setTag('x', namespace='http://jabber.org/protocol/muc')
conn.send(join)
time.sleep(1)
else:
msg.setType('chat')
msg.setTo(to)
if not module.check_mode:
conn.send(msg)
time.sleep(1)
conn.disconnect()
except Exception as e:
module.fail_json(msg="unable to send msg: %s" % to_native(e), exception=traceback.format_exc())
module.exit_json(changed=False, to=to, user=user, msg=msg.getBody())
if __name__ == '__main__':
main()