Ejemplo n.º 1
0
def sanity_check():
    """
    #TODO: investigate why DNS sometimes not working
    Different sanity checks
    - detect if there's mailbox that's using a invalid domain
    """
    mailbox_ids = (db.session.query(Mailbox.id).filter(
        Mailbox.verified == True, Mailbox.disabled == False).all())
    mailbox_ids = [e[0] for e in mailbox_ids]

    # iterate over id instead of mailbox directly
    # as a mailbox can be deleted during the sleep time
    for mailbox_id in mailbox_ids:
        mailbox = Mailbox.get(mailbox_id)
        # a mailbox has been deleted
        if not mailbox:
            continue

        # hack to not query DNS too often
        sleep(1)

        if not email_domain_can_be_used_as_mailbox(mailbox.email):
            mailbox.nb_failed_checks += 1
            nb_email_log = nb_email_log_for_mailbox(mailbox)
            log_func = LOG.warning

            # send a warning
            if mailbox.nb_failed_checks == 5:
                if mailbox.user.email != mailbox.email:
                    send_email(
                        mailbox.user.email,
                        f"Mailbox {mailbox.email} is disabled",
                        render("transactional/disable-mailbox-warning.txt",
                               mailbox=mailbox),
                        render(
                            "transactional/disable-mailbox-warning.html",
                            mailbox=mailbox,
                        ),
                    )

            # alert if too much fail and nb_email_log > 100
            if mailbox.nb_failed_checks > 10 and nb_email_log > 100:
                log_func = LOG.exception
                mailbox.disabled = True

                if mailbox.user.email != mailbox.email:
                    send_email(
                        mailbox.user.email,
                        f"Mailbox {mailbox.email} is disabled",
                        render("transactional/disable-mailbox.txt",
                               mailbox=mailbox),
                        render("transactional/disable-mailbox.html",
                               mailbox=mailbox),
                    )

            log_func(
                "issue with mailbox %s domain. #alias %s, nb email log %s",
                mailbox,
                mailbox.nb_alias(),
                nb_email_log,
            )
        else:  # reset nb check
            mailbox.nb_failed_checks = 0

        db.session.commit()

    for user in User.filter_by(activated=True).all():
        if user.email.lower().strip().replace(" ", "") != user.email:
            LOG.exception("%s does not have sanitized email", user)

    for alias in Alias.query.all():
        if alias.email.lower().strip().replace(" ", "") != alias.email:
            LOG.exception("Alias %s email not sanitized", alias)

    for contact in Contact.query.all():
        if contact.reply_email.lower().strip().replace(
                " ", "") != contact.reply_email:
            LOG.exception("Contact %s reply-email not sanitized", contact)

    for mailbox in Mailbox.query.all():
        if mailbox.email.lower().strip().replace(" ", "") != mailbox.email:
            LOG.exception("Mailbox %s address not sanitized", mailbox)

    LOG.d("Finish sanity check")
Ejemplo n.º 2
0
def custom_alias():
    # check if user has not exceeded the alias quota
    if not current_user.can_create_new_alias():
        LOG.warning("user %s tries to create custom alias", current_user)
        flash(
            "You have reached free plan limit, please upgrade to create new aliases",
            "warning",
        )
        return redirect(url_for("dashboard.index"))

    user_custom_domains = [cd.domain for cd in current_user.verified_custom_domains()]
    suffixes = get_available_suffixes(current_user)
    at_least_a_premium_domain = False
    for suffix in suffixes:
        if not suffix.is_custom and suffix.is_premium:
            at_least_a_premium_domain = True
            break

    mailboxes = current_user.mailboxes()

    if request.method == "POST":
        alias_prefix = request.form.get("prefix").strip().lower().replace(" ", "")
        signed_suffix = request.form.get("suffix")
        mailbox_ids = request.form.getlist("mailboxes")
        alias_note = request.form.get("note")

        if not check_alias_prefix(alias_prefix):
            flash(
                "Only lowercase letters, numbers, dashes (-) and underscores (_) "
                "are currently supported for alias prefix. Cannot be more than 40 letters",
                "error",
            )
            return redirect(url_for("dashboard.custom_alias"))

        # check if mailbox is not tempered with
        mailboxes = []
        for mailbox_id in mailbox_ids:
            mailbox = Mailbox.get(mailbox_id)
            if (
                not mailbox
                or mailbox.user_id != current_user.id
                or not mailbox.verified
            ):
                flash("Something went wrong, please retry", "warning")
                return redirect(url_for("dashboard.custom_alias"))
            mailboxes.append(mailbox)

        if not mailboxes:
            flash("At least one mailbox must be selected", "error")
            return redirect(url_for("dashboard.custom_alias"))

        # hypothesis: user will click on the button in the 600 secs
        try:
            alias_suffix = signer.unsign(signed_suffix, max_age=600).decode()
        except SignatureExpired:
            LOG.warning("Alias creation time expired for %s", current_user)
            flash("Alias creation time is expired, please retry", "warning")
            return redirect(url_for("dashboard.custom_alias"))
        except Exception:
            LOG.warning("Alias suffix is tampered, user %s", current_user)
            flash("Unknown error, refresh the page", "error")
            return redirect(url_for("dashboard.custom_alias"))

        if verify_prefix_suffix(current_user, alias_prefix, alias_suffix):
            full_alias = alias_prefix + alias_suffix

            general_error_msg = f"{full_alias} cannot be used"

            if Alias.get_by(email=full_alias):
                alias = Alias.get_by(email=full_alias)
                if alias.user_id == current_user.id:
                    flash(f"You already have this alias {full_alias}", "error")
                else:
                    flash(general_error_msg, "error")
            elif DomainDeletedAlias.get_by(email=full_alias):
                domain_deleted_alias: DomainDeletedAlias = DomainDeletedAlias.get_by(
                    email=full_alias
                )
                custom_domain = domain_deleted_alias.domain
                if domain_deleted_alias.user_id == current_user.id:
                    flash(
                        f"You have deleted this alias before. You can restore it on "
                        f"{custom_domain.domain} 'Deleted Alias' page",
                        "error",
                    )
                else:
                    # should never happen as user can only choose their domains
                    LOG.exception(
                        "Deleted Alias %s does not belong to user %s",
                        domain_deleted_alias,
                    )

            elif DeletedAlias.get_by(email=full_alias):
                flash(general_error_msg, "error")

            else:
                custom_domain_id = None
                # get the custom_domain_id if alias is created with a custom domain
                if alias_suffix.startswith("@"):
                    alias_domain = alias_suffix[1:]
                    domain = CustomDomain.get_by(domain=alias_domain)

                    # check if the alias is currently in the domain trash
                    if domain and DomainDeletedAlias.get_by(
                        domain_id=domain.id, email=full_alias
                    ):
                        flash(
                            f"Alias {full_alias} is currently in the {domain.domain} trash. "
                            f"Please remove it from the trash in order to re-create it.",
                            "warning",
                        )
                        return redirect(url_for("dashboard.custom_alias"))

                    if domain:
                        custom_domain_id = domain.id

                try:
                    alias = Alias.create(
                        user_id=current_user.id,
                        email=full_alias,
                        note=alias_note,
                        mailbox_id=mailboxes[0].id,
                        custom_domain_id=custom_domain_id,
                    )
                    db.session.flush()
                except IntegrityError:
                    LOG.warning("Alias %s already exists", full_alias)
                    db.session.rollback()
                    flash("Unknown error, please retry", "error")
                    return redirect(url_for("dashboard.custom_alias"))

                for i in range(1, len(mailboxes)):
                    AliasMailbox.create(
                        alias_id=alias.id,
                        mailbox_id=mailboxes[i].id,
                    )

                db.session.commit()
                flash(f"Alias {full_alias} has been created", "success")

                return redirect(url_for("dashboard.index", highlight_alias_id=alias.id))
        # only happen if the request has been "hacked"
        else:
            flash("something went wrong", "warning")

    return render_template(
        "dashboard/custom_alias.html",
        user_custom_domains=user_custom_domains,
        suffixes=suffixes,
        at_least_a_premium_domain=at_least_a_premium_domain,
        mailboxes=mailboxes,
    )
Ejemplo n.º 3
0
def handle_bounce(contact: Contact, alias: Alias, msg: Message, user: User):
    address = alias.email
    email_log: EmailLog = EmailLog.create(contact_id=contact.id,
                                          bounced=True,
                                          user_id=contact.user_id)
    db.session.commit()

    nb_bounced = EmailLog.filter_by(contact_id=contact.id,
                                    bounced=True).count()
    disable_alias_link = f"{URL}/dashboard/unsubscribe/{alias.id}"

    # <<< Store the bounced email >>>
    # generate a name for the email
    random_name = str(uuid.uuid4())

    full_report_path = f"refused-emails/full-{random_name}.eml"
    s3.upload_email_from_bytesio(full_report_path, BytesIO(msg.as_bytes()),
                                 random_name)

    file_path = None
    mailbox = alias.mailbox
    orig_msg = get_orig_message_from_bounce(msg)
    if not orig_msg:
        # Some MTA does not return the original message in bounce message
        # nothing we can do here
        LOG.warning(
            "Cannot parse original message from bounce message %s %s %s %s",
            alias,
            user,
            contact,
            full_report_path,
        )
    else:
        file_path = f"refused-emails/{random_name}.eml"
        s3.upload_email_from_bytesio(file_path, BytesIO(orig_msg.as_bytes()),
                                     random_name)
        # <<< END Store the bounced email >>>

        mailbox_id = int(orig_msg[_MAILBOX_ID_HEADER])
        mailbox = Mailbox.get(mailbox_id)
        if not mailbox or mailbox.user_id != user.id:
            LOG.exception(
                "Tampered message mailbox_id %s, %s, %s, %s %s",
                mailbox_id,
                user,
                alias,
                contact,
                full_report_path,
            )
            # use the alias default mailbox
            mailbox = alias.mailbox

    refused_email = RefusedEmail.create(path=file_path,
                                        full_report_path=full_report_path,
                                        user_id=user.id)
    db.session.flush()

    email_log.refused_email_id = refused_email.id
    email_log.bounced_mailbox_id = mailbox.id
    db.session.commit()

    LOG.d("Create refused email %s", refused_email)

    refused_email_url = (URL + f"/dashboard/refused_email?highlight_id=" +
                         str(email_log.id))

    # inform user if this is the first bounced email
    if nb_bounced == 1:
        LOG.d(
            "Inform user %s about bounced email sent by %s to alias %s",
            user,
            contact.website_email,
            address,
        )
        send_email_with_rate_control(
            user,
            ALERT_BOUNCE_EMAIL,
            user.email,
            f"Email from {contact.website_email} to {address} cannot be delivered to your inbox",
            render(
                "transactional/bounced-email.txt",
                name=user.name,
                alias=alias,
                website_email=contact.website_email,
                disable_alias_link=disable_alias_link,
                refused_email_url=refused_email_url,
                mailbox_email=mailbox.email,
            ),
            render(
                "transactional/bounced-email.html",
                name=user.name,
                alias=alias,
                website_email=contact.website_email,
                disable_alias_link=disable_alias_link,
                refused_email_url=refused_email_url,
                mailbox_email=mailbox.email,
            ),
        )
    # disable the alias the second time email is bounced
    elif nb_bounced >= 2:
        LOG.d(
            "Bounce happens again with alias %s from %s. Disable alias now ",
            address,
            contact.website_email,
        )
        if alias.cannot_be_disabled:
            LOG.warning("%s cannot be disabled", alias)
        else:
            alias.enabled = False
        db.session.commit()

        send_email_with_rate_control(
            user,
            ALERT_BOUNCE_EMAIL,
            user.email,
            f"Alias {address} has been disabled due to second undelivered email from {contact.website_email}",
            render(
                "transactional/automatic-disable-alias.txt",
                name=user.name,
                alias=alias,
                website_email=contact.website_email,
                refused_email_url=refused_email_url,
                mailbox_email=mailbox.email,
            ),
            render(
                "transactional/automatic-disable-alias.html",
                name=user.name,
                alias=alias,
                website_email=contact.website_email,
                refused_email_url=refused_email_url,
                mailbox_email=mailbox.email,
            ),
        )
Ejemplo n.º 4
0
def new_custom_alias_v3():
    """
    Create a new custom alias
    Same as v2 but accept a list of mailboxes as input
    Input:
        alias_prefix, for ex "www_groupon_com"
        signed_suffix, either [email protected] or @my-domain.com
        mailbox_ids: list of int
        optional "hostname" in args
        optional "note"
        optional "name"

    Output:
        201 if success
        409 if the alias already exists

    """
    user: User = g.user
    if not user.can_create_new_alias():
        LOG.d("user %s cannot create any custom alias", user)
        return (
            jsonify(
                error=
                "You have reached the limitation of a free account with the maximum of "
                f"{MAX_NB_EMAIL_FREE_PLAN} aliases, please upgrade your plan to create more aliases"
            ),
            400,
        )

    hostname = request.args.get("hostname")

    data = request.get_json()
    if not data:
        return jsonify(error="request body cannot be empty"), 400

    alias_prefix = data.get("alias_prefix",
                            "").strip().lower().replace(" ", "")
    signed_suffix = data.get("signed_suffix", "").strip()
    mailbox_ids = data.get("mailbox_ids")
    note = data.get("note")
    name = data.get("name")
    alias_prefix = convert_to_id(alias_prefix)

    # check if mailbox is not tempered with
    mailboxes = []
    for mailbox_id in mailbox_ids:
        mailbox = Mailbox.get(mailbox_id)
        if not mailbox or mailbox.user_id != user.id or not mailbox.verified:
            return jsonify(error="Errors with Mailbox"), 400
        mailboxes.append(mailbox)

    if not mailboxes:
        return jsonify(error="At least one mailbox must be selected"), 400

    # hypothesis: user will click on the button in the 600 secs
    try:
        alias_suffix = signer.unsign(signed_suffix, max_age=600).decode()
    except SignatureExpired:
        LOG.warning("Alias creation time expired for %s", user)
        return jsonify(
            error="Alias creation time is expired, please retry"), 412
    except Exception:
        LOG.warning("Alias suffix is tampered, user %s", user)
        return jsonify(error="Tampered suffix"), 400

    if not verify_prefix_suffix(user, alias_prefix, alias_suffix):
        return jsonify(error="wrong alias prefix or suffix"), 400

    full_alias = alias_prefix + alias_suffix
    if (Alias.get_by(email=full_alias) or DeletedAlias.get_by(email=full_alias)
            or DomainDeletedAlias.get_by(email=full_alias)):
        LOG.d("full alias already used %s", full_alias)
        return jsonify(error=f"alias {full_alias} already exists"), 409

    custom_domain_id = None
    if alias_suffix.startswith("@"):
        alias_domain = alias_suffix[1:]
        domain = CustomDomain.get_by(domain=alias_domain)
        if domain:
            custom_domain_id = domain.id

    alias = Alias.create(
        user_id=user.id,
        email=full_alias,
        note=note,
        name=name or None,
        mailbox_id=mailboxes[0].id,
        custom_domain_id=custom_domain_id,
    )
    db.session.flush()

    for i in range(1, len(mailboxes)):
        AliasMailbox.create(
            alias_id=alias.id,
            mailbox_id=mailboxes[i].id,
        )

    db.session.commit()

    if hostname:
        AliasUsedOn.create(alias_id=alias.id,
                           hostname=hostname,
                           user_id=alias.user_id)
        db.session.commit()

    return (
        jsonify(alias=full_alias,
                **serialize_alias_info_v2(get_alias_info_v2(alias))),
        201,
    )
Ejemplo n.º 5
0
def mailbox_route():
    mailboxes = (Mailbox.query.filter_by(user_id=current_user.id).order_by(
        Mailbox.created_at.desc()).all())

    new_mailbox_form = NewMailboxForm()

    if request.method == "POST":
        if request.form.get("form-name") == "delete":
            mailbox_id = request.form.get("mailbox-id")
            mailbox = Mailbox.get(mailbox_id)

            if not mailbox or mailbox.user_id != current_user.id:
                flash("Unknown error. Refresh the page", "warning")
                return redirect(url_for("dashboard.mailbox_route"))

            if mailbox.id == current_user.default_mailbox_id:
                flash("You cannot delete default mailbox", "error")
                return redirect(url_for("dashboard.mailbox_route"))

            email = mailbox.email
            Mailbox.delete(mailbox_id)
            db.session.commit()
            flash(f"Mailbox {email} has been deleted", "success")

            return redirect(url_for("dashboard.mailbox_route"))
        if request.form.get("form-name") == "set-default":
            mailbox_id = request.form.get("mailbox-id")
            mailbox = Mailbox.get(mailbox_id)

            if not mailbox or mailbox.user_id != current_user.id:
                flash("Unknown error. Refresh the page", "warning")
                return redirect(url_for("dashboard.mailbox_route"))

            if mailbox.id == current_user.default_mailbox_id:
                flash("This mailbox is already default one", "error")
                return redirect(url_for("dashboard.mailbox_route"))

            if not mailbox.verified:
                flash("Cannot set unverified mailbox as default", "error")
                return redirect(url_for("dashboard.mailbox_route"))

            current_user.default_mailbox_id = mailbox.id
            db.session.commit()
            flash(f"Mailbox {mailbox.email} is set as Default Mailbox",
                  "success")

            return redirect(url_for("dashboard.mailbox_route"))

        elif request.form.get("form-name") == "create":
            if not current_user.is_premium():
                flash("Only premium plan can add additional mailbox",
                      "warning")
                return redirect(url_for("dashboard.mailbox_route"))

            if new_mailbox_form.validate():
                mailbox_email = (
                    new_mailbox_form.email.data.lower().strip().replace(
                        " ", ""))

                if mailbox_already_used(mailbox_email, current_user):
                    flash(f"{mailbox_email} already used", "error")
                elif not email_domain_can_be_used_as_mailbox(mailbox_email):
                    flash(f"You cannot use {mailbox_email}.", "error")
                else:
                    new_mailbox = Mailbox.create(email=mailbox_email,
                                                 user_id=current_user.id)
                    db.session.commit()

                    send_verification_email(current_user, new_mailbox)

                    flash(
                        f"You are going to receive an email to confirm {mailbox_email}.",
                        "success",
                    )

                    return redirect(
                        url_for("dashboard.mailbox_detail_route",
                                mailbox_id=new_mailbox.id))

    return render_template(
        "dashboard/mailbox.html",
        mailboxes=mailboxes,
        new_mailbox_form=new_mailbox_form,
        EMAIL_DOMAIN=EMAIL_DOMAIN,
        ALIAS_DOMAINS=ALIAS_DOMAINS,
    )
Ejemplo n.º 6
0
def mailbox_detail_route(mailbox_id):
    mailbox = Mailbox.get(mailbox_id)
    if not mailbox or mailbox.user_id != current_user.id:
        flash("You cannot see this page", "warning")
        return redirect(url_for("dashboard.index"))

    change_email_form = ChangeEmailForm()

    if mailbox.new_email:
        pending_email = mailbox.new_email
    else:
        pending_email = None

    if request.method == "POST":
        if (request.form.get("form-name") == "update-email"
                and change_email_form.validate_on_submit()):
            new_email = change_email_form.email.data.lower().strip()
            if new_email != mailbox.email and not pending_email:
                # check if this email is not already used
                if mailbox_already_used(
                        new_email,
                        current_user) or Alias.get_by(email=new_email):
                    flash(f"Email {new_email} already used", "error")
                elif not email_can_be_used_as_mailbox(new_email):
                    flash("You cannot use this email address as your mailbox",
                          "error")
                else:
                    mailbox.new_email = new_email
                    db.session.commit()

                    try:
                        verify_mailbox_change(current_user, mailbox, new_email)
                    except SMTPRecipientsRefused:
                        flash(
                            f"Incorrect mailbox, please recheck {mailbox.email}",
                            "error",
                        )
                    else:
                        flash(
                            f"You are going to receive an email to confirm {new_email}.",
                            "success",
                        )
                    return redirect(
                        url_for("dashboard.mailbox_detail_route",
                                mailbox_id=mailbox_id))
        elif request.form.get("form-name") == "force-spf":
            if not ENFORCE_SPF:
                flash("SPF enforcement globally not enabled", "error")
                return redirect(url_for("dashboard.index"))

            mailbox.force_spf = (True if request.form.get("spf-status") == "on"
                                 else False)
            db.session.commit()
            flash(
                "SPF enforcement was " +
                "enabled" if request.form.get("spf-status") else "disabled" +
                " successfully",
                "success",
            )
            return redirect(
                url_for("dashboard.mailbox_detail_route",
                        mailbox_id=mailbox_id))
        elif request.form.get("form-name") == "add-authorized-address":
            address = request.form.get("email").lower().strip().replace(
                " ", "")
            if AuthorizedAddress.get_by(mailbox_id=mailbox.id, email=address):
                flash(f"{address} already added", "error")
            else:
                AuthorizedAddress.create(
                    user_id=current_user.id,
                    mailbox_id=mailbox.id,
                    email=address,
                    commit=True,
                )
                flash(f"{address} added as authorized address", "success")

            return redirect(
                url_for("dashboard.mailbox_detail_route",
                        mailbox_id=mailbox_id))
        elif request.form.get("form-name") == "delete-authorized-address":
            authorized_address_id = request.form.get("authorized-address-id")
            authorized_address: AuthorizedAddress = AuthorizedAddress.get(
                authorized_address_id)
            if not authorized_address or authorized_address.mailbox_id != mailbox.id:
                flash("Unknown error. Refresh the page", "warning")
            else:
                address = authorized_address.email
                AuthorizedAddress.delete(authorized_address_id)
                db.session.commit()
                flash(f"{address} has been deleted", "success")

            return redirect(
                url_for("dashboard.mailbox_detail_route",
                        mailbox_id=mailbox_id))
        elif request.form.get("form-name") == "pgp":
            if request.form.get("action") == "save":
                if not current_user.is_premium():
                    flash("Only premium plan can add PGP Key", "warning")
                    return redirect(
                        url_for("dashboard.mailbox_detail_route",
                                mailbox_id=mailbox_id))

                mailbox.pgp_public_key = request.form.get("pgp")
                try:
                    mailbox.pgp_finger_print = load_public_key(
                        mailbox.pgp_public_key)
                except PGPException:
                    flash("Cannot add the public key, please verify it",
                          "error")
                else:
                    db.session.commit()
                    flash("Your PGP public key is saved successfully",
                          "success")
                    return redirect(
                        url_for("dashboard.mailbox_detail_route",
                                mailbox_id=mailbox_id))
            elif request.form.get("action") == "remove":
                # Free user can decide to remove their added PGP key
                mailbox.pgp_public_key = None
                mailbox.pgp_finger_print = None
                db.session.commit()
                flash("Your PGP public key is removed successfully", "success")
                return redirect(
                    url_for("dashboard.mailbox_detail_route",
                            mailbox_id=mailbox_id))
        elif request.form.get("form-name") == "generic-subject":
            if request.form.get("action") == "save":
                if not mailbox.pgp_finger_print:
                    flash(
                        "Generic subject can only be used on PGP-enabled mailbox",
                        "error",
                    )
                    return redirect(
                        url_for("dashboard.mailbox_detail_route",
                                mailbox_id=mailbox_id))

                mailbox.generic_subject = request.form.get("generic-subject")
                db.session.commit()
                flash("Generic subject for PGP-encrypted email is enabled",
                      "success")
                return redirect(
                    url_for("dashboard.mailbox_detail_route",
                            mailbox_id=mailbox_id))
            elif request.form.get("action") == "remove":
                mailbox.generic_subject = None
                db.session.commit()
                flash("Generic subject for PGP-encrypted email is disabled",
                      "success")
                return redirect(
                    url_for("dashboard.mailbox_detail_route",
                            mailbox_id=mailbox_id))

    spf_available = ENFORCE_SPF
    return render_template("dashboard/mailbox_detail.html", **locals())
Ejemplo n.º 7
0
Archivo: cron.py Proyecto: ricocf/app
def sanity_check():
    """
    #TODO: investigate why DNS sometimes not working
    Different sanity checks
    - detect if there's mailbox that's using a invalid domain
    """
    mailbox_ids = (db.session.query(Mailbox.id).filter(
        Mailbox.verified.is_(True), Mailbox.disabled.is_(False)).all())
    mailbox_ids = [e[0] for e in mailbox_ids]

    # iterate over id instead of mailbox directly
    # as a mailbox can be deleted during the sleep time
    for mailbox_id in mailbox_ids:
        mailbox = Mailbox.get(mailbox_id)
        # a mailbox has been deleted
        if not mailbox:
            continue

        # hack to not query DNS too often
        sleep(1)

        if not email_can_be_used_as_mailbox(mailbox.email):
            mailbox.nb_failed_checks += 1
            nb_email_log = nb_email_log_for_mailbox(mailbox)

            # send a warning
            if mailbox.nb_failed_checks == 5:
                if mailbox.user.email != mailbox.email:
                    send_email(
                        mailbox.user.email,
                        f"Mailbox {mailbox.email} is disabled",
                        render("transactional/disable-mailbox-warning.txt",
                               mailbox=mailbox),
                        render(
                            "transactional/disable-mailbox-warning.html",
                            mailbox=mailbox,
                        ),
                    )

            # alert if too much fail and nb_email_log > 100
            if mailbox.nb_failed_checks > 10 and nb_email_log > 100:
                mailbox.disabled = True

                if mailbox.user.email != mailbox.email:
                    send_email(
                        mailbox.user.email,
                        f"Mailbox {mailbox.email} is disabled",
                        render("transactional/disable-mailbox.txt",
                               mailbox=mailbox),
                        render("transactional/disable-mailbox.html",
                               mailbox=mailbox),
                    )

            LOG.warning(
                "issue with mailbox %s domain. #alias %s, nb email log %s",
                mailbox,
                mailbox.nb_alias(),
                nb_email_log,
            )
        else:  # reset nb check
            mailbox.nb_failed_checks = 0

        db.session.commit()

    for user in User.filter_by(activated=True).all():
        if sanitize_email(user.email) != user.email:
            LOG.exception("%s does not have sanitized email", user)

    for alias in Alias.query.all():
        if sanitize_email(alias.email) != alias.email:
            LOG.exception("Alias %s email not sanitized", alias)

        if alias.name and "\n" in alias.name:
            alias.name = alias.name.replace("\n", "")
            db.session.commit()
            LOG.exception("Alias %s name contains linebreak %s", alias,
                          alias.name)

    contact_email_sanity_date = arrow.get("2021-01-12")
    for contact in Contact.query.all():
        if sanitize_email(contact.reply_email) != contact.reply_email:
            LOG.exception("Contact %s reply-email not sanitized", contact)

        if (sanitize_email(contact.website_email) != contact.website_email
                and contact.created_at > contact_email_sanity_date):
            LOG.exception("Contact %s website-email not sanitized", contact)

        if not contact.invalid_email and not is_valid_email(
                contact.website_email):
            LOG.exception("%s invalid email", contact)
            contact.invalid_email = True
            db.session.commit()

    for mailbox in Mailbox.query.all():
        if sanitize_email(mailbox.email) != mailbox.email:
            LOG.exception("Mailbox %s address not sanitized", mailbox)

    for contact in Contact.query.all():
        if normalize_reply_email(contact.reply_email) != contact.reply_email:
            LOG.exception(
                "Contact %s reply email is not normalized %s",
                contact,
                contact.reply_email,
            )

    for domain in CustomDomain.query.all():
        if domain.name and "\n" in domain.name:
            LOG.exception("Domain %s name contain linebreak %s", domain,
                          domain.name)

    migrate_domain_trash()
    set_custom_domain_for_alias()

    LOG.d("Finish sanity check")
Ejemplo n.º 8
0
def update_alias(alias_id):
    """
    Update alias note
    Input:
        alias_id: in url
        note (optional): in body
        name (optional): in body
        mailbox_id (optional): in body
        disable_pgp (optional): in body
    Output:
        200
    """
    data = request.get_json()
    if not data:
        return jsonify(error="request body cannot be empty"), 400

    user = g.user
    alias: Alias = Alias.get(alias_id)

    if not alias or alias.user_id != user.id:
        return jsonify(error="Forbidden"), 403

    changed = False
    if "note" in data:
        new_note = data.get("note")
        alias.note = new_note
        changed = True

    if "mailbox_id" in data:
        mailbox_id = int(data.get("mailbox_id"))
        mailbox = Mailbox.get(mailbox_id)
        if not mailbox or mailbox.user_id != user.id or not mailbox.verified:
            return jsonify(error="Forbidden"), 400

        alias.mailbox_id = mailbox_id
        changed = True

    if "mailbox_ids" in data:
        mailbox_ids = [int(m_id) for m_id in data.get("mailbox_ids")]
        mailboxes: [Mailbox] = []

        # check if all mailboxes belong to user
        for mailbox_id in mailbox_ids:
            mailbox = Mailbox.get(mailbox_id)
            if not mailbox or mailbox.user_id != user.id or not mailbox.verified:
                return jsonify(error="Forbidden"), 400
            mailboxes.append(mailbox)

        if not mailboxes:
            return jsonify(error="Must choose at least one mailbox"), 400

        # <<< update alias mailboxes >>>
        # first remove all existing alias-mailboxes links
        AliasMailbox.query.filter_by(alias_id=alias.id).delete()
        db.session.flush()

        # then add all new mailboxes
        for i, mailbox in enumerate(mailboxes):
            if i == 0:
                alias.mailbox_id = mailboxes[0].id
            else:
                AliasMailbox.create(alias_id=alias.id, mailbox_id=mailbox.id)
        # <<< END update alias mailboxes >>>

        changed = True

    if "name" in data:
        # to make sure alias name doesn't contain linebreak
        new_name = data.get("name")
        if new_name:
            new_name = new_name.replace("\n", "")
        alias.name = new_name
        changed = True

    if "disable_pgp" in data:
        alias.disable_pgp = data.get("disable_pgp")
        changed = True

    if "pinned" in data:
        alias.pinned = data.get("pinned")
        changed = True

    if changed:
        db.session.commit()

    return jsonify(ok=True), 200
Ejemplo n.º 9
0
def update_mailbox(mailbox_id):
    """
    Update mailbox
    Input:
        mailbox_id: in url
        (optional) default: in body. Set a mailbox as the default mailbox.
        (optional) email: in body. Change a mailbox email.
        (optional) cancel_email_change: in body. Cancel mailbox email change.
    Output:
        200 if updated successfully

    """
    user = g.user
    mailbox = Mailbox.get(mailbox_id)

    if not mailbox or mailbox.user_id != user.id:
        return jsonify(error="Forbidden"), 403

    data = request.get_json() or {}
    changed = False
    if "default" in data:
        is_default = data.get("default")
        if is_default:
            if not mailbox.verified:
                return (
                    jsonify(
                        error="Unverified mailbox cannot be used as default mailbox"
                    ),
                    400,
                )
            user.default_mailbox_id = mailbox.id
            changed = True

    if "email" in data:
        new_email = sanitize_email(data.get("email"))

        if mailbox_already_used(new_email, user):
            return jsonify(error=f"{new_email} already used"), 400
        elif not email_can_be_used_as_mailbox(new_email):
            return (
                jsonify(
                    error=f"{new_email} cannot be used. Please note a mailbox cannot "
                    f"be a disposable email address"
                ),
                400,
            )

        try:
            verify_mailbox_change(user, mailbox, new_email)
        except SMTPRecipientsRefused:
            return jsonify(error=f"Incorrect mailbox, please recheck {new_email}"), 400
        else:
            mailbox.new_email = new_email
            changed = True

    if "cancel_email_change" in data:
        cancel_email_change = data.get("cancel_email_change")
        if cancel_email_change:
            mailbox.new_email = None
            changed = True

    if changed:
        db.session.commit()

    return jsonify(updated=True), 200
Ejemplo n.º 10
0
def custom_alias():
    # check if user has not exceeded the alias quota
    if not current_user.can_create_new_alias():
        LOG.warning("user %s tries to create custom alias", current_user)
        flash(
            "You have reached free plan limit, please upgrade to create new aliases",
            "warning",
        )
        return redirect(url_for("dashboard.index"))

    user_custom_domains = [
        cd.domain for cd in current_user.verified_custom_domains()
    ]
    # List of (is_custom_domain, alias-suffix, time-signed alias-suffix)
    suffixes = available_suffixes(current_user)

    mailboxes = current_user.mailboxes()

    if request.method == "POST":
        alias_prefix = request.form.get("prefix").strip().lower().replace(
            " ", "")
        signed_suffix = request.form.get("suffix")
        mailbox_ids = request.form.getlist("mailboxes")
        alias_note = request.form.get("note")

        # check if mailbox is not tempered with
        mailboxes = []
        for mailbox_id in mailbox_ids:
            mailbox = Mailbox.get(mailbox_id)
            if (not mailbox or mailbox.user_id != current_user.id
                    or not mailbox.verified):
                flash("Something went wrong, please retry", "warning")
                return redirect(url_for("dashboard.custom_alias"))
            mailboxes.append(mailbox)

        if not mailboxes:
            flash("At least one mailbox must be selected", "error")
            return redirect(url_for("dashboard.custom_alias"))

        # hypothesis: user will click on the button in the 600 secs
        try:
            alias_suffix = signer.unsign(signed_suffix, max_age=600).decode()
        except SignatureExpired:
            LOG.warning("Alias creation time expired for %s", current_user)
            flash("Alias creation time is expired, please retry", "warning")
            return redirect(url_for("dashboard.custom_alias"))
        except Exception:
            LOG.warning("Alias suffix is tampered, user %s", current_user)
            flash("Unknown error, refresh the page", "error")
            return redirect(url_for("dashboard.custom_alias"))

        if verify_prefix_suffix(current_user, alias_prefix, alias_suffix):
            full_alias = alias_prefix + alias_suffix

            if (Alias.get_by(email=full_alias)
                    or DeletedAlias.get_by(email=full_alias)
                    or DomainDeletedAlias.get_by(email=full_alias)):
                LOG.d("full alias already used %s", full_alias)
                flash(
                    f"Alias {full_alias} already exists, please choose another one",
                    "warning",
                )
            else:
                custom_domain_id = None
                # get the custom_domain_id if alias is created with a custom domain
                if alias_suffix.startswith("@"):
                    alias_domain = alias_suffix[1:]
                    domain = CustomDomain.get_by(domain=alias_domain)

                    # check if the alias is currently in the domain trash
                    if domain and DomainDeletedAlias.get_by(
                            domain_id=domain.id, email=full_alias):
                        flash(
                            f"Alias {full_alias} is currently in the {domain.domain} trash. "
                            f"Please remove it from the trash in order to re-create it.",
                            "warning",
                        )
                        return redirect(url_for("dashboard.custom_alias"))

                    if domain:
                        custom_domain_id = domain.id

                alias = Alias.create(
                    user_id=current_user.id,
                    email=full_alias,
                    note=alias_note,
                    mailbox_id=mailboxes[0].id,
                    custom_domain_id=custom_domain_id,
                )
                db.session.flush()

                for i in range(1, len(mailboxes)):
                    AliasMailbox.create(
                        alias_id=alias.id,
                        mailbox_id=mailboxes[i].id,
                    )

                db.session.commit()
                flash(f"Alias {full_alias} has been created", "success")

                return redirect(
                    url_for("dashboard.index", highlight_alias_id=alias.id))
        # only happen if the request has been "hacked"
        else:
            flash("something went wrong", "warning")

    return render_template(
        "dashboard/custom_alias.html",
        user_custom_domains=user_custom_domains,
        suffixes=suffixes,
        mailboxes=mailboxes,
    )
Ejemplo n.º 11
0
def update_custom_domain(custom_domain_id):
    """
    Update alias note
    Input:
        custom_domain_id: in url
    In body:
        catch_all (optional): boolean
        random_prefix_generation (optional): boolean
        name (optional): in body
        mailbox_ids (optional): array of mailbox_id
    Output:
        200
    """
    data = request.get_json()
    if not data:
        return jsonify(error="request body cannot be empty"), 400

    user = g.user
    custom_domain: CustomDomain = CustomDomain.get(custom_domain_id)

    if not custom_domain or custom_domain.user_id != user.id:
        return jsonify(error="Forbidden"), 403

    changed = False
    if "catch_all" in data:
        catch_all = data.get("catch_all")
        custom_domain.catch_all = catch_all
        changed = True

    if "random_prefix_generation" in data:
        random_prefix_generation = data.get("random_prefix_generation")
        custom_domain.random_prefix_generation = random_prefix_generation
        changed = True

    if "name" in data:
        name = data.get("name")
        custom_domain.name = name
        changed = True

    if "mailbox_ids" in data:
        mailbox_ids = [int(m_id) for m_id in data.get("mailbox_ids")]
        if mailbox_ids:
            # check if mailbox is not tempered with
            mailboxes = []
            for mailbox_id in mailbox_ids:
                mailbox = Mailbox.get(mailbox_id)
                if not mailbox or mailbox.user_id != user.id or not mailbox.verified:
                    return jsonify(error="Forbidden"), 400
                mailboxes.append(mailbox)

            # first remove all existing domain-mailboxes links
            DomainMailbox.filter_by(domain_id=custom_domain.id).delete()
            Session.flush()

            for mailbox in mailboxes:
                DomainMailbox.create(domain_id=custom_domain.id,
                                     mailbox_id=mailbox.id)

            changed = True

    if changed:
        Session.commit()

    # refresh
    custom_domain = CustomDomain.get(custom_domain_id)
    return jsonify(custom_domain=custom_domain_to_dict(custom_domain)), 200
Ejemplo n.º 12
0
def directory():
    dirs = (Directory.query.filter_by(user_id=current_user.id).order_by(
        Directory.created_at.desc()).all())

    mailboxes = current_user.mailboxes()

    new_dir_form = NewDirForm()

    if request.method == "POST":
        if request.form.get("form-name") == "delete":
            dir_id = request.form.get("dir-id")
            dir = Directory.get(dir_id)

            if not dir:
                flash("Unknown error. Refresh the page", "warning")
                return redirect(url_for("dashboard.directory"))
            elif dir.user_id != current_user.id:
                flash("You cannot delete this directory", "warning")
                return redirect(url_for("dashboard.directory"))

            name = dir.name
            Directory.delete(dir_id)
            db.session.commit()
            flash(f"Directory {name} has been deleted", "success")

            return redirect(url_for("dashboard.directory"))
        elif request.form.get("form-name") == "update":
            dir_id = request.form.get("dir-id")
            dir = Directory.get(dir_id)

            if not dir or dir.user_id != current_user.id:
                flash("Unknown error. Refresh the page", "warning")
                return redirect(url_for("dashboard.directory"))

            mailbox_ids = request.form.getlist("mailbox_ids")
            # check if mailbox is not tempered with
            mailboxes = []
            for mailbox_id in mailbox_ids:
                mailbox = Mailbox.get(mailbox_id)
                if (not mailbox or mailbox.user_id != current_user.id
                        or not mailbox.verified):
                    flash("Something went wrong, please retry", "warning")
                    return redirect(url_for("dashboard.directory"))
                mailboxes.append(mailbox)

            if not mailboxes:
                flash("You must select at least 1 mailbox", "warning")
                return redirect(url_for("dashboard.directory"))

            # first remove all existing directory-mailboxes links
            DirectoryMailbox.query.filter_by(directory_id=dir.id).delete()
            db.session.flush()

            for mailbox in mailboxes:
                DirectoryMailbox.create(directory_id=dir.id,
                                        mailbox_id=mailbox.id)

            db.session.commit()
            flash(f"Directory {dir.name} has been updated", "success")

            return redirect(url_for("dashboard.directory"))
        elif request.form.get("form-name") == "create":
            if not current_user.is_premium():
                flash("Only premium plan can add directory", "warning")
                return redirect(url_for("dashboard.directory"))

            if current_user.nb_directory() >= MAX_NB_DIRECTORY:
                flash(
                    f"You cannot have more than {MAX_NB_DIRECTORY} directories",
                    "warning",
                )
                return redirect(url_for("dashboard.directory"))

            if new_dir_form.validate():
                new_dir_name = new_dir_form.name.data.lower()

                if Directory.get_by(name=new_dir_name):
                    flash(f"{new_dir_name} already added", "warning")
                elif new_dir_name in ("reply", "ra"):
                    flash(
                        "directory name cannot be *reply* or *ra*, please choose another name",
                        "warning",
                    )
                else:
                    new_dir = Directory.create(name=new_dir_name,
                                               user_id=current_user.id)
                    db.session.commit()
                    mailbox_ids = request.form.getlist("mailbox_ids")
                    if mailbox_ids:
                        # check if mailbox is not tempered with
                        mailboxes = []
                        for mailbox_id in mailbox_ids:
                            mailbox = Mailbox.get(mailbox_id)
                            if (not mailbox
                                    or mailbox.user_id != current_user.id
                                    or not mailbox.verified):
                                flash("Something went wrong, please retry",
                                      "warning")
                                return redirect(url_for("dashboard.directory"))
                            mailboxes.append(mailbox)

                        for mailbox in mailboxes:
                            DirectoryMailbox.create(directory_id=new_dir.id,
                                                    mailbox_id=mailbox.id)

                        db.session.commit()

                    flash(f"Directory {new_dir.name} is created", "success")

                    return redirect(url_for("dashboard.directory"))

    return render_template(
        "dashboard/directory.html",
        dirs=dirs,
        new_dir_form=new_dir_form,
        mailboxes=mailboxes,
        EMAIL_DOMAIN=EMAIL_DOMAIN,
        ALIAS_DOMAINS=ALIAS_DOMAINS,
    )
Ejemplo n.º 13
0
def mailbox_route():
    mailboxes = Mailbox.query.filter_by(user_id=current_user.id).all()

    new_mailbox_form = NewMailboxForm()

    if request.method == "POST":
        if request.form.get("form-name") == "delete":
            mailbox_id = request.form.get("mailbox-id")
            mailbox = Mailbox.get(mailbox_id)

            if not mailbox or mailbox.user_id != current_user.id:
                flash("Unknown error. Refresh the page", "warning")
                return redirect(url_for("dashboard.mailbox_route"))

            if mailbox.id == current_user.default_mailbox_id:
                flash("You cannot delete default mailbox", "error")
                return redirect(url_for("dashboard.mailbox_route"))

            email = mailbox.email
            Mailbox.delete(mailbox_id)
            db.session.commit()
            flash(f"Mailbox {email} has been deleted", "success")

            return redirect(url_for("dashboard.mailbox_route"))
        if request.form.get("form-name") == "set-default":
            mailbox_id = request.form.get("mailbox-id")
            mailbox = Mailbox.get(mailbox_id)

            if not mailbox or mailbox.user_id != current_user.id:
                flash("Unknown error. Refresh the page", "warning")
                return redirect(url_for("dashboard.mailbox_route"))

            if mailbox.id == current_user.default_mailbox_id:
                flash("This mailbox is already default one", "error")
                return redirect(url_for("dashboard.mailbox_route"))

            if not mailbox.verified:
                flash("Cannot set unverified mailbox as default", "error")
                return redirect(url_for("dashboard.mailbox_route"))

            current_user.default_mailbox_id = mailbox.id
            db.session.commit()
            flash(f"Mailbox {mailbox.email} is set as Default Mailbox",
                  "success")

            return redirect(url_for("dashboard.mailbox_route"))

        elif request.form.get("form-name") == "create":
            if not current_user.is_premium():
                flash("Only premium plan can add additional mailbox",
                      "warning")
                return redirect(url_for("dashboard.mailbox_route"))

            if new_mailbox_form.validate():
                mailbox_email = new_mailbox_form.email.data.lower()

                if mailbox_already_used(mailbox_email, current_user):
                    flash(f"{mailbox_email} already used", "error")
                elif not can_be_used_as_personal_email(mailbox_email):
                    flash(f"You cannot use {mailbox_email}.", "error")
                else:
                    new_mailbox = Mailbox.create(email=mailbox_email,
                                                 user_id=current_user.id)
                    db.session.commit()

                    s = Signer(MAILBOX_SECRET)
                    mailbox_id_signed = s.sign(str(new_mailbox.id)).decode()
                    verification_url = (URL + "/dashboard/mailbox_verify" +
                                        f"?mailbox_id={mailbox_id_signed}")
                    send_email(
                        mailbox_email,
                        f"Please confirm your email {mailbox_email}",
                        render(
                            "transactional/verify-mailbox.txt",
                            user=current_user,
                            link=verification_url,
                            mailbox_email=mailbox_email,
                        ),
                        render(
                            "transactional/verify-mailbox.html",
                            user=current_user,
                            link=verification_url,
                            mailbox_email=mailbox_email,
                        ),
                    )

                    flash(
                        f"You are going to receive an email to confirm {mailbox_email}.",
                        "success",
                    )

                    return redirect(
                        url_for("dashboard.mailbox_detail_route",
                                mailbox_id=new_mailbox.id))

    return render_template(
        "dashboard/mailbox.html",
        mailboxes=mailboxes,
        new_mailbox_form=new_mailbox_form,
        EMAIL_DOMAIN=EMAIL_DOMAIN,
        ALIAS_DOMAINS=ALIAS_DOMAINS,
    )
Ejemplo n.º 14
0
def mailbox_detail_route(mailbox_id):
    mailbox = Mailbox.get(mailbox_id)
    if not mailbox or mailbox.user_id != current_user.id:
        flash("You cannot see this page", "warning")
        return redirect(url_for("dashboard.index"))

    change_email_form = ChangeEmailForm()

    if mailbox.new_email:
        pending_email = mailbox.new_email
    else:
        pending_email = None

    if request.method == "POST":
        if (
            request.form.get("form-name") == "update-email"
            and change_email_form.validate_on_submit()
        ):
            new_email = change_email_form.email.data
            if new_email != mailbox.email and not pending_email:
                # check if this email is not already used
                if (
                    mailbox_already_used(new_email, current_user)
                    or Alias.get_by(email=new_email)
                    or DeletedAlias.get_by(email=new_email)
                ):
                    flash(f"Email {new_email} already used", "error")
                elif not email_domain_can_be_used_as_mailbox(new_email):
                    flash("You cannot use this email address as your mailbox", "error")
                else:
                    mailbox.new_email = new_email
                    db.session.commit()

                    s = Signer(MAILBOX_SECRET)
                    mailbox_id_signed = s.sign(str(mailbox.id)).decode()
                    verification_url = (
                        URL
                        + "/dashboard/mailbox/confirm_change"
                        + f"?mailbox_id={mailbox_id_signed}"
                    )

                    try:
                        send_email(
                            new_email,
                            f"Confirm mailbox change on SimpleLogin",
                            render(
                                "transactional/verify-mailbox-change.txt",
                                user=current_user,
                                link=verification_url,
                                mailbox_email=mailbox.email,
                                mailbox_new_email=new_email,
                            ),
                            render(
                                "transactional/verify-mailbox-change.html",
                                user=current_user,
                                link=verification_url,
                                mailbox_email=mailbox.email,
                                mailbox_new_email=new_email,
                            ),
                        )
                    except SMTPRecipientsRefused:
                        flash(
                            f"Incorrect mailbox, please recheck {mailbox.email}",
                            "error",
                        )
                    else:
                        flash(
                            f"You are going to receive an email to confirm {new_email}.",
                            "success",
                        )
                    return redirect(
                        url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox_id)
                    )
        elif request.form.get("form-name") == "force-spf":
            if not ENFORCE_SPF:
                flash("SPF enforcement globally not enabled", "error")
                return redirect(url_for("dashboard.index"))

            mailbox.force_spf = (
                True if request.form.get("spf-status") == "on" else False
            )
            db.session.commit()
            flash(
                "SPF enforcement was " + "enabled"
                if request.form.get("spf-status")
                else "disabled" + " succesfully",
                "success",
            )
            return redirect(
                url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox_id)
            )
        elif request.form.get("form-name") == "pgp":
            if request.form.get("action") == "save":
                if not current_user.is_premium():
                    flash("Only premium plan can add PGP Key", "warning")
                    return redirect(
                        url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox_id)
                    )

                mailbox.pgp_public_key = request.form.get("pgp")
                try:
                    mailbox.pgp_finger_print = load_public_key(mailbox.pgp_public_key)
                except PGPException:
                    flash("Cannot add the public key, please verify it", "error")
                else:
                    db.session.commit()
                    flash("Your PGP public key is saved successfully", "success")
                    return redirect(
                        url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox_id)
                    )
            elif request.form.get("action") == "remove":
                # Free user can decide to remove their added PGP key
                mailbox.pgp_public_key = None
                mailbox.pgp_finger_print = None
                db.session.commit()
                flash("Your PGP public key is removed successfully", "success")
                return redirect(
                    url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox_id)
                )

    spf_available = ENFORCE_SPF
    return render_template("dashboard/mailbox_detail.html", **locals())
Ejemplo n.º 15
0
                    user_email = user.email
                    LOG.w("Delete user %s", user)
                    User.delete(user.id)
                    Session.commit()

                    send_email(
                        user_email,
                        "Your SimpleLogin account has been deleted",
                        render("transactional/account-delete.txt"),
                        render("transactional/account-delete.html"),
                        retries=3,
                    )
                elif job.name == JOB_DELETE_MAILBOX:
                    mailbox_id = job.payload.get("mailbox_id")
                    mailbox = Mailbox.get(mailbox_id)
                    if not mailbox:
                        continue

                    mailbox_email = mailbox.email
                    user = mailbox.user

                    Mailbox.delete(mailbox_id)
                    Session.commit()
                    LOG.d("Mailbox %s %s deleted", mailbox_id, mailbox_email)

                    send_email(
                        user.email,
                        f"Your mailbox {mailbox_email} has been deleted",
                        f"""Mailbox {mailbox_email} along with its aliases are deleted successfully.
Regards,