Пример #1
0
def compute_metric2() -> Metric2:
    now = arrow.now()
    _24h_ago = now.shift(days=-1)

    nb_referred_user_paid = 0
    for user in User.filter(User.referral_id.isnot(None)):
        if user.is_paid():
            nb_referred_user_paid += 1

    return Metric2.create(
        date=now,
        # user stats
        nb_user=User.count(),
        nb_activated_user=User.filter_by(activated=True).count(),
        # subscription stats
        nb_premium=Subscription.filter(
            Subscription.cancelled.is_(False)).count(),
        nb_cancelled_premium=Subscription.filter(
            Subscription.cancelled.is_(True)).count(),
        # todo: filter by expires_date > now
        nb_apple_premium=AppleSubscription.count(),
        nb_manual_premium=ManualSubscription.filter(
            ManualSubscription.end_at > now,
            ManualSubscription.is_giveaway.is_(False),
        ).count(),
        nb_coinbase_premium=CoinbaseSubscription.filter(
            CoinbaseSubscription.end_at > now).count(),
        # referral stats
        nb_referred_user=User.filter(User.referral_id.isnot(None)).count(),
        nb_referred_user_paid=nb_referred_user_paid,
        nb_alias=Alias.count(),
        # email log stats
        nb_forward_last_24h=EmailLog.filter(
            EmailLog.created_at > _24h_ago).filter_by(bounced=False,
                                                      is_spam=False,
                                                      is_reply=False,
                                                      blocked=False).count(),
        nb_bounced_last_24h=EmailLog.filter(
            EmailLog.created_at > _24h_ago).filter_by(bounced=True).count(),
        nb_total_bounced_last_24h=Bounce.filter(
            Bounce.created_at > _24h_ago).count(),
        nb_reply_last_24h=EmailLog.filter(
            EmailLog.created_at > _24h_ago).filter_by(is_reply=True).count(),
        nb_block_last_24h=EmailLog.filter(
            EmailLog.created_at > _24h_ago).filter_by(blocked=True).count(),
        # other stats
        nb_verified_custom_domain=CustomDomain.filter_by(
            verified=True).count(),
        nb_subdomain=CustomDomain.filter_by(is_sl_subdomain=True).count(),
        nb_directory=Directory.count(),
        nb_deleted_directory=DeletedDirectory.count(),
        nb_deleted_subdomain=DeletedSubdomain.count(),
        nb_app=Client.count(),
        commit=True,
    )
Пример #2
0
def export_data():
    """
    Get user data
    Output:
        Alias, custom domain and app info

    """
    user = g.user

    data = {
        "email": user.email,
        "name": user.name,
        "aliases": [],
        "apps": [],
        "custom_domains": [],
    }

    for alias in Alias.filter_by(user_id=user.id).all():  # type: Alias
        data["aliases"].append(dict(email=alias.email, enabled=alias.enabled))

    for custom_domain in CustomDomain.filter_by(user_id=user.id).all():
        data["custom_domains"].append(custom_domain.domain)

    for app in Client.filter_by(user_id=user.id):  # type: Client
        data["apps"].append(dict(name=app.name, home_url=app.home_url))

    return jsonify(data)
Пример #3
0
def get_custom_domains():
    user = g.user
    custom_domains = CustomDomain.filter_by(user_id=user.id,
                                            is_sl_subdomain=False).all()

    return jsonify(
        custom_domains=[custom_domain_to_dict(cd) for cd in custom_domains])
Пример #4
0
def check_custom_domain():
    LOG.d("Check verified domain for DNS issues")

    for custom_domain in CustomDomain.filter_by(
            verified=True):  # type: CustomDomain
        try:
            check_single_custom_domain(custom_domain)
        except ObjectDeletedError:
            LOG.i("custom domain has been deleted")
Пример #5
0
def setting():
    form = SettingForm()
    promo_form = PromoCodeForm()
    change_email_form = ChangeEmailForm()

    email_change = EmailChange.get_by(user_id=current_user.id)
    if email_change:
        pending_email = email_change.new_email
    else:
        pending_email = None

    if request.method == "POST":
        if request.form.get("form-name") == "update-email":
            if change_email_form.validate():
                # whether user can proceed with the email update
                new_email_valid = True
                if (change_email_form.email.data.lower().strip() !=
                        current_user.email and not pending_email):
                    new_email = change_email_form.email.data.strip().lower()

                    # check if this email is not already used
                    if personal_email_already_used(new_email) or Alias.get_by(
                            email=new_email):
                        flash(f"Email {new_email} already used", "error")
                        new_email_valid = False
                    elif not email_can_be_used_as_mailbox(new_email):
                        flash(
                            "You cannot use this email address as your personal inbox.",
                            "error",
                        )
                        new_email_valid = False
                    # a pending email change with the same email exists from another user
                    elif EmailChange.get_by(new_email=new_email):
                        other_email_change: EmailChange = EmailChange.get_by(
                            new_email=new_email)
                        LOG.warning(
                            "Another user has a pending %s with the same email address. Current user:%s",
                            other_email_change,
                            current_user,
                        )

                        if other_email_change.is_expired():
                            LOG.d("delete the expired email change %s",
                                  other_email_change)
                            EmailChange.delete(other_email_change.id)
                            db.session.commit()
                        else:
                            flash(
                                "You cannot use this email address as your personal inbox.",
                                "error",
                            )
                            new_email_valid = False

                    if new_email_valid:
                        email_change = EmailChange.create(
                            user_id=current_user.id,
                            code=random_string(
                                60),  # todo: make sure the code is unique
                            new_email=new_email,
                        )
                        db.session.commit()
                        send_change_email_confirmation(current_user,
                                                       email_change)
                        flash(
                            "A confirmation email is on the way, please check your inbox",
                            "success",
                        )
                        return redirect(url_for("dashboard.setting"))
        if request.form.get("form-name") == "update-profile":
            if form.validate():
                profile_updated = False
                # update user info
                if form.name.data != current_user.name:
                    current_user.name = form.name.data
                    db.session.commit()
                    profile_updated = True

                if form.profile_picture.data:
                    file_path = random_string(30)
                    file = File.create(user_id=current_user.id, path=file_path)

                    s3.upload_from_bytesio(
                        file_path, BytesIO(form.profile_picture.data.read()))

                    db.session.flush()
                    LOG.d("upload file %s to s3", file)

                    current_user.profile_picture_id = file.id
                    db.session.commit()
                    profile_updated = True

                if profile_updated:
                    flash(f"Your profile has been updated", "success")
                    return redirect(url_for("dashboard.setting"))

        elif request.form.get("form-name") == "change-password":
            flash(
                "You are going to receive an email containing instructions to change your password",
                "success",
            )
            send_reset_password_email(current_user)
            return redirect(url_for("dashboard.setting"))

        elif request.form.get("form-name") == "notification-preference":
            choose = request.form.get("notification")
            if choose == "on":
                current_user.notification = True
            else:
                current_user.notification = False
            db.session.commit()
            flash("Your notification preference has been updated", "success")
            return redirect(url_for("dashboard.setting"))

        elif request.form.get("form-name") == "delete-account":
            LOG.warning("Delete account %s", current_user)
            User.delete(current_user.id)
            db.session.commit()
            flash("Your account has been deleted", "success")
            logout_user()
            return redirect(url_for("auth.register"))

        elif request.form.get("form-name") == "change-alias-generator":
            scheme = int(request.form.get("alias-generator-scheme"))
            if AliasGeneratorEnum.has_value(scheme):
                current_user.alias_generator = scheme
                db.session.commit()
            flash("Your preference has been updated", "success")
            return redirect(url_for("dashboard.setting"))

        elif request.form.get(
                "form-name") == "change-random-alias-default-domain":
            default_domain = request.form.get("random-alias-default-domain")

            if default_domain:
                sl_domain: SLDomain = SLDomain.get_by(domain=default_domain)
                if sl_domain:
                    if sl_domain.premium_only and not current_user.is_premium(
                    ):
                        flash("You cannot use this domain", "error")
                        return redirect(url_for("dashboard.setting"))

                    # make sure only default_random_alias_domain_id or default_random_alias_public_domain_id is set
                    current_user.default_random_alias_public_domain_id = sl_domain.id
                    current_user.default_random_alias_domain_id = None
                else:
                    custom_domain = CustomDomain.get_by(domain=default_domain)
                    if custom_domain:
                        # sanity check
                        if (custom_domain.user_id != current_user.id
                                or not custom_domain.verified):
                            LOG.exception("%s cannot use domain %s",
                                          current_user, default_domain)
                        else:
                            # make sure only default_random_alias_domain_id or
                            # default_random_alias_public_domain_id is set
                            current_user.default_random_alias_domain_id = (
                                custom_domain.id)
                            current_user.default_random_alias_public_domain_id = None

            else:
                current_user.default_random_alias_domain_id = None
                current_user.default_random_alias_public_domain_id = None

            db.session.commit()
            flash("Your preference has been updated", "success")
            return redirect(url_for("dashboard.setting"))

        elif request.form.get("form-name") == "change-sender-format":
            sender_format = int(request.form.get("sender-format"))
            if SenderFormatEnum.has_value(sender_format):
                current_user.sender_format = sender_format
                db.session.commit()
                flash("Your sender format preference has been updated",
                      "success")
            db.session.commit()
            return redirect(url_for("dashboard.setting"))

        elif request.form.get("form-name") == "replace-ra":
            choose = request.form.get("replace-ra")
            if choose == "on":
                current_user.replace_reverse_alias = True
            else:
                current_user.replace_reverse_alias = False
            db.session.commit()
            flash("Your preference has been updated", "success")
            return redirect(url_for("dashboard.setting"))

        elif request.form.get("form-name") == "export-data":
            data = {
                "email": current_user.email,
                "name": current_user.name,
                "aliases": [],
                "apps": [],
                "custom_domains": [],
            }

            for alias in Alias.filter_by(
                    user_id=current_user.id).all():  # type: Alias
                data["aliases"].append(
                    dict(email=alias.email, enabled=alias.enabled))

            for custom_domain in CustomDomain.filter_by(
                    user_id=current_user.id).all():
                data["custom_domains"].append(custom_domain.domain)

            for app in Client.filter_by(
                    user_id=current_user.id):  # type: Client
                data["apps"].append(
                    dict(name=app.name,
                         home_url=app.home_url,
                         published=app.published))

            return Response(
                json.dumps(data),
                mimetype="text/json",
                headers={
                    "Content-Disposition": "attachment;filename=data.json"
                },
            )
        elif request.form.get("form-name") == "export-alias":
            data = [["alias", "note", "enabled"]]
            for alias in Alias.filter_by(
                    user_id=current_user.id).all():  # type: Alias
                data.append([alias.email, alias.note, alias.enabled])

            si = StringIO()
            cw = csv.writer(si)
            cw.writerows(data)
            output = make_response(si.getvalue())
            output.headers[
                "Content-Disposition"] = "attachment; filename=aliases.csv"
            output.headers["Content-type"] = "text/csv"
            return output

    manual_sub = ManualSubscription.get_by(user_id=current_user.id)
    return render_template(
        "dashboard/setting.html",
        form=form,
        PlanEnum=PlanEnum,
        SenderFormatEnum=SenderFormatEnum,
        promo_form=promo_form,
        change_email_form=change_email_form,
        pending_email=pending_email,
        AliasGeneratorEnum=AliasGeneratorEnum,
        manual_sub=manual_sub,
        FIRST_ALIAS_DOMAIN=FIRST_ALIAS_DOMAIN,
    )
Пример #6
0
def setting():
    form = SettingForm()
    promo_form = PromoCodeForm()

    email_change = EmailChange.get_by(user_id=current_user.id)
    if email_change:
        pending_email = email_change.new_email
    else:
        pending_email = None

    if request.method == "POST":
        if request.form.get("form-name") == "update-profile":
            if form.validate():
                profile_updated = False
                # update user info
                if form.name.data != current_user.name:
                    current_user.name = form.name.data
                    db.session.commit()
                    profile_updated = True

                if form.profile_picture.data:
                    file_path = random_string(30)
                    file = File.create(path=file_path)

                    s3.upload_from_bytesio(
                        file_path, BytesIO(form.profile_picture.data.read()))

                    db.session.flush()
                    LOG.d("upload file %s to s3", file)

                    current_user.profile_picture_id = file.id
                    db.session.commit()
                    profile_updated = True

                if profile_updated:
                    flash(f"Your profile has been updated", "success")

                if (form.email.data and form.email.data != current_user.email
                        and not pending_email):
                    new_email = form.email.data

                    # check if this email is not used by other user, or as alias
                    if (User.get_by(email=new_email)
                            or GenEmail.get_by(email=new_email)
                            or DeletedAlias.get_by(email=new_email)):
                        flash(f"Email {new_email} already used", "error")
                    elif new_email.endswith(EMAIL_DOMAIN):
                        flash(
                            "You cannot use alias as your personal inbox. Nice try though 😉",
                            "error",
                        )
                    else:
                        email_change = EmailChange.create(
                            user_id=current_user.id,
                            code=random_string(
                                60),  # todo: make sure the code is unique
                            new_email=new_email,
                        )
                        db.session.commit()
                        send_change_email_confirmation(current_user,
                                                       email_change)
                        flash(
                            "A confirmation email is on the way, please check your inbox",
                            "success",
                        )

        elif request.form.get("form-name") == "change-password":
            send_reset_password_email(current_user)

        elif request.form.get("form-name") == "notification-preference":
            choose = request.form.get("notification")
            if choose == "on":
                current_user.notification = True
            else:
                current_user.notification = False
            db.session.commit()
            flash("Your notification preference has been updated", "success")

        elif request.form.get("form-name") == "delete-account":
            User.delete(current_user.id)
            db.session.commit()
            flash("Your account has been deleted", "success")
            logout_user()
            return redirect(url_for("auth.register"))

        elif request.form.get("form-name") == "change-alias-generator":
            scheme = int(request.form.get("alias-generator-scheme"))
            if AliasGeneratorEnum.has_value(scheme):
                current_user.alias_generator = scheme
                db.session.commit()
            flash("Your preference has been updated", "success")

        elif request.form.get("form-name") == "export-data":
            data = {
                "email": current_user.email,
                "name": current_user.name,
                "aliases": [],
                "apps": [],
                "custom_domains": [],
            }

            for alias in GenEmail.filter_by(
                    user_id=current_user.id).all():  # type: GenEmail
                data["aliases"].append(
                    dict(email=alias.email, enabled=alias.enabled))

            for custom_domain in CustomDomain.filter_by(
                    user_id=current_user.id).all():
                data["custom_domains"].append(custom_domain.domain)

            for app in Client.filter_by(
                    user_id=current_user.id):  # type: Client
                data["apps"].append(
                    dict(name=app.name,
                         home_url=app.home_url,
                         published=app.published))

            return Response(
                json.dumps(data),
                mimetype="text/json",
                headers={
                    "Content-Disposition": "attachment;filename=data.json"
                },
            )

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

    return render_template(
        "dashboard/setting.html",
        form=form,
        PlanEnum=PlanEnum,
        promo_form=promo_form,
        pending_email=pending_email,
        AliasGeneratorEnum=AliasGeneratorEnum,
    )
Пример #7
0
def custom_domain():
    custom_domains = CustomDomain.filter_by(user_id=current_user.id,
                                            is_sl_subdomain=False).all()
    mailboxes = current_user.mailboxes()
    new_custom_domain_form = NewCustomDomainForm()

    errors = {}

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

            if new_custom_domain_form.validate():
                new_domain = new_custom_domain_form.domain.data.lower().strip()

                if new_domain.startswith("http://"):
                    new_domain = new_domain[len("http://"):]

                if new_domain.startswith("https://"):
                    new_domain = new_domain[len("https://"):]

                if SLDomain.get_by(domain=new_domain):
                    flash("A custom domain cannot be a built-in domain.",
                          "error")
                elif CustomDomain.get_by(domain=new_domain):
                    flash(f"{new_domain} already used", "error")
                elif get_email_domain_part(current_user.email) == new_domain:
                    flash(
                        "You cannot add a domain that you are currently using for your personal email. "
                        "Please change your personal email to your real email",
                        "error",
                    )
                elif Mailbox.filter(
                        Mailbox.verified.is_(True),
                        Mailbox.email.endswith(f"@{new_domain}")).first():
                    flash(
                        f"{new_domain} already used in a SimpleLogin mailbox",
                        "error")
                else:
                    new_custom_domain = CustomDomain.create(
                        domain=new_domain, user_id=current_user.id)
                    # new domain has ownership verified if its parent has the ownership verified
                    for root_cd in current_user.custom_domains:
                        if (new_domain.endswith("." + root_cd.domain)
                                and root_cd.ownership_verified):
                            LOG.i(
                                "%s ownership verified thanks to %s",
                                new_custom_domain,
                                root_cd,
                            )
                            new_custom_domain.ownership_verified = True

                    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.custom_domain"))
                            mailboxes.append(mailbox)

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

                        Session.commit()

                    flash(f"New domain {new_custom_domain.domain} is created",
                          "success")

                    return redirect(
                        url_for(
                            "dashboard.domain_detail_dns",
                            custom_domain_id=new_custom_domain.id,
                        ))

    return render_template(
        "dashboard/custom_domain.html",
        custom_domains=custom_domains,
        new_custom_domain_form=new_custom_domain_form,
        EMAIL_SERVERS_WITH_PRIORITY=EMAIL_SERVERS_WITH_PRIORITY,
        errors=errors,
        mailboxes=mailboxes,
    )
Пример #8
0
def setting():
    form = SettingForm()
    promo_form = PromoCodeForm()
    change_email_form = ChangeEmailForm()

    email_change = EmailChange.get_by(user_id=current_user.id)
    if email_change:
        pending_email = email_change.new_email
    else:
        pending_email = None

    if request.method == "POST":
        if request.form.get("form-name") == "update-email":
            if change_email_form.validate():
                if (change_email_form.email.data.lower().strip() !=
                        current_user.email and not pending_email):
                    new_email = change_email_form.email.data.strip().lower()

                    # check if this email is not already used
                    if personal_email_already_used(new_email) or Alias.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 personal inbox.",
                            "error",
                        )
                    else:
                        email_change = EmailChange.create(
                            user_id=current_user.id,
                            code=random_string(
                                60),  # todo: make sure the code is unique
                            new_email=new_email,
                        )
                        db.session.commit()
                        send_change_email_confirmation(current_user,
                                                       email_change)
                        flash(
                            "A confirmation email is on the way, please check your inbox",
                            "success",
                        )
                        return redirect(url_for("dashboard.setting"))
        if request.form.get("form-name") == "update-profile":
            if form.validate():
                profile_updated = False
                # update user info
                if form.name.data != current_user.name:
                    current_user.name = form.name.data
                    db.session.commit()
                    profile_updated = True

                if form.profile_picture.data:
                    file_path = random_string(30)
                    file = File.create(user_id=current_user.id, path=file_path)

                    s3.upload_from_bytesio(
                        file_path, BytesIO(form.profile_picture.data.read()))

                    db.session.flush()
                    LOG.d("upload file %s to s3", file)

                    current_user.profile_picture_id = file.id
                    db.session.commit()
                    profile_updated = True

                if profile_updated:
                    flash(f"Your profile has been updated", "success")
                    return redirect(url_for("dashboard.setting"))

        elif request.form.get("form-name") == "change-password":
            flash(
                "You are going to receive an email containing instructions to change your password",
                "success",
            )
            send_reset_password_email(current_user)
            return redirect(url_for("dashboard.setting"))

        elif request.form.get("form-name") == "notification-preference":
            choose = request.form.get("notification")
            if choose == "on":
                current_user.notification = True
            else:
                current_user.notification = False
            db.session.commit()
            flash("Your notification preference has been updated", "success")
            return redirect(url_for("dashboard.setting"))

        elif request.form.get("form-name") == "delete-account":
            LOG.warning("Delete account %s", current_user)
            User.delete(current_user.id)
            db.session.commit()
            flash("Your account has been deleted", "success")
            logout_user()
            return redirect(url_for("auth.register"))

        elif request.form.get("form-name") == "change-alias-generator":
            scheme = int(request.form.get("alias-generator-scheme"))
            if AliasGeneratorEnum.has_value(scheme):
                current_user.alias_generator = scheme
                db.session.commit()
            flash("Your preference has been updated", "success")
            return redirect(url_for("dashboard.setting"))

        elif request.form.get(
                "form-name") == "change-random-alias-default-domain":
            default_domain = request.form.get("random-alias-default-domain")
            if default_domain:
                default_domain_id = int(default_domain)
                # sanity check
                domain = CustomDomain.get(default_domain_id)
                if (not domain or domain.user_id != current_user.id
                        or not domain.verified):
                    flash(
                        "Something went wrong, sorry for the inconvenience. Please retry. ",
                        "error",
                    )
                    return redirect(url_for("dashboard.setting"))
                current_user.default_random_alias_domain_id = default_domain_id
            else:
                current_user.default_random_alias_domain_id = None

            db.session.commit()
            flash("Your preference has been updated", "success")
            return redirect(url_for("dashboard.setting"))

        elif request.form.get("form-name") == "change-sender-format":
            sender_format = int(request.form.get("sender-format"))
            if SenderFormatEnum.has_value(sender_format):
                current_user.sender_format = sender_format
                db.session.commit()
                flash("Your sender format preference has been updated",
                      "success")
            db.session.commit()
            return redirect(url_for("dashboard.setting"))

        elif request.form.get("form-name") == "replace-ra":
            choose = request.form.get("replace-ra")
            if choose == "on":
                current_user.replace_reverse_alias = True
            else:
                current_user.replace_reverse_alias = False
            db.session.commit()
            flash("Your preference has been updated", "success")
            return redirect(url_for("dashboard.setting"))

        elif request.form.get("form-name") == "export-data":
            data = {
                "email": current_user.email,
                "name": current_user.name,
                "aliases": [],
                "apps": [],
                "custom_domains": [],
            }

            for alias in Alias.filter_by(
                    user_id=current_user.id).all():  # type: Alias
                data["aliases"].append(
                    dict(email=alias.email, enabled=alias.enabled))

            for custom_domain in CustomDomain.filter_by(
                    user_id=current_user.id).all():
                data["custom_domains"].append(custom_domain.domain)

            for app in Client.filter_by(
                    user_id=current_user.id):  # type: Client
                data["apps"].append(
                    dict(name=app.name,
                         home_url=app.home_url,
                         published=app.published))

            return Response(
                json.dumps(data),
                mimetype="text/json",
                headers={
                    "Content-Disposition": "attachment;filename=data.json"
                },
            )

    manual_sub = ManualSubscription.get_by(user_id=current_user.id)
    return render_template(
        "dashboard/setting.html",
        form=form,
        PlanEnum=PlanEnum,
        SenderFormatEnum=SenderFormatEnum,
        promo_form=promo_form,
        change_email_form=change_email_form,
        pending_email=pending_email,
        AliasGeneratorEnum=AliasGeneratorEnum,
        manual_sub=manual_sub,
        FIRST_ALIAS_DOMAIN=FIRST_ALIAS_DOMAIN,
    )
Пример #9
0
def get_alias_suffixes(user: User) -> [AliasSuffix]:
    """
    Similar to as get_available_suffixes() but also return custom domain that doesn't have MX set up.
    """
    user_custom_domains = CustomDomain.filter_by(
        user_id=user.id, ownership_verified=True).all()

    alias_suffixes: [AliasSuffix] = []

    # put custom domain first
    # for each user domain, generate both the domain and a random suffix version
    for custom_domain in user_custom_domains:
        if custom_domain.random_prefix_generation:
            suffix = "." + user.get_random_alias_suffix(
            ) + "@" + custom_domain.domain
            alias_suffix = AliasSuffix(
                is_custom=True,
                suffix=suffix,
                is_premium=False,
                domain=custom_domain.domain,
                mx_verified=custom_domain.verified,
            )
            if user.default_alias_custom_domain_id == custom_domain.id:
                alias_suffixes.insert(0, alias_suffix)
            else:
                alias_suffixes.append(alias_suffix)

        suffix = "@" + custom_domain.domain
        alias_suffix = AliasSuffix(
            is_custom=True,
            suffix=suffix,
            is_premium=False,
            domain=custom_domain.domain,
            mx_verified=custom_domain.verified,
        )

        # put the default domain to top
        # only if random_prefix_generation isn't enabled
        if (user.default_alias_custom_domain_id == custom_domain.id
                and not custom_domain.random_prefix_generation):
            alias_suffixes.insert(0, alias_suffix)
        else:
            alias_suffixes.append(alias_suffix)

    # then SimpleLogin domain
    for sl_domain in user.get_sl_domains():
        suffix = (("" if DISABLE_ALIAS_SUFFIX else "." +
                   user.get_random_alias_suffix()) + "@" + sl_domain.domain)
        alias_suffix = AliasSuffix(
            is_custom=False,
            suffix=suffix,
            is_premium=sl_domain.premium_only,
            domain=sl_domain.domain,
            mx_verified=True,
        )

        # put the default domain to top
        if user.default_alias_public_domain_id == sl_domain.id:
            alias_suffixes.insert(0, alias_suffix)
        else:
            alias_suffixes.append(alias_suffix)

    return alias_suffixes
Пример #10
0
def subdomain_route():
    if not current_user.subdomain_is_available():
        flash("Unknown error, redirect to the home page", "error")
        return redirect(url_for("dashboard.index"))

    sl_domains = SLDomain.filter_by(can_use_subdomain=True).all()
    subdomains = CustomDomain.filter_by(
        user_id=current_user.id, is_sl_subdomain=True
    ).all()

    errors = {}

    if request.method == "POST":
        if request.form.get("form-name") == "create":
            if not current_user.is_premium():
                flash("Only premium plan can add subdomain", "warning")
                return redirect(request.url)

            if current_user.subdomain_quota <= 0:
                flash(
                    f"You can't create more than {MAX_NB_SUBDOMAIN} subdomains", "error"
                )
                return redirect(request.url)

            subdomain = request.form.get("subdomain").lower().strip()
            domain = request.form.get("domain").lower().strip()

            if len(subdomain) < 3:
                flash("Subdomain must have at least 3 characters", "error")
                return redirect(request.url)

            if re.fullmatch(_SUBDOMAIN_PATTERN, subdomain) is None:
                flash(
                    "Subdomain can only contain lowercase letters, numbers and dashes (-)",
                    "error",
                )
                return redirect(request.url)

            if subdomain.endswith("-"):
                flash("Subdomain can't end with dash (-)", "error")
                return redirect(request.url)

            if domain not in [sl_domain.domain for sl_domain in sl_domains]:
                LOG.e("Domain %s is tampered by %s", domain, current_user)
                flash("Unknown error, refresh the page", "error")
                return redirect(request.url)

            full_domain = f"{subdomain}.{domain}"

            if CustomDomain.get_by(domain=full_domain):
                flash(f"{full_domain} already used", "error")
            elif Mailbox.filter(
                Mailbox.verified.is_(True),
                Mailbox.email.endswith(f"@{full_domain}"),
            ).first():
                flash(f"{full_domain} already used in a SimpleLogin mailbox", "error")
            else:
                try:
                    new_custom_domain = CustomDomain.create(
                        is_sl_subdomain=True,
                        catch_all=True,  # by default catch-all is enabled
                        domain=full_domain,
                        user_id=current_user.id,
                        verified=True,
                        dkim_verified=False,  # wildcard DNS does not work for DKIM
                        spf_verified=True,
                        dmarc_verified=False,  # wildcard DNS does not work for DMARC
                        ownership_verified=True,
                        commit=True,
                    )
                except SubdomainInTrashError:
                    flash(
                        f"{full_domain} has been used before and cannot be reused",
                        "error",
                    )
                else:
                    flash(
                        f"New subdomain {new_custom_domain.domain} is created",
                        "success",
                    )

                    return redirect(
                        url_for(
                            "dashboard.domain_detail",
                            custom_domain_id=new_custom_domain.id,
                        )
                    )

    return render_template(
        "dashboard/subdomain.html",
        sl_domains=sl_domains,
        errors=errors,
        subdomains=subdomains,
    )