diff sat/plugins/plugin_misc_email_invitation.py @ 3028:ab2696e34d29

Python 3 port: /!\ this is a huge commit /!\ starting from this commit, SàT is needs Python 3.6+ /!\ SàT maybe be instable or some feature may not work anymore, this will improve with time This patch port backend, bridge and frontends to Python 3. Roughly this has been done this way: - 2to3 tools has been applied (with python 3.7) - all references to python2 have been replaced with python3 (notably shebangs) - fixed files not handled by 2to3 (notably the shell script) - several manual fixes - fixed issues reported by Python 3 that where not handled in Python 2 - replaced "async" with "async_" when needed (it's a reserved word from Python 3.7) - replaced zope's "implements" with @implementer decorator - temporary hack to handle data pickled in database, as str or bytes may be returned, to be checked later - fixed hash comparison for password - removed some code which is not needed anymore with Python 3 - deactivated some code which needs to be checked (notably certificate validation) - tested with jp, fixed reported issues until some basic commands worked - ported Primitivus (after porting dependencies like urwid satext) - more manual fixes
author Goffi <goffi@goffi.org>
date Tue, 13 Aug 2019 19:08:41 +0200
parents 420897488080
children fee60f17ebac
line wrap: on
line diff
--- a/sat/plugins/plugin_misc_email_invitation.py	Wed Jul 31 11:31:22 2019 +0200
+++ b/sat/plugins/plugin_misc_email_invitation.py	Tue Aug 13 19:08:41 2019 +0200
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 
 # SAT plugin for file tansfer
@@ -41,23 +41,23 @@
     C.PI_RECOMMENDATIONS: ["IDENTITY"],
     C.PI_MAIN: "InvitationsPlugin",
     C.PI_HANDLER: "no",
-    C.PI_DESCRIPTION: _(u"""invitation of people without XMPP account""")
+    C.PI_DESCRIPTION: _("""invitation of people without XMPP account""")
 }
 
 
 SUFFIX_MAX = 5
-INVITEE_PROFILE_TPL = u"guest@@{uuid}"
-KEY_ID = u'id'
-KEY_JID = u'jid'
-KEY_CREATED = u'created'
-KEY_LAST_CONNECTION = u'last_connection'
-KEY_GUEST_PROFILE = u'guest_profile'
-KEY_PASSWORD = u'password'
-KEY_EMAILS_EXTRA = u'emails_extra'
-EXTRA_RESERVED = {KEY_ID, KEY_JID, KEY_CREATED, u'jid_', u'jid', KEY_LAST_CONNECTION,
+INVITEE_PROFILE_TPL = "guest@@{uuid}"
+KEY_ID = 'id'
+KEY_JID = 'jid'
+KEY_CREATED = 'created'
+KEY_LAST_CONNECTION = 'last_connection'
+KEY_GUEST_PROFILE = 'guest_profile'
+KEY_PASSWORD = 'password'
+KEY_EMAILS_EXTRA = 'emails_extra'
+EXTRA_RESERVED = {KEY_ID, KEY_JID, KEY_CREATED, 'jid_', 'jid', KEY_LAST_CONNECTION,
                   KEY_GUEST_PROFILE, KEY_PASSWORD, KEY_EMAILS_EXTRA}
-DEFAULT_SUBJECT = D_(u"You have been invited by {host_name} to {app_name}")
-DEFAULT_BODY = D_(u"""Hello {name}!
+DEFAULT_SUBJECT = D_("You have been invited by {host_name} to {app_name}")
+DEFAULT_BODY = D_("""Hello {name}!
 
 You have received an invitation from {host_name} to participate to "{app_name}".
 To join, you just have to click on the following URL:
@@ -73,34 +73,34 @@
 class InvitationsPlugin(object):
 
     def __init__(self, host):
-        log.info(_(u"plugin Invitations initialization"))
+        log.info(_("plugin Invitations initialization"))
         self.host = host
-        self.invitations = persistent.LazyPersistentBinaryDict(u'invitations')
+        self.invitations = persistent.LazyPersistentBinaryDict('invitations')
         host.bridge.addMethod("invitationCreate", ".plugin", in_sign='sasssssssssa{ss}s',
                               out_sign='a{ss}',
                               method=self._create,
-                              async=True)
+                              async_=True)
         host.bridge.addMethod("invitationGet", ".plugin", in_sign='s', out_sign='a{ss}',
                               method=self.get,
-                              async=True)
+                              async_=True)
         host.bridge.addMethod("invitationModify", ".plugin", in_sign='sa{ss}b',
                               out_sign='',
                               method=self._modify,
-                              async=True)
+                              async_=True)
         host.bridge.addMethod("invitationList", ".plugin", in_sign='s',
                               out_sign='a{sa{ss}}',
                               method=self._list,
-                              async=True)
+                              async_=True)
 
     def checkExtra(self, extra):
         if EXTRA_RESERVED.intersection(extra):
             raise ValueError(
-                _(u"You can't use following key(s) in extra, they are reserved: {}")
-                .format(u', '.join(EXTRA_RESERVED.intersection(extra))))
+                _("You can't use following key(s) in extra, they are reserved: {}")
+                .format(', '.join(EXTRA_RESERVED.intersection(extra))))
 
-    def _create(self, email=u'', emails_extra=None, jid_=u'', password=u'', name=u'',
-                host_name=u'', language=u'', url_template=u'', message_subject=u'',
-                message_body=u'', extra=None, profile=u''):
+    def _create(self, email='', emails_extra=None, jid_='', password='', name='',
+                host_name='', language='', url_template='', message_subject='',
+                message_body='', extra=None, profile=''):
         # XXX: we don't use **kwargs here to keep arguments name for introspection with
         #      D-Bus bridge
         if emails_extra is None:
@@ -109,10 +109,10 @@
         if extra is None:
             extra = {}
         else:
-            extra = {unicode(k): unicode(v) for k,v in extra.iteritems()}
+            extra = {str(k): str(v) for k,v in extra.items()}
 
         kwargs = {"extra": extra,
-                  KEY_EMAILS_EXTRA: [unicode(e) for e in emails_extra]
+                  KEY_EMAILS_EXTRA: [str(e) for e in emails_extra]
                   }
 
         # we need to be sure that values are unicode, else they won't be pickled correctly
@@ -121,7 +121,7 @@
                     "url_template", "message_subject", "message_body", "profile"):
             value = locals()[key]
             if value:
-                kwargs[key] = unicode(value)
+                kwargs[key] = str(value)
         d = self.create(**kwargs)
         def serialize(data):
             data[KEY_JID] = data[KEY_JID].full()
@@ -131,7 +131,7 @@
 
     @defer.inlineCallbacks
     def create(self, **kwargs):
-        ur"""Create an invitation
+        r"""Create an invitation
 
         This will create an XMPP account and a profile, and use a UUID to retrieve them.
         The profile is automatically generated in the form guest@@[UUID], this way they
@@ -194,29 +194,29 @@
         extra = kwargs.pop('extra', {})
         if set(kwargs).intersection(extra):
             raise ValueError(
-                _(u"You can't use following key(s) in both args and extra: {}").format(
-                u', '.join(set(kwargs).intersection(extra))))
+                _("You can't use following key(s) in both args and extra: {}").format(
+                ', '.join(set(kwargs).intersection(extra))))
 
         self.checkExtra(extra)
 
-        email = kwargs.pop(u'email', None)
-        emails_extra = kwargs.pop(u'emails_extra', [])
+        email = kwargs.pop('email', None)
+        emails_extra = kwargs.pop('emails_extra', [])
         if not email and emails_extra:
             raise ValueError(
-                _(u'You need to provide a main email address before using emails_extra'))
+                _('You need to provide a main email address before using emails_extra'))
 
         if (email is not None
             and not 'url_template' in kwargs
             and not 'message_body' in kwargs):
             raise ValueError(
-                _(u"You need to provide url_template if you use default message body"))
+                _("You need to provide url_template if you use default message body"))
 
         ## uuid
-        log.info(_(u"creating an invitation"))
-        id_ = unicode(shortuuid.uuid())
+        log.info(_("creating an invitation"))
+        id_ = str(shortuuid.uuid())
 
         ## XMPP account creation
-        password = kwargs.pop(u'password', None)
+        password = kwargs.pop('password', None)
         if password is None:
            password = utils.generatePassword()
         assert password
@@ -228,13 +228,13 @@
         #        saved and could be used to encrypt profile password.
         extra[KEY_PASSWORD] = password
 
-        jid_ = kwargs.pop(u'jid_', None)
+        jid_ = kwargs.pop('jid_', None)
         if not jid_:
             domain = self.host.memory.getConfig(None, 'xmpp_domain')
             if not domain:
                 # TODO: fallback to profile's domain
-                raise ValueError(_(u"You need to specify xmpp_domain in sat.conf"))
-            jid_ = u"invitation-{uuid}@{domain}".format(uuid=shortuuid.uuid(),
+                raise ValueError(_("You need to specify xmpp_domain in sat.conf"))
+            jid_ = "invitation-{uuid}@{domain}".format(uuid=shortuuid.uuid(),
                                                         domain=domain)
         jid_ = jid.JID(jid_)
         if jid_.user:
@@ -245,11 +245,11 @@
             except error.StanzaError as e:
                 prefix = jid_.user
                 idx = 0
-                while e.condition == u'conflict':
+                while e.condition == 'conflict':
                     if idx >= SUFFIX_MAX:
-                        raise exceptions.ConflictError(_(u"Can't create XMPP account"))
-                    jid_.user = prefix + '_' + unicode(idx)
-                    log.info(_(u"requested jid already exists, trying with {}".format(
+                        raise exceptions.ConflictError(_("Can't create XMPP account"))
+                    jid_.user = prefix + '_' + str(idx)
+                    log.info(_("requested jid already exists, trying with {}".format(
                         jid_.full())))
                     try:
                         yield self.host.plugins['XEP-0077'].registerNewAccount(jid_,
@@ -258,10 +258,10 @@
                         idx += 1
                     else:
                         break
-                if e.condition != u'conflict':
+                if e.condition != 'conflict':
                     raise e
 
-            log.info(_(u"account {jid_} created").format(jid_=jid_.full()))
+            log.info(_("account {jid_} created").format(jid_=jid_.full()))
 
         ## profile creation
 
@@ -273,66 +273,66 @@
                                         profile_key=guest_profile)
         yield self.host.memory.setParam("Password", password, "Connection",
                                         profile_key=guest_profile)
-        name = kwargs.pop(u'name', None)
+        name = kwargs.pop('name', None)
         if name is not None:
-            extra[u'name'] = name
+            extra['name'] = name
             try:
-                id_plugin = self.host.plugins[u'IDENTITY']
+                id_plugin = self.host.plugins['IDENTITY']
             except KeyError:
                 pass
             else:
                 yield self.host.connect(guest_profile, password)
                 guest_client = self.host.getClient(guest_profile)
-                yield id_plugin.setIdentity(guest_client, {u'nick': name})
+                yield id_plugin.setIdentity(guest_client, {'nick': name})
                 yield self.host.disconnect(guest_profile)
 
         ## email
-        language = kwargs.pop(u'language', None)
+        language = kwargs.pop('language', None)
         if language is not None:
-            extra[u'language'] = language.strip()
+            extra['language'] = language.strip()
 
         if email is not None:
-            extra[u'email'] = email
+            extra['email'] = email
             data_format.iter2dict(KEY_EMAILS_EXTRA, extra)
-            url_template = kwargs.pop(u'url_template', '')
+            url_template = kwargs.pop('url_template', '')
             format_args = {
-                u'uuid': id_,
-                u'app_name': C.APP_NAME,
-                u'app_url': C.APP_URL}
+                'uuid': id_,
+                'app_name': C.APP_NAME,
+                'app_url': C.APP_URL}
 
             if name is None:
-                format_args[u'name'] = email
+                format_args['name'] = email
             else:
-                format_args[u'name'] = name
+                format_args['name'] = name
 
-            profile = kwargs.pop(u'profile', None)
+            profile = kwargs.pop('profile', None)
             if profile is None:
-                format_args[u'profile'] = u''
+                format_args['profile'] = ''
             else:
-                format_args[u'profile'] = extra[u'profile'] = profile
+                format_args['profile'] = extra['profile'] = profile
 
-            host_name = kwargs.pop(u'host_name', None)
+            host_name = kwargs.pop('host_name', None)
             if host_name is None:
-                format_args[u'host_name'] = profile or _(u"somebody")
+                format_args['host_name'] = profile or _("somebody")
             else:
-                format_args[u'host_name'] = extra[u'host_name'] = host_name
+                format_args['host_name'] = extra['host_name'] = host_name
 
             invite_url = url_template.format(**format_args)
-            format_args[u'url'] = invite_url
+            format_args['url'] = invite_url
 
             yield sat_email.sendEmail(
                 self.host,
                 [email] + emails_extra,
-                (kwargs.pop(u'message_subject', None) or DEFAULT_SUBJECT).format(
+                (kwargs.pop('message_subject', None) or DEFAULT_SUBJECT).format(
                     **format_args),
-                (kwargs.pop(u'message_body', None) or DEFAULT_BODY).format(**format_args),
+                (kwargs.pop('message_body', None) or DEFAULT_BODY).format(**format_args),
             )
 
         ## extra data saving
         self.invitations[id_] = extra
 
         if kwargs:
-            log.warning(_(u"Not all arguments have been consumed: {}").format(kwargs))
+            log.warning(_("Not all arguments have been consumed: {}").format(kwargs))
 
         extra[KEY_ID] = id_
         extra[KEY_JID] = jid_
@@ -348,7 +348,7 @@
         return self.invitations[id_]
 
     def _modify(self, id_, new_extra, replace):
-        return self.modify(id_, {unicode(k): unicode(v) for k,v in new_extra.iteritems()},
+        return self.modify(id_, {str(k): str(v) for k,v in new_extra.items()},
                            replace)
 
     def modify(self, id_, new_extra, replace=False):
@@ -372,9 +372,9 @@
                         continue
             else:
                 new_data = current_data
-                for k,v in new_extra.iteritems():
+                for k,v in new_extra.items():
                     if k in EXTRA_RESERVED:
-                        log.warning(_(u"Skipping reserved key {key}".format(k)))
+                        log.warning(_("Skipping reserved key {key}".format(k)))
                         continue
                     if v:
                         new_data[k] = v
@@ -401,9 +401,9 @@
             C.PROF_KEY_NONE: don't filter invitations
         @return list(unicode): invitations uids
         """
-        invitations = yield self.invitations.items()
+        invitations = yield list(self.invitations.items())
         if profile != C.PROF_KEY_NONE:
-            invitations = {id_:data for id_, data in invitations.iteritems()
-                           if data.get(u'profile') == profile}
+            invitations = {id_:data for id_, data in invitations.items()
+                           if data.get('profile') == profile}
 
         defer.returnValue(invitations)