diff --git a/doc/additional/migrating.rst b/doc/additional/migrating.rst
index 22acee28b..a094bbe77 100644
--- a/doc/additional/migrating.rst
+++ b/doc/additional/migrating.rst
@@ -6,56 +6,4 @@ Migrating from other software
PostfixAdmin
************
-Modoboa provides a simple script to migrate an
-existing `PostfixAdmin (version 2.3.3+)
-`_ database to a Modoboa one.
-
-.. note::
- This script is only suitable for a new installation.
-
-First, you must follow the :ref:`installation` step to create a fresh
-Modoboa database.
-
-Once done, edit the :file:`settings.py` file. First, add a new database
-connection named ``pfxadmin`` into the ``DATABASES`` variable
-corresponding to your PostfixAdmin setup::
-
- DATABASES = {
- "default" : {
- # default connection definition
- },
- "pfxadmin" : {
- "ENGINE" : "",
- "NAME" : "",
- "USER" : "",
- "PASSWORD" : "",
- }
- }
-
-This connection should correspond to the one defined in PostfixAdmin's
-configuration file.
-
-Then, uncomment the line containing
-``'modoboa.tools.pfxadmin_migrate'`` inside the ``MODOBOA_APPS``
-variable and save your changes.
-
-You are now ready to start the migration so run the following commands::
-
- $ cd
- $ python manage.py migrate_from_postfixadmin -s
-
-```` must be replaced by the scheme used within
-postfixadmin (``crypt`` most of the time).
-
-Depending on how many domains/mailboxes your existing setup contains,
-the migration can be long. Just wait for the script's ending.
-
-The procedure is over, edit the :file:`settings.py` file and:
-
-* remove the ``pfxadmin`` database connection from the ``DATABASES``
- variable
-* remove the ``'modoboa.tools.pfxadmin_migrate',`` from the
- ``MODOBOA_APPS`` variable
-
-You should be able to connect to Modoboa using the same credentials
-you were using to connect to PostfixAdmin.
+`Official repository `_.
diff --git a/modoboa/tools/__init__.py b/modoboa/tools/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/modoboa/tools/pfxadmin_migrate/__init__.py b/modoboa/tools/pfxadmin_migrate/__init__.py
deleted file mode 100755
index e69de29bb..000000000
diff --git a/modoboa/tools/pfxadmin_migrate/management/__init__.py b/modoboa/tools/pfxadmin_migrate/management/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/modoboa/tools/pfxadmin_migrate/management/commands/__init__.py b/modoboa/tools/pfxadmin_migrate/management/commands/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/modoboa/tools/pfxadmin_migrate/management/commands/migrate_from_postfixadmin.py b/modoboa/tools/pfxadmin_migrate/management/commands/migrate_from_postfixadmin.py
deleted file mode 100644
index 4f22bf79b..000000000
--- a/modoboa/tools/pfxadmin_migrate/management/commands/migrate_from_postfixadmin.py
+++ /dev/null
@@ -1,290 +0,0 @@
-# coding: utf-8
-
-"""Automatic postfixadmin to Modoboa migration
-===========================================
-
-This script provides an easy way to migrate an existing postfixadmin
-database to a Modoboa one.
-
-As the two products do not share the same schema, some information
-will be lost on the new database. Here is the list:
- * Description, transport and backup MX for domains
- * Logs
- * Fetchmail table
-
-Domain administrators that manage more than one domain will not be
-completly restored. They will only be administrator of the domain that
-corresponds to their username.
-
-Vacation messages will not be migrated by this script. Users will have
-to define new messages by using Modoboa's autoreply plugin. (if
-activated)
-
-One last point about passwords. PostfixAdmin uses different hash
-algorithms to store passwords so this script does not modify
-them. They are copied "as is" in the new database. In order to use
-them, use the ``--passwords-scheme`` to tell Modoboa which scheme was
-used by PostfixAdmin.
-
-.. note::
-
- Actually, it appears that the ``crypt`` scheme is compatible with
- PostfixAdmin ``md5crypt`` algorithm...
-
-"""
-from optparse import make_option
-import re
-
-from django.contrib.auth.models import Group
-from django.core.management.base import BaseCommand
-from django.db import transaction
-
-import modoboa.core.models as core_models
-import modoboa.extensions.admin.models as admin_models
-from modoboa.lib.email_utils import split_mailbox
-from modoboa.extensions.admin import grant_access_to_all_objects
-
-from modoboa.tools.pfxadmin_migrate import models as pf_models
-
-
-def set_account_password(account, password, scheme):
- """Correclty format the password before storing it.
-
- It seems postfixadmin sometimes prepend the scheme... but not
- always :p
-
- """
- if re.search(r"\{[\w-]+\}", password):
- account.password = password
- else:
- account.password = "{%s}%s" % (scheme.upper(), password)
-
-
-class Command(BaseCommand):
-
- """Migration from postfixadmin."""
-
- help = "This command eases the migration from a postfixadmin database."
- option_list = BaseCommand.option_list + (
- make_option(
- "-f", "--from", dest="_from", default="pfxadmin",
- help="Name of postfixadmin db connection declared in settings.py"
- "(default is pfxadmin)"
- ),
- make_option(
- "-t", "--to", dest="_to", default="default",
- help="Name of the Modoboa db connection declared in settings.py"
- " (default is default)"
- ),
- make_option(
- "-s", "--passwords-scheme", default="crypt",
- help="Scheme used to crypt user passwords"
- )
- )
-
- def _migrate_dates(self, oldobj):
- """Creates a new ObjectDates instance.
-
- Due to Django limitations, we only retrieve the creation date. The
- modification date will be set to 'now' because the corresponding
- field in Modoboa's schema has the 'auto_now' attribute to True.
- (see https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.DateField).
-
- :param oldobj: the PostfixAdmin record which is beeing migrated.
- """
- dates = admin_models.base.ObjectDates()
- dates.save()
-
- dates.creation = oldobj.created
- dates.save()
- return dates
-
- def _migrate_domain_aliases(self, domain, options, creator):
- """Migrate aliases of a single domain."""
- print "\tMigrating domain aliases"
- old_domain_aliases = pf_models.AliasDomain.objects.using(
- options["_from"]
- ).filter(target_domain=domain.name)
- for old_da in old_domain_aliases:
- try:
- target_domain = admin_models.Domain.objects \
- .using(options["_to"]).get(name=old_da.target_domain)
- new_da = admin_models.DomainAlias()
- new_da.name = old_da.alias_domain
- new_da.target = target_domain
- new_da.enabled = old_da.active
- new_da.dates = self._migrate_dates(old_da)
- new_da.save(using=options["_to"])
- new_da.post_create(creator)
- except core_models.Domain.DoesNotExist:
- print ("Warning: target domain %s does not exists, "
- "not creating alias domain %s"
- % old_da.target_domain, old_da.alias_domain)
- continue
-
- def _migrate_mailbox_aliases(self, domain, options, creator):
- """Migrate mailbox aliases of a single domain."""
- print "\tMigrating mailbox aliases"
- old_aliases = pf_models.Alias.objects \
- .using(options["_from"]).filter(domain=domain.name)
- for old_al in old_aliases:
- if old_al.address == old_al.goto:
- continue
- new_al = admin_models.Alias()
- local_part, tmp = split_mailbox(old_al.address)
- if local_part is None or not len(local_part):
- if tmp is None or not len(tmp):
- print (
- "Warning: skipping alias %s (cannot retrieve local "
- "part). You will need to recreate it manually."
- % old_al.address
- )
- continue
- new_al.address = "*"
- else:
- new_al.address = local_part
- new_al.domain = domain
- new_al.enabled = old_al.active
- extmboxes = []
- intmboxes = []
- for goto in old_al.goto.split(","):
- try:
- mb = admin_models.Mailbox.objects \
- .using(options["_to"]).get(user__username=goto)
- except admin_models.Mailbox.DoesNotExist:
- extmboxes += [goto]
- else:
- intmboxes += [mb]
- new_al.dates = self._migrate_dates(old_al)
- new_al.save(
- int_rcpts=intmboxes, ext_rcpts=extmboxes, creator=creator,
- using=options["_to"]
- )
-
- def _migrate_mailboxes(self, domain, options, creator):
- """Migrate mailboxes of a single domain."""
- print "\tMigrating mailboxes"
- old_mboxes = pf_models.Mailbox.objects \
- .using(options["_from"]).filter(domain=domain.name)
- for old_mb in old_mboxes:
- new_user = core_models.User()
- new_user.username = old_mb.username
- new_user.first_name = old_mb.name.partition(' ')[0]
- new_user.last_name = old_mb.name.partition(' ')[2]
- new_user.email = old_mb.username
- new_user.is_active = old_mb.active
- new_user.date_joined = old_mb.created
- set_account_password(
- new_user, old_mb.password, options["passwords_scheme"])
- new_user.dates = self._migrate_dates(old_mb)
- new_user.save(creator=creator, using=options["_to"])
- new_user.set_role("SimpleUsers")
-
- new_mb = admin_models.Mailbox()
- new_mb.user = new_user
- new_mb.address = old_mb.local_part
- new_mb.domain = domain
- new_mb.dates = self._migrate_dates(old_mb)
- new_mb.set_quota(old_mb.quota / 1024000, override_rules=True)
- new_mb.save(creator=creator, using=options["_to"])
-
- def _migrate_domain(self, pf_domain, options, creator):
- """Single domain migration."""
- print "\nMigrating domain %s" % pf_domain.domain
- newdom = admin_models.Domain()
- newdom.name = pf_domain.domain
- newdom.enabled = pf_domain.active
- newdom.quota = pf_domain.maxquota
- newdom.dates = self._migrate_dates(pf_domain)
- newdom.save(creator=creator, using=options["_to"])
- self._migrate_domain_aliases(newdom, options, creator)
- self._migrate_mailboxes(newdom, options, creator)
- self._migrate_mailbox_aliases(newdom, options, creator)
- self._migrate_admins(options, creator, pf_domain, newdom)
-
- def _migrate_admins(self, options, creator, pf_domain, newdom=None):
- """Administrators migration.
-
- :param pf_domain: The ``pf_models.Domain`` to get the admins to migrate.
- :param newdom: If None the admins will not be linked to any domain
- and they will be SuperAdmins. Otherwise link them to
- this domain and make them DomainAdmins.
- """
-
- if newdom is None:
- print "\nMigrating super administrators"
- else:
- print "\tMigrating administrators"
-
- dagroup = Group.objects.using(options["_to"]).get(name="DomainAdmins")
- for old_admin in pf_domain.admins.all():
- if newdom is None:
- print "\tMigrating %s" % old_admin.username
-
- # In postfixadmin, it's possible to have an admin account
- # and a user account with the same username but they are
- # completely different entities, so they do not have a
- # common password. In Modoboa this does not makes sense, so
- # we actually merge these two entities into a single user
- # if both are found. Question is, which password should we
- # use?. The admin password is used to be on the secure
- # side.
- try:
- user = core_models.User.objects \
- .using(options["_to"]).get(username=old_admin.username)
-
- if "SimpleUsers" == user.group:
- print (
- "Warning: Found an admin account with the same "
- "username as normal user '%s'. The existing user will "
- "be promoted to admin and it's password changed to the "
- "admin's account password. " % user.username
- )
-
- except core_models.User.DoesNotExist:
- user = core_models.User()
- user.username = old_admin.username
- user.email = old_admin.username
- user.is_active = old_admin.active
- user.save(creator=creator, using=options["_to"])
-
- user.date_joined = old_admin.modified
- set_account_password(
- user, old_admin.password, options["passwords_scheme"])
-
- if newdom is None:
- grant_access_to_all_objects(user, "SuperAdmins")
- user.is_superuser = True
- else:
- user.groups.add(dagroup)
- newdom.add_admin(user)
-
- user.save(using=options["_to"])
-
- def _do_migration(self, options, creator_username='admin'):
- """Run the complete migration."""
- pf_domains = pf_models.Domain.objects.using(
- options["_from"]).exclude(domain='ALL')
- creator = core_models.User.objects.using(options["_to"]).get(
- username=creator_username)
- for pf_domain in pf_domains:
-
- try:
- # Skip this domain if it's an alias
- is_domain_alias = pf_models.AliasDomain.objects \
- .using(options["_from"]).get(alias_domain=pf_domain.domain)
- print "Info: %s looks like an alias domain, skipping it" \
- % pf_domain.domain
- continue
- except pf_models.AliasDomain.DoesNotExist:
- self._migrate_domain(pf_domain, options, creator)
-
- # Handle the ALL domain
- pf_domain = pf_models.Domain.objects.using(
- options["_from"]).get(domain='ALL')
- self._migrate_admins(options, creator, pf_domain)
-
- def handle(self, *args, **options):
- """Command entry point."""
- with transaction.commit_on_success():
- self._do_migration(options)
diff --git a/modoboa/tools/pfxadmin_migrate/models.py b/modoboa/tools/pfxadmin_migrate/models.py
deleted file mode 100755
index f94e2f775..000000000
--- a/modoboa/tools/pfxadmin_migrate/models.py
+++ /dev/null
@@ -1,201 +0,0 @@
-# coding: utf-8
-
-"""postfixadmin models."""
-
-# This is an auto-generated Django model module.
-# You'll have to do the following manually to clean this up:
-# * Rearrange models' order
-# * Make sure each model has one field with primary_key=True
-# Feel free to rename the models, but don't rename db_table values or
-# field names.
-#
-# Also note: You'll have to insert the output of 'django-admin.py
-# sqlcustom [appname]' into your database.
-
-from django.db import models
-
-
-class Admin(models.Model):
- username = models.CharField(max_length=255, primary_key=True)
- password = models.CharField(max_length=255)
- created = models.DateTimeField()
- modified = models.DateTimeField()
- active = models.IntegerField()
-
- class Meta:
- db_table = u'admin'
- app_label = 'pfxadmin_migrate'
- managed = False
-
-
-class Alias(models.Model):
- address = models.CharField(max_length=255, primary_key=True)
- goto = models.TextField()
- domain = models.CharField(max_length=255)
- created = models.DateTimeField()
- modified = models.DateTimeField()
- active = models.IntegerField()
-
- class Meta:
- db_table = u'alias'
- app_label = 'pfxadmin_migrate'
- managed = False
-
-
-class AliasDomain(models.Model):
- alias_domain = models.CharField(max_length=255, primary_key=True)
- target_domain = models.CharField(max_length=255)
- created = models.DateTimeField()
- modified = models.DateTimeField()
- active = models.IntegerField()
-
- class Meta:
- db_table = u'alias_domain'
- app_label = 'pfxadmin_migrate'
- managed = False
-
-
-class Config(models.Model):
- id = models.IntegerField(primary_key=True)
- name = models.CharField(unique=True, max_length=60)
- value = models.CharField(max_length=60)
-
- class Meta:
- db_table = u'config'
- app_label = 'pfxadmin_migrate'
- managed = False
-
-
-class Domain(models.Model):
- domain = models.CharField(max_length=255, primary_key=True)
- description = models.CharField(max_length=255)
- aliases = models.IntegerField()
- mailboxes = models.IntegerField()
- maxquota = models.BigIntegerField()
- quota = models.BigIntegerField()
- transport = models.CharField(max_length=255)
- backupmx = models.IntegerField()
- created = models.DateTimeField()
- modified = models.DateTimeField()
- active = models.IntegerField()
- admins = models.ManyToManyField(Admin, through='DomainAdmins')
-
- class Meta:
- db_table = u'domain'
- app_label = 'pfxadmin_migrate'
- managed = False
-
-
-class DomainAdmins(models.Model):
- username = models.ForeignKey(Admin, db_column='username')
- domain = models.ForeignKey(Domain, db_column='domain')
- created = models.DateTimeField(primary_key=True)
- active = models.IntegerField()
-
- class Meta:
- db_table = u'domain_admins'
- app_label = 'pfxadmin_migrate'
- managed = False
-
-
-class Fetchmail(models.Model):
- id = models.IntegerField(primary_key=True)
- mailbox = models.CharField(max_length=255)
- src_server = models.CharField(max_length=255)
- src_auth = models.CharField(max_length=33, blank=True)
- src_user = models.CharField(max_length=255)
- src_password = models.CharField(max_length=255)
- src_folder = models.CharField(max_length=255)
- poll_time = models.IntegerField()
- fetchall = models.IntegerField()
- keep = models.IntegerField()
- protocol = models.CharField(max_length=12, blank=True)
- usessl = models.IntegerField()
- extra_options = models.TextField(blank=True)
- returned_text = models.TextField(blank=True)
- mda = models.CharField(max_length=255)
- date = models.DateTimeField()
-
- class Meta:
- db_table = u'fetchmail'
- app_label = 'pfxadmin_migrate'
- managed = False
-
-
-class Log(models.Model):
- timestamp = models.DateTimeField()
- username = models.CharField(max_length=255)
- domain = models.CharField(max_length=255)
- action = models.CharField(max_length=255)
- data = models.TextField()
-
- class Meta:
- db_table = u'log'
- app_label = 'pfxadmin_migrate'
- managed = False
-
-
-class Mailbox(models.Model):
- username = models.CharField(max_length=255, primary_key=True)
- password = models.CharField(max_length=255)
- name = models.CharField(max_length=255)
- maildir = models.CharField(max_length=255)
- quota = models.BigIntegerField()
- local_part = models.CharField(max_length=255)
- domain = models.CharField(max_length=255)
- created = models.DateTimeField()
- modified = models.DateTimeField()
- active = models.IntegerField()
-
- class Meta:
- db_table = u'mailbox'
- app_label = 'pfxadmin_migrate'
- managed = False
-
-
-class Quota(models.Model):
- username = models.CharField(max_length=255, primary_key=True)
- path = models.CharField(max_length=100, primary_key=True)
- current = models.BigIntegerField(null=True, blank=True)
-
- class Meta:
- db_table = u'quota'
- app_label = 'pfxadmin_migrate'
- managed = False
-
-
-class Quota2(models.Model):
- username = models.CharField(max_length=100, primary_key=True)
- bytes = models.BigIntegerField()
- messages = models.IntegerField()
-
- class Meta:
- db_table = u'quota2'
- app_label = 'pfxadmin_migrate'
- managed = False
-
-
-class Vacation(models.Model):
- email = models.CharField(max_length=255)
- subject = models.CharField(max_length=255)
- body = models.TextField()
- cache = models.TextField()
- domain = models.CharField(max_length=255)
- created = models.DateTimeField()
- active = models.IntegerField()
-
- class Meta:
- db_table = u'vacation'
- app_label = 'pfxadmin_migrate'
- managed = False
-
-
-class VacationNotification(models.Model):
- on_vacation = models.ForeignKey(Vacation, db_column='on_vacation')
- notified = models.CharField(max_length=255, primary_key=True)
- notified_at = models.DateTimeField()
-
- class Meta:
- db_table = u'vacation_notification'
- app_label = 'pfxadmin_migrate'
- managed = False