We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Update user password in commandline
A feature request, the ability to set/reset a user's password from the Mailu command line. Similar to the [user](https://mailu.io/master/cli.html#user) command:

```
docker-compose exec admin flask mailu password myuser example.net 'password123'
```

My particular interest is for the Demo server, some people find it necessary to change the admin password from time to time. Executing a full reset takes over a minute, in which period the Demo is not available. I don't want to execute it more frequent than daily. It would be nice to can just call above command every few minutes and always override the admin's password with the correct one. See also #987.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `core/admin/mailu/manage.py`
Content:
```
1 from mailu import models
2 
3 from flask import current_app as app
4 from flask import cli as flask_cli
5 
6 import flask
7 import os
8 import socket
9 import uuid
10 import click
11 
12 
13 db = models.db
14 
15 
16 @click.group()
17 def mailu(cls=flask_cli.FlaskGroup):
18     """ Mailu command line
19     """
20 
21 
22 @mailu.command()
23 @flask_cli.with_appcontext
24 def advertise():
25     """ Advertise this server against statistic services.
26     """
27     if os.path.isfile(app.config["INSTANCE_ID_PATH"]):
28         with open(app.config["INSTANCE_ID_PATH"], "r") as handle:
29             instance_id = handle.read()
30     else:
31         instance_id = str(uuid.uuid4())
32         with open(app.config["INSTANCE_ID_PATH"], "w") as handle:
33             handle.write(instance_id)
34     if not app.config["DISABLE_STATISTICS"]:
35         try:
36             socket.gethostbyname(app.config["STATS_ENDPOINT"].format(instance_id))
37         except:
38             pass
39 
40 
41 @mailu.command()
42 @click.argument('localpart')
43 @click.argument('domain_name')
44 @click.argument('password')
45 @flask_cli.with_appcontext
46 def admin(localpart, domain_name, password):
47     """ Create an admin user
48     """
49     domain = models.Domain.query.get(domain_name)
50     if not domain:
51         domain = models.Domain(name=domain_name)
52         db.session.add(domain)
53     user = models.User(
54         localpart=localpart,
55         domain=domain,
56         global_admin=True
57     )
58     user.set_password(password)
59     db.session.add(user)
60     db.session.commit()
61 
62 
63 @mailu.command()
64 @click.argument('localpart')
65 @click.argument('domain_name')
66 @click.argument('password')
67 @click.argument('hash_scheme', required=False)
68 @flask_cli.with_appcontext
69 def user(localpart, domain_name, password, hash_scheme=None):
70     """ Create a user
71     """
72     if hash_scheme is None:
73         hash_scheme = app.config['PASSWORD_SCHEME']
74     domain = models.Domain.query.get(domain_name)
75     if not domain:
76         domain = models.Domain(name=domain_name)
77         db.session.add(domain)
78     user = models.User(
79         localpart=localpart,
80         domain=domain,
81         global_admin=False
82     )
83     user.set_password(password, hash_scheme=hash_scheme)
84     db.session.add(user)
85     db.session.commit()
86 
87 
88 @mailu.command()
89 @click.argument('domain_name')
90 @click.option('-u', '--max-users')
91 @click.option('-a', '--max-aliases')
92 @click.option('-q', '--max-quota-bytes')
93 @flask_cli.with_appcontext
94 def domain(domain_name, max_users=-1, max_aliases=-1, max_quota_bytes=0):
95     """ Create a domain
96     """
97     domain = models.Domain.query.get(domain_name)
98     if not domain:
99         domain = models.Domain(name=domain_name, max_users=max_users,
100                                max_aliases=max_aliases, max_quota_bytes=max_quota_bytes)
101         db.session.add(domain)
102         db.session.commit()
103 
104 
105 @mailu.command()
106 @click.argument('localpart')
107 @click.argument('domain_name')
108 @click.argument('password_hash')
109 @click.argument('hash_scheme')
110 @flask_cli.with_appcontext
111 def user_import(localpart, domain_name, password_hash, hash_scheme = None):
112     """ Import a user along with password hash.
113     """
114     if hash_scheme is None:
115         hash_scheme = app.config['PASSWORD_SCHEME']
116     domain = models.Domain.query.get(domain_name)
117     if not domain:
118         domain = models.Domain(name=domain_name)
119         db.session.add(domain)
120     user = models.User(
121         localpart=localpart,
122         domain=domain,
123         global_admin=False
124     )
125     user.set_password(password_hash, hash_scheme=hash_scheme, raw=True)
126     db.session.add(user)
127     db.session.commit()
128 
129 
130 @mailu.command()
131 @click.option('-v', '--verbose')
132 @click.option('-d', '--delete-objects')
133 @flask_cli.with_appcontext
134 def config_update(verbose=False, delete_objects=False):
135     """sync configuration with data from YAML-formatted stdin"""
136     import yaml
137     import sys
138     new_config = yaml.load(sys.stdin)
139     # print new_config
140     domains = new_config.get('domains', [])
141     tracked_domains = set()
142     for domain_config in domains:
143         if verbose:
144             print(str(domain_config))
145         domain_name = domain_config['name']
146         max_users = domain_config.get('max_users', -1)
147         max_aliases = domain_config.get('max_aliases', -1)
148         max_quota_bytes = domain_config.get('max_quota_bytes', 0)
149         tracked_domains.add(domain_name)
150         domain = models.Domain.query.get(domain_name)
151         if not domain:
152             domain = models.Domain(name=domain_name,
153                                    max_users=max_users,
154                                    max_aliases=max_aliases,
155                                    max_quota_bytes=max_quota_bytes)
156             db.session.add(domain)
157             print("Added " + str(domain_config))
158         else:
159             domain.max_users = max_users
160             domain.max_aliases = max_aliases
161             domain.max_quota_bytes = max_quota_bytes
162             db.session.add(domain)
163             print("Updated " + str(domain_config))
164 
165     users = new_config.get('users', [])
166     tracked_users = set()
167     user_optional_params = ('comment', 'quota_bytes', 'global_admin',
168                             'enable_imap', 'enable_pop', 'forward_enabled',
169                             'forward_destination', 'reply_enabled',
170                             'reply_subject', 'reply_body', 'displayed_name',
171                             'spam_enabled', 'email', 'spam_threshold')
172     for user_config in users:
173         if verbose:
174             print(str(user_config))
175         localpart = user_config['localpart']
176         domain_name = user_config['domain']
177         password_hash = user_config.get('password_hash', None)
178         hash_scheme = user_config.get('hash_scheme', None)
179         domain = models.Domain.query.get(domain_name)
180         email = '{0}@{1}'.format(localpart, domain_name)
181         optional_params = {}
182         for k in user_optional_params:
183             if k in user_config:
184                 optional_params[k] = user_config[k]
185         if not domain:
186             domain = models.Domain(name=domain_name)
187             db.session.add(domain)
188         user = models.User.query.get(email)
189         tracked_users.add(email)
190         tracked_domains.add(domain_name)
191         if not user:
192             user = models.User(
193                 localpart=localpart,
194                 domain=domain,
195                 **optional_params
196             )
197         else:
198             for k in optional_params:
199                 setattr(user, k, optional_params[k])
200         user.set_password(password_hash, hash_scheme=hash_scheme, raw=True)
201         db.session.add(user)
202 
203     aliases = new_config.get('aliases', [])
204     tracked_aliases = set()
205     for alias_config in aliases:
206         if verbose:
207             print(str(alias_config))
208         localpart = alias_config['localpart']
209         domain_name = alias_config['domain']
210         if type(alias_config['destination']) is str:
211             destination = alias_config['destination'].split(',')
212         else:
213             destination = alias_config['destination']
214         wildcard = alias_config.get('wildcard', False)
215         domain = models.Domain.query.get(domain_name)
216         email = '{0}@{1}'.format(localpart, domain_name)
217         if not domain:
218             domain = models.Domain(name=domain_name)
219             db.session.add(domain)
220         alias = models.Alias.query.get(email)
221         tracked_aliases.add(email)
222         tracked_domains.add(domain_name)
223         if not alias:
224             alias = models.Alias(
225                 localpart=localpart,
226                 domain=domain,
227                 wildcard=wildcard,
228                 destination=destination,
229                 email=email
230             )
231         else:
232             alias.destination = destination
233             alias.wildcard = wildcard
234         db.session.add(alias)
235 
236     db.session.commit()
237 
238     managers = new_config.get('managers', [])
239     # tracked_managers=set()
240     for manager_config in managers:
241         if verbose:
242             print(str(manager_config))
243         domain_name = manager_config['domain']
244         user_name = manager_config['user']
245         domain = models.Domain.query.get(domain_name)
246         manageruser = models.User.query.get(user_name + '@' + domain_name)
247         if manageruser not in domain.managers:
248             domain.managers.append(manageruser)
249         db.session.add(domain)
250 
251     db.session.commit()
252 
253     if delete_objects:
254         for user in db.session.query(models.User).all():
255             if not (user.email in tracked_users):
256                 if verbose:
257                     print("Deleting user: " + str(user.email))
258                 db.session.delete(user)
259         for alias in db.session.query(models.Alias).all():
260             if not (alias.email in tracked_aliases):
261                 if verbose:
262                     print("Deleting alias: " + str(alias.email))
263                 db.session.delete(alias)
264         for domain in db.session.query(models.Domain).all():
265             if not (domain.name in tracked_domains):
266                 if verbose:
267                     print("Deleting domain: " + str(domain.name))
268                 db.session.delete(domain)
269     db.session.commit()
270 
271 
272 @mailu.command()
273 @click.argument('email')
274 @flask_cli.with_appcontext
275 def user_delete(email):
276     """delete user"""
277     user = models.User.query.get(email)
278     if user:
279         db.session.delete(user)
280     db.session.commit()
281 
282 
283 @mailu.command()
284 @click.argument('email')
285 @flask_cli.with_appcontext
286 def alias_delete(email):
287     """delete alias"""
288     alias = models.Alias.query.get(email)
289     if alias:
290         db.session.delete(alias)
291     db.session.commit()
292 
293 
294 @mailu.command()
295 @click.argument('localpart')
296 @click.argument('domain_name')
297 @click.argument('destination')
298 @click.option('-w', '--wildcard', is_flag=True)
299 @flask_cli.with_appcontext
300 def alias(localpart, domain_name, destination, wildcard=False):
301     """ Create an alias
302     """
303     domain = models.Domain.query.get(domain_name)
304     if not domain:
305         domain = models.Domain(name=domain_name)
306         db.session.add(domain)
307     alias = models.Alias(
308         localpart=localpart,
309         domain=domain,
310         wildcard=wildcard,
311         destination=destination.split(','),
312         email="%s@%s" % (localpart, domain_name)
313     )
314     db.session.add(alias)
315     db.session.commit()
316 
317 
318 @mailu.command()
319 @click.argument('domain_name')
320 @click.argument('max_users')
321 @click.argument('max_aliases')
322 @click.argument('max_quota_bytes')
323 @flask_cli.with_appcontext
324 def setlimits(domain_name, max_users, max_aliases, max_quota_bytes):
325     """ Set domain limits
326     """
327     domain = models.Domain.query.get(domain_name)
328     domain.max_users = max_users
329     domain.max_aliases = max_aliases
330     domain.max_quota_bytes = max_quota_bytes
331     db.session.add(domain)
332     db.session.commit()
333 
334 
335 @mailu.command()
336 @click.argument('domain_name')
337 @click.argument('user_name')
338 @flask_cli.with_appcontext
339 def setmanager(domain_name, user_name='manager'):
340     """ Make a user manager of a domain
341     """
342     domain = models.Domain.query.get(domain_name)
343     manageruser = models.User.query.get(user_name + '@' + domain_name)
344     domain.managers.append(manageruser)
345     db.session.add(domain)
346     db.session.commit()
347 
348 
349 if __name__ == '__main__':
350     cli()
351
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/core/admin/mailu/manage.py b/core/admin/mailu/manage.py
--- a/core/admin/mailu/manage.py
+++ b/core/admin/mailu/manage.py
@@ -85,6 +85,26 @@
     db.session.commit()
 
 
+@mailu.command()
+@click.argument('localpart')
+@click.argument('domain_name')
+@click.argument('password')
+@click.argument('hash_scheme', required=False)
+@flask_cli.with_appcontext
+def password(localpart, domain_name, password, hash_scheme=None):
+    """ Change the password of an user
+    """
+    email = '{0}@{1}'.format(localpart, domain_name)
+    user   = models.User.query.get(email)
+    if hash_scheme is None:
+        hash_scheme = app.config['PASSWORD_SCHEME']
+    if user:
+        user.set_password(password, hash_scheme=hash_scheme)
+    else:
+        print("User " + email + " not found.")
+    db.session.commit()
+
+
 @mailu.command()
 @click.argument('domain_name')
 @click.option('-u', '--max-users')

{"golden_diff": "diff --git a/core/admin/mailu/manage.py b/core/admin/mailu/manage.py\n--- a/core/admin/mailu/manage.py\n+++ b/core/admin/mailu/manage.py\n@@ -85,6 +85,26 @@\n     db.session.commit()\n \n \n+@mailu.command()\n+@click.argument('localpart')\n+@click.argument('domain_name')\n+@click.argument('password')\n+@click.argument('hash_scheme', required=False)\n+@flask_cli.with_appcontext\n+def password(localpart, domain_name, password, hash_scheme=None):\n+    \"\"\" Change the password of an user\n+    \"\"\"\n+    email = '{0}@{1}'.format(localpart, domain_name)\n+    user   = models.User.query.get(email)\n+    if hash_scheme is None:\n+        hash_scheme = app.config['PASSWORD_SCHEME']\n+    if user:\n+        user.set_password(password, hash_scheme=hash_scheme)\n+    else:\n+        print(\"User \" + email + \" not found.\")\n+    db.session.commit()\n+\n+\n @mailu.command()\n @click.argument('domain_name')\n @click.option('-u', '--max-users')\n", "issue": "Update user password in commandline\nA feature request, the ability to set/reset a user's password from the Mailu command line. Similar to the [user](https://mailu.io/master/cli.html#user) command:\r\n\r\n```\r\ndocker-compose exec admin flask mailu password myuser example.net 'password123'\r\n```\r\n\r\nMy particular interest is for the Demo server, some people find it necessary to change the admin password from time to time. Executing a full reset takes over a minute, in which period the Demo is not available. I don't want to execute it more frequent than daily. It would be nice to can just call above command every few minutes and always override the admin's password with the correct one. See also #987.\r\n\n", "before_files": [{"content": "from mailu import models\n\nfrom flask import current_app as app\nfrom flask import cli as flask_cli\n\nimport flask\nimport os\nimport socket\nimport uuid\nimport click\n\n\ndb = models.db\n\n\n@click.group()\ndef mailu(cls=flask_cli.FlaskGroup):\n    \"\"\" Mailu command line\n    \"\"\"\n\n\n@mailu.command()\n@flask_cli.with_appcontext\ndef advertise():\n    \"\"\" Advertise this server against statistic services.\n    \"\"\"\n    if os.path.isfile(app.config[\"INSTANCE_ID_PATH\"]):\n        with open(app.config[\"INSTANCE_ID_PATH\"], \"r\") as handle:\n            instance_id = handle.read()\n    else:\n        instance_id = str(uuid.uuid4())\n        with open(app.config[\"INSTANCE_ID_PATH\"], \"w\") as handle:\n            handle.write(instance_id)\n    if not app.config[\"DISABLE_STATISTICS\"]:\n        try:\n            socket.gethostbyname(app.config[\"STATS_ENDPOINT\"].format(instance_id))\n        except:\n            pass\n\n\n@mailu.command()\n@click.argument('localpart')\n@click.argument('domain_name')\n@click.argument('password')\n@flask_cli.with_appcontext\ndef admin(localpart, domain_name, password):\n    \"\"\" Create an admin user\n    \"\"\"\n    domain = models.Domain.query.get(domain_name)\n    if not domain:\n        domain = models.Domain(name=domain_name)\n        db.session.add(domain)\n    user = models.User(\n        localpart=localpart,\n        domain=domain,\n        global_admin=True\n    )\n    user.set_password(password)\n    db.session.add(user)\n    db.session.commit()\n\n\n@mailu.command()\n@click.argument('localpart')\n@click.argument('domain_name')\n@click.argument('password')\n@click.argument('hash_scheme', required=False)\n@flask_cli.with_appcontext\ndef user(localpart, domain_name, password, hash_scheme=None):\n    \"\"\" Create a user\n    \"\"\"\n    if hash_scheme is None:\n        hash_scheme = app.config['PASSWORD_SCHEME']\n    domain = models.Domain.query.get(domain_name)\n    if not domain:\n        domain = models.Domain(name=domain_name)\n        db.session.add(domain)\n    user = models.User(\n        localpart=localpart,\n        domain=domain,\n        global_admin=False\n    )\n    user.set_password(password, hash_scheme=hash_scheme)\n    db.session.add(user)\n    db.session.commit()\n\n\n@mailu.command()\n@click.argument('domain_name')\n@click.option('-u', '--max-users')\n@click.option('-a', '--max-aliases')\n@click.option('-q', '--max-quota-bytes')\n@flask_cli.with_appcontext\ndef domain(domain_name, max_users=-1, max_aliases=-1, max_quota_bytes=0):\n    \"\"\" Create a domain\n    \"\"\"\n    domain = models.Domain.query.get(domain_name)\n    if not domain:\n        domain = models.Domain(name=domain_name, max_users=max_users,\n                               max_aliases=max_aliases, max_quota_bytes=max_quota_bytes)\n        db.session.add(domain)\n        db.session.commit()\n\n\n@mailu.command()\n@click.argument('localpart')\n@click.argument('domain_name')\n@click.argument('password_hash')\n@click.argument('hash_scheme')\n@flask_cli.with_appcontext\ndef user_import(localpart, domain_name, password_hash, hash_scheme = None):\n    \"\"\" Import a user along with password hash.\n    \"\"\"\n    if hash_scheme is None:\n        hash_scheme = app.config['PASSWORD_SCHEME']\n    domain = models.Domain.query.get(domain_name)\n    if not domain:\n        domain = models.Domain(name=domain_name)\n        db.session.add(domain)\n    user = models.User(\n        localpart=localpart,\n        domain=domain,\n        global_admin=False\n    )\n    user.set_password(password_hash, hash_scheme=hash_scheme, raw=True)\n    db.session.add(user)\n    db.session.commit()\n\n\n@mailu.command()\n@click.option('-v', '--verbose')\n@click.option('-d', '--delete-objects')\n@flask_cli.with_appcontext\ndef config_update(verbose=False, delete_objects=False):\n    \"\"\"sync configuration with data from YAML-formatted stdin\"\"\"\n    import yaml\n    import sys\n    new_config = yaml.load(sys.stdin)\n    # print new_config\n    domains = new_config.get('domains', [])\n    tracked_domains = set()\n    for domain_config in domains:\n        if verbose:\n            print(str(domain_config))\n        domain_name = domain_config['name']\n        max_users = domain_config.get('max_users', -1)\n        max_aliases = domain_config.get('max_aliases', -1)\n        max_quota_bytes = domain_config.get('max_quota_bytes', 0)\n        tracked_domains.add(domain_name)\n        domain = models.Domain.query.get(domain_name)\n        if not domain:\n            domain = models.Domain(name=domain_name,\n                                   max_users=max_users,\n                                   max_aliases=max_aliases,\n                                   max_quota_bytes=max_quota_bytes)\n            db.session.add(domain)\n            print(\"Added \" + str(domain_config))\n        else:\n            domain.max_users = max_users\n            domain.max_aliases = max_aliases\n            domain.max_quota_bytes = max_quota_bytes\n            db.session.add(domain)\n            print(\"Updated \" + str(domain_config))\n\n    users = new_config.get('users', [])\n    tracked_users = set()\n    user_optional_params = ('comment', 'quota_bytes', 'global_admin',\n                            'enable_imap', 'enable_pop', 'forward_enabled',\n                            'forward_destination', 'reply_enabled',\n                            'reply_subject', 'reply_body', 'displayed_name',\n                            'spam_enabled', 'email', 'spam_threshold')\n    for user_config in users:\n        if verbose:\n            print(str(user_config))\n        localpart = user_config['localpart']\n        domain_name = user_config['domain']\n        password_hash = user_config.get('password_hash', None)\n        hash_scheme = user_config.get('hash_scheme', None)\n        domain = models.Domain.query.get(domain_name)\n        email = '{0}@{1}'.format(localpart, domain_name)\n        optional_params = {}\n        for k in user_optional_params:\n            if k in user_config:\n                optional_params[k] = user_config[k]\n        if not domain:\n            domain = models.Domain(name=domain_name)\n            db.session.add(domain)\n        user = models.User.query.get(email)\n        tracked_users.add(email)\n        tracked_domains.add(domain_name)\n        if not user:\n            user = models.User(\n                localpart=localpart,\n                domain=domain,\n                **optional_params\n            )\n        else:\n            for k in optional_params:\n                setattr(user, k, optional_params[k])\n        user.set_password(password_hash, hash_scheme=hash_scheme, raw=True)\n        db.session.add(user)\n\n    aliases = new_config.get('aliases', [])\n    tracked_aliases = set()\n    for alias_config in aliases:\n        if verbose:\n            print(str(alias_config))\n        localpart = alias_config['localpart']\n        domain_name = alias_config['domain']\n        if type(alias_config['destination']) is str:\n            destination = alias_config['destination'].split(',')\n        else:\n            destination = alias_config['destination']\n        wildcard = alias_config.get('wildcard', False)\n        domain = models.Domain.query.get(domain_name)\n        email = '{0}@{1}'.format(localpart, domain_name)\n        if not domain:\n            domain = models.Domain(name=domain_name)\n            db.session.add(domain)\n        alias = models.Alias.query.get(email)\n        tracked_aliases.add(email)\n        tracked_domains.add(domain_name)\n        if not alias:\n            alias = models.Alias(\n                localpart=localpart,\n                domain=domain,\n                wildcard=wildcard,\n                destination=destination,\n                email=email\n            )\n        else:\n            alias.destination = destination\n            alias.wildcard = wildcard\n        db.session.add(alias)\n\n    db.session.commit()\n\n    managers = new_config.get('managers', [])\n    # tracked_managers=set()\n    for manager_config in managers:\n        if verbose:\n            print(str(manager_config))\n        domain_name = manager_config['domain']\n        user_name = manager_config['user']\n        domain = models.Domain.query.get(domain_name)\n        manageruser = models.User.query.get(user_name + '@' + domain_name)\n        if manageruser not in domain.managers:\n            domain.managers.append(manageruser)\n        db.session.add(domain)\n\n    db.session.commit()\n\n    if delete_objects:\n        for user in db.session.query(models.User).all():\n            if not (user.email in tracked_users):\n                if verbose:\n                    print(\"Deleting user: \" + str(user.email))\n                db.session.delete(user)\n        for alias in db.session.query(models.Alias).all():\n            if not (alias.email in tracked_aliases):\n                if verbose:\n                    print(\"Deleting alias: \" + str(alias.email))\n                db.session.delete(alias)\n        for domain in db.session.query(models.Domain).all():\n            if not (domain.name in tracked_domains):\n                if verbose:\n                    print(\"Deleting domain: \" + str(domain.name))\n                db.session.delete(domain)\n    db.session.commit()\n\n\n@mailu.command()\n@click.argument('email')\n@flask_cli.with_appcontext\ndef user_delete(email):\n    \"\"\"delete user\"\"\"\n    user = models.User.query.get(email)\n    if user:\n        db.session.delete(user)\n    db.session.commit()\n\n\n@mailu.command()\n@click.argument('email')\n@flask_cli.with_appcontext\ndef alias_delete(email):\n    \"\"\"delete alias\"\"\"\n    alias = models.Alias.query.get(email)\n    if alias:\n        db.session.delete(alias)\n    db.session.commit()\n\n\n@mailu.command()\n@click.argument('localpart')\n@click.argument('domain_name')\n@click.argument('destination')\n@click.option('-w', '--wildcard', is_flag=True)\n@flask_cli.with_appcontext\ndef alias(localpart, domain_name, destination, wildcard=False):\n    \"\"\" Create an alias\n    \"\"\"\n    domain = models.Domain.query.get(domain_name)\n    if not domain:\n        domain = models.Domain(name=domain_name)\n        db.session.add(domain)\n    alias = models.Alias(\n        localpart=localpart,\n        domain=domain,\n        wildcard=wildcard,\n        destination=destination.split(','),\n        email=\"%s@%s\" % (localpart, domain_name)\n    )\n    db.session.add(alias)\n    db.session.commit()\n\n\n@mailu.command()\n@click.argument('domain_name')\n@click.argument('max_users')\n@click.argument('max_aliases')\n@click.argument('max_quota_bytes')\n@flask_cli.with_appcontext\ndef setlimits(domain_name, max_users, max_aliases, max_quota_bytes):\n    \"\"\" Set domain limits\n    \"\"\"\n    domain = models.Domain.query.get(domain_name)\n    domain.max_users = max_users\n    domain.max_aliases = max_aliases\n    domain.max_quota_bytes = max_quota_bytes\n    db.session.add(domain)\n    db.session.commit()\n\n\n@mailu.command()\n@click.argument('domain_name')\n@click.argument('user_name')\n@flask_cli.with_appcontext\ndef setmanager(domain_name, user_name='manager'):\n    \"\"\" Make a user manager of a domain\n    \"\"\"\n    domain = models.Domain.query.get(domain_name)\n    manageruser = models.User.query.get(user_name + '@' + domain_name)\n    domain.managers.append(manageruser)\n    db.session.add(domain)\n    db.session.commit()\n\n\nif __name__ == '__main__':\n    cli()\n", "path": "core/admin/mailu/manage.py"}], "after_files": [{"content": "from mailu import models\n\nfrom flask import current_app as app\nfrom flask import cli as flask_cli\n\nimport flask\nimport os\nimport socket\nimport uuid\nimport click\n\n\ndb = models.db\n\n\n@click.group()\ndef mailu(cls=flask_cli.FlaskGroup):\n    \"\"\" Mailu command line\n    \"\"\"\n\n\n@mailu.command()\n@flask_cli.with_appcontext\ndef advertise():\n    \"\"\" Advertise this server against statistic services.\n    \"\"\"\n    if os.path.isfile(app.config[\"INSTANCE_ID_PATH\"]):\n        with open(app.config[\"INSTANCE_ID_PATH\"], \"r\") as handle:\n            instance_id = handle.read()\n    else:\n        instance_id = str(uuid.uuid4())\n        with open(app.config[\"INSTANCE_ID_PATH\"], \"w\") as handle:\n            handle.write(instance_id)\n    if not app.config[\"DISABLE_STATISTICS\"]:\n        try:\n            socket.gethostbyname(app.config[\"STATS_ENDPOINT\"].format(instance_id))\n        except:\n            pass\n\n\n@mailu.command()\n@click.argument('localpart')\n@click.argument('domain_name')\n@click.argument('password')\n@flask_cli.with_appcontext\ndef admin(localpart, domain_name, password):\n    \"\"\" Create an admin user\n    \"\"\"\n    domain = models.Domain.query.get(domain_name)\n    if not domain:\n        domain = models.Domain(name=domain_name)\n        db.session.add(domain)\n    user = models.User(\n        localpart=localpart,\n        domain=domain,\n        global_admin=True\n    )\n    user.set_password(password)\n    db.session.add(user)\n    db.session.commit()\n\n\n@mailu.command()\n@click.argument('localpart')\n@click.argument('domain_name')\n@click.argument('password')\n@click.argument('hash_scheme', required=False)\n@flask_cli.with_appcontext\ndef user(localpart, domain_name, password, hash_scheme=None):\n    \"\"\" Create a user\n    \"\"\"\n    if hash_scheme is None:\n        hash_scheme = app.config['PASSWORD_SCHEME']\n    domain = models.Domain.query.get(domain_name)\n    if not domain:\n        domain = models.Domain(name=domain_name)\n        db.session.add(domain)\n    user = models.User(\n        localpart=localpart,\n        domain=domain,\n        global_admin=False\n    )\n    user.set_password(password, hash_scheme=hash_scheme)\n    db.session.add(user)\n    db.session.commit()\n\n\n@mailu.command()\n@click.argument('localpart')\n@click.argument('domain_name')\n@click.argument('password')\n@click.argument('hash_scheme', required=False)\n@flask_cli.with_appcontext\ndef password(localpart, domain_name, password, hash_scheme=None):\n    \"\"\" Change the password of an user\n    \"\"\"\n    email = '{0}@{1}'.format(localpart, domain_name)\n    user   = models.User.query.get(email)\n    if hash_scheme is None:\n        hash_scheme = app.config['PASSWORD_SCHEME']\n    if user:\n        user.set_password(password, hash_scheme=hash_scheme)\n    else:\n        print(\"User \" + email + \" not found.\")\n    db.session.commit()\n\n\n@mailu.command()\n@click.argument('domain_name')\n@click.option('-u', '--max-users')\n@click.option('-a', '--max-aliases')\n@click.option('-q', '--max-quota-bytes')\n@flask_cli.with_appcontext\ndef domain(domain_name, max_users=-1, max_aliases=-1, max_quota_bytes=0):\n    \"\"\" Create a domain\n    \"\"\"\n    domain = models.Domain.query.get(domain_name)\n    if not domain:\n        domain = models.Domain(name=domain_name, max_users=max_users,\n                               max_aliases=max_aliases, max_quota_bytes=max_quota_bytes)\n        db.session.add(domain)\n        db.session.commit()\n\n\n@mailu.command()\n@click.argument('localpart')\n@click.argument('domain_name')\n@click.argument('password_hash')\n@click.argument('hash_scheme')\n@flask_cli.with_appcontext\ndef user_import(localpart, domain_name, password_hash, hash_scheme = None):\n    \"\"\" Import a user along with password hash.\n    \"\"\"\n    if hash_scheme is None:\n        hash_scheme = app.config['PASSWORD_SCHEME']\n    domain = models.Domain.query.get(domain_name)\n    if not domain:\n        domain = models.Domain(name=domain_name)\n        db.session.add(domain)\n    user = models.User(\n        localpart=localpart,\n        domain=domain,\n        global_admin=False\n    )\n    user.set_password(password_hash, hash_scheme=hash_scheme, raw=True)\n    db.session.add(user)\n    db.session.commit()\n\n\n@mailu.command()\n@click.option('-v', '--verbose')\n@click.option('-d', '--delete-objects')\n@flask_cli.with_appcontext\ndef config_update(verbose=False, delete_objects=False):\n    \"\"\"sync configuration with data from YAML-formatted stdin\"\"\"\n    import yaml\n    import sys\n    new_config = yaml.load(sys.stdin)\n    # print new_config\n    domains = new_config.get('domains', [])\n    tracked_domains = set()\n    for domain_config in domains:\n        if verbose:\n            print(str(domain_config))\n        domain_name = domain_config['name']\n        max_users = domain_config.get('max_users', -1)\n        max_aliases = domain_config.get('max_aliases', -1)\n        max_quota_bytes = domain_config.get('max_quota_bytes', 0)\n        tracked_domains.add(domain_name)\n        domain = models.Domain.query.get(domain_name)\n        if not domain:\n            domain = models.Domain(name=domain_name,\n                                   max_users=max_users,\n                                   max_aliases=max_aliases,\n                                   max_quota_bytes=max_quota_bytes)\n            db.session.add(domain)\n            print(\"Added \" + str(domain_config))\n        else:\n            domain.max_users = max_users\n            domain.max_aliases = max_aliases\n            domain.max_quota_bytes = max_quota_bytes\n            db.session.add(domain)\n            print(\"Updated \" + str(domain_config))\n\n    users = new_config.get('users', [])\n    tracked_users = set()\n    user_optional_params = ('comment', 'quota_bytes', 'global_admin',\n                            'enable_imap', 'enable_pop', 'forward_enabled',\n                            'forward_destination', 'reply_enabled',\n                            'reply_subject', 'reply_body', 'displayed_name',\n                            'spam_enabled', 'email', 'spam_threshold')\n    for user_config in users:\n        if verbose:\n            print(str(user_config))\n        localpart = user_config['localpart']\n        domain_name = user_config['domain']\n        password_hash = user_config.get('password_hash', None)\n        hash_scheme = user_config.get('hash_scheme', None)\n        domain = models.Domain.query.get(domain_name)\n        email = '{0}@{1}'.format(localpart, domain_name)\n        optional_params = {}\n        for k in user_optional_params:\n            if k in user_config:\n                optional_params[k] = user_config[k]\n        if not domain:\n            domain = models.Domain(name=domain_name)\n            db.session.add(domain)\n        user = models.User.query.get(email)\n        tracked_users.add(email)\n        tracked_domains.add(domain_name)\n        if not user:\n            user = models.User(\n                localpart=localpart,\n                domain=domain,\n                **optional_params\n            )\n        else:\n            for k in optional_params:\n                setattr(user, k, optional_params[k])\n        user.set_password(password_hash, hash_scheme=hash_scheme, raw=True)\n        db.session.add(user)\n\n    aliases = new_config.get('aliases', [])\n    tracked_aliases = set()\n    for alias_config in aliases:\n        if verbose:\n            print(str(alias_config))\n        localpart = alias_config['localpart']\n        domain_name = alias_config['domain']\n        if type(alias_config['destination']) is str:\n            destination = alias_config['destination'].split(',')\n        else:\n            destination = alias_config['destination']\n        wildcard = alias_config.get('wildcard', False)\n        domain = models.Domain.query.get(domain_name)\n        email = '{0}@{1}'.format(localpart, domain_name)\n        if not domain:\n            domain = models.Domain(name=domain_name)\n            db.session.add(domain)\n        alias = models.Alias.query.get(email)\n        tracked_aliases.add(email)\n        tracked_domains.add(domain_name)\n        if not alias:\n            alias = models.Alias(\n                localpart=localpart,\n                domain=domain,\n                wildcard=wildcard,\n                destination=destination,\n                email=email\n            )\n        else:\n            alias.destination = destination\n            alias.wildcard = wildcard\n        db.session.add(alias)\n\n    db.session.commit()\n\n    managers = new_config.get('managers', [])\n    # tracked_managers=set()\n    for manager_config in managers:\n        if verbose:\n            print(str(manager_config))\n        domain_name = manager_config['domain']\n        user_name = manager_config['user']\n        domain = models.Domain.query.get(domain_name)\n        manageruser = models.User.query.get(user_name + '@' + domain_name)\n        if manageruser not in domain.managers:\n            domain.managers.append(manageruser)\n        db.session.add(domain)\n\n    db.session.commit()\n\n    if delete_objects:\n        for user in db.session.query(models.User).all():\n            if not (user.email in tracked_users):\n                if verbose:\n                    print(\"Deleting user: \" + str(user.email))\n                db.session.delete(user)\n        for alias in db.session.query(models.Alias).all():\n            if not (alias.email in tracked_aliases):\n                if verbose:\n                    print(\"Deleting alias: \" + str(alias.email))\n                db.session.delete(alias)\n        for domain in db.session.query(models.Domain).all():\n            if not (domain.name in tracked_domains):\n                if verbose:\n                    print(\"Deleting domain: \" + str(domain.name))\n                db.session.delete(domain)\n    db.session.commit()\n\n\n@mailu.command()\n@click.argument('email')\n@flask_cli.with_appcontext\ndef user_delete(email):\n    \"\"\"delete user\"\"\"\n    user = models.User.query.get(email)\n    if user:\n        db.session.delete(user)\n    db.session.commit()\n\n\n@mailu.command()\n@click.argument('email')\n@flask_cli.with_appcontext\ndef alias_delete(email):\n    \"\"\"delete alias\"\"\"\n    alias = models.Alias.query.get(email)\n    if alias:\n        db.session.delete(alias)\n    db.session.commit()\n\n\n@mailu.command()\n@click.argument('localpart')\n@click.argument('domain_name')\n@click.argument('destination')\n@click.option('-w', '--wildcard', is_flag=True)\n@flask_cli.with_appcontext\ndef alias(localpart, domain_name, destination, wildcard=False):\n    \"\"\" Create an alias\n    \"\"\"\n    domain = models.Domain.query.get(domain_name)\n    if not domain:\n        domain = models.Domain(name=domain_name)\n        db.session.add(domain)\n    alias = models.Alias(\n        localpart=localpart,\n        domain=domain,\n        wildcard=wildcard,\n        destination=destination.split(','),\n        email=\"%s@%s\" % (localpart, domain_name)\n    )\n    db.session.add(alias)\n    db.session.commit()\n\n\n@mailu.command()\n@click.argument('domain_name')\n@click.argument('max_users')\n@click.argument('max_aliases')\n@click.argument('max_quota_bytes')\n@flask_cli.with_appcontext\ndef setlimits(domain_name, max_users, max_aliases, max_quota_bytes):\n    \"\"\" Set domain limits\n    \"\"\"\n    domain = models.Domain.query.get(domain_name)\n    domain.max_users = max_users\n    domain.max_aliases = max_aliases\n    domain.max_quota_bytes = max_quota_bytes\n    db.session.add(domain)\n    db.session.commit()\n\n\n@mailu.command()\n@click.argument('domain_name')\n@click.argument('user_name')\n@flask_cli.with_appcontext\ndef setmanager(domain_name, user_name='manager'):\n    \"\"\" Make a user manager of a domain\n    \"\"\"\n    domain = models.Domain.query.get(domain_name)\n    manageruser = models.User.query.get(user_name + '@' + domain_name)\n    domain.managers.append(manageruser)\n    db.session.add(domain)\n    db.session.commit()\n\n\nif __name__ == '__main__':\n    cli()\n", "path": "core/admin/mailu/manage.py"}]}