summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/jwebmail/__init__.py53
-rw-r--r--src/jwebmail/model/read_mails.py13
-rw-r--r--src/jwebmail/read_mails.py65
-rw-r--r--src/jwebmail/render_mail.py4
-rw-r--r--src/jwebmail/templates/_main_table.html4
-rw-r--r--src/jwebmail/templates/about.html6
-rw-r--r--src/jwebmail/templates/login.html8
-rw-r--r--src/jwebmail/translations/de/LC_MESSAGES/messages.po106
-rw-r--r--src/jwebmail/webmail.py139
9 files changed, 184 insertions, 214 deletions
diff --git a/src/jwebmail/__init__.py b/src/jwebmail/__init__.py
index db0c796..b5279fb 100644
--- a/src/jwebmail/__init__.py
+++ b/src/jwebmail/__init__.py
@@ -1,8 +1,8 @@
-import os.path as ospath
+import os.path as os_path
import pwd
import sys
-from flask import Flask
+from flask import Flask, g
from flask_babel import Babel, get_locale
from flask_login import LoginManager, login_required
from jinja2 import ChainableUndefined
@@ -21,6 +21,7 @@ from .webmail import (
readmail,
sendmail,
writemail,
+ DEFAULT_LANGUAGE,
)
if sys.version_info >= (3, 11):
@@ -37,9 +38,9 @@ def validate_config(app):
assert "@" in conf["JWEBMAIL"]["ADMIN_MAIL"]
assert pwd.getpwnam(conf["JWEBMAIL"]["READ_MAILS"]["MAILBOX_USER"])
- assert ospath.isdir(conf["JWEBMAIL"]["READ_MAILS"]["MAILBOX"])
- assert ospath.isfile(conf["JWEBMAIL"]["READ_MAILS"]["AUTHENTICATOR"])
- assert ospath.isfile(conf["JWEBMAIL"]["READ_MAILS"]["BACKEND"])
+ assert os_path.isdir(conf["JWEBMAIL"]["READ_MAILS"]["MAILBOX"])
+ assert os_path.isfile(conf["JWEBMAIL"]["READ_MAILS"]["AUTHENTICATOR"])
+ assert os_path.isfile(conf["JWEBMAIL"]["READ_MAILS"]["BACKEND"])
def create_app():
@@ -49,7 +50,7 @@ def create_app():
app.config.from_file("../../jwebmail.toml", load=toml_load, text=False)
validate_config(app)
- Babel(app, locale_selector=lambda: "de")
+ Babel(app, locale_selector=lambda: g.get("lang_code", DEFAULT_LANGUAGE))
app.cli.add_command(compile_css_command)
@@ -58,32 +59,62 @@ def create_app():
login_manager.user_loader(load_user)
login_manager.init_app(app)
+ add_view_funcs(app)
+ route(app)
+
@app.context_processor
def inject_version():
return {"version": "4.0", "get_locale": get_locale, "format_mail": format_mail}
- add_view_funcs(app)
- route(app)
+ @app.url_defaults
+ def add_language_code(endpoint, values):
+ if "lang_code" in values:
+ return
+ if app.url_map.is_endpoint_expecting(endpoint, "lang_code"):
+ values["lang_code"] = g.get("lang_code", DEFAULT_LANGUAGE)
+
+ @app.url_value_preprocessor
+ def pull_lang_code(endpoint, values):
+ g.lang_code = (
+ values.pop("lang_code", DEFAULT_LANGUAGE) if values else DEFAULT_LANGUAGE
+ )
return app
def route(app):
app.add_url_rule("/", view_func=login, methods=["GET", "POST"])
+ app.add_url_rule("/<lang_code>/", view_func=login, methods=["GET", "POST"])
app.add_url_rule("/about", view_func=about)
+ app.add_url_rule("/<lang_code>/about", view_func=about)
+
app.add_url_rule("/logout", view_func=logout)
+ app.add_url_rule("/<lang_code>/logout", view_func=logout)
dh = login_required(displayheaders)
app.add_url_rule("/home/", view_func=dh)
app.add_url_rule("/home/<folder>", view_func=dh)
+ app.add_url_rule("/<lang_code>/home/", view_func=dh)
+ app.add_url_rule("/<lang_code>/home/<folder>", view_func=dh)
+ lr_readmail = login_required(readmail)
+ app.add_url_rule("/read/<msgid>", endpoint="read", view_func=lr_readmail)
+ app.add_url_rule("/read/<folder>/<msgid>", endpoint="read", view_func=lr_readmail)
app.add_url_rule(
- "/read/<msgid>", endpoint="read", view_func=login_required(readmail)
+ "/<lang_code>/read/<msgid>", endpoint="read", view_func=lr_readmail
)
- app.add_url_rule("/raw/<msgid>", endpoint="raw", view_func=login_required(rawmail))
+ app.add_url_rule(
+ "/<lang_code>/read/<folder>/<msgid>", endpoint="read", view_func=lr_readmail
+ )
+
+ lr_rawmail = login_required(rawmail)
+ app.add_url_rule("/raw/<msgid>", endpoint="raw", view_func=rawmail)
+ app.add_url_rule("/raw/<folder>/<msgid>", endpoint="raw", view_func=rawmail)
- app.add_url_rule("/write", endpoint="write", view_func=login_required(writemail))
+ lr_writemail = login_required(writemail)
+ app.add_url_rule("/write", endpoint="write", view_func=lr_writemail)
+ app.add_url_rule("/<lang_code>/write", endpoint="write", view_func=lr_writemail)
app.add_url_rule(
"/write", endpoint="send", view_func=login_required(sendmail), methods=["POST"]
)
diff --git a/src/jwebmail/model/read_mails.py b/src/jwebmail/model/read_mails.py
index f82b601..291fa1e 100644
--- a/src/jwebmail/model/read_mails.py
+++ b/src/jwebmail/model/read_mails.py
@@ -31,13 +31,12 @@ class QMailAuthuser:
shell=True,
timeout=2,
)
- match completed_proc.returncode:
- case 0:
- return True
- case 1:
- return False
- case n:
- raise QMAuthError("authentication error", n)
+ if completed_proc.returncode == 0:
+ return True
+ if completed_proc.returncode == 1:
+ return False
+ else:
+ raise QMAuthError("authentication error", completed_proc.returncode)
except TimeoutExpired:
return False
diff --git a/src/jwebmail/read_mails.py b/src/jwebmail/read_mails.py
index e1c3b8c..915567c 100644
--- a/src/jwebmail/read_mails.py
+++ b/src/jwebmail/read_mails.py
@@ -1,11 +1,17 @@
-import dbm
-import shelve
-
+import redis
from flask import current_app, g
-from flask_login import current_user
+from flask_login import UserMixin, current_user
from .model.read_mails import QMailAuthuser
+EXPIRATION_SEC = 60 * 60 * 25
+
+
+class JWebmailUser(UserMixin):
+ def __init__(self, mail_addr, password):
+ self.id = mail_addr
+ self.password = password
+
def build_qma(username, password):
authenticator = current_app.config["JWEBMAIL"]["READ_MAILS"]["AUTHENTICATOR"]
@@ -22,29 +28,52 @@ def login(username, password):
return build_qma(username, password).verify_user()
-def add_user(user):
- with shelve.open("user_sessions", flag="c") as s:
- s[user.get_id()] = user
+def add_user(user: JWebmailUser):
+ passwd = current_app.config["JWEBMAIL"]["READ_MAILS"]["SESSION_STORE_PASSWD"]
+ r = redis.Redis(
+ host="localhost",
+ port=6379,
+ decode_responses=True,
+ protocol=3,
+ username="jwebmail",
+ password=passwd,
+ )
+ r.setex(f"jwm:user:{user.get_id()}", EXPIRATION_SEC, user.password)
-def load_user(username):
- try:
- with shelve.open("user_sessions", flag="r") as s:
- user = s[username]
- return user
- except dbm.error:
- return None
- except KeyError:
+def load_user(username: str) -> JWebmailUser:
+ passwd = current_app.config["JWEBMAIL"]["READ_MAILS"]["SESSION_STORE_PASSWD"]
+ r = redis.Redis(
+ host="localhost",
+ port=6379,
+ decode_responses=True,
+ protocol=3,
+ username="jwebmail",
+ password=passwd,
+ )
+ passwd = r.getex(f"jwm:user:{username}", EXPIRATION_SEC)
+ if passwd is None:
return None
+ return JWebmailUser(username, passwd)
def get_read_mails_logged_in():
if "read_mails" in g:
return g.read_mails
- with shelve.open("user_sessions", flag="r") as s:
- user_data = s[current_user.get_id()]
+ passwd = current_app.config["JWEBMAIL"]["READ_MAILS"]["SESSION_STORE_PASSWD"]
+ r = redis.Redis(
+ host="localhost",
+ port=6379,
+ decode_responses=True,
+ protocol=3,
+ username="jwebmail",
+ password=passwd,
+ )
+ passwd = r.get(f"jwm:user:{current_user.get_id()}")
+ if passwd is None:
+ raise KeyError(current_user.get_id())
- qma = build_qma(current_user.get_id(), user_data.password)
+ qma = build_qma(current_user.get_id(), passwd)
g.read_mails = qma
return qma
diff --git a/src/jwebmail/render_mail.py b/src/jwebmail/render_mail.py
index 0e5406c..804b200 100644
--- a/src/jwebmail/render_mail.py
+++ b/src/jwebmail/render_mail.py
@@ -143,7 +143,9 @@ def mime_render(maintype, subtype, content, path):
renderer = MIMERenderSubs.get((maintype, subtype)) or MIMERenderSubs.get(maintype)
if not renderer:
- return f'<p class="jwm-body-unsupported">Unsupported MIME type of <code>{maintype}/{subtype}</code>.</p>\n'
+ typ = f"<code>{maintype}/{subtype}</code>"
+ msg = gettext("Unsupported MIME type of {mime_type}.").format(mime_type=typ)
+ return f'<p class="jwm-body-unsupported">{msg}</p>\n'
return renderer(subtype, content, path)
diff --git a/src/jwebmail/templates/_main_table.html b/src/jwebmail/templates/_main_table.html
index 02f9f81..b331087 100644
--- a/src/jwebmail/templates/_main_table.html
+++ b/src/jwebmail/templates/_main_table.html
@@ -18,7 +18,9 @@
</div>
<div class="column is-10">
- <a href="{{ url_for('read', msgid=msg.message_handle) }}">{{ msg.head.subject or '_' }}</a>
+ <a href="{{ url_for('read', msgid=msg.message_handle, folder=folder) }}">
+ {{ msg.head.subject or '_' }}
+ </a>
</div>
<div class="column is-2">
diff --git a/src/jwebmail/templates/about.html b/src/jwebmail/templates/about.html
index e197275..836b668 100644
--- a/src/jwebmail/templates/about.html
+++ b/src/jwebmail/templates/about.html
@@ -40,7 +40,11 @@
<ul>
{% for lang in languages %}
<li>
- {{ get_locale().languages[lang.language] }}
+ {% if lang == get_locale() %}
+ <a href="{{ url_for('about', lang_code=lang.language) }}"><strong>{{ get_locale().languages[lang.language] }}</strong></a>
+ {% else %}
+ <a href="{{ url_for('about', lang_code=lang.language) }}">{{ get_locale().languages[lang.language] }}</a>
+ {% endif %}
</li>
{% endfor %}
</ul>
diff --git a/src/jwebmail/templates/login.html b/src/jwebmail/templates/login.html
index 2915de3..d73238d 100644
--- a/src/jwebmail/templates/login.html
+++ b/src/jwebmail/templates/login.html
@@ -26,7 +26,7 @@
</div>
{% endif %}
- <form name="login1" method="POST" class="pure-form pure-form-aligned jwm-round">
+ <form name="login1" id="login1" method="POST" class="pure-form pure-form-aligned jwm-round">
{{ login_form.csrf_token }}
@@ -74,10 +74,10 @@
{% block scripts %}
<script type="text/javascript">
- if (!document.login1.userid.value) {
- document.login1.userid.focus();
+ if (!document.forms.login1.username.value) {
+ document.forms.login1.username.focus();
} else {
- document.login1.password.focus();
+ document.forms.login1.password.focus();
}
</script>
{% endblock %}
diff --git a/src/jwebmail/translations/de/LC_MESSAGES/messages.po b/src/jwebmail/translations/de/LC_MESSAGES/messages.po
index ad48d16..48b1dca 100644
--- a/src/jwebmail/translations/de/LC_MESSAGES/messages.po
+++ b/src/jwebmail/translations/de/LC_MESSAGES/messages.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2023-11-26 19:21+0100\n"
+"POT-Creation-Date: 2023-12-04 22:59+0100\n"
"PO-Revision-Date: 2023-11-23 12:18+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de\n"
@@ -18,150 +18,160 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.13.1\n"
-#: jwebmail/render_mail.py:63
+#: src/jwebmail/render_mail.py:67
msgid "Attachment {filename} of type {filetype}"
msgstr "Anhang {filename} des types {filetype}"
-#: jwebmail/render_mail.py:113
+#: src/jwebmail/render_mail.py:117
msgid "From"
msgstr "Von"
-#: jwebmail/render_mail.py:114
+#: src/jwebmail/render_mail.py:118
msgid "To"
-msgstr "Fuer"
+msgstr "Für"
-#: jwebmail/render_mail.py:115
+#: src/jwebmail/render_mail.py:119
msgid "CC"
msgstr ""
-#: jwebmail/render_mail.py:116
+#: src/jwebmail/render_mail.py:120
msgid "BCC"
msgstr ""
-#: jwebmail/webmail.py:39
+#: src/jwebmail/render_mail.py:147
+msgid "Unsupported MIME type of {mime_type}."
+msgstr "Nicht unterstützter MIME-Typ {mime_type}."
+
+#: src/jwebmail/webmail.py:27
msgid "Username"
msgstr "Nutzername"
-#: jwebmail/webmail.py:41
+#: src/jwebmail/webmail.py:29
msgid "Password"
msgstr "Passwort"
-#: jwebmail/webmail.py:75
-msgid "login failed"
-msgstr "Login fehlgeschlagen"
+#: src/jwebmail/webmail.py:64
+msgid "login failed!"
+msgstr "Login fehlgeschlagen!"
-#: jwebmail/webmail.py:120
+#: src/jwebmail/webmail.py:111
msgid "displaying <b>{start} - {end}</b> of <b>{total}</b> {record_name}"
msgstr "zeige <b>{start} - {end}</b> von <b>{total}</b> {record_name} an"
-#: jwebmail/webmail.py:173
+#: src/jwebmail/webmail.py:164
msgid "succ_move"
msgstr "erfolgreich Verschoben"
-#: jwebmail/webmail.py:221
+#: src/jwebmail/webmail.py:211
msgid "error_send"
msgstr "Fehler beim senden"
-#: jwebmail/webmail.py:223
+#: src/jwebmail/webmail.py:213
msgid "succ_send"
msgstr "erfolgreich Verschoben"
-#: jwebmail/templates/_bot_nav.html:11
+#: src/jwebmail/templates/_bot_nav.html:11
msgid "Move to"
msgstr "Verschiebe nach"
-#: jwebmail/templates/_bot_nav.html:19 jwebmail/templates/_folders.html:21
+#: src/jwebmail/templates/_bot_nav.html:19
+#: src/jwebmail/templates/_folders.html:21
msgid "Home"
msgstr "Ursprung"
-#: jwebmail/templates/_bot_nav.html:26
+#: src/jwebmail/templates/_bot_nav.html:26
msgid "Move"
msgstr "Verschieben"
-#: jwebmail/templates/_bot_nav.html:38
+#: src/jwebmail/templates/_bot_nav.html:38
msgid "Remove"
-msgstr "Loeschen"
+msgstr "Löschen"
-#: jwebmail/templates/_bot_nav.html:44
+#: src/jwebmail/templates/_bot_nav.html:44
msgid "check all"
msgstr "alle markieren"
-#: jwebmail/templates/_folders.html:36
+#: src/jwebmail/templates/_folders.html:36
#, python-format
msgid "%(total_new_mails)s new"
msgstr "%(total_new_mails)s neu"
-#: jwebmail/templates/_folders.html:41
+#: src/jwebmail/templates/_folders.html:41
msgid "mailbox size: "
-msgstr "mailbox groesse: "
+msgstr "mailbox größe: "
-#: jwebmail/templates/_top_nav.html:4
+#: src/jwebmail/templates/_top_nav.html:4
msgid "Logout"
msgstr "Abmelden"
-#: jwebmail/templates/_top_nav.html:5
+#: src/jwebmail/templates/_top_nav.html:5
msgid "Write"
msgstr "Schreiben"
-#: jwebmail/templates/_top_nav.html:11
+#: src/jwebmail/templates/_top_nav.html:11
msgid "Search"
msgstr "Suchen"
-#: jwebmail/templates/_top_nav.html:26
+#: src/jwebmail/templates/_top_nav.html:26
msgid "Sort"
msgstr "Sortieren"
-#: jwebmail/templates/_top_nav.html:32 jwebmail/templates/_top_nav.html:33
+#: src/jwebmail/templates/_top_nav.html:32
+#: src/jwebmail/templates/_top_nav.html:33
msgid "Date"
msgstr "Datum"
-#: jwebmail/templates/_top_nav.html:32 jwebmail/templates/_top_nav.html:34
-#: jwebmail/templates/_top_nav.html:35
+#: src/jwebmail/templates/_top_nav.html:32
+#: src/jwebmail/templates/_top_nav.html:34
+#: src/jwebmail/templates/_top_nav.html:35
msgid "Descending"
msgstr "Absteigend"
-#: jwebmail/templates/_top_nav.html:33 jwebmail/templates/_top_nav.html:36
+#: src/jwebmail/templates/_top_nav.html:33
+#: src/jwebmail/templates/_top_nav.html:36
msgid "Ascending"
msgstr "Aufsteigend"
-#: jwebmail/templates/_top_nav.html:34
+#: src/jwebmail/templates/_top_nav.html:34
msgid "Size"
-msgstr "Groesse"
+msgstr "Größe"
-#: jwebmail/templates/_top_nav.html:35 jwebmail/templates/_top_nav.html:36
+#: src/jwebmail/templates/_top_nav.html:35
+#: src/jwebmail/templates/_top_nav.html:36
msgid "Sender"
msgstr "Sender"
-#: jwebmail/templates/about.html:57 jwebmail/templates/login.html:13
-#: jwebmail/templates/login.html:69
+#: src/jwebmail/templates/about.html:61 src/jwebmail/templates/login.html:8
+#: src/jwebmail/templates/login.html:64
msgid "Login"
msgstr "Anmelden"
-#: jwebmail/templates/displayheaders.html:25
+#: src/jwebmail/templates/displayheaders.html:25
msgid "This folder is empty!"
msgstr "Dieses Verzeichnis ist leer!"
-#: jwebmail/templates/mainlayout.html:22
+#: src/jwebmail/templates/mainlayout.html:22
msgid "About"
-msgstr "Ueber"
+msgstr "Über"
-#: jwebmail/templates/mainlayout.html:25
+#: src/jwebmail/templates/mainlayout.html:25
msgid "Version"
msgstr "Version"
-#: jwebmail/templates/not_found.html:19
+#: src/jwebmail/templates/not_found.html:19
msgid "start page"
msgstr "Startseite"
-#: jwebmail/templates/readmail.html:12 jwebmail/templates/writemail.html:78
+#: src/jwebmail/templates/readmail.html:12
+#: src/jwebmail/templates/writemail.html:78
msgid "back"
-msgstr "zurueck"
+msgstr "zurück"
-#: jwebmail/templates/writemail.html:62
+#: src/jwebmail/templates/writemail.html:62
msgid "attach file"
-msgstr "Datei anhaengen"
+msgstr "Datei anhängen"
-#: jwebmail/templates/writemail.html:71
+#: src/jwebmail/templates/writemail.html:71
msgid "Send"
msgstr "Senden"
diff --git a/src/jwebmail/webmail.py b/src/jwebmail/webmail.py
index 4e47dbd..2cfc834 100644
--- a/src/jwebmail/webmail.py
+++ b/src/jwebmail/webmail.py
@@ -2,7 +2,7 @@ from urllib.parse import urlparse
from flask import abort, current_app, flash, redirect, render_template, request, url_for
from flask_babel import gettext, lazy_gettext
-from flask_login import UserMixin, current_user, login_user, logout_user
+from flask_login import current_user, login_user, logout_user
from flask_paginate import Pagination, get_page_parameter, get_per_page_parameter
from flask_wtf import FlaskForm
from wtforms import (
@@ -16,15 +16,11 @@ from wtforms import (
)
from .model.read_mails import QMAuthError
-from .read_mails import add_user, get_read_mails_logged_in
+from .read_mails import JWebmailUser, add_user, get_read_mails_logged_in
from .read_mails import login as rm_login
from .render_mail import to_mime_type
-
-class JWebmailUser(UserMixin):
- def __init__(self, mail_addr, password):
- self.id = mail_addr
- self.password = password
+DEFAULT_LANGUAGE = "de"
class LoginForm(FlaskForm):
@@ -51,6 +47,7 @@ def login():
form = LoginForm()
warn = ""
+ rc = 200
if form.validate_on_submit():
if rm_login(form.username.data, form.password.data):
@@ -58,15 +55,17 @@ def login():
add_user(user)
login_user(user)
- next = request.args.get("next")
+ nxt = request.args.get("next")
- if urlparse(next).netloc:
+ if urlparse(nxt).netloc:
abort(401)
- return redirect(next or url_for("displayheaders"), 303)
+ return redirect(nxt or url_for("displayheaders"), 303)
else:
- warn = gettext("login failed")
+ warn = gettext("login failed!")
+ elif request.method == "POST":
+ rc = 401
- return render_template("login.html", login_form=form, warn=warn), 401
+ return render_template("login.html", login_form=form, warn=warn), rc
def logout():
@@ -137,13 +136,13 @@ def displayheaders(folder=""):
return render_template("displayheaders.html", **vals)
-def readmail(msgid):
+def readmail(msgid, folder=""):
try:
- mail = get_read_mails_logged_in().show("", msgid)
+ mail = get_read_mails_logged_in().show(folder, msgid)
except QMAuthError:
return render_template("not_found.html"), 404
- return render_template("readmail.html", msg=mail)
+ return render_template("readmail.html", msg=mail, folder=folder)
def writemail():
@@ -166,10 +165,10 @@ def move(folder):
return redirect(url_for("displayheaders"), 303)
-def rawmail(msgid):
+def rawmail(msgid, folder=""):
path = request.args.get("path", "")
- content = get_read_mails_logged_in().raw("", msgid, path)
+ content = get_read_mails_logged_in().raw(folder, msgid, path)
headers = []
@@ -213,109 +212,3 @@ def sendmail():
flash(gettext("succ_send"))
return redirect(url_for("displayheaders"), 303)
-
-
-"""
-sub remove {
- my $self = shift;
-
- my $v = $self->validation;
- $v->csrf_protect;
- $v->required('mail');
-
- if ($v->has_error) {
- $self->reply->exception('errors in ' . join('', $v->failed->@*));
- return;
- }
-
- my $auth = $self->stash(STS_AUTH);
-
- my $mm = $self->every_param('mail');
- my $folder = $self->stash('folder');
-
- $self->users->remove($auth, $folder, $_) for @$mm;
-
- $self->res->code(303);
- $self->redirect_to('displayheaders');
-}
-
-
-### session password handling
-
-use constant { S_PASSWD => 'pw', S_OTP_S3D_PW => 'otp_s3d_pw' };
-
-sub _rand_data {
- my $len = shift;
-
- if (TRUE_RANDOM) {
- #return makerandom_octet(Length => $len, Strength => 0); # was used for Crypt::Random
- return urandom($len);
- }
- else {
- my $res = '';
- for (0..$len-1) {
- vec($res, $_, 8) = int rand 256;
- }
-
- return $res;
- }
-}
-
-sub _session_passwd {
- my ($self, $passwd, $challenge) = @_;
- my $secAlg = LOGIN_SCHEME;
-
- $self->_warn_crypt;
-
- if (defined $passwd) { # set
- if ($secAlg eq fc 'cram_md5') {
- $self->session(S_PASSWD() => $passwd, challenge => $challenge);
- }
- elsif ($secAlg eq fc 'plain') {
- unless ($passwd) {
- $self->s3d(S_PASSWD, '');
- delete $self->session->{S_OTP_S3D_PW()};
- return;
- }
- die "'$passwd' contains invalid character \\n" if $passwd =~ /\n/;
- if (length $passwd < 20) {
- $passwd .= "\n" . ' ' x (20 - length($passwd) - 1);
- }
- my $passwd_utf8 = encode('UTF-8', $passwd);
- my $rand_bytes = _rand_data(length $passwd_utf8);
- $self->s3d(S_PASSWD, b64_encode($passwd_utf8 ^ $rand_bytes, ''));
- $self->session(S_OTP_S3D_PW, b64_encode($rand_bytes, ''));
- }
- else {
- die
- }
- }
- else { # get
- if ($secAlg eq fc 'cram_md5') {
- wantarray or carp "you forgot the challenge";
- return ($self->session(S_PASSWD), $self->session('challenge'));
- }
- elsif ($secAlg eq fc 'plain') {
- my $pw = b64_decode($self->s3d(S_PASSWD) || '');
- my $otp = b64_decode($self->session(S_OTP_S3D_PW) || '');
- my ($res) = split "\n", decode('UTF-8', $pw ^ $otp), 2;
- return $res;
- }
- else {
- die
- }
- }
-}
-
-sub _warn_crypt {
- my $self = shift;
-
- state $once = 0;
-
- if ( !TRUE_RANDOM && !$once && LOGIN_SCHEME eq fc 'plain' ) {
- $self->log->warn("Falling back to pseudo random generation. Please install Crypt::URandom");
- $once = 1;
- }
-}
-
-"""