mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-10-27 23:53:32 +03:00
Compare commits
No commits in common. "master" and "v1.10.2.136-beta.3" have entirely different histories.
master
...
v1.10.2.13
4390 changed files with 418773 additions and 322293 deletions
|
|
@ -1,4 +0,0 @@
|
||||||
last 1 version
|
|
||||||
> 30%
|
|
||||||
not IE 11
|
|
||||||
not IE_Mob 11
|
|
||||||
19
.cmds
19
.cmds
|
|
@ -1,19 +0,0 @@
|
||||||
|
|
||||||
# yarn
|
|
||||||
yarn install
|
|
||||||
yarn upgrade xxx@1.2.3
|
|
||||||
|
|
||||||
# transifex
|
|
||||||
tx pull -a
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
yarn outdated
|
|
||||||
yarn upgrade-interactive --exact --latest
|
|
||||||
|
|
||||||
# webpack
|
|
||||||
webpack --color --watch
|
|
||||||
|
|
||||||
#
|
|
||||||
gpg --import x
|
|
||||||
gpg --detach-sign --armor --openpgp -u 87DA4591 --passphrase ?? x
|
|
||||||
for ff in `ls ./build/dist/releases/**/**/*.zip | xargs`; do gpg --detach-sign --no-tty --yes --armor --openpgp -u 87DA4591 --passphrase ?? $ff; done
|
|
||||||
|
|
@ -1,233 +0,0 @@
|
||||||
#! /bin/sh
|
|
||||||
|
|
||||||
##
|
|
||||||
# Wrapper for various setup scripts included in the docker-mailserver
|
|
||||||
#
|
|
||||||
|
|
||||||
INFO=$(docker ps \
|
|
||||||
--no-trunc \
|
|
||||||
--format="{{.Image}}\t{{.Names}}\t{{.Command}}" | \
|
|
||||||
grep "/bin/sh -c 'supervisord -c /etc/supervisor/supervisord.conf'")
|
|
||||||
|
|
||||||
IMAGE_NAME=$(echo $INFO | awk '{print $1}')
|
|
||||||
CONTAINER_NAME=$(echo $INFO | awk '{print $2}')
|
|
||||||
CONFIG_PATH="$(pwd)/config"
|
|
||||||
|
|
||||||
if [ -z "$IMAGE_NAME" ]; then
|
|
||||||
IMAGE_NAME=tvial/docker-mailserver:latest
|
|
||||||
fi
|
|
||||||
|
|
||||||
_inspect() {
|
|
||||||
if _docker_image_exists "$IMAGE_NAME"; then
|
|
||||||
echo "Image: $IMAGE_NAME"
|
|
||||||
else
|
|
||||||
echo "Image: '$IMAGE_NAME' can’t be found."
|
|
||||||
fi
|
|
||||||
if [ -n "$CONTAINER_NAME" ]; then
|
|
||||||
echo "Container: $CONTAINER_NAME"
|
|
||||||
else
|
|
||||||
echo "Container: Not running, please start docker-mailserver."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_usage() {
|
|
||||||
echo "Usage: $0 [-i IMAGE_NAME] [-c CONTAINER_NAME] <subcommand> <subcommand> [args]
|
|
||||||
|
|
||||||
OPTIONS:
|
|
||||||
|
|
||||||
-i IMAGE_NAME The name of the docker-mailserver image, by default
|
|
||||||
'tvial/docker-mailserver:latest'.
|
|
||||||
-c CONTAINER_NAME The name of the running container.
|
|
||||||
|
|
||||||
-p PATH config folder path (default: $(pwd)/config)
|
|
||||||
|
|
||||||
SUBCOMMANDS:
|
|
||||||
|
|
||||||
email:
|
|
||||||
|
|
||||||
$0 email add <email> [<password>]
|
|
||||||
$0 email update <email> [<password>]
|
|
||||||
$0 email del <email>
|
|
||||||
$0 email restrict <add|del|list> <send|receive> [<email>]
|
|
||||||
$0 email list
|
|
||||||
|
|
||||||
alias:
|
|
||||||
$0 alias add <email> <recipient>
|
|
||||||
$0 alias del <email> <recipient>
|
|
||||||
$0 alias list
|
|
||||||
|
|
||||||
config:
|
|
||||||
|
|
||||||
$0 config dkim
|
|
||||||
$0 config ssl
|
|
||||||
|
|
||||||
debug:
|
|
||||||
|
|
||||||
$0 debug fetchmail
|
|
||||||
$0 debug fail2ban [<unban> <ip-address>]
|
|
||||||
$0 debug show-mail-logs
|
|
||||||
$0 debug inspect
|
|
||||||
$0 debug login <commands>
|
|
||||||
"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
_docker_image_exists() {
|
|
||||||
if docker history -q "$1" >/dev/null 2>&1; then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_docker_image() {
|
|
||||||
if ! _docker_image_exists "$IMAGE_NAME"; then
|
|
||||||
echo "Image '$IMAGE_NAME' not found. Pulling ..."
|
|
||||||
docker pull "$IMAGE_NAME"
|
|
||||||
fi
|
|
||||||
docker run \
|
|
||||||
--rm \
|
|
||||||
-v "$CONFIG_PATH":/tmp/docker-mailserver \
|
|
||||||
-ti "$IMAGE_NAME" $@
|
|
||||||
}
|
|
||||||
|
|
||||||
_docker_container() {
|
|
||||||
if [ -n "$CONTAINER_NAME" ]; then
|
|
||||||
docker exec -ti "$CONTAINER_NAME" $@
|
|
||||||
else
|
|
||||||
echo "The docker-mailserver is not running!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
while getopts ":c:i:p:" OPT; do
|
|
||||||
case $OPT in
|
|
||||||
c)
|
|
||||||
CONTAINER_NAME="$OPTARG"
|
|
||||||
;;
|
|
||||||
i)
|
|
||||||
IMAGE_NAME="$OPTARG"
|
|
||||||
;;
|
|
||||||
p)
|
|
||||||
case "$OPTARG" in
|
|
||||||
/*)
|
|
||||||
CONFIG_PATH="$OPTARG"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
CONFIG_PATH="$(pwd)/$OPTARG"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
if [ ! -d "$CONFIG_PATH" ]; then
|
|
||||||
echo "Directory doesn't exist"
|
|
||||||
_usage
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
\?)
|
|
||||||
echo "Invalid option: -$OPTARG" >&2
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
shift $((OPTIND-1))
|
|
||||||
|
|
||||||
case $1 in
|
|
||||||
|
|
||||||
email)
|
|
||||||
shift
|
|
||||||
case $1 in
|
|
||||||
add)
|
|
||||||
shift
|
|
||||||
_docker_image addmailuser $@
|
|
||||||
;;
|
|
||||||
update)
|
|
||||||
shift
|
|
||||||
_docker_image updatemailuser $@
|
|
||||||
;;
|
|
||||||
del)
|
|
||||||
shift
|
|
||||||
_docker_image delmailuser $@
|
|
||||||
;;
|
|
||||||
restrict)
|
|
||||||
shift
|
|
||||||
_docker_container restrict-access $@
|
|
||||||
;;
|
|
||||||
list)
|
|
||||||
_docker_image listmailuser
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
_usage
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
|
|
||||||
alias)
|
|
||||||
shift
|
|
||||||
case $1 in
|
|
||||||
add)
|
|
||||||
shift
|
|
||||||
_docker_image addalias $@
|
|
||||||
;;
|
|
||||||
del)
|
|
||||||
shift
|
|
||||||
_docker_image delalias $@
|
|
||||||
;;
|
|
||||||
list)
|
|
||||||
shift
|
|
||||||
_docker_image listalias $@
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
_usage
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
|
|
||||||
config)
|
|
||||||
shift
|
|
||||||
case $1 in
|
|
||||||
dkim)
|
|
||||||
_docker_image generate-dkim-config
|
|
||||||
;;
|
|
||||||
ssl)
|
|
||||||
_docker_image generate-ssl-certificate
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
_usage
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
|
|
||||||
debug)
|
|
||||||
shift
|
|
||||||
case $1 in
|
|
||||||
fetchmail)
|
|
||||||
_docker_image debug-fetchmail
|
|
||||||
;;
|
|
||||||
fail2ban)
|
|
||||||
shift
|
|
||||||
_docker_container fail2ban $@
|
|
||||||
;;
|
|
||||||
show-mail-logs)
|
|
||||||
_docker_container cat /var/log/mail/mail.log
|
|
||||||
;;
|
|
||||||
inspect)
|
|
||||||
_inspect
|
|
||||||
;;
|
|
||||||
login)
|
|
||||||
shift
|
|
||||||
if [ -z "$1" ]; then
|
|
||||||
_docker_container /bin/bash
|
|
||||||
else
|
|
||||||
_docker_container /bin/bash -c "$@"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
_usage
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
_usage
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
server {
|
|
||||||
listen 80 default;
|
|
||||||
listen 443 ssl;
|
|
||||||
server_name localhost _;
|
|
||||||
root /var/www;
|
|
||||||
|
|
||||||
index index.php index.html;
|
|
||||||
|
|
||||||
autoindex on;
|
|
||||||
charset utf-8;
|
|
||||||
client_max_body_size 500m;
|
|
||||||
|
|
||||||
ssl_certificate /etc/nginx/ssl/localhost.cert;
|
|
||||||
ssl_certificate_key /etc/nginx/ssl/localhost.key;
|
|
||||||
|
|
||||||
gzip on;
|
|
||||||
gzip_disable "msie6";
|
|
||||||
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;
|
|
||||||
gzip_vary on;
|
|
||||||
|
|
||||||
access_log /var/log/nginx/default.access.log;
|
|
||||||
error_log /var/log/nginx/default.error.log;
|
|
||||||
|
|
||||||
location = /favicon.ico { access_log off; log_not_found off; }
|
|
||||||
location ~* favicon\.(ico|png)$ { access_log off; log_not_found off; }
|
|
||||||
location = /browserconfig.xml { access_log off; log_not_found off; }
|
|
||||||
location = /robots.txt { access_log off; log_not_found off; }
|
|
||||||
location = /humans.txt { access_log off; log_not_found off; }
|
|
||||||
location = /apple-touch-icon.png { access_log off; log_not_found off; }
|
|
||||||
location = /apple-touch-icon-precomposed.png { access_log off; log_not_found off; }
|
|
||||||
location ~ /\.ht { deny all; return 404; }
|
|
||||||
location ~ /\.git { deny all; return 404; }
|
|
||||||
location ~ /\.svn { deny all; return 404; }
|
|
||||||
|
|
||||||
location ~* ^.+\.(?:jpe?g|gif|bmp|ico|png|css|js|swf)$ {
|
|
||||||
expires 30d;
|
|
||||||
access_log off;
|
|
||||||
add_header Pragma public;
|
|
||||||
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~ \.php(/|$) {
|
|
||||||
include fastcgi_params;
|
|
||||||
fastcgi_split_path_info ^(.+\.php)(/.*)$;
|
|
||||||
fastcgi_index index.php;
|
|
||||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
|
||||||
fastcgi_param HTTPS off;
|
|
||||||
fastcgi_pass php:9000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
SERVER_NAME=localhost
|
|
||||||
SUBJECT="/C=RU/ST=RND/L=Taganrog/O=Umbrella Web/CN=${SERVER_NAME}"
|
|
||||||
|
|
||||||
mkdir -p ./ssl
|
|
||||||
|
|
||||||
if [ -f ./ssl/${SERVER_NAME}.cert ]; then
|
|
||||||
rm -rf ./ssl/${SERVER_NAME}.cert
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -f ./ssl/${SERVER_NAME}.key ]; then
|
|
||||||
rm -rf ./ssl/${SERVER_NAME}.key
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Generating ROOT pem files
|
|
||||||
openssl req -x509 -new -nodes -newkey rsa:2048 -keyout ./ssl/server_rootCA.key -sha256 -days 1024 -out ./ssl/server_rootCA.pem -subj "${SUBJECT}" 2> /dev/null
|
|
||||||
|
|
||||||
# Generating v3.ext file
|
|
||||||
cat <<EOF > ./ssl/v3.ext
|
|
||||||
authorityKeyIdentifier=keyid,issuer
|
|
||||||
basicConstraints=CA:FALSE
|
|
||||||
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
|
|
||||||
subjectAltName = @alt_names
|
|
||||||
|
|
||||||
[alt_names]
|
|
||||||
DNS.1 = ${SERVER_NAME}
|
|
||||||
DNS.2 = www.${SERVER_NAME}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo " - Generating SSL key file"
|
|
||||||
openssl req -new -newkey rsa:2048 -sha256 -nodes -newkey rsa:2048 -keyout ./ssl/${SERVER_NAME}.key -subj "${SUBJECT}" -out ./ssl/server_rootCA.csr 2> /dev/null
|
|
||||||
|
|
||||||
echo " - Generating SSL certificate file"
|
|
||||||
openssl x509 -req -in ./ssl/server_rootCA.csr -CA ./ssl/server_rootCA.pem -CAkey ./ssl/server_rootCA.key -CAcreateserial -out ./ssl/${SERVER_NAME}.cert -days 3650 -sha256 -extfile ./ssl/v3.ext 2> /dev/null
|
|
||||||
|
|
||||||
# echo " - Adding certificate into local keychain"
|
|
||||||
# sudo security add-trusted-cert -d -r trustRoot -k "/Library/Keychains/System.keychain" ./ssl/server_rootCA.pem 2> /dev/null
|
|
||||||
|
|
||||||
echo " - Runing garbage collector"
|
|
||||||
rm -rf ./ssl/server_rootCA.csr ./ssl/server_rootCA.key ./ssl/server_rootCA.pem ./ssl/v3.ext ./.srl
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
FROM php:7.4-fpm
|
|
||||||
|
|
||||||
RUN apt-get update
|
|
||||||
|
|
||||||
RUN apt-get install -y \
|
|
||||||
git unzip wget zip curl mlocate \
|
|
||||||
libicu-dev libpcre3-dev libicu-dev \
|
|
||||||
build-essential chrpath libssl-dev \
|
|
||||||
libxft-dev libfreetype6 libfreetype6-dev \
|
|
||||||
libpng-dev libjpeg62-turbo-dev \
|
|
||||||
libfontconfig1 libfontconfig1-dev libzip-dev libldap2-dev
|
|
||||||
|
|
||||||
RUN pecl install xxtea-1.0.11 && \
|
|
||||||
docker-php-ext-enable xxtea
|
|
||||||
|
|
||||||
RUN docker-php-ext-configure intl && \
|
|
||||||
docker-php-ext-configure ldap && \
|
|
||||||
docker-php-ext-configure gd --with-freetype=/usr/include/ --with-jpeg=/usr/include/ && \
|
|
||||||
docker-php-ext-install opcache pdo_mysql zip intl gd ldap
|
|
||||||
|
|
||||||
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
|
|
||||||
|
|
||||||
RUN curl --location --output /usr/local/bin/phpunit https://phar.phpunit.de/phpunit.phar && chmod +x /usr/local/bin/phpunit
|
|
||||||
|
|
||||||
RUN apt-get -y autoremove && apt-get clean
|
|
||||||
|
|
||||||
RUN sed -i '/^;catch_workers_output/ccatch_workers_output = yes' '/usr/local/etc/php-fpm.d/www.conf'
|
|
||||||
|
|
||||||
EXPOSE 9000
|
|
||||||
|
|
||||||
CMD ["php-fpm"]
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
date.timezone = UTC
|
|
||||||
upload_max_filesize = 24M
|
|
||||||
post_max_size = 25M
|
|
||||||
|
|
||||||
# log_errors = On
|
|
||||||
# display_errors = On
|
|
||||||
# error_reporting = E_ALL
|
|
||||||
# error_log = /dev/stderr
|
|
||||||
|
|
@ -1,192 +0,0 @@
|
||||||
# syntax=docker/dockerfile:1
|
|
||||||
|
|
||||||
FROM alpine:3.24.3 AS builder
|
|
||||||
RUN apk add --no-cache php85 php85-json php-phar php-zip
|
|
||||||
RUN apk add --no-cache npm
|
|
||||||
RUN npm install -g gulp yarn
|
|
||||||
WORKDIR /source
|
|
||||||
COPY package.json yarn.lock ./
|
|
||||||
RUN yarn install
|
|
||||||
COPY . .
|
|
||||||
# Patch release.php with hotfix from: https://github.com/xgbstar1/snappymail-docker/blob/main/Dockerfile, so that release.php doesn't fail with error
|
|
||||||
RUN sed -i 's_^if.*rename.*snappymail.v.0.0.0.*$_if (!!system("mv snappymail/v/0.0.0 snappymail/v/{$package->version}")) {_' cli/release.php || true
|
|
||||||
RUN php release.php
|
|
||||||
RUN set -eux; \
|
|
||||||
VERSION=$( ls build/dist/releases/webmail ); \
|
|
||||||
ls -al build/dist/releases/webmail/$VERSION/snappymail-$VERSION.tar.gz; \
|
|
||||||
mkdir -p /snappymail; \
|
|
||||||
tar -zxvf build/dist/releases/webmail/$VERSION/snappymail-$VERSION.tar.gz -C /snappymail; \
|
|
||||||
find /snappymail -type d -exec chmod 550 {} \; ; \
|
|
||||||
find /snappymail -type f -exec chmod 440 {} \; ; \
|
|
||||||
find /snappymail/data -type d -exec chmod 750 {} \; ; \
|
|
||||||
# Remove unneeded files
|
|
||||||
rm -v /snappymail/README.md /snappymail/_include.php
|
|
||||||
|
|
||||||
# Inspired by the original Rainloop dockerfile from youtous on GitLab
|
|
||||||
FROM php:8.5-fpm-alpine AS final
|
|
||||||
|
|
||||||
LABEL org.label-schema.description="SnappyMail webmail client image using nginx, php-fpm on Alpine"
|
|
||||||
|
|
||||||
# Install dependencies such as nginx
|
|
||||||
RUN apk add --no-cache ca-certificates nginx supervisor bash
|
|
||||||
|
|
||||||
# Install PHP extensions
|
|
||||||
# apcu
|
|
||||||
RUN set -eux; \
|
|
||||||
apk add --no-cache --virtual .build-dependencies $PHPIZE_DEPS; \
|
|
||||||
pecl install apcu; \
|
|
||||||
docker-php-ext-enable apcu; \
|
|
||||||
docker-php-source delete; \
|
|
||||||
apk del .build-dependencies;
|
|
||||||
|
|
||||||
# gd
|
|
||||||
RUN set -eux; \
|
|
||||||
apk add --no-cache freetype libjpeg-turbo libpng; \
|
|
||||||
apk add --no-cache --virtual .deps freetype-dev libjpeg-turbo-dev libpng-dev; \
|
|
||||||
docker-php-ext-configure gd --with-freetype --with-jpeg; \
|
|
||||||
docker-php-ext-install gd; \
|
|
||||||
apk del .deps
|
|
||||||
|
|
||||||
# gmagick
|
|
||||||
# RUN set -eux; \
|
|
||||||
# apk add --no-cache graphicsmagick libgomp; \
|
|
||||||
# apk add --no-cache --virtual .deps graphicsmagick-dev libtool; \
|
|
||||||
# apk add --no-cache --virtual .build-dependencies $PHPIZE_DEPS; \
|
|
||||||
# pecl install gmagick-2.0.6RC1; \
|
|
||||||
# docker-php-ext-enable gmagick; \
|
|
||||||
# docker-php-source delete; \
|
|
||||||
# apk del .build-dependencies; \
|
|
||||||
# apk del .deps
|
|
||||||
|
|
||||||
# gnupg
|
|
||||||
RUN set -eux; \
|
|
||||||
apk add --no-cache gnupg gpgme; \
|
|
||||||
apk add --no-cache --virtual .deps gpgme-dev; \
|
|
||||||
apk add --no-cache --virtual .build-dependencies $PHPIZE_DEPS; \
|
|
||||||
pecl install gnupg; \
|
|
||||||
docker-php-ext-enable gnupg; \
|
|
||||||
docker-php-source delete; \
|
|
||||||
apk del .build-dependencies; \
|
|
||||||
apk del .deps
|
|
||||||
|
|
||||||
# imagick
|
|
||||||
RUN set -eux; \
|
|
||||||
apk add --no-cache imagemagick libgomp; \
|
|
||||||
apk add --no-cache --virtual .deps imagemagick-dev; \
|
|
||||||
apk add --no-cache --virtual .build-dependencies $PHPIZE_DEPS; \
|
|
||||||
echo | pecl install imagick; \
|
|
||||||
docker-php-ext-enable imagick; \
|
|
||||||
docker-php-source delete; \
|
|
||||||
apk del .build-dependencies; \
|
|
||||||
apk del .deps
|
|
||||||
|
|
||||||
# intl
|
|
||||||
RUN set -eux; \
|
|
||||||
apk add --no-cache icu-libs; \
|
|
||||||
apk add --no-cache --virtual .deps icu-dev; \
|
|
||||||
docker-php-ext-configure intl; \
|
|
||||||
docker-php-ext-install intl; \
|
|
||||||
apk del .deps
|
|
||||||
|
|
||||||
# ldap
|
|
||||||
RUN set -eux; \
|
|
||||||
apk add --no-cache libldap; \
|
|
||||||
apk add --no-cache --virtual .deps openldap-dev; \
|
|
||||||
docker-php-ext-configure ldap; \
|
|
||||||
docker-php-ext-install ldap; \
|
|
||||||
apk del .deps
|
|
||||||
|
|
||||||
# mysql
|
|
||||||
RUN docker-php-ext-install pdo_mysql
|
|
||||||
|
|
||||||
# postgres
|
|
||||||
RUN set -eux; \
|
|
||||||
apk add --no-cache postgresql-libs; \
|
|
||||||
apk add --no-cache --virtual .deps postgresql-dev; \
|
|
||||||
docker-php-ext-install pdo_pgsql; \
|
|
||||||
apk del .deps
|
|
||||||
|
|
||||||
# redis
|
|
||||||
RUN set -eux; \
|
|
||||||
apk add --no-cache liblzf zstd-libs; \
|
|
||||||
apk add --no-cache --virtual .deps zstd-dev; \
|
|
||||||
apk add --no-cache --virtual .build-dependencies $PHPIZE_DEPS; \
|
|
||||||
pecl install igbinary; \
|
|
||||||
docker-php-ext-enable igbinary; \
|
|
||||||
pecl install --configureoptions 'enable-redis-igbinary="yes" enable-redis-lzf="yes" enable-redis-zstd="yes"' redis; \
|
|
||||||
docker-php-ext-enable redis; \
|
|
||||||
docker-php-source delete; \
|
|
||||||
apk del .build-dependencies; \
|
|
||||||
apk del .deps
|
|
||||||
|
|
||||||
# sqlite
|
|
||||||
RUN set -eux; \
|
|
||||||
apk add --no-cache sqlite-libs; \
|
|
||||||
apk add --no-cache --virtual .deps sqlite-dev; \
|
|
||||||
docker-php-ext-install pdo_sqlite; \
|
|
||||||
apk del .deps
|
|
||||||
|
|
||||||
# tidy
|
|
||||||
RUN set -eux; \
|
|
||||||
apk add --no-cache tidyhtml; \
|
|
||||||
apk add --no-cache --virtual .deps tidyhtml-dev; \
|
|
||||||
docker-php-ext-install tidy; \
|
|
||||||
apk del .deps
|
|
||||||
|
|
||||||
# uuid
|
|
||||||
RUN set -eux; \
|
|
||||||
apk add --no-cache libuuid; \
|
|
||||||
apk add --no-cache --virtual .deps util-linux-dev; \
|
|
||||||
apk add --no-cache --virtual .build-dependencies $PHPIZE_DEPS; \
|
|
||||||
pecl install uuid; \
|
|
||||||
docker-php-ext-enable uuid; \
|
|
||||||
docker-php-source delete; \
|
|
||||||
apk del .build-dependencies; \
|
|
||||||
apk del .deps
|
|
||||||
|
|
||||||
# xxtea - Manually install php8 compatible version from https://github.com/xxtea/xxtea-pecl master branch
|
|
||||||
RUN set -eux; \
|
|
||||||
apk add --no-cache --virtual .build-dependencies $PHPIZE_DEPS; \
|
|
||||||
wget -q https://github.com/xxtea/xxtea-pecl/tarball/3f5888a29045e12301254151737c5dab4523a1c1 -O xxtea.tar; \
|
|
||||||
echo '9cbfd9c27255767deb26ddedf69e738d401d88ac9762d82c8510f9768842ca18 xxtea.tar' | sha256sum -c -; \
|
|
||||||
tar -C /usr/src -xvf xxtea.tar; \
|
|
||||||
cd /usr/src/xxtea-xxtea-pecl-3f5888a; \
|
|
||||||
phpize; \
|
|
||||||
./configure --with-php-config=/usr/local/bin/php-config --enable-xxtea=yes; \
|
|
||||||
make install; \
|
|
||||||
docker-php-ext-enable xxtea; \
|
|
||||||
cd -; \
|
|
||||||
rm -fv xxtea.tar; \
|
|
||||||
rm -rfv /usr/src/xxtea*; \
|
|
||||||
apk del .build-dependencies;
|
|
||||||
|
|
||||||
# zip
|
|
||||||
RUN set -eux; \
|
|
||||||
apk add --no-cache libzip; \
|
|
||||||
apk add --no-cache --virtual .deps libzip-dev; \
|
|
||||||
docker-php-ext-install zip; \
|
|
||||||
apk del .deps
|
|
||||||
|
|
||||||
# Install snappymail
|
|
||||||
# The 'www-data' user/group in alpine is 82:82. The 'nginx' user/group in alpine is 101:101, and is part of www-data group
|
|
||||||
COPY --chown=www-data:www-data --from=builder /snappymail /snappymail
|
|
||||||
# Use a custom snappymail data folder
|
|
||||||
RUN mv -v /snappymail/data /var/lib/snappymail;
|
|
||||||
# Setup configs
|
|
||||||
COPY --chown=root:root .docker/release/files /
|
|
||||||
RUN set -eux; \
|
|
||||||
chown www-data:www-data /snappymail/include.php; \
|
|
||||||
chmod 440 /snappymail/include.php; \
|
|
||||||
chmod +x /entrypoint.sh; \
|
|
||||||
# Disable the built-in php-fpm configs, since we're using our own config
|
|
||||||
mv -v /usr/local/etc/php-fpm.d/docker.conf /usr/local/etc/php-fpm.d/docker.conf.disabled; \
|
|
||||||
mv -v /usr/local/etc/php-fpm.d/www.conf /usr/local/etc/php-fpm.d/www.conf.disabled; \
|
|
||||||
mv -v /usr/local/etc/php-fpm.d/zz-docker.conf /usr/local/etc/php-fpm.d/zz-docker.conf.disabled;
|
|
||||||
|
|
||||||
USER root
|
|
||||||
WORKDIR /snappymail
|
|
||||||
VOLUME /var/lib/snappymail
|
|
||||||
EXPOSE 8888
|
|
||||||
EXPOSE 9000
|
|
||||||
ENTRYPOINT []
|
|
||||||
CMD ["/entrypoint.sh"]
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
DEBUG=${DEBUG:-}
|
|
||||||
if [ "$DEBUG" = 'true' ]; then
|
|
||||||
set -x
|
|
||||||
fi
|
|
||||||
UPLOAD_MAX_SIZE=${UPLOAD_MAX_SIZE:-25M}
|
|
||||||
MEMORY_LIMIT=${MEMORY_LIMIT:-128M}
|
|
||||||
SECURE_COOKIES=${SECURE_COOKIES:-true}
|
|
||||||
|
|
||||||
# Disable listening on ipv6 for ipv4-only environments, or nginx will fail to start
|
|
||||||
if ! ip a | grep 'inet6 ' > /dev/null; then
|
|
||||||
echo "[INFO] ipv6 is disabled. Configuring nginx to not listen on ipv6"
|
|
||||||
sed -i '/listen \[::\]:/d' /etc/nginx/nginx.conf
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Set attachment size limit
|
|
||||||
sed -i "s/<UPLOAD_MAX_SIZE>/$UPLOAD_MAX_SIZE/g" /usr/local/etc/php-fpm.d/php-fpm.conf /etc/nginx/nginx.conf
|
|
||||||
sed -i "s/<MEMORY_LIMIT>/$MEMORY_LIMIT/g" /usr/local/etc/php-fpm.d/php-fpm.conf
|
|
||||||
|
|
||||||
# Secure cookies
|
|
||||||
if [ "${SECURE_COOKIES}" = 'true' ]; then
|
|
||||||
echo "[INFO] Secure cookies activated"
|
|
||||||
{
|
|
||||||
echo 'session.cookie_httponly = On';
|
|
||||||
echo 'session.cookie_secure = On';
|
|
||||||
echo 'session.use_only_cookies = On';
|
|
||||||
} > /usr/local/etc/php/conf.d/cookies.ini;
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[INFO] Snappymail version: $( ls /snappymail/snappymail/v )"
|
|
||||||
|
|
||||||
# Set permissions on snappymail data
|
|
||||||
echo "[INFO] Setting permissions on /var/lib/snappymail"
|
|
||||||
chown -R www-data:www-data /var/lib/snappymail/
|
|
||||||
chmod 550 /var/lib/snappymail/
|
|
||||||
find /var/lib/snappymail/ -type d -exec chmod 750 {} \;
|
|
||||||
|
|
||||||
# Create snappymail default config if absent
|
|
||||||
SNAPPYMAIL_CONFIG_FILE=/var/lib/snappymail/_data_/_default_/configs/application.ini
|
|
||||||
if [ ! -f "$SNAPPYMAIL_CONFIG_FILE" ]; then
|
|
||||||
echo "[INFO] Creating default Snappymail configuration: $SNAPPYMAIL_CONFIG_FILE"
|
|
||||||
# Run snappymail and exit. This populates the snappymail data directory and generates the config file
|
|
||||||
# On error, print php exception and exit
|
|
||||||
EXITCODE=
|
|
||||||
su - www-data -s /bin/sh -c 'php /snappymail/index.php' > /tmp/out || EXITCODE=$?
|
|
||||||
if [ -n "$EXITCODE" ]; then
|
|
||||||
cat /tmp/out
|
|
||||||
exit "$EXITCODE"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[INFO] Overriding values in snappymail configuration: $SNAPPYMAIL_CONFIG_FILE"
|
|
||||||
# Enable output of snappymail logs
|
|
||||||
sed '/^\; Enable logging/{
|
|
||||||
N
|
|
||||||
s/enable = Off/enable = On/
|
|
||||||
}' -i $SNAPPYMAIL_CONFIG_FILE
|
|
||||||
# Redirect snappymail logs to stderr /stdout
|
|
||||||
sed 's/^filename = .*/filename = "stderr"/' -i $SNAPPYMAIL_CONFIG_FILE
|
|
||||||
sed 's/^write_on_error_only = .*/write_on_error_only = Off/' -i $SNAPPYMAIL_CONFIG_FILE
|
|
||||||
sed 's/^write_on_php_error_only = .*/write_on_php_error_only = On/' -i $SNAPPYMAIL_CONFIG_FILE
|
|
||||||
# Always enable snappymail Auth logging
|
|
||||||
sed 's/^auth_logging = .*/auth_logging = On/' -i $SNAPPYMAIL_CONFIG_FILE
|
|
||||||
sed 's/^auth_logging_filename = .*/auth_logging_filename = "auth.log"/' -i $SNAPPYMAIL_CONFIG_FILE
|
|
||||||
sed 's/^auth_logging_format = .*/auth_logging_format = "[{date:Y-m-d H:i:s}] Auth failed: ip={request:ip} user={imap:login} host={imap:host} port={imap:port}"/' -i $SNAPPYMAIL_CONFIG_FILE
|
|
||||||
sed 's/^auth_syslog = .*/auth_syslog = Off/' -i $SNAPPYMAIL_CONFIG_FILE
|
|
||||||
|
|
||||||
(
|
|
||||||
while ! nc -vz -w 1 127.0.0.1 8888 > /dev/null 2>&1; do echo "[INFO] Checking whether nginx is alive"; sleep 1; done
|
|
||||||
while ! nc -vz -w 1 127.0.0.1 9000 > /dev/null 2>&1; do echo "[INFO] Checking whether php-fpm is alive"; sleep 1; done
|
|
||||||
# Create snappymail admin password if absent
|
|
||||||
SNAPPYMAIL_ADMIN_PASSWORD_FILE=/var/lib/snappymail/_data_/_default_/admin_password.txt
|
|
||||||
if [ ! -f "$SNAPPYMAIL_ADMIN_PASSWORD_FILE" ]; then
|
|
||||||
echo "[INFO] Creating Snappymail admin password file: $SNAPPYMAIL_ADMIN_PASSWORD_FILE"
|
|
||||||
wget -T 1 -qO- 'http://127.0.0.1:8888/?/AdminAppData/0/12345/' > /dev/null
|
|
||||||
echo "[INFO] Snappymail Admin Panel ready at http://localhost:8888/?admin. Login using password in $SNAPPYMAIL_ADMIN_PASSWORD_FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
wget -T 1 -qO- 'http://127.0.0.1:8888/' > /dev/null
|
|
||||||
echo "[INFO] Snappymail ready at http://localhost:8888/"
|
|
||||||
) &
|
|
||||||
|
|
||||||
# RUN !
|
|
||||||
exec /usr/bin/supervisord -c /supervisor.conf --pidfile /run/supervisord.pid
|
|
||||||
|
|
@ -1,108 +0,0 @@
|
||||||
worker_processes auto;
|
|
||||||
pid /tmp/nginx.pid;
|
|
||||||
|
|
||||||
events {
|
|
||||||
worker_connections 1024;
|
|
||||||
use epoll;
|
|
||||||
}
|
|
||||||
|
|
||||||
http {
|
|
||||||
include /etc/nginx/mime.types;
|
|
||||||
default_type application/octet-stream;
|
|
||||||
|
|
||||||
access_log off;
|
|
||||||
error_log /dev/stderr error;
|
|
||||||
|
|
||||||
sendfile on;
|
|
||||||
keepalive_timeout 15;
|
|
||||||
keepalive_disable msie6;
|
|
||||||
keepalive_requests 100;
|
|
||||||
tcp_nopush on;
|
|
||||||
tcp_nodelay on;
|
|
||||||
server_tokens off;
|
|
||||||
|
|
||||||
fastcgi_temp_path /tmp/fastcgi 1 2;
|
|
||||||
client_body_temp_path /tmp/client_body 1 2;
|
|
||||||
proxy_temp_path /tmp/proxy 1 2;
|
|
||||||
uwsgi_temp_path /tmp/uwsgi 1 2;
|
|
||||||
scgi_temp_path /tmp/scgi 1 2;
|
|
||||||
|
|
||||||
gzip on;
|
|
||||||
gzip_comp_level 5;
|
|
||||||
gzip_min_length 512;
|
|
||||||
gzip_buffers 4 8k;
|
|
||||||
gzip_proxied any;
|
|
||||||
gzip_vary on;
|
|
||||||
gzip_disable "msie6";
|
|
||||||
gzip_types
|
|
||||||
text/css
|
|
||||||
text/javascript
|
|
||||||
text/xml
|
|
||||||
text/plain
|
|
||||||
text/x-component
|
|
||||||
application/javascript
|
|
||||||
application/x-javascript
|
|
||||||
application/json
|
|
||||||
application/xml
|
|
||||||
application/rss+xml
|
|
||||||
application/vnd.ms-fontobject
|
|
||||||
font/truetype
|
|
||||||
font/opentype
|
|
||||||
image/svg+xml;
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 8888;
|
|
||||||
listen [::]:8888;
|
|
||||||
#listen [::]:8888 ipv6only=off;
|
|
||||||
root /snappymail;
|
|
||||||
index index.php;
|
|
||||||
charset utf-8;
|
|
||||||
|
|
||||||
client_max_body_size <UPLOAD_MAX_SIZE>;
|
|
||||||
|
|
||||||
location ^~ /data {
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ index.php;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assets cache control
|
|
||||||
# --------------------------------------
|
|
||||||
location ~* \.(?:html|xml|json)$ {
|
|
||||||
expires -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~* \.(?:css|js)$ {
|
|
||||||
expires 7d;
|
|
||||||
add_header Pragma public;
|
|
||||||
add_header Cache-Control "public";
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~* \.(?:gif|jpe?g|png|ico|otf|eot|svg|ttf|woff|woff2)$ {
|
|
||||||
expires 30d;
|
|
||||||
log_not_found off;
|
|
||||||
add_header Pragma public;
|
|
||||||
add_header Cache-Control "public";
|
|
||||||
}
|
|
||||||
|
|
||||||
# PHP Backend
|
|
||||||
# --------------------------------------
|
|
||||||
location ~* \.php$ {
|
|
||||||
try_files $uri =404;
|
|
||||||
include fastcgi_params;
|
|
||||||
fastcgi_split_path_info ^(.+\.php)(/.*)$;
|
|
||||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
|
||||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
|
||||||
fastcgi_param HTTP_PROXY "";
|
|
||||||
fastcgi_index index.php;
|
|
||||||
fastcgi_pass 127.0.0.1:9000;
|
|
||||||
fastcgi_intercept_errors on;
|
|
||||||
fastcgi_request_buffering off;
|
|
||||||
fastcgi_param REMOTE_ADDR $http_x_real_ip;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
<?php
|
|
||||||
$stdIn = STDIN;
|
|
||||||
$stdOut = STDOUT;
|
|
||||||
|
|
||||||
fwrite($stdOut, "READY\n");
|
|
||||||
|
|
||||||
while(true) {
|
|
||||||
if (false == $line = trim(fgets($stdIn))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$match = null;
|
|
||||||
if (preg_match('/eventname:(.*?) /', $line, $match)) {
|
|
||||||
if (in_array($match[1], ['PROCESS_STATE_EXITED', 'PROCESS_STATE_STOPPED', 'PROCESS_STATE_FATAL'])) {
|
|
||||||
exec('kill -15 '.file_get_contents('/run/supervisord.pid'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fwrite($stdOut, "RESULT 2\nOK");
|
|
||||||
|
|
||||||
sleep(1);
|
|
||||||
fwrite($stdOut, "READY\n");
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<?php
|
|
||||||
define('APP_DATA_FOLDER_PATH', '/var/lib/snappymail/');
|
|
||||||
?>
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
[supervisord]
|
|
||||||
nodaemon=true
|
|
||||||
user=root
|
|
||||||
logfile=/dev/null
|
|
||||||
logfile_maxbytes=0
|
|
||||||
|
|
||||||
[program:nginx]
|
|
||||||
command=nginx -c /etc/nginx/nginx.conf -g 'daemon off;'
|
|
||||||
process_name=%(program_name)s_%(process_num)02d
|
|
||||||
user=root
|
|
||||||
numprocs=1
|
|
||||||
autostart=true
|
|
||||||
autorestart=false
|
|
||||||
startsecs=0
|
|
||||||
stdout_logfile=/dev/stdout
|
|
||||||
stdout_logfile_maxbytes=0
|
|
||||||
stderr_logfile=/dev/stderr
|
|
||||||
stderr_logfile_maxbytes=0
|
|
||||||
|
|
||||||
[program:php-fpm]
|
|
||||||
command=php-fpm -F
|
|
||||||
process_name=%(program_name)s_%(process_num)02d
|
|
||||||
user=root
|
|
||||||
numprocs=1
|
|
||||||
autostart=true
|
|
||||||
autorestart=false
|
|
||||||
startsecs=0
|
|
||||||
stdout_logfile=/dev/stdout
|
|
||||||
stdout_logfile_maxbytes=0
|
|
||||||
stderr_logfile=/dev/stderr
|
|
||||||
stderr_logfile_maxbytes=0
|
|
||||||
|
|
||||||
[eventlistener:subprocess-stopped]
|
|
||||||
command=php /listener.php
|
|
||||||
process_name=%(program_name)s_%(process_num)02d
|
|
||||||
user=root
|
|
||||||
numprocs=1
|
|
||||||
events=PROCESS_STATE_EXITED,PROCESS_STATE_STOPPED,PROCESS_STATE_FATAL
|
|
||||||
autostart=true
|
|
||||||
autorestart=unexpected
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
[global]
|
|
||||||
daemonize = no
|
|
||||||
error_log = /dev/stderr
|
|
||||||
log_buffering = no
|
|
||||||
|
|
||||||
[default]
|
|
||||||
listen = 9000
|
|
||||||
user = www-data
|
|
||||||
listen.owner = www-data
|
|
||||||
listen.group = www-data
|
|
||||||
pm = ondemand
|
|
||||||
pm.max_children = 30
|
|
||||||
pm.process_idle_timeout = 10s
|
|
||||||
pm.max_requests = 500
|
|
||||||
catch_workers_output = yes
|
|
||||||
decorate_workers_output = no
|
|
||||||
chdir = /
|
|
||||||
pm.status_path = /status
|
|
||||||
php_admin_value[log_errors] = On
|
|
||||||
php_admin_value[expose_php] = Off
|
|
||||||
php_admin_value[display_errors] = Off
|
|
||||||
php_admin_value[date.timezone] = UTC
|
|
||||||
php_admin_value[post_max_size] = <UPLOAD_MAX_SIZE>
|
|
||||||
php_admin_value[upload_max_filesize] = <UPLOAD_MAX_SIZE>
|
|
||||||
php_admin_value[memory_limit] = <MEMORY_LIMIT>
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# This script uses docker buildx to build a docker image, and loads it into the docker daemon. Then it executes ./test.sh to test the docker image
|
|
||||||
# It is useful for testing release builds in development
|
|
||||||
set -eu
|
|
||||||
IMAGE=snappymail/snappymail:test
|
|
||||||
DOCKER_BUILDX=1 docker build -t "$IMAGE" -f .docker/release/Dockerfile .
|
|
||||||
.docker/release/test/test.sh "$IMAGE"
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
# See: https://github.com/GoogleContainerTools/container-structure-test
|
|
||||||
# See: https://github.com/GoogleContainerTools/container-structure-test/issues/78
|
|
||||||
schemaVersion: 2.0.0
|
|
||||||
commandTests:
|
|
||||||
- name: Integration test
|
|
||||||
command: /bin/sh
|
|
||||||
args:
|
|
||||||
- -c
|
|
||||||
- |
|
|
||||||
set -eux
|
|
||||||
DEBUG=true /entrypoint.sh > /tmp/test 2>&1 &
|
|
||||||
sleep 5
|
|
||||||
cat /tmp/test
|
|
||||||
pidof supervisord
|
|
||||||
pidof nginx
|
|
||||||
pidof php-fpm
|
|
||||||
ls -al /var/lib/snappymail/_data_/_default_/configs/application.ini
|
|
||||||
ls -al /var/lib/snappymail/_data_/_default_/admin_password.txt
|
|
||||||
nc -vz 127.0.0.1 8888
|
|
||||||
nc -vz 127.0.0.1 9000
|
|
||||||
wget -S -T 3 -O /dev/null http://127.0.0.1:8888
|
|
||||||
kill `pidof supervisord`
|
|
||||||
metadataTest:
|
|
||||||
exposedPorts: ["8888", "9000"]
|
|
||||||
volumes: ["/var/lib/snappymail"]
|
|
||||||
entrypoint: []
|
|
||||||
cmd: ["/entrypoint.sh"]
|
|
||||||
workdir: /snappymail
|
|
||||||
user: root
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# This script tests a given docker image using https://github.com/GoogleContainerTools/container-structure-test
|
|
||||||
set -eu
|
|
||||||
SCRIPT_DIR=$( cd "$(dirname "$0")" && pwd )
|
|
||||||
IMAGE=${1:-}
|
|
||||||
echo "Testing image: $IMAGE"
|
|
||||||
docker run --rm -i \
|
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
|
||||||
-v "$SCRIPT_DIR/config.yaml:/config.yaml:ro" \
|
|
||||||
gcr.io/gcp-runtimes/container-structure-test:latest test --image "$IMAGE" --config config.yaml
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
/.git
|
|
||||||
/node_modules
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
[*.php]
|
|
||||||
indent_style = tab
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
*.*
|
|
||||||
!/dev/**/*.js
|
|
||||||
!/tasks/**/*.js
|
|
||||||
!/*.js
|
|
||||||
287
.eslintrc.js
287
.eslintrc.js
|
|
@ -1,64 +1,237 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
parser: 'babel-eslint',
|
"extends": "eslint:recommended",
|
||||||
// extends: ['eslint:recommended', 'plugin:prettier/recommended'],
|
"ecmaFeatures": {
|
||||||
extends: ['eslint:recommended'],
|
"modules": true
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: 11,
|
|
||||||
sourceType: 'module'
|
|
||||||
},
|
},
|
||||||
env: {
|
"parserOptions": {
|
||||||
node: true,
|
"ecmaVersion": 6,
|
||||||
browser: true,
|
"sourceType": "module"
|
||||||
es2020: true
|
},
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"commonjs": true,
|
||||||
|
"es6": true,
|
||||||
|
"browser": true
|
||||||
},
|
},
|
||||||
globals: {
|
"globals": {
|
||||||
// SnappyMail
|
"RL_COMMUNITY": true,
|
||||||
'rainloopI18N': "readonly",
|
"ko": true,
|
||||||
'rainloopTEMPLATES': "readonly",
|
"ssm": true,
|
||||||
'rl': "readonly",
|
"moment": true,
|
||||||
'shortcuts': "readonly",
|
"ifvisible": true,
|
||||||
// '__APP_BOOT': "readonly",
|
"crossroads": true,
|
||||||
// deb/boot.js
|
"hasher": true,
|
||||||
'progressJs': "readonly",
|
"key": true,
|
||||||
// others
|
"Jua": true,
|
||||||
'openpgp': "readonly",
|
"_": true,
|
||||||
'CKEDITOR': "readonly",
|
"$": true,
|
||||||
'Squire': "readonly",
|
"Dropbox": true
|
||||||
'SquireUI': "readonly",
|
|
||||||
// node_modules/knockout but dev/External/ko.js is used
|
|
||||||
'ko': "readonly",
|
|
||||||
// vendors/routes/
|
|
||||||
'hasher': "readonly",
|
|
||||||
'Crossroads': "readonly",
|
|
||||||
// vendors/jua
|
|
||||||
'Jua': "readonly",
|
|
||||||
// vendors/bootstrap/bootstrap.native.js
|
|
||||||
'BSN': "readonly",
|
|
||||||
// Mailvelope
|
|
||||||
'mailvelope': "readonly",
|
|
||||||
// Punycode
|
|
||||||
'IDN': "readonly",
|
|
||||||
// Turndown
|
|
||||||
'TurndownService': "readonly",
|
|
||||||
// Marked
|
|
||||||
'marked': "readonly"
|
|
||||||
},
|
},
|
||||||
// http://eslint.org/docs/rules/
|
"rules": {
|
||||||
rules: {
|
|
||||||
'no-cond-assign': 0,
|
"strict": 2, // require or disallow strict mode directives
|
||||||
// plugins
|
|
||||||
'no-mixed-spaces-and-tabs': 'off',
|
// errors
|
||||||
'max-len': [
|
|
||||||
'error',
|
"comma-dangle": [2, "never"], // disallow or enforce trailing commas
|
||||||
120,
|
"no-cond-assign": [2, "always"], // disallow assignment in conditional expressions
|
||||||
2,
|
"no-console": 1, // disallow use of console (off by default in the node environment)
|
||||||
{
|
"no-constant-condition": 2, // disallow use of constant expressions in conditions
|
||||||
ignoreComments: true,
|
"no-control-regex": 2, // disallow control characters in regular expressions
|
||||||
ignoreUrls: true,
|
"no-debugger": 2, // disallow use of debugger
|
||||||
ignoreTrailingComments: true,
|
"no-dupe-args": 2, // disallow duplicate arguments in functions
|
||||||
ignorePattern: '(^\\s*(const|let|var)\\s.+=\\s*require\\s*\\(|^import\\s.+\\sfrom\\s.+;$)'
|
"no-dupe-keys": 2, // disallow duplicate keys when creating object literals
|
||||||
|
"no-duplicate-case": 2, // disallow a duplicate case label.
|
||||||
|
"no-empty": 2, // disallow empty statements
|
||||||
|
"no-empty-character-class": 2, // disallow the use of empty character classes in regular expressions
|
||||||
|
"no-ex-assign": 2, // disallow assigning to the exception in a catch block
|
||||||
|
"no-extra-boolean-cast": 2, // disallow double-negation boolean casts in a boolean context
|
||||||
|
"no-extra-parens": 0, // disallow unnecessary parentheses (off by default)
|
||||||
|
"no-extra-semi": 2, // disallow unnecessary semicolons
|
||||||
|
"no-func-assign": 2, // disallow overwriting functions written as function declarations
|
||||||
|
"no-inner-declarations": 2, // disallow function or variable declarations in nested blocks
|
||||||
|
"no-invalid-regexp": 2, // disallow invalid regular expression strings in the RegExp constructor
|
||||||
|
"no-irregular-whitespace": 2, // disallow irregular whitespace outside of strings and comments
|
||||||
|
"no-negated-in-lhs": 2, // disallow negation of the left operand of an in expression
|
||||||
|
"no-obj-calls": 2, // disallow the use of object properties of the global object (Math and JSON) as functions
|
||||||
|
"no-regex-spaces": 2, // disallow multiple spaces in a regular expression literal
|
||||||
|
"no-sparse-arrays": 2, // disallow sparse arrays
|
||||||
|
"no-unexpected-multiline": 2, // disallow confusing multiline expressions
|
||||||
|
"no-unreachable": 2, // disallow unreachable statements after a return, throw, continue, or break statement
|
||||||
|
"use-isnan": 2, // disallow comparisons with the value NaN
|
||||||
|
"valid-typeof": 2, // Ensure that the results of typeof are compared against a valid string
|
||||||
|
|
||||||
|
// "valid-jsdoc": [2, { // Ensure JSDoc comments are valid (off by default)
|
||||||
|
// "requireParamDescription": false,
|
||||||
|
// "requireReturnDescription": false
|
||||||
|
// }],
|
||||||
|
|
||||||
|
// best practices
|
||||||
|
|
||||||
|
"accessor-pairs": 2, // enforce getter and setter pairs in objects
|
||||||
|
"block-scoped-var": 2, // treat var statements as if they were block scoped (off by default). 0: deep destructuring is not compatible https://github.com/eslint/eslint/issues/1863
|
||||||
|
// "complexity": 0, // specify the maximum cyclomatic complexity allowed in a program (off by default)
|
||||||
|
"consistent-return": 2, // require return statements to either always or never specify values
|
||||||
|
"curly": 2, // specify curly brace conventions for all control statements
|
||||||
|
"default-case": 2, // require default case in switch statements (off by default)
|
||||||
|
"dot-location": [2, "property"], // enforce consistent newlines before and after dots
|
||||||
|
"dot-notation": 2, // encourages use of dot notation whenever possible
|
||||||
|
"eqeqeq": 2, // require the use of === and !==
|
||||||
|
"guard-for-in": 2, // make sure for-in loops have an if statement (off by default)
|
||||||
|
"no-alert": 2, // disallow the use of alert, confirm, and prompt
|
||||||
|
"no-caller": 2, // disallow use of arguments.caller or arguments.callee
|
||||||
|
"no-div-regex": 2, // disallow division operators explicitly at beginning of regular expression (off by default)
|
||||||
|
"no-else-return": 2, // disallow else after a return in an if (off by default)
|
||||||
|
"no-empty-label": 2, // disallow use of labels for anything other then loops and switches
|
||||||
|
"no-eq-null": 2, // disallow comparisons to null without a type-checking operator (off by default)
|
||||||
|
"no-eval": 2, // disallow use of eval()
|
||||||
|
"no-extend-native": 2, // disallow adding to native types
|
||||||
|
"no-extra-bind": 2, // disallow unnecessary function binding
|
||||||
|
"no-fallthrough": 2, // disallow fallthrough of case statements
|
||||||
|
"no-floating-decimal": 2, // disallow the use of leading or trailing decimal points in numeric literals (off by default)
|
||||||
|
"no-implied-eval": 2, // disallow use of eval()-like methods
|
||||||
|
"no-iterator": 2, // disallow usage of __iterator__ property
|
||||||
|
"no-labels": 2, // disallow use of labeled statements
|
||||||
|
"no-lone-blocks": 2, // disallow unnecessary nested blocks
|
||||||
|
"no-loop-func": 2, // disallow creation of functions within loops
|
||||||
|
"no-multi-spaces": 2, // disallow use of multiple spaces
|
||||||
|
"no-multi-str": 2, // disallow use of multiline strings
|
||||||
|
"no-native-reassign": 2, // disallow reassignments of native objects
|
||||||
|
"no-new": 2, // disallow use of new operator when not part of the assignment or comparison
|
||||||
|
"no-new-func": 2, // disallow use of new operator for Function object
|
||||||
|
"no-new-wrappers": 2, // disallows creating new instances of String,Number, and Boolean
|
||||||
|
"no-octal": 2, // disallow use of octal literals
|
||||||
|
"no-octal-escape": 2, // disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251";
|
||||||
|
// "no-param-reassign": 2, // disallow reassignment of function parameters (off by default)
|
||||||
|
"no-process-env": 2, // disallow use of process.env (off by default)
|
||||||
|
"no-proto": 2, // disallow usage of __proto__ property
|
||||||
|
"no-redeclare": 2, // disallow declaring the same variable more then once
|
||||||
|
"no-return-assign": 2, // disallow use of assignment in return statement
|
||||||
|
"no-script-url": 2, // disallow use of javascript: urls.
|
||||||
|
"no-self-compare": 2, // disallow comparisons where both sides are exactly the same (off by default)
|
||||||
|
"no-sequences": 2, // disallow use of comma operator
|
||||||
|
"no-throw-literal": 2, // restrict what can be thrown as an exception (off by default)
|
||||||
|
"no-unused-expressions": 2, // disallow usage of expressions in statement position
|
||||||
|
"no-void": 2, // disallow use of void operator (off by default)
|
||||||
|
"no-warning-comments": 1, // disallow usage of configurable warning terms in comments": 2, // e.g. TODO or FIXME (off by default)
|
||||||
|
"no-with": 2, // disallow use of the with statement
|
||||||
|
"radix": 2, // require use of the second argument for parseInt() (off by default)
|
||||||
|
// "vars-on-top": 2, // requires to declare all vars on top of their containing scope (off by default)
|
||||||
|
"wrap-iife": 2, // require immediate function invocation to be wrapped in parentheses (off by default)
|
||||||
|
"yoda": [2, "always"], // require or disallow Yoda conditions
|
||||||
|
|
||||||
|
// variables
|
||||||
|
|
||||||
|
"no-catch-shadow": 2, // disallow the catch clause parameter name being the same as a variable in the outer scope (off by default in the node environment)
|
||||||
|
"no-delete-var": 2, // disallow deletion of variables
|
||||||
|
"no-label-var": 2, // disallow labels that share a name with a variable
|
||||||
|
"no-shadow": 2, // disallow declaration of variables already declared in the outer scope
|
||||||
|
"no-shadow-restricted-names": 2, // disallow shadowing of names such as arguments
|
||||||
|
"no-undef": 2, // disallow use of undeclared variables unless mentioned in a /*global */ block
|
||||||
|
"no-undef-init": 2, // disallow use of undefined when initializing variables
|
||||||
|
"no-undefined": 2, // disallow use of undefined variable (off by default)
|
||||||
|
"no-unused-vars": 2, // disallow declaration of variables that are not used in the code
|
||||||
|
"no-use-before-define": 2, // disallow use of variables before they are defined
|
||||||
|
|
||||||
|
// stylistic issues
|
||||||
|
|
||||||
|
"array-bracket-spacing": 1, // enforce consistent spacing inside array brackets
|
||||||
|
"block-spacing": [1, "never"], // enforce consistent spacing inside single-line blocks
|
||||||
|
|
||||||
|
// "brace-style": [1, "1tbs"], // enforce consistent brace style for blocks
|
||||||
|
|
||||||
|
// "camelcase": 1, // enforce camelcase naming convention
|
||||||
|
"comma-spacing": 1, // enforce consistent spacing before and after commas
|
||||||
|
"comma-style": 1, // enforce consistent comma style
|
||||||
|
"computed-property-spacing": 1, // enforce consistent spacing inside computed property brackets
|
||||||
|
"consistent-this": [1, "self"], // enforce consistent naming when capturing the current execution context
|
||||||
|
"eol-last": 1, // enforce at least one newline at the end of files
|
||||||
|
"id-match": 1, // require identifiers to match a specified regular expression
|
||||||
|
|
||||||
|
"indent": [1, "tab", { // enforce consistent indentation
|
||||||
|
"SwitchCase": 1,
|
||||||
|
"VariableDeclarator": {
|
||||||
|
"var": 1,
|
||||||
|
"let": 1,
|
||||||
|
"const": 1
|
||||||
}
|
}
|
||||||
],
|
}],
|
||||||
'no-constant-condition': ["error", { "checkLoops": false }]
|
|
||||||
|
"key-spacing": 1, // enforce consistent spacing between keys and values in object literal properties
|
||||||
|
"linebreak-style": [1, "windows"], // enforce consistent linebreak style
|
||||||
|
"lines-around-comment": 0, // require empty lines around comments
|
||||||
|
"max-depth": 0, // enforce a maximum depth that blocks can be nested
|
||||||
|
"max-len": [1, 200], // enforce a maximum line length
|
||||||
|
"max-lines": 0, // enforce a maximum file length
|
||||||
|
"max-nested-callbacks": [1, 5], // enforce a maximum depth that callbacks can be nested
|
||||||
|
"max-params": 0, // enforce a maximum number of parameters in function definitions
|
||||||
|
"max-statements": 0, // enforce a maximum number of statements allowed in function blocks
|
||||||
|
"max-statements-per-line": 0, // enforce a maximum number of statements allowed per line
|
||||||
|
"new-cap": 1, // require constructor function names to begin with a capital letter
|
||||||
|
"new-parens": 1, // require parentheses when invoking a constructor with no arguments
|
||||||
|
"newline-after-var": 0, // require or disallow an empty line after var declarations
|
||||||
|
"newline-before-return": 0, // require an empty line before return statements
|
||||||
|
"newline-per-chained-call": 0, // require a newline after each call in a method chain
|
||||||
|
"no-array-constructor": 1, // disallow Array constructors
|
||||||
|
"no-bitwise": 1, // disallow bitwise operators
|
||||||
|
"no-continue": 1, // disallow continue statements
|
||||||
|
"no-inline-comments": 0, // disallow inline comments after code
|
||||||
|
"no-lonely-if": 0, // disallow if statements as the only statement in else blocks
|
||||||
|
"no-mixed-operators": 0, // disallow mixes of different operators
|
||||||
|
"no-mixed-spaces-and-tabs": 2, // disallow mixed spaces and tabs for indentation
|
||||||
|
"no-multiple-empty-lines": 2, // disallow multiple empty lines
|
||||||
|
"no-negated-condition": 0, // disallow negated conditions
|
||||||
|
"no-nested-ternary": 0, // disallow nested ternary expressions
|
||||||
|
"no-new-object": 2, // disallow Object constructors
|
||||||
|
"no-plusplus": [1, { // disallow the unary operators ++ and --
|
||||||
|
"allowForLoopAfterthoughts": true
|
||||||
|
}],
|
||||||
|
"no-restricted-syntax": 1, // disallow specified syntax
|
||||||
|
"no-spaced-func": 1, // disallow spacing between function identifiers and their applications
|
||||||
|
"no-ternary": 0, // disallow ternary operators
|
||||||
|
"no-trailing-spaces": 1, // disallow trailing whitespace at the end of lines
|
||||||
|
"no-underscore-dangle": 0, // disallow dangling underscores in identifiers
|
||||||
|
"no-unneeded-ternary": 1, // disallow ternary operators when simpler alternatives exist
|
||||||
|
"no-whitespace-before-property": 0, // disallow whitespace before properties
|
||||||
|
"object-curly-newline": 0, // enforce consistent line breaks inside braces
|
||||||
|
"object-curly-spacing": [1, "never"], // enforce consistent spacing inside braces
|
||||||
|
|
||||||
|
"object-property-newline": [0, { // enforce placing object properties on separate lines
|
||||||
|
"allowMultiplePropertiesPerLine": false
|
||||||
|
}],
|
||||||
|
|
||||||
|
// "one-var": [2, { // enforce variables to be declared either together or separately in functions
|
||||||
|
// "var": "always",
|
||||||
|
// "let": "always",
|
||||||
|
// "const": "never"
|
||||||
|
// }],
|
||||||
|
|
||||||
|
"one-var-declaration-per-line": [0, "always"], // require or disallow newlines around var declarations
|
||||||
|
"operator-assignment": 1, // require or disallow assignment operator shorthand where possible
|
||||||
|
"operator-linebreak": [1, "after"], // enforce consistent linebreak style for operators
|
||||||
|
"padded-blocks": [0, "never"], // require or disallow padding within blocks
|
||||||
|
"quote-props": [0, "as-needed"], // require quotes around object literal property names
|
||||||
|
"quotes": [2, "single"], // enforce the consistent use of either backticks, double, or single quotes
|
||||||
|
"require-jsdoc": 1, // require JSDoc comments
|
||||||
|
"semi": [1, "always"], // require or disallow semicolons instead of ASI
|
||||||
|
"semi-spacing": 1, // enforce consistent spacing before and after semicolons
|
||||||
|
"sort-vars": 0, // require variables within the same declaration block to be sorted
|
||||||
|
"space-before-blocks": 1, // enforce consistent spacing before blocks
|
||||||
|
"space-before-function-paren": [1, "never"], // enforce consistent spacing before function definition opening parenthesis
|
||||||
|
"space-in-parens": 1, // enforce consistent spacing inside parentheses
|
||||||
|
"space-infix-ops": 1, // require spacing around operators
|
||||||
|
"space-unary-ops": 1, // enforce consistent spacing before or after unary operators
|
||||||
|
"spaced-comment": 1, // enforce consistent spacing after the // or /* in a comment
|
||||||
|
// "unicode-bom": [1, "never"], // require or disallow the Unicode BOM
|
||||||
|
"wrap-regex": 1, // require parenthesis around regex literals
|
||||||
|
|
||||||
|
// es6
|
||||||
|
|
||||||
|
"constructor-super": 2, // require super() calls in constructors
|
||||||
|
"no-class-assign": 2, // disallow reassigning class members
|
||||||
|
"no-const-assign": 2, // disallow reassigning const variables
|
||||||
|
"no-dupe-class-members": 2, // disallow duplicate class members
|
||||||
|
"no-this-before-super": 2, // disallow this/super before calling super() in constructors
|
||||||
|
"prefer-const": 2 // require const declarations for variables that are never reassigned after declared
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
|
|
@ -1,2 +0,0 @@
|
||||||
github: the-djmaze
|
|
||||||
custom: ["https://www.paypal.me/thedjmaze", "https://snappymail.eu"]
|
|
||||||
8
.github/ISSUE_TEMPLATE.md
vendored
8
.github/ISSUE_TEMPLATE.md
vendored
|
|
@ -1,8 +0,0 @@
|
||||||
|
|
||||||
**SnappyMail version, browser, browser version:**
|
|
||||||
|
|
||||||
**Expected behavior and actual behavior:**
|
|
||||||
|
|
||||||
**Steps to reproduce the problem:**
|
|
||||||
|
|
||||||
**Logs or screenshots:**
|
|
||||||
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -1,38 +0,0 @@
|
||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**To Reproduce**
|
|
||||||
Steps to reproduce the behavior:
|
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '....'
|
|
||||||
3. Scroll down to '....'
|
|
||||||
4. See error
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**Please complete the following information:**
|
|
||||||
- Browser: [e.g. chromium 100, firefox 100, safari 14, mobile]
|
|
||||||
- IMAP daemon: [e.g. courier, dovecot]
|
|
||||||
- PHP version:
|
|
||||||
- SnappyMail Version:
|
|
||||||
- Mode: [e.g. standalone, nextcloud, cyberpanel, docker]
|
|
||||||
|
|
||||||
**Debug/logging information**
|
|
||||||
[Read here how to log](https://github.com/the-djmaze/snappymail/wiki/FAQ#how-do-i-enable-logging)
|
|
||||||
- [ ] I've placed them here (few lines) or as attachments (many lines)
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
||||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
|
@ -1,20 +0,0 @@
|
||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
||||||
93
.github/workflows/docker-pr.yml
vendored
93
.github/workflows/docker-pr.yml
vendored
|
|
@ -1,93 +0,0 @@
|
||||||
name: docker-pr
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
# This step generates the docker tags
|
|
||||||
- name: Docker meta
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v4
|
|
||||||
env:
|
|
||||||
# This env var ensures {{sha}} is a real commit SHA for type=ref,event=pr
|
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
|
||||||
with:
|
|
||||||
images: |
|
|
||||||
djmaze/snappymail
|
|
||||||
ghcr.io/${{ github.repository }}
|
|
||||||
# type=ref,event=pr generates tag(s) on PRs only. E.g. 'pr-123', 'pr-123-abc0123'
|
|
||||||
tags: |
|
|
||||||
type=ref,event=pr
|
|
||||||
type=ref,suffix=-{{sha}},event=pr
|
|
||||||
# The rest of the org.opencontainers.image.xxx labels are dynamically generated
|
|
||||||
labels: |
|
|
||||||
org.opencontainers.image.description=SnappyMail
|
|
||||||
org.opencontainers.image.licenses=AGPLv3
|
|
||||||
|
|
||||||
# See: https://github.com/docker/build-push-action/blob/v2.6.1/docs/advanced/cache.md#github-cache
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v2
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
id: buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
|
|
||||||
- name: Cache Docker layers
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: /tmp/.buildx-cache
|
|
||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-buildx-
|
|
||||||
|
|
||||||
# See: https://github.com/docker/buildx/issues/59
|
|
||||||
- name: Build
|
|
||||||
id: build
|
|
||||||
uses: docker/build-push-action@v3
|
|
||||||
with:
|
|
||||||
context: '.'
|
|
||||||
file: ./.docker/release/Dockerfile
|
|
||||||
platforms: linux/amd64
|
|
||||||
push: false
|
|
||||||
load: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
|
|
||||||
|
|
||||||
- name: Docker images
|
|
||||||
run: |
|
|
||||||
docker images
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: |
|
|
||||||
TAG=$( echo "${{ steps.meta.outputs.tags }}" | head -n1 )
|
|
||||||
.docker/release/test/test.sh "$TAG"
|
|
||||||
|
|
||||||
- name: Build all archs
|
|
||||||
uses: docker/build-push-action@v3
|
|
||||||
with:
|
|
||||||
context: '.'
|
|
||||||
file: ./.docker/release/Dockerfile
|
|
||||||
platforms: linux/386,linux/amd64,linux/arm64
|
|
||||||
push: false
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
|
|
||||||
|
|
||||||
# Temp fix
|
|
||||||
# https://github.com/docker/build-push-action/issues/252
|
|
||||||
# https://github.com/moby/buildkit/issues/1896
|
|
||||||
- name: Move cache
|
|
||||||
run: |
|
|
||||||
rm -rf /tmp/.buildx-cache
|
|
||||||
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
|
|
||||||
117
.github/workflows/docker.yml
vendored
117
.github/workflows/docker.yml
vendored
|
|
@ -1,117 +0,0 @@
|
||||||
name: docker
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- 'v2.*'
|
|
||||||
|
|
||||||
# This is needed to push to GitHub Container Registry. See https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
# This step generates the docker tags
|
|
||||||
- name: Docker meta
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v4
|
|
||||||
env:
|
|
||||||
# This env var ensures {{sha}} is a real commit SHA for type=ref,event=pr
|
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
|
||||||
with:
|
|
||||||
images: |
|
|
||||||
djmaze/snappymail
|
|
||||||
ghcr.io/${{ github.repository }}
|
|
||||||
# type=ref,event=branch generates tag(s) on branch only. E.g. 'master', 'master-abc0123'
|
|
||||||
# type=ref,event=tag generates tag(s) on tags only. E.g. 'v0.0.0', 'v0.0.0-abc0123', and 'latest'
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=tag
|
|
||||||
# The rest of the org.opencontainers.image.xxx labels are dynamically generated
|
|
||||||
labels: |
|
|
||||||
org.opencontainers.image.description=SnappyMail
|
|
||||||
org.opencontainers.image.licenses=AGPLv3
|
|
||||||
|
|
||||||
# See: https://github.com/docker/build-push-action/blob/v2.6.1/docs/advanced/cache.md#github-cache
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v2
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
id: buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
|
|
||||||
- name: Cache Docker layers
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: /tmp/.buildx-cache
|
|
||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-buildx-
|
|
||||||
|
|
||||||
- name: Login to Docker Hub registry
|
|
||||||
if: startsWith(github.ref, 'refs/tags/') # Login only on tags
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
|
||||||
if: startsWith(github.ref, 'refs/tags/') # Login only on tags
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
# See: https://github.com/docker/buildx/issues/59
|
|
||||||
- name: Build
|
|
||||||
id: build
|
|
||||||
uses: docker/build-push-action@v3
|
|
||||||
with:
|
|
||||||
context: '.'
|
|
||||||
file: ./.docker/release/Dockerfile
|
|
||||||
platforms: linux/amd64
|
|
||||||
push: false
|
|
||||||
load: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
|
|
||||||
|
|
||||||
- name: Docker images
|
|
||||||
run: |
|
|
||||||
docker images
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: |
|
|
||||||
TAG=$( echo "${{ steps.meta.outputs.tags }}" | head -n1 )
|
|
||||||
.docker/release/test/test.sh "$TAG"
|
|
||||||
|
|
||||||
- name: Build and push
|
|
||||||
id: build-and-push
|
|
||||||
uses: docker/build-push-action@v3
|
|
||||||
with:
|
|
||||||
context: '.'
|
|
||||||
file: ./.docker/release/Dockerfile
|
|
||||||
# TODO: Add more arches?
|
|
||||||
# platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/s390x
|
|
||||||
platforms: linux/386,linux/amd64,linux/arm64
|
|
||||||
push: ${{ startsWith(github.ref, 'refs/tags/') }} # Push only on tags
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
|
|
||||||
|
|
||||||
# Temp fix
|
|
||||||
# https://github.com/docker/build-push-action/issues/252
|
|
||||||
# https://github.com/moby/buildkit/issues/1896
|
|
||||||
- name: Move cache
|
|
||||||
run: |
|
|
||||||
rm -rf /tmp/.buildx-cache
|
|
||||||
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
|
|
||||||
41
.gitignore
vendored
41
.gitignore
vendored
|
|
@ -1,32 +1,23 @@
|
||||||
*.bak
|
/.idea
|
||||||
*.old
|
|
||||||
/api.php
|
/api.php
|
||||||
/*.log
|
/error.log
|
||||||
/snappymail/v/0.0.0/static/*
|
/nbproject
|
||||||
|
/npm-debug.log
|
||||||
|
/rainloop.sublime-project
|
||||||
|
/rainloop.sublime-workspace
|
||||||
|
/rainloop/v/0.0.0/static/ckeditor/*
|
||||||
|
!/rainloop/v/0.0.0/static/ckeditor/.gitempty
|
||||||
|
/rainloop/v/0.0.0/static/svg/*
|
||||||
|
!/rainloop/v/0.0.0/static/svg/.gitempty
|
||||||
|
/rainloop/v/0.0.0/static/css/*.css
|
||||||
|
/rainloop/v/0.0.0/static/js/*.js
|
||||||
|
/rainloop/v/0.0.0/static/js/**/*.js
|
||||||
/node_modules
|
/node_modules
|
||||||
/build/local
|
/build/local
|
||||||
/build/dist
|
/build/dist
|
||||||
/build/tmp
|
/build/tmp
|
||||||
/build/docker
|
|
||||||
/.docker/dev/.cache
|
|
||||||
/.docker/dev/mail/config
|
|
||||||
/.docker/dev/nginx/ssl/*
|
|
||||||
!/.docker/dev/nginx/ssl/.gitempty
|
|
||||||
/.docker/release/snappymail-*.zip
|
|
||||||
/dist
|
|
||||||
/data
|
/data
|
||||||
|
.DS_Store
|
||||||
|
/tests/fix.php
|
||||||
/MULTIPLY
|
/MULTIPLY
|
||||||
/include.php
|
/include.php
|
||||||
/issues
|
|
||||||
.idea/
|
|
||||||
.env
|
|
||||||
/test
|
|
||||||
/public_html
|
|
||||||
/vendors/knockout/spec
|
|
||||||
/vendors/openpgp-5
|
|
||||||
!/vendors/openpgp-5/dist
|
|
||||||
/vendors/squire2
|
|
||||||
!/vendors/squire2/dist
|
|
||||||
/vendors/vanillaqr.js/
|
|
||||||
/integrations/nextcloud/rainloop
|
|
||||||
/integrations/owncloud/rainloop
|
|
||||||
7
.gitmodules
vendored
7
.gitmodules
vendored
|
|
@ -1,7 +0,0 @@
|
||||||
[submodule "vendors/openpgp-5"]
|
|
||||||
path = vendors/openpgp-5
|
|
||||||
url = git@github.com:the-djmaze/openpgpjs.git
|
|
||||||
[submodule "vendors/squire2"]
|
|
||||||
path = vendors/squire2
|
|
||||||
url = git@github.com:the-djmaze/Squire.git
|
|
||||||
branch = snappymail
|
|
||||||
51
.htaccess
51
.htaccess
|
|
@ -1,51 +0,0 @@
|
||||||
<FilesMatch "index\.php">
|
|
||||||
AcceptPathInfo On
|
|
||||||
</FilesMatch>
|
|
||||||
|
|
||||||
<IfModule mod_rewrite.c>
|
|
||||||
RewriteEngine On
|
|
||||||
# Redirect cPanel
|
|
||||||
RewriteRule cpsess.* https://%{HTTP_HOST}/ [L,R=301]
|
|
||||||
|
|
||||||
RewriteCond %{REQUEST_FILENAME} !-f
|
|
||||||
RewriteCond %{REQUEST_FILENAME} !-d
|
|
||||||
RewriteRule ^(.+)$ index.php/$1 [L,QSA]
|
|
||||||
</IfModule>
|
|
||||||
|
|
||||||
<IfModule mod_expires.c>
|
|
||||||
ExpiresActive On
|
|
||||||
ExpiresByType text/css A15768000
|
|
||||||
ExpiresByType text/html A15768000
|
|
||||||
ExpiresByType application/javascript A15768000
|
|
||||||
ExpiresByType image/gif A15768000
|
|
||||||
ExpiresByType image/jpeg A15768000
|
|
||||||
ExpiresByType image/png A15768000
|
|
||||||
ExpiresByType image/svg+xml A15768000
|
|
||||||
ExpiresByType image/webp A15768000
|
|
||||||
ExpiresByType image/vnd.microsoft.icon A15768000
|
|
||||||
ExpiresByType font/woff A15768000
|
|
||||||
ExpiresByType font/woff2 A15768000
|
|
||||||
</IfModule>
|
|
||||||
|
|
||||||
<IfModule mod_headers.c>
|
|
||||||
# Header set Cache-Control "public, max-age=31536000"
|
|
||||||
# Header set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src 'self' data:; style-src 'self' 'unsafe-inline'"
|
|
||||||
# Header set Referrer-Policy "no-referrer"
|
|
||||||
# Header set Strict-Transport-Security "max-age=31536000"
|
|
||||||
Header set imagetoolbar "no"
|
|
||||||
# Header set X-Content-Type-Options "nosniff"
|
|
||||||
# Header set X-XSS-Protection "1; mode=block"
|
|
||||||
Header set Service-Worker-Allowed "/"
|
|
||||||
|
|
||||||
# Google FLoC
|
|
||||||
# Header set Permissions-Policy "interest-cohort=()"
|
|
||||||
</IfModule>
|
|
||||||
|
|
||||||
#<IfModule mod_brotli.c>
|
|
||||||
# AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/xml text/css text/javascript application/javascript
|
|
||||||
#</IfModule>
|
|
||||||
|
|
||||||
<IfModule mod_deflate.c>
|
|
||||||
AddOutputFilterByType DEFLATE text/css text/html text/plain text/xml application/xml text/javascript application/javascript
|
|
||||||
AddOutputFilterByType DEFLATE font/opentype font/otf font/ttf font/woff
|
|
||||||
</IfModule>
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
*.*
|
|
||||||
!/dev/**/*.js
|
|
||||||
!/tasks/**/*.js
|
|
||||||
!/*.js
|
|
||||||
26
.prettierrc
26
.prettierrc
|
|
@ -1,26 +0,0 @@
|
||||||
{
|
|
||||||
"printWidth": 120,
|
|
||||||
"semi": true,
|
|
||||||
"singleQuote": true,
|
|
||||||
"useTabs": true,
|
|
||||||
"bracketSpacing": true,
|
|
||||||
"trailingComma": "none",
|
|
||||||
"arrowParens": "always",
|
|
||||||
"endOfLine": "lf",
|
|
||||||
"quoteProps": "preserve",
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": "*.js",
|
|
||||||
"options": {
|
|
||||||
"parser": "babel"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": "*.jsx",
|
|
||||||
"options": {
|
|
||||||
"parser": "babel"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
15
.travis.yml
Normal file
15
.travis.yml
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
language: php
|
||||||
|
php:
|
||||||
|
- '5.3'
|
||||||
|
- '5.4'
|
||||||
|
- '5.5'
|
||||||
|
- '5.6'
|
||||||
|
- '7.0'
|
||||||
|
before_script:
|
||||||
|
- sudo apt-get update
|
||||||
|
- sudo apt-get install -y build-essential libssl-dev
|
||||||
|
- curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.1/install.sh | bash
|
||||||
|
- nvm install 4.4.5
|
||||||
|
- nvm use 4.4.5
|
||||||
|
- npm install -g gulp
|
||||||
|
- npm install
|
||||||
14
.tx/config
Normal file
14
.tx/config
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[main]
|
||||||
|
host = https://www.transifex.com
|
||||||
|
type = YAML
|
||||||
|
minimum_perc = 60
|
||||||
|
|
||||||
|
[rainloop-webmail.rainloop-webmail]
|
||||||
|
file_filter = rainloop/v/0.0.0/app/localization/webmail/<lang>.yml
|
||||||
|
source_file = rainloop/v/0.0.0/app/localization/webmail/_source.en.yml
|
||||||
|
source_lang = en
|
||||||
|
|
||||||
|
[rainloop-webmail.rainloop-admin]
|
||||||
|
file_filter = rainloop/v/0.0.0/app/localization/admin/<lang>.yml
|
||||||
|
source_file = rainloop/v/0.0.0/app/localization/admin/_source.en.yml
|
||||||
|
source_lang = en
|
||||||
1
.tx/readme.txt
Normal file
1
.tx/readme.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
tx pull -a
|
||||||
2422
CHANGELOG.md
2422
CHANGELOG.md
File diff suppressed because it is too large
Load diff
|
|
@ -1,130 +0,0 @@
|
||||||
|
|
||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
We as members, contributors, and leaders pledge to make participation in our
|
|
||||||
community a harassment-free experience for everyone, regardless of age, body
|
|
||||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
||||||
identity and expression, level of experience, education, socio-economic status,
|
|
||||||
nationality, personal appearance, race, religion, or sexual identity
|
|
||||||
and orientation.
|
|
||||||
|
|
||||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
||||||
diverse, inclusive, and healthy community.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to a positive environment for our
|
|
||||||
community include:
|
|
||||||
|
|
||||||
* Demonstrating empathy and kindness toward other people
|
|
||||||
* Being respectful of differing opinions, viewpoints, and experiences
|
|
||||||
* Giving and gracefully accepting constructive feedback
|
|
||||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
|
||||||
and learning from the experience
|
|
||||||
* Focusing on what is best not just for us as individuals, but for the
|
|
||||||
overall community
|
|
||||||
|
|
||||||
Examples of unacceptable behavior include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery, and sexual attention or
|
|
||||||
advances of any kind
|
|
||||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or email
|
|
||||||
address, without their explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
|
||||||
professional setting
|
|
||||||
|
|
||||||
## Enforcement Responsibilities
|
|
||||||
|
|
||||||
Community leaders are responsible for clarifying and enforcing our standards of
|
|
||||||
acceptable behavior and will take appropriate and fair corrective action in
|
|
||||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
||||||
or harmful.
|
|
||||||
|
|
||||||
Community leaders have the right and responsibility to remove, edit, or reject
|
|
||||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
|
||||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
|
||||||
decisions when appropriate.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies within all community spaces, and also applies when
|
|
||||||
an individual is officially representing the community in public spaces.
|
|
||||||
Examples of representing our community include using an official e-mail address,
|
|
||||||
posting via an official social media account, or acting as an appointed
|
|
||||||
representative at an online or offline event.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
||||||
reported to the community leaders responsible for enforcement at
|
|
||||||
[support@snappymail.eu](support@snappymail.eu).
|
|
||||||
All complaints will be reviewed and investigated promptly and fairly.
|
|
||||||
|
|
||||||
All community leaders are obligated to respect the privacy and security of the
|
|
||||||
reporter of any incident.
|
|
||||||
|
|
||||||
## Enforcement Guidelines
|
|
||||||
|
|
||||||
Community leaders will follow these Community Impact Guidelines in determining
|
|
||||||
the consequences for any action they deem in violation of this Code of Conduct:
|
|
||||||
|
|
||||||
### 1. Correction
|
|
||||||
|
|
||||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
|
||||||
unprofessional or unwelcome in the community.
|
|
||||||
|
|
||||||
**Consequence**: A private, written warning from community leaders, providing
|
|
||||||
clarity around the nature of the violation and an explanation of why the
|
|
||||||
behavior was inappropriate. A public apology may be requested.
|
|
||||||
|
|
||||||
### 2. Warning
|
|
||||||
|
|
||||||
**Community Impact**: A violation through a single incident or series
|
|
||||||
of actions.
|
|
||||||
|
|
||||||
**Consequence**: A warning with consequences for continued behavior. No
|
|
||||||
interaction with the people involved, including unsolicited interaction with
|
|
||||||
those enforcing the Code of Conduct, for a specified period of time. This
|
|
||||||
includes avoiding interactions in community spaces as well as external channels
|
|
||||||
like social media. Violating these terms may lead to a temporary or
|
|
||||||
permanent ban.
|
|
||||||
|
|
||||||
### 3. Temporary Ban
|
|
||||||
|
|
||||||
**Community Impact**: A serious violation of community standards, including
|
|
||||||
sustained inappropriate behavior.
|
|
||||||
|
|
||||||
**Consequence**: A temporary ban from any sort of interaction or public
|
|
||||||
communication with the community for a specified period of time. No public or
|
|
||||||
private interaction with the people involved, including unsolicited interaction
|
|
||||||
with those enforcing the Code of Conduct, is allowed during this period.
|
|
||||||
Violating these terms may lead to a permanent ban.
|
|
||||||
|
|
||||||
### 4. Permanent Ban
|
|
||||||
|
|
||||||
**Community Impact**: Demonstrating a pattern of violation of community
|
|
||||||
standards, including sustained inappropriate behavior, harassment of an
|
|
||||||
individual, or aggression toward or disparagement of classes of individuals.
|
|
||||||
|
|
||||||
**Consequence**: A permanent ban from any sort of public interaction within
|
|
||||||
the community.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
||||||
version 2.0, available at
|
|
||||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
|
||||||
|
|
||||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
|
||||||
enforcement ladder](https://github.com/mozilla/diversity).
|
|
||||||
|
|
||||||
[homepage]: https://www.contributor-covenant.org
|
|
||||||
|
|
||||||
For answers to common questions about this code of conduct, see the FAQ at
|
|
||||||
https://www.contributor-covenant.org/faq. Translations are available at
|
|
||||||
https://www.contributor-covenant.org/translations.
|
|
||||||
|
|
||||||
|
|
@ -1,47 +1,21 @@
|
||||||
**Thanks for contributing to SnappyMail Webmail!**
|
**Thanks for contributing to RainLoop Webmail!**
|
||||||
|
|
||||||
1. Fork the repo, do work in a feature branch.
|
1. Sign the CLA (individual or corporate) at <http://www.rainloop.net/contribute/>.
|
||||||
2. Issue a pull request.
|
2. Fork the repo, do work in a feature branch.
|
||||||
|
3. Issue a pull request.
|
||||||
---
|
|
||||||
|
---
|
||||||
**Getting started**
|
|
||||||
|
**Getting started**
|
||||||
1. Install PHP
|
|
||||||
2. Install node.js - `https://nodejs.org/download/`
|
1. Install node.js - `https://nodejs.org/download/`
|
||||||
3. Install yarn - `https://yarnpkg.com/en/docs/install`
|
2. Install gulp - `npm install gulp -g`
|
||||||
4. Install gulp - `npm install gulp -g`
|
3. Fork rainloop - `https://github.com/RainLoop/rainloop-webmail/issues/new#fork-destination-box`
|
||||||
5. Fork snappymail from https://github.com/the-djmaze/snappymail
|
4. Clone rainloop - `git clone git@github.com:USERNAME/rainloop-webmail.git rainloop`
|
||||||
6. Clone snappymail - `git clone git@github.com:USERNAME/snappymail.git snappymail`
|
5. `cd rainloop`
|
||||||
7. `cd snappymail`
|
6. Install gulp add gulp plugins into project directory - `npm install`
|
||||||
8. Install all dependencies - `yarn install`
|
7. Run gulp - `gulp`
|
||||||
9. Run gulp - `gulp`
|
|
||||||
|
---
|
||||||
---
|
|
||||||
|
If you have any questions, open an issue or email support@rainloop.net.
|
||||||
**Debugging JavaScript**
|
|
||||||
|
|
||||||
1. Edit data/\_data_/\_default_/configs/application.ini
|
|
||||||
2. Set 'use_app_debug_js' (and optionally 'use_app_debug_css') to 'On'
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Editing HTML Template Files**
|
|
||||||
|
|
||||||
1. Edit data/\_data_/\_default_/configs/application.ini
|
|
||||||
2. Set `[cache] system_data` to Off
|
|
||||||
|
|
||||||
**Release**
|
|
||||||
|
|
||||||
1. Install gzip
|
|
||||||
2. Install brotli
|
|
||||||
3. php release.php
|
|
||||||
|
|
||||||
Options:
|
|
||||||
* `php release.php --aur` = Build Arch Linux package
|
|
||||||
* `php release.php --docker` = Build Docker instance
|
|
||||||
* `php release.php --plugins` = Build plugins
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
If you have any questions, open an issue or email support@snappymail.eu.
|
|
||||||
|
|
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -658,4 +658,4 @@ specific requirements.
|
||||||
You should also get your employer (if you work as a programmer) or school,
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||||
<http://www.gnu.org/licenses/>.
|
<http://www.gnu.org/licenses/>.
|
||||||
76
Makefile
76
Makefile
|
|
@ -1,76 +0,0 @@
|
||||||
#!make
|
|
||||||
|
|
||||||
rebuild: _down
|
|
||||||
docker compose build --no-cache
|
|
||||||
|
|
||||||
up: _up status
|
|
||||||
_up:
|
|
||||||
docker compose up -d
|
|
||||||
|
|
||||||
stop: _stop status
|
|
||||||
_stop:
|
|
||||||
docker compose stop
|
|
||||||
|
|
||||||
down: _down status
|
|
||||||
_down:
|
|
||||||
docker compose down
|
|
||||||
|
|
||||||
restart: _stop _up status
|
|
||||||
|
|
||||||
status:
|
|
||||||
@docker compose ps
|
|
||||||
|
|
||||||
tx:
|
|
||||||
@docker compose run --no-deps --rm tx tx pull -a -s -f -d
|
|
||||||
|
|
||||||
console-node:
|
|
||||||
@docker compose run --no-deps --rm node sh
|
|
||||||
console-tx:
|
|
||||||
@docker compose run --no-deps --rm tx sh
|
|
||||||
console-php:
|
|
||||||
@docker compose exec php sh
|
|
||||||
console: console-node
|
|
||||||
|
|
||||||
logs:
|
|
||||||
@docker compose logs --tail=100 -f
|
|
||||||
logs-db:
|
|
||||||
@docker compose logs --tail=100 -f db
|
|
||||||
logs-php:
|
|
||||||
@docker compose logs --tail=100 -f php
|
|
||||||
logs-node:
|
|
||||||
@docker compose logs --tail=100 -f node
|
|
||||||
logs-nginx:
|
|
||||||
@docker compose logs --tail=100 -f nginx
|
|
||||||
logs-mail:
|
|
||||||
@docker compose logs --tail=100 -f mail
|
|
||||||
logs-tx:
|
|
||||||
@docker compose logs --tail=100 -f tx
|
|
||||||
|
|
||||||
rl-lint:
|
|
||||||
@docker compose run --no-deps --rm node gulp lint
|
|
||||||
rl-dev:
|
|
||||||
@docker compose run --no-deps --rm node npm run watch-js
|
|
||||||
rl-compile:
|
|
||||||
@docker compose run --no-deps --rm node gulp build
|
|
||||||
rl-compile-with-source:
|
|
||||||
@docker compose run --no-deps --rm node gulp build --source
|
|
||||||
rl-watch-css:
|
|
||||||
@docker compose run --no-deps --rm node npm run watch-css
|
|
||||||
rl-watch-js:
|
|
||||||
@docker compose run --no-deps --rm node npm run watch-js
|
|
||||||
|
|
||||||
rl-build:
|
|
||||||
@docker compose run --no-deps --rm node gulp all
|
|
||||||
|
|
||||||
yarn-install:
|
|
||||||
@docker compose run --no-deps --rm node yarn install
|
|
||||||
yarn-outdated:
|
|
||||||
@docker compose run --no-deps --rm node yarn outdated
|
|
||||||
yarn-upgrade:
|
|
||||||
@docker compose run --no-deps --rm node yarn upgrade-interactive --exact --latest
|
|
||||||
|
|
||||||
gpg:
|
|
||||||
docker run -it --rm -w=/var/www \
|
|
||||||
-v $(shell pwd)/.docker/.cache/.gnupg:/root/.gnupg \
|
|
||||||
-v $(shell pwd):/var/www \
|
|
||||||
ubuntu:16.04 bash
|
|
||||||
249
README.md
249
README.md
|
|
@ -1,226 +1,23 @@
|
||||||
<div align="center">
|
RainLoop Webmail [](https://travis-ci.org/RainLoop/rainloop-webmail)
|
||||||
<a href="https://github.com/the-djmaze/snappymail">
|
==================
|
||||||
<img src="https://snappymail.eu/static/img/logo-256x256-white.png">
|
|
||||||
</a>
|
Simple, modern & fast web-based email client.
|
||||||
<br>
|
|
||||||
<h1>SnappyMail</h1>
|
Modest system requirements, decent performance, simple installation and upgrade, no database required - all these make RainLoop Webmail a perfect choice for your email solution.
|
||||||
<br>
|
|
||||||
|
For more information about the product, check [rainloop.net](http://www.rainloop.net/).
|
||||||
[](https://github.com/the-djmaze/snappymail/actions/workflows/docker.yml)
|
|
||||||
[](https://hub.docker.com/r/djmaze/snappymail/tags)
|
Information about installing the product, check the [documentation page](http://www.rainloop.net/docs/installation/).
|
||||||
|
|
||||||
<p>
|
## Attention
|
||||||
Simple, modern, lightweight & fast web-based email client.
|
|
||||||
</p>
|
This is **NOT** a stable version of RainLoop Webmail.
|
||||||
<p>
|
It's not recommended to use in production environment.
|
||||||
The drastically upgraded & secured fork of <a href="https://github.com/RainLoop/rainloop-webmail">RainLoop Webmail Community edition</a>.
|
|
||||||
</p>
|
## License
|
||||||
<p>
|
|
||||||
We thank the RainLoop Team for making a great PHP 5 product that was good in the past.
|
**RainLoop Webmail (Community edition)** is released under
|
||||||
</p>
|
**GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (AGPL)**.
|
||||||
<p>
|
http://www.gnu.org/licenses/agpl-3.0.html
|
||||||
Up to date system requirements, snappy performance, simple installation and upgrade, no database required
|
|
||||||
- all these make SnappyMail a good choice.
|
Copyright (c) 2016 Rainloop Team
|
||||||
</p>
|
|
||||||
<h2></h2>
|
|
||||||
<br>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
For more information about the product, check [snappymail.eu](https://snappymail.eu/).
|
|
||||||
|
|
||||||
Information about installing the product, check the [wiki page](https://github.com/the-djmaze/snappymail/wiki/Installation-instructions).
|
|
||||||
|
|
||||||
And don't forget to read the whole [Wiki](https://github.com/the-djmaze/snappymail/wiki).
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
**SnappyMail** is released under
|
|
||||||
**GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (AGPL)**.
|
|
||||||
http://www.gnu.org/licenses/agpl-3.0.html
|
|
||||||
|
|
||||||
Copyright (c) 2020 - 2024 SnappyMail
|
|
||||||
Copyright (c) 2013 - 2022 RainLoop
|
|
||||||
|
|
||||||
## Modifications
|
|
||||||
|
|
||||||
This fork of RainLoop has the following changes:
|
|
||||||
|
|
||||||
* Privacy/GDPR friendly (no: Social, Gravatar, Facebook, Google, Twitter, DropBox, X-Mailer)
|
|
||||||
* Admin uses password_hash/password_verify
|
|
||||||
* Auth failed attempts written to syslog
|
|
||||||
* Added Fail2ban instructions
|
|
||||||
* ES2020
|
|
||||||
* PHP 7.4+ required
|
|
||||||
* PHP mbstring extension required
|
|
||||||
* PHP replaced pclZip with PharData and ZipArchive
|
|
||||||
* Dark mode
|
|
||||||
* Added option to remove background/font colors from messages for real "dark mode"
|
|
||||||
* Removed BackwardCapability (class \RainLoop\Account)
|
|
||||||
* Removed ChangePassword (re-implemented as plugin)
|
|
||||||
* Removed POP3 support
|
|
||||||
* Removed background video support
|
|
||||||
* Removed Sentry (Application Monitoring and Error Tracking Software)
|
|
||||||
* Removed Spyc yaml
|
|
||||||
* Removed OwnCloud
|
|
||||||
* Replaced gulp-uglify with gulp-terser
|
|
||||||
* CRLF => LF line endings
|
|
||||||
* Embed boot.js and boot.css into index.html
|
|
||||||
* Removal of old JavaScript code (things are native these days)
|
|
||||||
* Added modified [Squire](https://github.com/the-djmaze/Squire/tree/snappymail) HTML editor as replacement for CKEditor
|
|
||||||
* Updated [Sabre/VObject](https://github.com/sabre-io/vobject)
|
|
||||||
* Split Admin specific JavaScript code from User code
|
|
||||||
* Split Sieve specific JavaScript code from User code
|
|
||||||
* JSON reviver
|
|
||||||
* Better memory garbage collection management
|
|
||||||
* Added serviceworker for Notifications
|
|
||||||
* Added advanced Sieve scripts editor
|
|
||||||
* Slimmed down language files
|
|
||||||
* Replaced webpack with rollup
|
|
||||||
* No user-agent detection (use device width)
|
|
||||||
* Added support to load plugins as .phar
|
|
||||||
* Replaced old Sabre library
|
|
||||||
* AddressBook Contacts support MySQL/MariaDB utf8mb4
|
|
||||||
* Added [Fetch Metadata Request Headers](https://www.w3.org/TR/fetch-metadata/) checks
|
|
||||||
* Reduced excessive DOM size
|
|
||||||
* Support [Kolab groupware](https://kolab.org/)
|
|
||||||
* Support many more [IMAP RFC's](https://snappymail.eu/comparison#IMAP)
|
|
||||||
* Support Sodium and OpenSSL for encryption
|
|
||||||
* Much better PGP support
|
|
||||||
|
|
||||||
|
|
||||||
### Supported browsers
|
|
||||||
|
|
||||||
This fork uses downsized/simplified versions of scripts and has no support for Internet Explorer nor Edge Legacy.
|
|
||||||
Supported are:
|
|
||||||
|
|
||||||
* Chrome 80+
|
|
||||||
* Edge 80+
|
|
||||||
* Firefox 78+
|
|
||||||
* Opera 67+
|
|
||||||
* Safari 13.1+
|
|
||||||
|
|
||||||
|
|
||||||
### Removal of old JavaScript
|
|
||||||
|
|
||||||
The result is faster and smaller download code (good for mobile networks).
|
|
||||||
|
|
||||||
* Added dev/prototype.js for some additional features
|
|
||||||
* Modified Jua.js to be without jQuery
|
|
||||||
* Replaced Autolinker with simple https/email detection
|
|
||||||
* Replaced momentToNode with proper HTML5 `<time>`
|
|
||||||
* Replaced resize listeners with ResizeObserver
|
|
||||||
* Replaced bootstrap.js with native drop-in replacement
|
|
||||||
* Replaced dev/Common/ClientStorageDriver/* with Web Storage Objects polyfill
|
|
||||||
* Replaced *Ajax with *Fetch classes because we use the Fetch API, not jQuery.ajax
|
|
||||||
* Replaced [knockoutjs](https://github.com/knockout/knockout) 3.4 with a modified 3.5.1
|
|
||||||
* Replaced knockout-sortable with native HTML5 drag&drop
|
|
||||||
* Replaced simplestatemanager with CSS @media
|
|
||||||
* Replaced inputosaurus with own code
|
|
||||||
* Replaced keymaster with own shortcuts handler
|
|
||||||
* Replaced OpenPGP.js v2 with OpenPGP.js v5
|
|
||||||
* Removed ifvisible.js
|
|
||||||
* Removed pikaday
|
|
||||||
* Removed underscore
|
|
||||||
* Removed polyfills
|
|
||||||
* Removed Modernizr
|
|
||||||
* Removed nanoscroll
|
|
||||||
* Removed lightgallery
|
|
||||||
* Removed jQuery
|
|
||||||
* Removed jquery-ui
|
|
||||||
* Removed jquery-scrollstop
|
|
||||||
* Removed jquery-mousewheel
|
|
||||||
* Removed matchmedia-polyfill
|
|
||||||
* Removed momentjs (use Intl)
|
|
||||||
* Removed opentip (use CSS)
|
|
||||||
* Removed non-community (aka Prem/Premium/License) code
|
|
||||||
* Removed ProgressJS
|
|
||||||
|
|
||||||
|
|
||||||
RainLoop 1.17 vs SnappyMail
|
|
||||||
|
|
||||||
|js/* |RainLoop |Snappy |
|
|
||||||
|--------------- |--------: |--------: |
|
|
||||||
|admin.js |2.170.153 | 84.925 |
|
|
||||||
|app.js |4.207.787 | 447.263 |
|
|
||||||
|boot.js | 868.735 | 4.343 |
|
|
||||||
|libs.js | 658.812 | 233.728 |
|
|
||||||
|sieve.js | 0 | 91.418 |
|
|
||||||
|polyfills.js | 334.608 | 0 |
|
|
||||||
|serviceworker.js | 0 | 285 |
|
|
||||||
|TOTAL |8.240.095 | 861.962 |
|
|
||||||
|
|
||||||
|js/min/* |RainLoop |Snappy |RL gzip |SM gzip |RL brotli |SM brotli |
|
|
||||||
|--------------- |--------: |--------: |------: |------: |--------: |--------: |
|
|
||||||
|admin.min.js | 256.831 | 41.719 | 73.606 | 14.022 | 60.877 | 12.567 |
|
|
||||||
|app.min.js | 515.367 | 202.101 |139.456 | 68.505 |110.485 | 58.481 |
|
|
||||||
|boot.min.js | 84.659 | 2.231 | 26.998 | 1.271 | 23.643 | 1.067 |
|
|
||||||
|libs.min.js | 584.772 | 110.646 |180.901 | 39.518 |155.182 | 35.207 |
|
|
||||||
|sieve.min.js | 0 | 45.504 | 0 | 11.131 | 0 | 9.917 |
|
|
||||||
|polyfills.min.js | 32.837 | 0 | 11.406 | 0 | 10.175 | 0 |
|
|
||||||
|TOTAL user |1.217.635 | 314.978 |358.761 |109.294 |299.485 | 94.755 |
|
|
||||||
|TOTAL user+sieve |1.217.635 | 360.482 |358.761 |120.425 |299.485 |104.672 |
|
|
||||||
|TOTAL admin | 959.099 | 154.596 |292.911 | 54.811 |249.877 | 48.841 |
|
|
||||||
|
|
||||||
For a user it is around 66% smaller and faster than traditional RainLoop.
|
|
||||||
|
|
||||||
### CSS changes
|
|
||||||
|
|
||||||
* Solve jQuery removed "features" with native css code
|
|
||||||
* Themes work in mobile mode
|
|
||||||
* Bugfix invalid/conflicting css rules
|
|
||||||
* Use flexbox
|
|
||||||
* Use border-box
|
|
||||||
* Split app.css to have separate admin.css
|
|
||||||
* Remove oldschool 'float'
|
|
||||||
* Remove unused css
|
|
||||||
* Removed html.no-css
|
|
||||||
* Removed dev/Styles/Cmd.less
|
|
||||||
* Removed dev/Styles/Scroll.less
|
|
||||||
* Removed Internet Explorer from normalize.css
|
|
||||||
* Removed node_modules/opentip/css/opentip.css
|
|
||||||
* Removed node_modules/pikaday/css/pikaday.css
|
|
||||||
* Removed unused vendors/bootstrap/less/*
|
|
||||||
* Removed vendors/jquery-nanoscroller/nanoscroller.css
|
|
||||||
* Removed vendors/jquery-letterfx/jquery-letterfx.min.css
|
|
||||||
* Removed vendors/Progress.js/minified/progressjs.min.css
|
|
||||||
* Removed gulp-autoprefixer
|
|
||||||
|
|
||||||
|
|
||||||
|css/* |RainLoop |Snappy |RL gzip |SM gzip |SM brotli |
|
|
||||||
|------------ |-------: |------: |------: |------: |--------: |
|
|
||||||
|app.css | 340.331 | 85.073 | 46.946 | 17.792 | 15.210 |
|
|
||||||
|app.min.css | 274.947 | 68.272 | 39.647 | 15.615 | 13.636 |
|
|
||||||
|boot.css | | 1.326 | | 664 | 545 |
|
|
||||||
|boot.min.css | | 1.071 | | 590 | 474 |
|
|
||||||
|admin.css | | 30.880 | | 7.045 | 6.127 |
|
|
||||||
|admin.min.css | | 24.959 | | 6.368 | 5.615 |
|
|
||||||
|
|
||||||
### PGP
|
|
||||||
RainLoop uses the old OpenPGP.js v2
|
|
||||||
SnappyMail v2.12 uses OpenPGP.js v5, GnuPG and Mailvelope.
|
|
||||||
SnappyMail is able to use and generate ECDSA and EDDSA keys, where RainLoop does not.
|
|
||||||
|
|
||||||
Since SnappyMail tries to achieve the best mobile experience, it forked OpenPGP.js to strip it down.
|
|
||||||
* remove all unused Node.js
|
|
||||||
* remove all old browsers support
|
|
||||||
See https://github.com/the-djmaze/openpgpjs for development
|
|
||||||
|
|
||||||
|OpenPGP |RainLoop |Snappy |RL gzip |SM gzip |RL brotli |SM brotli |
|
|
||||||
|--------------- |--------: |--------: |------: |-------: |--------: |--------: |
|
|
||||||
|openpgp.min.js | 330.742 | 546.165 |102.388 | 169.207 | 84.241 | 138.688 |
|
|
||||||
|openpgp.worker | 1.499 | | 824 | | 695 | |
|
|
||||||
|
|
||||||
|
|
||||||
### Squire vs CKEditor
|
|
||||||
The [Squire](https://github.com/neilj/Squire) implementation is not 100% compatible yet, but it shows the massive overhead of CKEditor.
|
|
||||||
|
|
||||||
Still TODO:
|
|
||||||
|
|
||||||
* support for tables (really needed?!?)
|
|
||||||
|
|
||||||
| | normal | min | gzip | min gzip |
|
|
||||||
|-------- |-------: |-------: |------: |--------: |
|
|
||||||
|squire | 122.321 | 41.906 | 31.867 | 14.330 |
|
|
||||||
|ckeditor | ? | 520.035 | ? | 155.916 |
|
|
||||||
|
|
||||||
CKEditor including the 7 asset requests (css,language,plugins,icons) is 633.46 KB / 180.47 KB (gzip).
|
|
||||||
|
|
|
||||||
40
SECURITY.md
40
SECURITY.md
|
|
@ -1,40 +0,0 @@
|
||||||
# Security Policy
|
|
||||||
|
|
||||||
## Supported Versions
|
|
||||||
|
|
||||||
Currently due to the fast development only the latest version receives security updates.
|
|
||||||
|
|
||||||
| Version | Supported |
|
|
||||||
| -------- | --------- |
|
|
||||||
| 2.34.x | ✔ |
|
|
||||||
| < 2.34.0 | ❌ |
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
|
||||||
|
|
||||||
Please report security issues or vulnerabilities as an encrypted email to [security@snappymail.eu](mailto:security@snappymail.eu).
|
|
||||||
Your report should be detailed enough with clear steps to reproduce and classify the found vulnerability.
|
|
||||||
|
|
||||||
You can find the PGP public key below and on the major public keyservers like [keys.openpgp.org](https://keys.openpgp.org).
|
|
||||||
```
|
|
||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
||||||
Comment: Type: 255-bit EdDSA
|
|
||||||
Comment: Fingerprint: 445D265124E6072671E64D0733F868A7E35E8277
|
|
||||||
|
|
||||||
mDMEYiE2QRYJKwYBBAHaRw8BAQdAqMrQUm6DddWcQNo0VEjNIu3Q6CfP3nokVv2Y
|
|
||||||
rNQ1avq0LFNuYXBweU1haWwgU2VjdXJpdHkgPHNlY3VyaXR5QHNuYXBweW1haWwu
|
|
||||||
ZXU+iJQEExYKADwWIQREXSZRJOYHJnHmTQcz+Gin416CdwUCYiE2QQIbAwULCQgH
|
|
||||||
AgMiAgEGFQoJCAsCBBYCAwECHgcCF4AACgkQM/hop+NegnfSGgD9GEHpOrvWpBGY
|
|
||||||
dYfvVd/+Lv5d+dFBcPyki9zu9zHfhwkBAL343EF6ZR0XwMlOQu9wu0hT9KBz4g55
|
|
||||||
6D41i0PrEaoBuDgEYiE2QRIKKwYBBAGXVQEFAQEHQMMr9gcVcJ3aiup/tpl8ZXxy
|
|
||||||
aJiJRGkPyNwGI5vxHMpZAwEIB4h4BBgWCgAgFiEERF0mUSTmByZx5k0HM/hop+Ne
|
|
||||||
gncFAmIhNkECGwwACgkQM/hop+NegndVhgD/SVGSKbF4G2W024VpW2tm3zCT+ue+
|
|
||||||
YMXQVq4SJt7UpWABAORudfJxsBqCRKtPlZMgGTJLjcOkyFJ9C2Fx7DeN0J4I
|
|
||||||
=nSOi
|
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
||||||
```
|
|
||||||
|
|
||||||
## Publishing and Credits
|
|
||||||
|
|
||||||
I will analyze and fix the reported issue as fast as possible.
|
|
||||||
Together with the reporter, I plan the disclosure of the found and fixed vulnerability.
|
|
||||||
Credits to the reporter are granted and can be included in all public communication if desired.
|
|
||||||
1
_MULTIPLY
Normal file
1
_MULTIPLY
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Rename this file to "MULTIPLY" to enable multiple domain installation
|
||||||
36
_include.php
36
_include.php
|
|
@ -1,31 +1,21 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
// Name this file as "include.php" to enable it.
|
// Rename this file to "include.php" to enable it.
|
||||||
|
|
||||||
//header('Strict-Transport-Security: max-age=31536000');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uncomment to enable multiple domain installation.
|
* @return string
|
||||||
*/
|
*/
|
||||||
//define('MULTIDOMAIN', 1);
|
function __get_custom_data_full_path()
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
return '/var/external-rainloop-data-folder/'; // custom data folder path
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uncomment to disable APCU.
|
* @return string
|
||||||
*/
|
*/
|
||||||
//define('APP_USE_APCU_CACHE', false);
|
function __get_additional_configuration_name()
|
||||||
|
{
|
||||||
/**
|
return '';
|
||||||
* Custom 'data' folder path
|
return defined('APP_SITE') && 0 < strlen(APP_SITE) ? APP_SITE.'.ini' : ''; // additional configuration file name
|
||||||
*/
|
}
|
||||||
//define('APP_DATA_FOLDER_PATH', dirname(__DIR__) . '/snappymail-data/');
|
|
||||||
//define('APP_DATA_FOLDER_PATH', '/var/external-snappymail-data-folder/');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Additional configuration file name
|
|
||||||
*/
|
|
||||||
//define('APP_CONFIGURATION_NAME', $_SERVER['HTTP_HOST'].'.ini');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Also update extensions on upgrade
|
|
||||||
*/
|
|
||||||
//define('SNAPPYMAIL_UPDATE_PLUGINS', 1);
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
||||||
|
|
||||||
mDMEYg0atBYJKwYBBAHaRw8BAQdA2S2tvGavChACjtBastsKRThD3rsBW1LUZLmN
|
|
||||||
Zbs4uaG0I1NuYXBweU1haWwgPHJlbGVhc2VzQHNuYXBweW1haWwuZXU+iJQEExYK
|
|
||||||
ADwWIQQQFuRweRRVQvi6EzVIIIuhMpDz6wUCYg0atAIbAwULCQgHAgMiAgEGFQoJ
|
|
||||||
CAsCBBYCAwECHgcCF4AACgkQSCCLoTKQ8+u9SAD/Q/IoAwjUkKDJBPq0RGwCFnl6
|
|
||||||
FG/VHB97CvBSpGOxtIsBAMCwMhWlsaBHAEqbzxiN+cdlMYwV23+SWLUJ/XMFgukE
|
|
||||||
=5ZwF
|
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
# Maintainer: George Rawlinson <george@rawlinson.net.nz>
|
|
||||||
|
|
||||||
pkgname=snappymail
|
|
||||||
pkgver=2.7.2
|
|
||||||
pkgrel=1
|
|
||||||
pkgdesc="modern PHP webmail client"
|
|
||||||
arch=('any')
|
|
||||||
license=('AGPL3')
|
|
||||||
url="https://github.com/the-djmaze/snappymail"
|
|
||||||
depends=('php-fpm')
|
|
||||||
makedepends=('php', 'nodejs' 'yarn' 'gulp', 'rollup')
|
|
||||||
optdepends=('mariadb: storage backend for contacts'
|
|
||||||
'php-pgsql: storage backend for contacts'
|
|
||||||
'php-sqlite: storage backend for contacts')
|
|
||||||
source=("$pkgname-$pkgver.tar.gz::$url/archive/v$pkgver.tar.gz"
|
|
||||||
"$pkgname.sysusers"
|
|
||||||
"$pkgname.tmpfiles")
|
|
||||||
b2sums=('a877041eaa74ca9824323b0ba80b3515205628825ed76fccff1511b2547c27419bae4733d24f8f36e6860c671cb609c4c5f31fa19529a92c628cb714570ff8ae'
|
|
||||||
'e020b2d4bc694ca056f5c15b148c69553ab610b5e1789f52543aa65e098f8097a41709b5b0fc22a6a01088a9d3f14d623b1b6e9ae2570acd4f380f429301c003'
|
|
||||||
'2536e11622895322cc752c6b651811b2122d3ae60099fe609609d7b45ba1ed00ea729c23f344405078698d161dbf9bcaffabf8eff14b740acdce3c681c513318')
|
|
||||||
|
|
||||||
prepare() {
|
|
||||||
sed -i "s/\$sCustomDataPath = '';/\$sCustomDataPath = '\/var\/lib\/$pkgname';/" "$pkgname-$pkgver/$pkgname/v/0.0.0/include.php"
|
|
||||||
|
|
||||||
# create folder for build output
|
|
||||||
mkdir -p build
|
|
||||||
}
|
|
||||||
|
|
||||||
build() {
|
|
||||||
cd "$pkgname-$pkgver"
|
|
||||||
yarn install
|
|
||||||
|
|
||||||
# build snappymail
|
|
||||||
php release.php --aur
|
|
||||||
bsdtar -x \
|
|
||||||
-C "$srcdir/build" \
|
|
||||||
-f "build/dist/releases/webmail/$pkgver/$pkgname-$pkgver.zip"
|
|
||||||
find . -type d -exec chmod 755 {} \;
|
|
||||||
find . -type f -exec chmod 644 {} \;
|
|
||||||
}
|
|
||||||
|
|
||||||
package() {
|
|
||||||
# directories
|
|
||||||
install -dm755 "$pkgdir/usr/share/snappymail" \
|
|
||||||
"$pkgdir/var/lib/snappymail"
|
|
||||||
|
|
||||||
# application files
|
|
||||||
cp -r "$srcdir/build/snappymail" "$pkgdir/usr/share/snappymail"
|
|
||||||
install -Dm644 -t "$pkgdir/usr/share/snappymail" "$srcdir/build/index.php"
|
|
||||||
|
|
||||||
# data files
|
|
||||||
cp -r "$srcdir/build/data" "$pkgdir/var/lib/snappymail"
|
|
||||||
|
|
||||||
# sysusers
|
|
||||||
install -Dm644 "$srcdir/$pkgname.sysusers" "$pkgdir/usr/lib/sysusers.d/$pkgname.conf"
|
|
||||||
install -Dm644 "$srcdir/$pkgname.tmpfiles" "$pkgdir/usr/lib/tmpfiles.d/$pkgname.conf"
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
u snappymail - "Snappymail User" /var/lib/snappymail
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Z /var/lib/snappymail - snappymail snappymail
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
<?php
|
|
||||||
echo "\x1b[33;1m === cPanel === \x1b[0m\n";
|
|
||||||
|
|
||||||
$cpanel_destination = "{$destPath}snappymail-{$package->version}-cpanel.tar";
|
|
||||||
|
|
||||||
@unlink($cpanel_destination);
|
|
||||||
@unlink("{$cpanel_destination}.gz");
|
|
||||||
|
|
||||||
$cpanel_tar = new PharData($cpanel_destination);
|
|
||||||
|
|
||||||
$cpanel_tar->buildFromDirectory('./integrations/cpanel', "@integrations/cpanel/@");
|
|
||||||
|
|
||||||
$cpanel_path = '/usr/local/cpanel/base/3rdparty/snappymail/';
|
|
||||||
|
|
||||||
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('snappymail/v'), RecursiveIteratorIterator::SELF_FIRST);
|
|
||||||
foreach ($files as $file) {
|
|
||||||
if (is_file($file)) {
|
|
||||||
$newFile = str_replace('\\', '/', $file);
|
|
||||||
$cpanel_tar->addFile($file, "{$cpanel_path}{$newFile}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$cpanel_tar->addFile('.htaccess', "{$cpanel_path}.htaccess");
|
|
||||||
$cpanel_tar->addFile('README.md', "{$cpanel_path}README.md");
|
|
||||||
$cpanel_tar->addFromString("{$cpanel_path}VERSION", $package->version);
|
|
||||||
|
|
||||||
$index = file_get_contents('index.php');
|
|
||||||
$index = str_replace('0.0.0', $package->version, $index);
|
|
||||||
$cpanel_tar->addFromString("{$cpanel_path}index.php", $index);
|
|
||||||
|
|
||||||
$data = file_get_contents('dev/serviceworker.js');
|
|
||||||
$cpanel_tar->addFromString("{$cpanel_path}serviceworker.js", $data);
|
|
||||||
|
|
||||||
$cpanel_tar->compress(Phar::GZ);
|
|
||||||
unlink($cpanel_destination);
|
|
||||||
$cpanel_destination .= '.gz';
|
|
||||||
|
|
||||||
echo "{$cpanel_destination} created\n";
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
<?php
|
|
||||||
echo "\x1b[33;1m === Debian === \x1b[0m\n";
|
|
||||||
|
|
||||||
// Debian Repository
|
|
||||||
define('DEB_SOURCE_DIR', __DIR__ . '/deb');
|
|
||||||
define('DEB_DEST_DIR', DEB_SOURCE_DIR . "/snappymail_{$package->version}-1_all");
|
|
||||||
is_dir(DEB_DEST_DIR) && passthru('rm -dfr '.escapeshellarg(DEB_DEST_DIR));
|
|
||||||
|
|
||||||
$dir = DEB_DEST_DIR . '/DEBIAN';
|
|
||||||
$data = file_get_contents(DEB_SOURCE_DIR . '/DEBIAN/control');
|
|
||||||
$data = str_replace('0.0.0', $package->version, $data);
|
|
||||||
mkdir($dir, 0755, true);
|
|
||||||
file_put_contents("{$dir}/control", $data);
|
|
||||||
copy(DEB_SOURCE_DIR . '/DEBIAN/postinst', $dir . '/postinst');
|
|
||||||
chmod($dir . '/postinst', 0755);
|
|
||||||
|
|
||||||
$dir = DEB_DEST_DIR . '/var/lib/snappymail';
|
|
||||||
mkdir($dir, 0755, true);
|
|
||||||
file_put_contents($dir . '/VERSION', $package->version);
|
|
||||||
copy('data/README.md', "{$dir}/README.md");
|
|
||||||
|
|
||||||
$dir = DEB_DEST_DIR . '/usr/share/doc/snappymail';
|
|
||||||
mkdir($dir, 0755, true);
|
|
||||||
copy('CODE_OF_CONDUCT.md', "{$dir}/CODE_OF_CONDUCT.md");
|
|
||||||
copy('CONTRIBUTING.md', "{$dir}/CONTRIBUTING.md");
|
|
||||||
copy('README.md', "{$dir}/README.md");
|
|
||||||
//copy('CODE_OF_CONDUCT.md', "{$dir}/CODE_OF_CONDUCT.md");
|
|
||||||
//usr/share/doc/snappymail/README.Debian
|
|
||||||
//usr/share/doc/snappymail/changelog.Debian.gz
|
|
||||||
//usr/share/doc/snappymail/copyright
|
|
||||||
|
|
||||||
// Move files into package directory
|
|
||||||
$dir = DEB_DEST_DIR . '/usr/share/snappymail';
|
|
||||||
mkdir($dir, 0755, true);
|
|
||||||
passthru('cp -r "' . dirname(__DIR__) . '/snappymail" "' . $dir . '"');
|
|
||||||
|
|
||||||
rename("{$dir}/snappymail/v/0.0.0", "{$dir}/snappymail/v/{$package->version}");
|
|
||||||
|
|
||||||
$data = file_get_contents('index.php');
|
|
||||||
file_put_contents("{$dir}/index.php", str_replace('0.0.0', $package->version, $data));
|
|
||||||
|
|
||||||
$data = file_get_contents('_include.php');
|
|
||||||
file_put_contents("{$dir}/include.php", preg_replace('@(external-snappymail-data-folder/\'\);)@', "\$1\ndefine('APP_DATA_FOLDER_PATH', '/var/lib/snappymail/');", $data));
|
|
||||||
|
|
||||||
passthru('dpkg --build ' . escapeshellarg(DEB_DEST_DIR));
|
|
||||||
|
|
||||||
$TARGET_DIR = __DIR__ . "/dist/releases/webmail/{$package->version}/";
|
|
||||||
|
|
||||||
passthru('mv '
|
|
||||||
. escapeshellarg(DEB_DEST_DIR.'.deb') . ' '
|
|
||||||
. escapeshellarg($TARGET_DIR . basename(DEB_DEST_DIR.'.deb'))
|
|
||||||
);
|
|
||||||
|
|
||||||
passthru('rm -dfr '.escapeshellarg(DEB_DEST_DIR));
|
|
||||||
|
|
||||||
// https://github.com/the-djmaze/snappymail/issues/185#issuecomment-1059420588
|
|
||||||
$cwd = getcwd();
|
|
||||||
chdir($TARGET_DIR);
|
|
||||||
passthru('dpkg-scanpackages . /dev/null > '.escapeshellarg($TARGET_DIR . 'Packages'));
|
|
||||||
passthru('dpkg-scanpackages . /dev/null | gzip -9c > '.escapeshellarg($TARGET_DIR . 'Packages.gz'));
|
|
||||||
$size = filesize($TARGET_DIR . 'Packages');
|
|
||||||
$gz_size = filesize($TARGET_DIR . 'Packages.gz');
|
|
||||||
$Release = 'Origin: SnappyMail Repository
|
|
||||||
Label: SnappyMail
|
|
||||||
Suite: stable
|
|
||||||
Codename: stable
|
|
||||||
Version: 1.0
|
|
||||||
Architectures: all
|
|
||||||
Components: main
|
|
||||||
Description: SnappyMail repository
|
|
||||||
Date: ' . gmdate('r') . '
|
|
||||||
MD5Sum:
|
|
||||||
' . hash_file('md5', $TARGET_DIR . 'Packages') . ' ' . $size . ' Packages
|
|
||||||
' . hash_file('md5', $TARGET_DIR . 'Packages.gz') . ' ' . $gz_size . ' Packages.gz
|
|
||||||
SHA1:
|
|
||||||
' . hash_file('sha1', $TARGET_DIR . 'Packages') . ' ' . $size . ' Packages
|
|
||||||
' . hash_file('sha1', $TARGET_DIR . 'Packages.gz') . ' ' . $gz_size . ' Packages.gz
|
|
||||||
SHA256:
|
|
||||||
' . hash_file('sha256', $TARGET_DIR . 'Packages') . ' ' . $size . ' Packages
|
|
||||||
' . hash_file('sha256', $TARGET_DIR . 'Packages.gz') . ' ' . $gz_size . ' Packages.gz
|
|
||||||
';
|
|
||||||
file_put_contents($TARGET_DIR . 'Release', $Release);
|
|
||||||
chdir($cwd);
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
Package: snappymail
|
|
||||||
Version: 0.0.0
|
|
||||||
Maintainer: SnappyMail <debian@snappymail.eu>
|
|
||||||
Depends: nginx | apache2 | httpd, php-fpm | libapache2-mod-php, php-json, php-mbstring
|
|
||||||
Recommends: php-intl, php-sodium, php-uuid
|
|
||||||
Suggests: php-sqlite3 | php-mysql | php-pgsql, php-curl, php-exif, php-gnupg, php-gd | php-gmagick | php-imagick, php-openssl, php-zip
|
|
||||||
Architecture: all
|
|
||||||
Homepage: https://snappymail.eu
|
|
||||||
Vcs-Browser: https://github.com/the-djmaze/snappymail
|
|
||||||
Vcs-Git: https://github.com/the-djmaze/snappymail.git
|
|
||||||
Description: SnappyMail is a PHP-based simple, modern, lightweight & fast web-based email client with no database requirements.
|
|
||||||
It supports IMAP, SMTP and Sieve protocols, multiple accounts and identities, an admin panel for configuration.
|
|
||||||
Plugins can be installed to further extend functionality.
|
|
||||||
Emails are not stored locally, but are accessed through IMAP.
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
chown -R www-data:www-data /var/lib/snappymail
|
|
||||||
chown -R root:root /usr/share/snappymail/
|
|
||||||
76
build/langs.php
Normal file
76
build/langs.php
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
\define('IS_ADMIN', isset($_GET['admin']) && '1' === (string) $_GET['admin']);
|
||||||
|
\define('LANGS_PATH', __DIR__.'/../rainloop/v/0.0.0/langs'.(IS_ADMIN ? '/admin' : ''));
|
||||||
|
|
||||||
|
function getLangStructure($sLangFile)
|
||||||
|
{
|
||||||
|
$sEngLang = \file_get_contents(LANGS_PATH.'/'.$sLangFile);
|
||||||
|
return $sEngLang ? \parse_ini_string($sEngLang, true) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeLangStructure($aFromLang, $aEngLang, &$iCount = 0)
|
||||||
|
{
|
||||||
|
$iCount = 0;
|
||||||
|
foreach ($aEngLang as $sSectionKey => $aSectionValue)
|
||||||
|
{
|
||||||
|
foreach (\array_keys($aSectionValue) as $sParamKey)
|
||||||
|
{
|
||||||
|
if (isset($aFromLang[$sSectionKey][$sParamKey]))
|
||||||
|
{
|
||||||
|
$aEngLang[$sSectionKey][$sParamKey] = $aFromLang[$sSectionKey][$sParamKey];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo $sSectionKey.'/'.$sParamKey.','."\n";
|
||||||
|
$iCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aEngLang;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveLangStructure($sLangFile, $aLang)
|
||||||
|
{
|
||||||
|
$aResultLines = array();
|
||||||
|
// $aResultLines[] = '; '.$sLangFile;
|
||||||
|
|
||||||
|
foreach ($aLang as $sSectionKey => $aSectionValue)
|
||||||
|
{
|
||||||
|
$aResultLines[] = '';
|
||||||
|
$aResultLines[] = '['.$sSectionKey.']';
|
||||||
|
|
||||||
|
foreach ($aSectionValue as $sParamKey => $sParamValue)
|
||||||
|
{
|
||||||
|
$aResultLines[] = $sParamKey.' = "'.
|
||||||
|
\str_replace(array('\\', '"'), array('\\\\', '\\"'), \trim($sParamValue)).'"';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
\file_put_contents(LANGS_PATH.'/'.$sLangFile, implode("\n", $aResultLines)."\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
$sNL = "\n";
|
||||||
|
$aEngLang = \getLangStructure('en.ini');
|
||||||
|
|
||||||
|
$aFiles = \glob(LANGS_PATH.'/*.ini');
|
||||||
|
foreach ($aFiles as $sFile)
|
||||||
|
{
|
||||||
|
$iCount = 0;
|
||||||
|
$sFileName = \basename($sFile);
|
||||||
|
|
||||||
|
$aNextLang = \getLangStructure($sFileName);
|
||||||
|
$aNewLang = \mergeLangStructure($aNextLang, $aEngLang, $iCount);
|
||||||
|
|
||||||
|
if (0 === $iCount)
|
||||||
|
{
|
||||||
|
echo $sFileName.': ok'.$sNL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo $sFileName.': changed ('.$iCount.')'.$sNL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// \saveLangStructure($sFileName, $aNewLang);
|
||||||
|
}
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
<?php
|
|
||||||
echo "\x1b[33;1m === Nextcloud === \x1b[0m\n";
|
|
||||||
|
|
||||||
$cert_dir = $_SERVER['HOME'].'/.nextcloud/certificates';
|
|
||||||
|
|
||||||
$nc_destination = "{$destPath}snappymail-{$package->version}-nextcloud.tar";
|
|
||||||
|
|
||||||
@unlink($nc_destination);
|
|
||||||
@unlink("{$nc_destination}.gz");
|
|
||||||
|
|
||||||
$nc_tar = new PharData($nc_destination);
|
|
||||||
$hashes = [];
|
|
||||||
|
|
||||||
file_put_contents(ROOT_DIR . '/integrations/nextcloud/snappymail/VERSION', $package->version);
|
|
||||||
$file = ROOT_DIR . '/integrations/nextcloud/snappymail/appinfo/info.xml';
|
|
||||||
file_put_contents($file, preg_replace('/<version>[^<]*</', "<version>{$package->version}<", file_get_contents($file)));
|
|
||||||
|
|
||||||
$nc_tar->buildFromDirectory('./integrations/nextcloud', "@integrations/nextcloud/snappymail/@");
|
|
||||||
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('integrations/nextcloud/snappymail'));
|
|
||||||
foreach ($files as $file) {
|
|
||||||
if (is_file($file)) {
|
|
||||||
$name = str_replace('\\', '/', $file);
|
|
||||||
$name = str_replace('integrations/nextcloud/snappymail/', '', $name);
|
|
||||||
$hashes[$name] = hash_file('sha512', $file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('snappymail/v'), RecursiveIteratorIterator::SELF_FIRST);
|
|
||||||
foreach ($files as $file) {
|
|
||||||
if (is_file($file)) {
|
|
||||||
$newFile = str_replace('\\', '/', $file);
|
|
||||||
$newName = str_replace('/.htaccess', '/_htaccess', $newFile);
|
|
||||||
$nc_tar->addFile($file, "snappymail/app/{$newName}");
|
|
||||||
$hashes["app/{$newFile}"] = hash_file('sha512', $file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
$nc_tar->addFile('data/.htaccess');
|
|
||||||
$nc_tar->addFromString('data/VERSION', $package->version);
|
|
||||||
$nc_tar->addFile('data/README.md');
|
|
||||||
$nc_tar->addFile('_include.php', 'snappymail/app/_include.php');
|
|
||||||
*/
|
|
||||||
$nc_tar->addFile('.htaccess', 'snappymail/app/_htaccess');
|
|
||||||
$hashes['app/.htaccess'] = hash_file('sha512', '.htaccess');
|
|
||||||
|
|
||||||
$index = file_get_contents('index.php');
|
|
||||||
$index = str_replace('0.0.0', $package->version, $index);
|
|
||||||
//$index = str_replace('snappymail/v/', '', $index);
|
|
||||||
$nc_tar->addFromString('snappymail/app/index.php', $index);
|
|
||||||
$hashes['app/index.php'] = hash('sha512', $index);
|
|
||||||
|
|
||||||
$nc_tar->addFile('README.md', 'snappymail/app/README.md');
|
|
||||||
$hashes['app/README.md'] = hash_file('sha512', 'README.md');
|
|
||||||
|
|
||||||
$nc_tar->addFile('CHANGELOG.md', 'snappymail/CHANGELOG.md');
|
|
||||||
$hashes['CHANGELOG.md'] = hash_file('sha512', 'CHANGELOG.md');
|
|
||||||
|
|
||||||
$data = file_get_contents('dev/serviceworker.js');
|
|
||||||
$nc_tar->addFromString('snappymail/app/serviceworker.js', $data);
|
|
||||||
$hashes['app/serviceworker.js'] = hash('sha512', $data);
|
|
||||||
|
|
||||||
spl_autoload_register(function($name){
|
|
||||||
$file = __DIR__ . '/' . str_replace('\\', '/', $name) . '.php';
|
|
||||||
require $file;
|
|
||||||
});
|
|
||||||
|
|
||||||
ksort($hashes);
|
|
||||||
$cert = file_get_contents($cert_dir.'/snappymail.crt');
|
|
||||||
$rsa = new \phpseclib\Crypt\RSA();
|
|
||||||
$rsa->loadKey(file_get_contents($cert_dir.'/snappymail.key'));
|
|
||||||
$x509 = new \phpseclib\File\X509();
|
|
||||||
$x509->loadX509($cert);
|
|
||||||
$x509->setPrivateKey($rsa);
|
|
||||||
$rsa->setSignatureMode(\phpseclib\Crypt\RSA::SIGNATURE_PSS);
|
|
||||||
$rsa->setMGFHash('sha512');
|
|
||||||
$rsa->setSaltLength(0);
|
|
||||||
$signature = $rsa->sign(json_encode($hashes));
|
|
||||||
$nc_tar->addFromString('snappymail/appinfo/signature.json', json_encode([
|
|
||||||
'hashes' => $hashes,
|
|
||||||
'signature' => base64_encode($signature),
|
|
||||||
'certificate' => $cert
|
|
||||||
], JSON_PRETTY_PRINT));
|
|
||||||
|
|
||||||
$nc_tar->compress(Phar::GZ);
|
|
||||||
unlink($nc_destination);
|
|
||||||
$nc_destination .= '.gz';
|
|
||||||
|
|
||||||
$signature = shell_exec("openssl dgst -sha512 -sign {$cert_dir}/snappymail.key {$nc_destination} | openssl base64");
|
|
||||||
file_put_contents($nc_destination.'.sig', $signature);
|
|
||||||
|
|
||||||
echo "{$nc_destination} created\n";
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
<?php
|
|
||||||
echo "\x1b[33;1m === ownCloud === \x1b[0m\n";
|
|
||||||
|
|
||||||
$cert_dir = false; // $_SERVER['HOME'].'/.owncloud/certificates';
|
|
||||||
|
|
||||||
$nc_destination = "{$destPath}snappymail-{$package->version}-owncloud.tar";
|
|
||||||
|
|
||||||
@unlink($nc_destination);
|
|
||||||
@unlink("{$nc_destination}.gz");
|
|
||||||
|
|
||||||
$nc_tar = new PharData($nc_destination);
|
|
||||||
$hashes = [];
|
|
||||||
|
|
||||||
file_put_contents(ROOT_DIR . '/integrations/owncloud/snappymail/VERSION', $package->version);
|
|
||||||
$file = ROOT_DIR . '/integrations/owncloud/snappymail/appinfo/info.xml';
|
|
||||||
file_put_contents($file, preg_replace('/<version>[^<]*</', "<version>{$package->version}<", file_get_contents($file)));
|
|
||||||
|
|
||||||
$nc_tar->buildFromDirectory('./integrations/owncloud', "@integrations/owncloud/snappymail/@");
|
|
||||||
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('integrations/owncloud/snappymail'));
|
|
||||||
foreach ($files as $file) {
|
|
||||||
if (is_file($file)) {
|
|
||||||
$name = str_replace('\\', '/', $file);
|
|
||||||
$name = str_replace('integrations/owncloud/snappymail/', '', $name);
|
|
||||||
$hashes[$name] = hash_file('sha512', $file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('snappymail/v'), RecursiveIteratorIterator::SELF_FIRST);
|
|
||||||
foreach ($files as $file) {
|
|
||||||
if (is_file($file)) {
|
|
||||||
$newFile = str_replace('\\', '/', $file);
|
|
||||||
// $newFile = str_replace("'snappymail/v/'.", '', $newFile);
|
|
||||||
$nc_tar->addFile($file, "snappymail/app/{$newFile}");
|
|
||||||
$hashes["app/{$newFile}"] = hash_file('sha512', $file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$nc_tar->delete("snappymail/app/snappymail/v/{$package->version}/app/.htaccess");
|
|
||||||
$nc_tar->addFile("snappymail/v/{$package->version}/app/.htaccess", "snappymail/app/snappymail/v/{$package->version}/app/_htaccess");
|
|
||||||
$nc_tar->delete("snappymail/app/snappymail/v/{$package->version}/static/.htaccess");
|
|
||||||
$nc_tar->addFile("snappymail/v/{$package->version}/static/.htaccess", "snappymail/app/snappymail/v/{$package->version}/static/_htaccess");
|
|
||||||
|
|
||||||
/*
|
|
||||||
$nc_tar->addFile('data/.htaccess');
|
|
||||||
$nc_tar->addFromString('data/VERSION', $package->version);
|
|
||||||
$nc_tar->addFile('data/README.md');
|
|
||||||
$nc_tar->addFile('_include.php', 'snappymail/app/_include.php');
|
|
||||||
*/
|
|
||||||
$nc_tar->addFile('.htaccess', 'snappymail/app/_htaccess');
|
|
||||||
$hashes['app/.htaccess'] = hash_file('sha512', '.htaccess');
|
|
||||||
|
|
||||||
$index = file_get_contents('index.php');
|
|
||||||
$index = str_replace('0.0.0', $package->version, $index);
|
|
||||||
//$index = str_replace('snappymail/v/', '', $index);
|
|
||||||
$nc_tar->addFromString('snappymail/app/index.php', $index);
|
|
||||||
$hashes['app/index.php'] = hash('sha512', $index);
|
|
||||||
|
|
||||||
$nc_tar->addFile('README.md', 'snappymail/app/README.md');
|
|
||||||
$hashes['app/README.md'] = hash_file('sha512', 'README.md');
|
|
||||||
|
|
||||||
$nc_tar->addFile('CHANGELOG.md', 'snappymail/CHANGELOG.md');
|
|
||||||
$hashes['CHANGELOG.md'] = hash_file('sha512', 'CHANGELOG.md');
|
|
||||||
|
|
||||||
$data = file_get_contents('dev/serviceworker.js');
|
|
||||||
$nc_tar->addFromString('snappymail/app/serviceworker.js', $data);
|
|
||||||
$hashes['app/serviceworker.js'] = hash('sha512', $data);
|
|
||||||
|
|
||||||
if ($cert_dir) {
|
|
||||||
spl_autoload_register(function($name){
|
|
||||||
$file = __DIR__ . '/' . str_replace('\\', '/', $name) . '.php';
|
|
||||||
require $file;
|
|
||||||
});
|
|
||||||
|
|
||||||
ksort($hashes);
|
|
||||||
$cert = file_get_contents($cert_dir.'/snappymail.crt');
|
|
||||||
$rsa = new \phpseclib\Crypt\RSA();
|
|
||||||
$rsa->loadKey(file_get_contents($cert_dir.'/snappymail.key'));
|
|
||||||
$x509 = new \phpseclib\File\X509();
|
|
||||||
$x509->loadX509($cert);
|
|
||||||
$x509->setPrivateKey($rsa);
|
|
||||||
$rsa->setSignatureMode(\phpseclib\Crypt\RSA::SIGNATURE_PSS);
|
|
||||||
$rsa->setMGFHash('sha512');
|
|
||||||
$rsa->setSaltLength(0);
|
|
||||||
$signature = $rsa->sign(json_encode($hashes));
|
|
||||||
$nc_tar->addFromString('snappymail/appinfo/signature.json', json_encode([
|
|
||||||
'hashes' => $hashes,
|
|
||||||
'signature' => base64_encode($signature),
|
|
||||||
'certificate' => $cert
|
|
||||||
], JSON_PRETTY_PRINT));
|
|
||||||
}
|
|
||||||
|
|
||||||
$nc_tar->compress(Phar::GZ);
|
|
||||||
unlink($nc_destination);
|
|
||||||
$nc_destination .= '.gz';
|
|
||||||
|
|
||||||
if ($cert_dir) {
|
|
||||||
$signature = shell_exec("openssl dgst -sha512 -sign {$cert_dir}/snappymail.key {$nc_destination} | openssl base64");
|
|
||||||
file_put_contents($nc_destination.'.sig', $signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "{$nc_destination} created\n";
|
|
||||||
23
build/owncloud/rainloop-app/INSTALL
Executable file
23
build/owncloud/rainloop-app/INSTALL
Executable file
|
|
@ -0,0 +1,23 @@
|
||||||
|
************************************************************************
|
||||||
|
*
|
||||||
|
* ownCloud - RainLoop Webmail package
|
||||||
|
*
|
||||||
|
* @author RainLoop Team
|
||||||
|
* @copyright 2016 RainLoop Team
|
||||||
|
*
|
||||||
|
* https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud
|
||||||
|
*
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
REQUIREMENTS:
|
||||||
|
- ownCloud version 6 or higher
|
||||||
|
|
||||||
|
|
||||||
|
INSTALL:
|
||||||
|
- Unpack the RainLoop Webmail application package in the apps directory of your OwnCloud instance
|
||||||
|
|
||||||
|
|
||||||
|
CONFIGURATION:
|
||||||
|
- ownCloud:
|
||||||
|
1) In the Apps > Enable 'RainLoop' plugin
|
||||||
|
2) In the Settings > Personal > Type your mail server email (login) and password
|
||||||
1
build/owncloud/rainloop-app/VERSION
Normal file
1
build/owncloud/rainloop-app/VERSION
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
0.0
|
||||||
19
build/owncloud/rainloop-app/admin.php
Normal file
19
build/owncloud/rainloop-app/admin.php
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ownCloud - RainLoop mail plugin
|
||||||
|
*
|
||||||
|
* @author RainLoop Team
|
||||||
|
* @copyright 2016 RainLoop Team
|
||||||
|
*
|
||||||
|
* https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud
|
||||||
|
*/
|
||||||
|
|
||||||
|
OCP\User::checkAdminUser();
|
||||||
|
|
||||||
|
OCP\Util::addScript('rainloop', 'admin');
|
||||||
|
|
||||||
|
$oTemplate = new OCP\Template('rainloop', 'admin-local');
|
||||||
|
$oTemplate->assign('rainloop-admin-panel-link', OC_RainLoop_Helper::getAppUrl().'?admin');
|
||||||
|
$oTemplate->assign('rainloop-autologin', OCP\Config::getAppValue('rainloop', 'rainloop-autologin', false));
|
||||||
|
return $oTemplate->fetchPage();
|
||||||
36
build/owncloud/rainloop-app/ajax/admin.php
Normal file
36
build/owncloud/rainloop-app/ajax/admin.php
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ownCloud - RainLoop mail plugin
|
||||||
|
*
|
||||||
|
* @author RainLoop Team
|
||||||
|
* @copyright 2016 RainLoop Team
|
||||||
|
*
|
||||||
|
* https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud
|
||||||
|
*/
|
||||||
|
|
||||||
|
OCP\JSON::checkAdminUser();
|
||||||
|
OCP\JSON::checkAppEnabled('rainloop');
|
||||||
|
OCP\JSON::callCheck();
|
||||||
|
|
||||||
|
$sUrl = '';
|
||||||
|
$sPath = '';
|
||||||
|
$bAutologin = false;
|
||||||
|
|
||||||
|
if (isset($_POST['appname']) && 'rainloop' === $_POST['appname'])
|
||||||
|
{
|
||||||
|
OCP\Config::setAppValue('rainloop', 'rainloop-autologin', isset($_POST['rainloop-autologin']) ?
|
||||||
|
'1' === $_POST['rainloop-autologin'] : false);
|
||||||
|
|
||||||
|
$bAutologin = OCP\Config::getAppValue('rainloop', 'rainloop-autologin', false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sleep(1);
|
||||||
|
OC_JSON::error(array('Message' => 'Invalid Argument(s)'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(1);
|
||||||
|
OCP\JSON::success(array('Message' => 'Saved successfully'));
|
||||||
|
return true;
|
||||||
47
build/owncloud/rainloop-app/ajax/personal.php
Normal file
47
build/owncloud/rainloop-app/ajax/personal.php
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ownCloud - RainLoop mail plugin
|
||||||
|
*
|
||||||
|
* @author RainLoop Team
|
||||||
|
* @copyright 2016 RainLoop Team
|
||||||
|
*
|
||||||
|
* https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud
|
||||||
|
*/
|
||||||
|
|
||||||
|
OCP\JSON::checkLoggedIn();
|
||||||
|
OCP\JSON::checkAppEnabled('rainloop');
|
||||||
|
OCP\JSON::callCheck();
|
||||||
|
|
||||||
|
$sEmail = '';
|
||||||
|
$sLogin = '';
|
||||||
|
|
||||||
|
if (isset($_POST['appname'], $_POST['rainloop-password'], $_POST['rainloop-email']) && 'rainloop' === $_POST['appname'])
|
||||||
|
{
|
||||||
|
$sUser = OCP\User::getUser();
|
||||||
|
|
||||||
|
$sPostEmail = $_POST['rainloop-email'];
|
||||||
|
|
||||||
|
OCP\Config::setUserValue($sUser, 'rainloop', 'rainloop-email', $sPostEmail);
|
||||||
|
|
||||||
|
$sPass = $_POST['rainloop-password'];
|
||||||
|
if ('******' !== $sPass && '' !== $sPass)
|
||||||
|
{
|
||||||
|
include_once OC_App::getAppPath('rainloop').'/lib/RainLoopHelper.php';
|
||||||
|
|
||||||
|
OCP\Config::setUserValue($sUser, 'rainloop', 'rainloop-password',
|
||||||
|
OC_RainLoop_Helper::encodePassword($sPass, md5($sPostEmail)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$sEmail = OCP\Config::getUserValue($sUser, 'rainloop', 'rainloop-email', '');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sleep(1);
|
||||||
|
OC_JSON::error(array('Message' => 'Invalid argument(s)', 'Email' => $sEmail));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(1);
|
||||||
|
OCP\JSON::success(array('Message' => 'Saved successfully', 'Email' => $sEmail));
|
||||||
|
return true;
|
||||||
36
build/owncloud/rainloop-app/app.php
Normal file
36
build/owncloud/rainloop-app/app.php
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
OCP\User::checkLoggedIn();
|
||||||
|
|
||||||
|
if (@file_exists(__DIR__.'/app/index.php'))
|
||||||
|
{
|
||||||
|
include_once OC_App::getAppPath('rainloop').'/lib/RainLoopHelper.php';
|
||||||
|
|
||||||
|
OC_RainLoop_Helper::regRainLoopDataFunction();
|
||||||
|
|
||||||
|
if (isset($_GET['OwnCloudAuth']))
|
||||||
|
{
|
||||||
|
$sEmail = '';
|
||||||
|
$sEncodedPassword = '';
|
||||||
|
|
||||||
|
$sUser = OCP\User::getUser();
|
||||||
|
|
||||||
|
if (OCP\Config::getAppValue('rainloop', 'rainloop-autologin', false))
|
||||||
|
{
|
||||||
|
$sEmail = $sUser;
|
||||||
|
$sEncodedPassword = OCP\Config::getUserValue($sUser, 'rainloop', 'rainloop-autologin-password', '');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$sEmail = OCP\Config::getUserValue($sUser, 'rainloop', 'rainloop-email', '');
|
||||||
|
$sEncodedPassword = OCP\Config::getUserValue($sUser, 'rainloop', 'rainloop-password', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
$sDecodedPassword = OC_RainLoop_Helper::decodePassword($sEncodedPassword, md5($sEmail));
|
||||||
|
|
||||||
|
$_ENV['___rainloop_owncloud_email'] = $sEmail;
|
||||||
|
$_ENV['___rainloop_owncloud_password'] = $sDecodedPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
include __DIR__.'/app/index.php';
|
||||||
|
}
|
||||||
34
build/owncloud/rainloop-app/appinfo/app.php
Normal file
34
build/owncloud/rainloop-app/appinfo/app.php
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ownCloud - RainLoop mail plugin
|
||||||
|
*
|
||||||
|
* @author RainLoop Team
|
||||||
|
* @copyright 2016 RainLoop Team
|
||||||
|
*
|
||||||
|
* https://github.com/RainLoop/owncloud
|
||||||
|
*/
|
||||||
|
|
||||||
|
OC::$CLASSPATH['OC_RainLoop_Helper'] = OC_App::getAppPath('rainloop') . '/lib/RainLoopHelper.php';
|
||||||
|
|
||||||
|
OCP\App::registerAdmin('rainloop', 'admin');
|
||||||
|
OCP\App::registerPersonal('rainloop', 'personal');
|
||||||
|
|
||||||
|
if (OCP\Config::getAppValue('rainloop', 'rainloop-autologin', false))
|
||||||
|
{
|
||||||
|
OCP\Util::connectHook('OC_User', 'post_login', 'OC_RainLoop_Helper', 'login');
|
||||||
|
OCP\Util::connectHook('OC_User', 'post_setPassword', 'OC_RainLoop_Helper', 'changePassword');
|
||||||
|
}
|
||||||
|
|
||||||
|
OCP\Util::connectHook('OC_User', 'logout', 'OC_RainLoop_Helper', 'logout');
|
||||||
|
|
||||||
|
OCP\Util::addScript('rainloop', 'rainloop');
|
||||||
|
|
||||||
|
OCP\App::addNavigationEntry(array(
|
||||||
|
'id' => 'rainloop_index',
|
||||||
|
'order' => 10,
|
||||||
|
'href' => OCP\Util::linkToRoute('rainloop_index'),
|
||||||
|
'icon' => OCP\Util::imagePath('rainloop', 'mail.png'),
|
||||||
|
'name' => 'Email'
|
||||||
|
));
|
||||||
|
|
||||||
17
build/owncloud/rainloop-app/appinfo/info.xml
Normal file
17
build/owncloud/rainloop-app/appinfo/info.xml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<info>
|
||||||
|
<id>rainloop</id>
|
||||||
|
<name>RainLoop</name>
|
||||||
|
<description>Simple, modern and fast web-based email client.</description>
|
||||||
|
<version>0.0</version>
|
||||||
|
<licence></licence>
|
||||||
|
<author>RainLoop Team</author>
|
||||||
|
<require>6.0</require>
|
||||||
|
<ocsid>165254</ocsid>
|
||||||
|
<dependencies>
|
||||||
|
<php min-version="5.3"/>
|
||||||
|
</dependencies>
|
||||||
|
<category>productivity</category>
|
||||||
|
<website>http://www.rainloop.net/</website>
|
||||||
|
<bugs>https://github.com/RainLoop/rainloop-webmail/issues</bugs>
|
||||||
|
</info>
|
||||||
13
build/owncloud/rainloop-app/appinfo/routes.php
Normal file
13
build/owncloud/rainloop-app/appinfo/routes.php
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$this->create('rainloop_index', '/')
|
||||||
|
->actionInclude('rainloop/index.php');
|
||||||
|
|
||||||
|
$this->create('rainloop_app', '/app/')
|
||||||
|
->actionInclude('rainloop/app.php');
|
||||||
|
|
||||||
|
$this->create('rainloop_ajax_personal', 'ajax/personal.php')
|
||||||
|
->actionInclude('rainloop/ajax/personal.php');
|
||||||
|
|
||||||
|
$this->create('rainloop_ajax_admin', 'ajax/admin.php')
|
||||||
|
->actionInclude('rainloop/ajax/admin.php');
|
||||||
6
build/owncloud/rainloop-app/css/style.css
Normal file
6
build/owncloud/rainloop-app/css/style.css
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
/*
|
||||||
|
Empty style sheet!
|
||||||
|
Only needed to give you the opportunity to theme the owncloud part
|
||||||
|
of the rainloop app with the theming system integrated in ownCoud
|
||||||
|
if the rainloop app is activated.
|
||||||
|
*/
|
||||||
BIN
build/owncloud/rainloop-app/img/logo-64x64.png
Executable file
BIN
build/owncloud/rainloop-app/img/logo-64x64.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 832 B |
BIN
build/owncloud/rainloop-app/img/mail.png
Executable file
BIN
build/owncloud/rainloop-app/img/mail.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 478 B |
27
build/owncloud/rainloop-app/index.php
Normal file
27
build/owncloud/rainloop-app/index.php
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ownCloud - RainLoop mail plugin
|
||||||
|
*
|
||||||
|
* @author RainLoop Team
|
||||||
|
* @copyright 2016 RainLoop Team
|
||||||
|
*
|
||||||
|
* https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud
|
||||||
|
*/
|
||||||
|
|
||||||
|
OCP\User::checkLoggedIn();
|
||||||
|
OCP\App::checkAppEnabled('rainloop');
|
||||||
|
OCP\App::setActiveNavigationEntry('rainloop_index');
|
||||||
|
|
||||||
|
// Load the empty file ../css/style.css, that's needed to allow theming of
|
||||||
|
// the ownCloud header and navigation if rainloop is the active app.
|
||||||
|
OCP\Util::addStyle('rainloop', 'style');
|
||||||
|
|
||||||
|
include_once OC_App::getAppPath('rainloop').'/lib/RainLoopHelper.php';
|
||||||
|
|
||||||
|
$sUrl = OC_RainLoop_Helper::normalizeUrl(OC_RainLoop_Helper::getAppUrl());
|
||||||
|
|
||||||
|
$oTemplate = new OCP\Template('rainloop', 'index', 'user');
|
||||||
|
$oTemplate->assign('rainloop-iframe-url', OC_RainLoop_Helper::normalizeUrl($sUrl).'?OwnCloudAuth');
|
||||||
|
|
||||||
|
$oTemplate->printpage();
|
||||||
13
build/owncloud/rainloop-app/js/admin.js
Executable file
13
build/owncloud/rainloop-app/js/admin.js
Executable file
|
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ownCloud - RainLoop mail plugin
|
||||||
|
*
|
||||||
|
* @author RainLoop Team
|
||||||
|
* @copyright 2016 RainLoop Team
|
||||||
|
*
|
||||||
|
* https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud
|
||||||
|
*/
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
RainLoopFormHelper('#mail-rainloop-admin-form', 'admin.php');
|
||||||
|
});
|
||||||
13
build/owncloud/rainloop-app/js/personal.js
Executable file
13
build/owncloud/rainloop-app/js/personal.js
Executable file
|
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ownCloud - RainLoop mail plugin
|
||||||
|
*
|
||||||
|
* @author RainLoop Team
|
||||||
|
* @copyright 2016 RainLoop Team
|
||||||
|
*
|
||||||
|
* https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud
|
||||||
|
*/
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
RainLoopFormHelper('#mail-rainloop-personal-form', 'personal.php');
|
||||||
|
});
|
||||||
76
build/owncloud/rainloop-app/js/rainloop.js
Executable file
76
build/owncloud/rainloop-app/js/rainloop.js
Executable file
|
|
@ -0,0 +1,76 @@
|
||||||
|
|
||||||
|
function RainLoopFormHelper(sID, sAjaxFile, fCallback)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var
|
||||||
|
oForm = $(sID),
|
||||||
|
oSubmit = $('#rainloop-save-button', oForm),
|
||||||
|
sSubmitValue = oSubmit.val(),
|
||||||
|
oDesc = oForm.find('.rainloop-result-desc')
|
||||||
|
;
|
||||||
|
|
||||||
|
oSubmit.click(function (oEvent) {
|
||||||
|
|
||||||
|
var oDefAjax = null;
|
||||||
|
|
||||||
|
oEvent.preventDefault();
|
||||||
|
|
||||||
|
oForm
|
||||||
|
.addClass('rainloop-ajax')
|
||||||
|
.removeClass('rainloop-error')
|
||||||
|
.removeClass('rainloop-success')
|
||||||
|
;
|
||||||
|
|
||||||
|
oDesc.text('');
|
||||||
|
oSubmit.val('...');
|
||||||
|
|
||||||
|
oDefAjax = $.ajax({
|
||||||
|
'type': 'POST',
|
||||||
|
'async': true,
|
||||||
|
'url': OC.filePath('rainloop', 'ajax', sAjaxFile),
|
||||||
|
'data': oForm.serialize(),
|
||||||
|
'dataType': 'json',
|
||||||
|
'global': true
|
||||||
|
});
|
||||||
|
|
||||||
|
oDefAjax.always(function (oData) {
|
||||||
|
|
||||||
|
var bResult = false;
|
||||||
|
|
||||||
|
oForm.removeClass('rainloop-ajax');
|
||||||
|
oSubmit.val(sSubmitValue);
|
||||||
|
|
||||||
|
if (oData)
|
||||||
|
{
|
||||||
|
bResult = 'success' === oData['status'];
|
||||||
|
if (oData['Message'])
|
||||||
|
{
|
||||||
|
oDesc.text(oData['Message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bResult)
|
||||||
|
{
|
||||||
|
oForm.addClass('rainloop-success');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
oForm.addClass('rainloop-error');
|
||||||
|
if ('' === oDesc.text())
|
||||||
|
{
|
||||||
|
oDesc.text('Error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fCallback)
|
||||||
|
{
|
||||||
|
fCallback(bResult, oData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch(e) {}
|
||||||
|
}
|
||||||
26
build/owncloud/rainloop-app/js/resize.js
Executable file
26
build/owncloud/rainloop-app/js/resize.js
Executable file
|
|
@ -0,0 +1,26 @@
|
||||||
|
$(function (window, document) {
|
||||||
|
|
||||||
|
var
|
||||||
|
buffer = 5,
|
||||||
|
ifr = document.getElementById('rliframe')
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
function pageY(elem) {
|
||||||
|
return elem.offsetParent ? (elem.offsetTop + pageY(elem.offsetParent)) : elem.offsetTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resizeIframe() {
|
||||||
|
var height = document.documentElement.clientHeight;
|
||||||
|
height -= pageY(ifr) + buffer;
|
||||||
|
height = (height < 0) ? 0 : height;
|
||||||
|
ifr.style.height = height + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ifr)
|
||||||
|
{
|
||||||
|
ifr.onload = resizeIframe;
|
||||||
|
window.onresize = resizeIframe;
|
||||||
|
}
|
||||||
|
|
||||||
|
}(window, document));
|
||||||
257
build/owncloud/rainloop-app/lib/RainLoopHelper.php
Normal file
257
build/owncloud/rainloop-app/lib/RainLoopHelper.php
Normal file
|
|
@ -0,0 +1,257 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class OC_RainLoop_Helper
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function getAppUrl()
|
||||||
|
{
|
||||||
|
if (class_exists('\\OCP\\Util'))
|
||||||
|
{
|
||||||
|
return OCP\Util::linkToRoute('rainloop_app');
|
||||||
|
}
|
||||||
|
|
||||||
|
$sRequestUri = empty($_SERVER['REQUEST_URI']) ? '': trim($_SERVER['REQUEST_URI']);
|
||||||
|
$sRequestUri = preg_replace('/index.php\/.+$/', 'index.php/', $sRequestUri);
|
||||||
|
$sRequestUri = $sRequestUri.'apps/rainloop/app/';
|
||||||
|
|
||||||
|
return '/'.ltrim($sRequestUri, '/\\');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sPath
|
||||||
|
* @param string $sEmail
|
||||||
|
* @param string $sPassword
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function getSsoHash($sPath, $sEmail, $sPassword)
|
||||||
|
{
|
||||||
|
$SsoHash = '';
|
||||||
|
|
||||||
|
$sPath = rtrim(trim($sPath), '\\/').'/index.php';
|
||||||
|
if (file_exists($sPath))
|
||||||
|
{
|
||||||
|
self::regRainLoopDataFunction();
|
||||||
|
|
||||||
|
$_ENV['RAINLOOP_INCLUDE_AS_API'] = true;
|
||||||
|
include $sPath;
|
||||||
|
|
||||||
|
if (class_exists('\\RainLoop\\Api'))
|
||||||
|
{
|
||||||
|
$SsoHash = \RainLoop\Api::GetUserSsoHash($sEmail, $sPassword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SsoHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sUrl
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function normalizeUrl($sUrl)
|
||||||
|
{
|
||||||
|
$sUrl = rtrim(trim($sUrl), '/\\');
|
||||||
|
if ('.php' !== strtolower(substr($sUrl, -4)))
|
||||||
|
{
|
||||||
|
$sUrl .= '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function encodePassword($sPassword, $sSalt)
|
||||||
|
{
|
||||||
|
if (function_exists('mcrypt_encrypt') && function_exists('mcrypt_create_iv') && function_exists('mcrypt_get_iv_size') &&
|
||||||
|
defined('MCRYPT_RIJNDAEL_256') && defined('MCRYPT_MODE_ECB') && defined('MCRYPT_RAND'))
|
||||||
|
{
|
||||||
|
return @trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($sSalt), base64_encode($sPassword),
|
||||||
|
MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))));
|
||||||
|
}
|
||||||
|
|
||||||
|
return @trim(base64_encode($sPassword));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function decodePassword($sPassword, $sSalt)
|
||||||
|
{
|
||||||
|
if (function_exists('mcrypt_encrypt') && function_exists('mcrypt_create_iv') && function_exists('mcrypt_get_iv_size') &&
|
||||||
|
defined('MCRYPT_RIJNDAEL_256') && defined('MCRYPT_MODE_ECB') && defined('MCRYPT_RAND'))
|
||||||
|
{
|
||||||
|
return @base64_decode(trim(@mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($sSalt), base64_decode(trim($sPassword)),
|
||||||
|
MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))));
|
||||||
|
}
|
||||||
|
|
||||||
|
return @base64_decode(trim($sPassword));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $aParams
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function login($aParams)
|
||||||
|
{
|
||||||
|
if (isset($aParams['uid'], $aParams['password']))
|
||||||
|
{
|
||||||
|
$sUser = $aParams['uid'];
|
||||||
|
|
||||||
|
$sEmail = $sUser;
|
||||||
|
$sPassword = $aParams['password'];
|
||||||
|
|
||||||
|
return OCP\Config::setUserValue($sUser, 'rainloop', 'rainloop-autologin-password',
|
||||||
|
self::encodePassword($sPassword, md5($sEmail)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function logout()
|
||||||
|
{
|
||||||
|
OCP\Config::setUserValue(
|
||||||
|
OCP\User::getUser(), 'rainloop', 'rainloop-autologin-password', '');
|
||||||
|
|
||||||
|
$sApiPath = __DIR__.'/../app/index.php';
|
||||||
|
if (file_exists($sApiPath))
|
||||||
|
{
|
||||||
|
self::regRainLoopDataFunction();
|
||||||
|
|
||||||
|
$_ENV['RAINLOOP_INCLUDE_AS_API'] = true;
|
||||||
|
include $sApiPath;
|
||||||
|
|
||||||
|
if (class_exists('\\RainLoop\\Api'))
|
||||||
|
{
|
||||||
|
\RainLoop\Api::LogoutCurrentLogginedUser();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function changePassword($aParams)
|
||||||
|
{
|
||||||
|
if (isset($aParams['uid'], $aParams['password']))
|
||||||
|
{
|
||||||
|
$sUser = $aParams['uid'];
|
||||||
|
|
||||||
|
$sEmail = $sUser;
|
||||||
|
$sPassword = $aParams['password'];
|
||||||
|
|
||||||
|
OCP\Util::writeLog('rainloop', 'rainloop|login: Setting new RainLoop password for '.$sEmail, OCP\Util::DEBUG);
|
||||||
|
|
||||||
|
OCP\Config::setUserValue($sUser, 'rainloop', 'rainloop-autologin-password',
|
||||||
|
self::encodePassword($sPassword, md5($sEmail)));
|
||||||
|
|
||||||
|
OCP\Config::setUserValue($sUser, 'rainloop', 'rainloop-password',
|
||||||
|
self::encodePassword($sPassword, md5($sEmail)));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function regRainLoopDataFunction()
|
||||||
|
{
|
||||||
|
if (!@function_exists('__get_custom_data_full_path'))
|
||||||
|
{
|
||||||
|
$_ENV['RAINLOOP_OWNCLOUD'] = true;
|
||||||
|
|
||||||
|
function __get_custom_data_full_path()
|
||||||
|
{
|
||||||
|
$sData = __DIR__.'/../../data/';
|
||||||
|
if (class_exists('OC_Config'))
|
||||||
|
{
|
||||||
|
$sData = rtrim(trim(OC_Config::getValue('datadirectory', '')), '\\/').'/';
|
||||||
|
}
|
||||||
|
else if (class_exists('OC'))
|
||||||
|
{
|
||||||
|
$sData = rtrim(trim(OC::$server->getSystemConfig()->getValue('datadirectory', '')), '\\/').'/';
|
||||||
|
}
|
||||||
|
|
||||||
|
return @is_dir($sData) ? $sData.'rainloop-storage' : '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function mimeContentType($filename) {
|
||||||
|
|
||||||
|
$mime_types = array(
|
||||||
|
|
||||||
|
'woff' => 'application/font-woff',
|
||||||
|
|
||||||
|
'txt' => 'text/plain',
|
||||||
|
'htm' => 'text/html',
|
||||||
|
'html' => 'text/html',
|
||||||
|
'php' => 'text/html',
|
||||||
|
'css' => 'text/css',
|
||||||
|
'js' => 'application/javascript',
|
||||||
|
'json' => 'application/json',
|
||||||
|
'xml' => 'application/xml',
|
||||||
|
'swf' => 'application/x-shockwave-flash',
|
||||||
|
'flv' => 'video/x-flv',
|
||||||
|
|
||||||
|
// images
|
||||||
|
'png' => 'image/png',
|
||||||
|
'jpe' => 'image/jpeg',
|
||||||
|
'jpeg' => 'image/jpeg',
|
||||||
|
'jpg' => 'image/jpeg',
|
||||||
|
'gif' => 'image/gif',
|
||||||
|
'bmp' => 'image/bmp',
|
||||||
|
'ico' => 'image/vnd.microsoft.icon',
|
||||||
|
'tiff' => 'image/tiff',
|
||||||
|
'tif' => 'image/tiff',
|
||||||
|
'svg' => 'image/svg+xml',
|
||||||
|
'svgz' => 'image/svg+xml',
|
||||||
|
|
||||||
|
// archives
|
||||||
|
'zip' => 'application/zip',
|
||||||
|
'rar' => 'application/x-rar-compressed',
|
||||||
|
'exe' => 'application/x-msdownload',
|
||||||
|
'msi' => 'application/x-msdownload',
|
||||||
|
'cab' => 'application/vnd.ms-cab-compressed',
|
||||||
|
|
||||||
|
// audio/video
|
||||||
|
'mp3' => 'audio/mpeg',
|
||||||
|
'qt' => 'video/quicktime',
|
||||||
|
'mov' => 'video/quicktime',
|
||||||
|
|
||||||
|
// adobe
|
||||||
|
'pdf' => 'application/pdf',
|
||||||
|
'psd' => 'image/vnd.adobe.photoshop',
|
||||||
|
'ai' => 'application/postscript',
|
||||||
|
'eps' => 'application/postscript',
|
||||||
|
'ps' => 'application/postscript',
|
||||||
|
|
||||||
|
// ms office
|
||||||
|
'doc' => 'application/msword',
|
||||||
|
'rtf' => 'application/rtf',
|
||||||
|
'xls' => 'application/vnd.ms-excel',
|
||||||
|
'ppt' => 'application/vnd.ms-powerpoint',
|
||||||
|
|
||||||
|
// open office
|
||||||
|
'odt' => 'application/vnd.oasis.opendocument.text',
|
||||||
|
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (0 < strpos($filename, '.'))
|
||||||
|
{
|
||||||
|
$ext = strtolower(array_pop(explode('.',$filename)));
|
||||||
|
if (array_key_exists($ext, $mime_types))
|
||||||
|
{
|
||||||
|
return $mime_types[$ext];
|
||||||
|
}
|
||||||
|
else if (function_exists('finfo_open'))
|
||||||
|
{
|
||||||
|
$finfo = finfo_open(FILEINFO_MIME);
|
||||||
|
$mimetype = finfo_file($finfo, $filename);
|
||||||
|
finfo_close($finfo);
|
||||||
|
return $mimetype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'application/octet-stream';
|
||||||
|
}
|
||||||
|
}
|
||||||
34
build/owncloud/rainloop-app/personal.php
Normal file
34
build/owncloud/rainloop-app/personal.php
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ownCloud - RainLoop mail plugin
|
||||||
|
*
|
||||||
|
* @author RainLoop Team
|
||||||
|
* @copyright 2016 RainLoop Team
|
||||||
|
*
|
||||||
|
* https://github.com/RainLoop/rainloop-webmail/tree/master/build/owncloud
|
||||||
|
*/
|
||||||
|
|
||||||
|
OCP\User::checkLoggedIn();
|
||||||
|
OCP\App::checkAppEnabled('rainloop');
|
||||||
|
|
||||||
|
OCP\Util::addScript('rainloop', 'personal');
|
||||||
|
|
||||||
|
if (OCP\Config::getAppValue('rainloop', 'rainloop-autologin', false))
|
||||||
|
{
|
||||||
|
$oTemplate = new OCP\Template('rainloop', 'empty');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$sUser = OCP\User::getUser();
|
||||||
|
|
||||||
|
$oTemplate = new OCP\Template('rainloop', 'personal');
|
||||||
|
|
||||||
|
$sEmail = OCP\Config::getUserValue($sUser, 'rainloop', 'rainloop-email', '');
|
||||||
|
$sPass = OCP\Config::getUserValue($sUser, 'rainloop', 'rainloop-password', '');
|
||||||
|
|
||||||
|
$oTemplate->assign('rainloop-email', $sEmail);
|
||||||
|
$oTemplate->assign('rainloop-password', 0 === strlen($sPass) && 0 === strlen($sEmail) ? '' : '******');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $oTemplate->fetchPage();
|
||||||
29
build/owncloud/rainloop-app/templates/admin-local.php
Normal file
29
build/owncloud/rainloop-app/templates/admin-local.php
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<div class="section">
|
||||||
|
<form id="mail-rainloop-admin-form" action="#" method="post">
|
||||||
|
<input type="hidden" name="requesttoken" value="<?php echo $_['requesttoken'] ?>" id="requesttoken">
|
||||||
|
<input type="hidden" name="appname" value="rainloop">
|
||||||
|
|
||||||
|
<fieldset class="personalblock">
|
||||||
|
<h2><?php p($l->t('RainLoop Webmail')); ?></h2>
|
||||||
|
<br />
|
||||||
|
<?php if ($_['rainloop-admin-panel-link']): ?>
|
||||||
|
<p>
|
||||||
|
<a href="<?php echo $_['rainloop-admin-panel-link'] ?>" target="_blank" style="text-decoration: underline">
|
||||||
|
<?php p($l->t('Go to RainLoop Webmail admin panel')); ?>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<br />
|
||||||
|
<?php endif; ?>
|
||||||
|
<p>
|
||||||
|
<input type="checkbox" id="rainloop-autologin" id="rainloop-autologin" name="rainloop-autologin" value="1" <?php if ($_['rainloop-autologin']): ?>checked="checked"<?php endif; ?> />
|
||||||
|
<label for="rainloop-autologin">
|
||||||
|
<?php p($l->t('Automatically login with ownCloud user credentials')); ?>
|
||||||
|
</label>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<input type="button" id="rainloop-save-button" name="rainloop-save-button" value="<?php p($l->t('Save')); ?>" />
|
||||||
|
<span class="rainloop-result-desc"></span>
|
||||||
|
</p>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
1
build/owncloud/rainloop-app/templates/empty.php
Executable file
1
build/owncloud/rainloop-app/templates/empty.php
Executable file
|
|
@ -0,0 +1 @@
|
||||||
|
<!-- empty -->
|
||||||
3
build/owncloud/rainloop-app/templates/index-empty.php
Executable file
3
build/owncloud/rainloop-app/templates/index-empty.php
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<div style="margin: 20px">
|
||||||
|
<?php p($l->t('RainLoop Webmail is not configured yet.')); ?>
|
||||||
|
</div>
|
||||||
3
build/owncloud/rainloop-app/templates/index.php
Executable file
3
build/owncloud/rainloop-app/templates/index.php
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<div style="box-sizing: border-box; width: 100%; height: 100%; padding: 0px; margin: 0px; background-color: #383c43; position: relative; overflow: hidden;"
|
||||||
|
><iframe id="rliframe" style="border: none; width: 100%; height: 100%; position: absolute; top: 0px; left: 0px; right: 0px; bottom: 0px;" tabindex="-1" frameborder="0"
|
||||||
|
src="<?php echo $_['rainloop-iframe-url']; ?>"></iframe></div><?php OCP\Util::addScript('rainloop', 'resize');
|
||||||
20
build/owncloud/rainloop-app/templates/personal.php
Normal file
20
build/owncloud/rainloop-app/templates/personal.php
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<div class="section">
|
||||||
|
<form id="mail-rainloop-personal-form" action="#" method="post">
|
||||||
|
<input type="hidden" name="requesttoken" value="<?php echo $_['requesttoken'] ?>" id="requesttoken">
|
||||||
|
<input type="hidden" name="appname" value="rainloop">
|
||||||
|
|
||||||
|
<fieldset class="personalblock">
|
||||||
|
<h2><?php p($l->t('RainLoop Webmail')); ?></h2>
|
||||||
|
<p>
|
||||||
|
<input type="text" id="rainloop-email" name="rainloop-email"
|
||||||
|
value="<?php echo $_['rainloop-email']; ?>" placeholder="<?php p($l->t('Email')); ?>" />
|
||||||
|
|
||||||
|
<input type="password" id="rainloop-password" name="rainloop-password"
|
||||||
|
value="<?php echo $_['rainloop-password']; ?>" placeholder="<?php p($l->t('Password')); ?>" />
|
||||||
|
|
||||||
|
<input type="button" id="rainloop-save-button" name="rainloop-save-button" value="<?php p($l->t('Save')); ?>" />
|
||||||
|
<span class="rainloop-result-desc"></span>
|
||||||
|
</p>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,277 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Random Number Generator
|
|
||||||
*
|
|
||||||
* PHP version 5
|
|
||||||
*
|
|
||||||
* Here's a short example of how to use this library:
|
|
||||||
* <code>
|
|
||||||
* <?php
|
|
||||||
* include 'vendor/autoload.php';
|
|
||||||
*
|
|
||||||
* echo bin2hex(\phpseclib\Crypt\Random::string(8));
|
|
||||||
* ?>
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* @category Crypt
|
|
||||||
* @package Random
|
|
||||||
* @author Jim Wigginton <terrafrost@php.net>
|
|
||||||
* @copyright 2007 Jim Wigginton
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
|
||||||
* @link http://phpseclib.sourceforge.net
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpseclib\Crypt;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pure-PHP Random Number Generator
|
|
||||||
*
|
|
||||||
* @package Random
|
|
||||||
* @author Jim Wigginton <terrafrost@php.net>
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
class Random
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Generate a random string.
|
|
||||||
*
|
|
||||||
* Although microoptimizations are generally discouraged as they impair readability this function is ripe with
|
|
||||||
* microoptimizations because this function has the potential of being called a huge number of times.
|
|
||||||
* eg. for RSA key generation.
|
|
||||||
*
|
|
||||||
* @param int $length
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
static function string($length)
|
|
||||||
{
|
|
||||||
if (!$length) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
|
|
||||||
try {
|
|
||||||
return \random_bytes($length);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
// If a sufficient source of randomness is unavailable, random_bytes() will throw an
|
|
||||||
// object that implements the Throwable interface (Exception, TypeError, Error).
|
|
||||||
// We don't actually need to do anything here. The string() method should just continue
|
|
||||||
// as normal. Note, however, that if we don't have a sufficient source of randomness for
|
|
||||||
// random_bytes(), most of the other calls here will fail too, so we'll end up using
|
|
||||||
// the PHP implementation.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
|
|
||||||
// method 1. prior to PHP 5.3 this would call rand() on windows hence the function_exists('class_alias') call.
|
|
||||||
// ie. class_alias is a function that was introduced in PHP 5.3
|
|
||||||
if (extension_loaded('mcrypt') && function_exists('class_alias')) {
|
|
||||||
return @mcrypt_create_iv($length);
|
|
||||||
}
|
|
||||||
// method 2. openssl_random_pseudo_bytes was introduced in PHP 5.3.0 but prior to PHP 5.3.4 there was,
|
|
||||||
// to quote <http://php.net/ChangeLog-5.php#5.3.4>, "possible blocking behavior". as of 5.3.4
|
|
||||||
// openssl_random_pseudo_bytes and mcrypt_create_iv do the exact same thing on Windows. ie. they both
|
|
||||||
// call php_win32_get_random_bytes():
|
|
||||||
//
|
|
||||||
// https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/openssl/openssl.c#L5008
|
|
||||||
// https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1392
|
|
||||||
//
|
|
||||||
// php_win32_get_random_bytes() is defined thusly:
|
|
||||||
//
|
|
||||||
// https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/win32/winutil.c#L80
|
|
||||||
//
|
|
||||||
// we're calling it, all the same, in the off chance that the mcrypt extension is not available
|
|
||||||
if (extension_loaded('openssl') && version_compare(PHP_VERSION, '5.3.4', '>=')) {
|
|
||||||
return openssl_random_pseudo_bytes($length);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// method 1. the fastest
|
|
||||||
if (extension_loaded('openssl')) {
|
|
||||||
return openssl_random_pseudo_bytes($length);
|
|
||||||
}
|
|
||||||
// method 2
|
|
||||||
static $fp = true;
|
|
||||||
if ($fp === true) {
|
|
||||||
// warning's will be output unles the error suppression operator is used. errors such as
|
|
||||||
// "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc.
|
|
||||||
$fp = @fopen('/dev/urandom', 'rb');
|
|
||||||
}
|
|
||||||
if ($fp !== true && $fp !== false) { // surprisingly faster than !is_bool() or is_resource()
|
|
||||||
$temp = fread($fp, $length);
|
|
||||||
if (strlen($temp) == $length) {
|
|
||||||
return $temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// method 3. pretty much does the same thing as method 2 per the following url:
|
|
||||||
// https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391
|
|
||||||
// surprisingly slower than method 2. maybe that's because mcrypt_create_iv does a bunch of error checking that we're
|
|
||||||
// not doing. regardless, this'll only be called if this PHP script couldn't open /dev/urandom due to open_basedir
|
|
||||||
// restrictions or some such
|
|
||||||
if (extension_loaded('mcrypt')) {
|
|
||||||
return @mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// at this point we have no choice but to use a pure-PHP CSPRNG
|
|
||||||
|
|
||||||
// cascade entropy across multiple PHP instances by fixing the session and collecting all
|
|
||||||
// environmental variables, including the previous session data and the current session
|
|
||||||
// data.
|
|
||||||
//
|
|
||||||
// mt_rand seeds itself by looking at the PID and the time, both of which are (relatively)
|
|
||||||
// easy to guess at. linux uses mouse clicks, keyboard timings, etc, as entropy sources, but
|
|
||||||
// PHP isn't low level to be able to use those as sources and on a web server there's not likely
|
|
||||||
// going to be a ton of keyboard or mouse action. web servers do have one thing that we can use
|
|
||||||
// however, a ton of people visiting the website. obviously you don't want to base your seeding
|
|
||||||
// soley on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled
|
|
||||||
// by the user and (2) this isn't just looking at the data sent by the current user - it's based
|
|
||||||
// on the data sent by all users. one user requests the page and a hash of their info is saved.
|
|
||||||
// another user visits the page and the serialization of their data is utilized along with the
|
|
||||||
// server envirnment stuff and a hash of the previous http request data (which itself utilizes
|
|
||||||
// a hash of the session data before that). certainly an attacker should be assumed to have
|
|
||||||
// full control over his own http requests. he, however, is not going to have control over
|
|
||||||
// everyone's http requests.
|
|
||||||
static $crypto = false, $v;
|
|
||||||
if ($crypto === false) {
|
|
||||||
// save old session data
|
|
||||||
$old_session_id = session_id();
|
|
||||||
$old_use_cookies = ini_get('session.use_cookies');
|
|
||||||
$old_session_cache_limiter = session_cache_limiter();
|
|
||||||
$_OLD_SESSION = isset($_SESSION) ? $_SESSION : false;
|
|
||||||
if ($old_session_id != '') {
|
|
||||||
session_write_close();
|
|
||||||
}
|
|
||||||
|
|
||||||
session_id(1);
|
|
||||||
ini_set('session.use_cookies', 0);
|
|
||||||
session_cache_limiter('');
|
|
||||||
session_start();
|
|
||||||
|
|
||||||
$v = $seed = $_SESSION['seed'] = pack('H*', sha1(
|
|
||||||
(isset($_SERVER) ? phpseclib_safe_serialize($_SERVER) : '') .
|
|
||||||
(isset($_POST) ? phpseclib_safe_serialize($_POST) : '') .
|
|
||||||
(isset($_GET) ? phpseclib_safe_serialize($_GET) : '') .
|
|
||||||
(isset($_COOKIE) ? phpseclib_safe_serialize($_COOKIE) : '') .
|
|
||||||
phpseclib_safe_serialize($GLOBALS) .
|
|
||||||
phpseclib_safe_serialize($_SESSION) .
|
|
||||||
phpseclib_safe_serialize($_OLD_SESSION)
|
|
||||||
));
|
|
||||||
if (!isset($_SESSION['count'])) {
|
|
||||||
$_SESSION['count'] = 0;
|
|
||||||
}
|
|
||||||
$_SESSION['count']++;
|
|
||||||
|
|
||||||
session_write_close();
|
|
||||||
|
|
||||||
// restore old session data
|
|
||||||
if ($old_session_id != '') {
|
|
||||||
session_id($old_session_id);
|
|
||||||
session_start();
|
|
||||||
ini_set('session.use_cookies', $old_use_cookies);
|
|
||||||
session_cache_limiter($old_session_cache_limiter);
|
|
||||||
} else {
|
|
||||||
if ($_OLD_SESSION !== false) {
|
|
||||||
$_SESSION = $_OLD_SESSION;
|
|
||||||
unset($_OLD_SESSION);
|
|
||||||
} else {
|
|
||||||
unset($_SESSION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// in SSH2 a shared secret and an exchange hash are generated through the key exchange process.
|
|
||||||
// the IV client to server is the hash of that "nonce" with the letter A and for the encryption key it's the letter C.
|
|
||||||
// if the hash doesn't produce enough a key or an IV that's long enough concat successive hashes of the
|
|
||||||
// original hash and the current hash. we'll be emulating that. for more info see the following URL:
|
|
||||||
//
|
|
||||||
// http://tools.ietf.org/html/rfc4253#section-7.2
|
|
||||||
//
|
|
||||||
// see the is_string($crypto) part for an example of how to expand the keys
|
|
||||||
$key = pack('H*', sha1($seed . 'A'));
|
|
||||||
$iv = pack('H*', sha1($seed . 'C'));
|
|
||||||
|
|
||||||
// ciphers are used as per the nist.gov link below. also, see this link:
|
|
||||||
//
|
|
||||||
// http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives
|
|
||||||
switch (true) {
|
|
||||||
case class_exists('\phpseclib\Crypt\AES'):
|
|
||||||
$crypto = new AES(Base::MODE_CTR);
|
|
||||||
break;
|
|
||||||
case class_exists('\phpseclib\Crypt\Twofish'):
|
|
||||||
$crypto = new Twofish(Base::MODE_CTR);
|
|
||||||
break;
|
|
||||||
case class_exists('\phpseclib\Crypt\Blowfish'):
|
|
||||||
$crypto = new Blowfish(Base::MODE_CTR);
|
|
||||||
break;
|
|
||||||
case class_exists('\phpseclib\Crypt\TripleDES'):
|
|
||||||
$crypto = new TripleDES(Base::MODE_CTR);
|
|
||||||
break;
|
|
||||||
case class_exists('\phpseclib\Crypt\DES'):
|
|
||||||
$crypto = new DES(Base::MODE_CTR);
|
|
||||||
break;
|
|
||||||
case class_exists('\phpseclib\Crypt\RC4'):
|
|
||||||
$crypto = new RC4();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
user_error(__CLASS__ . ' requires at least one symmetric cipher be loaded');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$crypto->setKey($key);
|
|
||||||
$crypto->setIV($iv);
|
|
||||||
$crypto->enableContinuousBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
//return $crypto->encrypt(str_repeat("\0", $length));
|
|
||||||
|
|
||||||
// the following is based off of ANSI X9.31:
|
|
||||||
//
|
|
||||||
// http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf
|
|
||||||
//
|
|
||||||
// OpenSSL uses that same standard for it's random numbers:
|
|
||||||
//
|
|
||||||
// http://www.opensource.apple.com/source/OpenSSL/OpenSSL-38/openssl/fips-1.0/rand/fips_rand.c
|
|
||||||
// (do a search for "ANS X9.31 A.2.4")
|
|
||||||
$result = '';
|
|
||||||
while (strlen($result) < $length) {
|
|
||||||
$i = $crypto->encrypt(microtime()); // strlen(microtime()) == 21
|
|
||||||
$r = $crypto->encrypt($i ^ $v); // strlen($v) == 20
|
|
||||||
$v = $crypto->encrypt($r ^ $i); // strlen($r) == 20
|
|
||||||
$result.= $r;
|
|
||||||
}
|
|
||||||
return substr($result, 0, $length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('phpseclib_safe_serialize')) {
|
|
||||||
/**
|
|
||||||
* Safely serialize variables
|
|
||||||
*
|
|
||||||
* If a class has a private __sleep() method it'll give a fatal error on PHP 5.2 and earlier.
|
|
||||||
* PHP 5.3 will emit a warning.
|
|
||||||
*
|
|
||||||
* @param mixed $arr
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function phpseclib_safe_serialize(&$arr)
|
|
||||||
{
|
|
||||||
if (is_object($arr)) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
if (!is_array($arr)) {
|
|
||||||
return serialize($arr);
|
|
||||||
}
|
|
||||||
// prevent circular array recursion
|
|
||||||
if (isset($arr['__phpseclib_marker'])) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
$safearr = array();
|
|
||||||
$arr['__phpseclib_marker'] = true;
|
|
||||||
foreach (array_keys($arr) as $key) {
|
|
||||||
// do not recurse on the '__phpseclib_marker' key itself, for smaller memory usage
|
|
||||||
if ($key !== '__phpseclib_marker') {
|
|
||||||
$safearr[$key] = phpseclib_safe_serialize($arr[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unset($arr['__phpseclib_marker']);
|
|
||||||
return serialize($safearr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,577 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pure-PHP ANSI Decoder
|
|
||||||
*
|
|
||||||
* PHP version 5
|
|
||||||
*
|
|
||||||
* If you call read() in \phpseclib\Net\SSH2 you may get {@link http://en.wikipedia.org/wiki/ANSI_escape_code ANSI escape codes} back.
|
|
||||||
* They'd look like chr(0x1B) . '[00m' or whatever (0x1B = ESC). They tell a
|
|
||||||
* {@link http://en.wikipedia.org/wiki/Terminal_emulator terminal emulator} how to format the characters, what
|
|
||||||
* color to display them in, etc. \phpseclib\File\ANSI is a {@link http://en.wikipedia.org/wiki/VT100 VT100} terminal emulator.
|
|
||||||
*
|
|
||||||
* @category File
|
|
||||||
* @package ANSI
|
|
||||||
* @author Jim Wigginton <terrafrost@php.net>
|
|
||||||
* @copyright 2012 Jim Wigginton
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
|
||||||
* @link http://phpseclib.sourceforge.net
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpseclib\File;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pure-PHP ANSI Decoder
|
|
||||||
*
|
|
||||||
* @package ANSI
|
|
||||||
* @author Jim Wigginton <terrafrost@php.net>
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
class ANSI
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Max Width
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $max_x;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Max Height
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $max_y;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Max History
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $max_history;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* History
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $history;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* History Attributes
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $history_attrs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current Column
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $x;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current Row
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $y;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Old Column
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $old_x;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Old Row
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $old_y;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An empty attribute cell
|
|
||||||
*
|
|
||||||
* @var object
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $base_attr_cell;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current attribute cell
|
|
||||||
*
|
|
||||||
* @var object
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $attr_cell;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An empty attribute row
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $attr_row;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current screen text
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $screen;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current screen attributes
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $attrs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current ANSI code
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $ansi;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tokenization
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $tokenization;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default Constructor.
|
|
||||||
*
|
|
||||||
* @return \phpseclib\File\ANSI
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function __construct()
|
|
||||||
{
|
|
||||||
$attr_cell = new \stdClass();
|
|
||||||
$attr_cell->bold = false;
|
|
||||||
$attr_cell->underline = false;
|
|
||||||
$attr_cell->blink = false;
|
|
||||||
$attr_cell->background = 'black';
|
|
||||||
$attr_cell->foreground = 'white';
|
|
||||||
$attr_cell->reverse = false;
|
|
||||||
$this->base_attr_cell = clone $attr_cell;
|
|
||||||
$this->attr_cell = clone $attr_cell;
|
|
||||||
|
|
||||||
$this->setHistory(200);
|
|
||||||
$this->setDimensions(80, 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set terminal width and height
|
|
||||||
*
|
|
||||||
* Resets the screen as well
|
|
||||||
*
|
|
||||||
* @param int $x
|
|
||||||
* @param int $y
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function setDimensions($x, $y)
|
|
||||||
{
|
|
||||||
$this->max_x = $x - 1;
|
|
||||||
$this->max_y = $y - 1;
|
|
||||||
$this->x = $this->y = 0;
|
|
||||||
$this->history = $this->history_attrs = array();
|
|
||||||
$this->attr_row = array_fill(0, $this->max_x + 2, $this->base_attr_cell);
|
|
||||||
$this->screen = array_fill(0, $this->max_y + 1, '');
|
|
||||||
$this->attrs = array_fill(0, $this->max_y + 1, $this->attr_row);
|
|
||||||
$this->ansi = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the number of lines that should be logged past the terminal height
|
|
||||||
*
|
|
||||||
* @param int $history
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function setHistory($history)
|
|
||||||
{
|
|
||||||
$this->max_history = $history;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load a string
|
|
||||||
*
|
|
||||||
* @param string $source
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function loadString($source)
|
|
||||||
{
|
|
||||||
$this->setDimensions($this->max_x + 1, $this->max_y + 1);
|
|
||||||
$this->appendString($source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appdend a string
|
|
||||||
*
|
|
||||||
* @param string $source
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function appendString($source)
|
|
||||||
{
|
|
||||||
$this->tokenization = array('');
|
|
||||||
for ($i = 0; $i < strlen($source); $i++) {
|
|
||||||
if (strlen($this->ansi)) {
|
|
||||||
$this->ansi.= $source[$i];
|
|
||||||
$chr = ord($source[$i]);
|
|
||||||
// http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements
|
|
||||||
// single character CSI's not currently supported
|
|
||||||
switch (true) {
|
|
||||||
case $this->ansi == "\x1B=":
|
|
||||||
$this->ansi = '';
|
|
||||||
continue 2;
|
|
||||||
case strlen($this->ansi) == 2 && $chr >= 64 && $chr <= 95 && $chr != ord('['):
|
|
||||||
case strlen($this->ansi) > 2 && $chr >= 64 && $chr <= 126:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
continue 2;
|
|
||||||
}
|
|
||||||
$this->tokenization[] = $this->ansi;
|
|
||||||
$this->tokenization[] = '';
|
|
||||||
// http://ascii-table.com/ansi-escape-sequences-vt-100.php
|
|
||||||
switch ($this->ansi) {
|
|
||||||
case "\x1B[H": // Move cursor to upper left corner
|
|
||||||
$this->old_x = $this->x;
|
|
||||||
$this->old_y = $this->y;
|
|
||||||
$this->x = $this->y = 0;
|
|
||||||
break;
|
|
||||||
case "\x1B[J": // Clear screen from cursor down
|
|
||||||
$this->history = array_merge($this->history, array_slice(array_splice($this->screen, $this->y + 1), 0, $this->old_y));
|
|
||||||
$this->screen = array_merge($this->screen, array_fill($this->y, $this->max_y, ''));
|
|
||||||
|
|
||||||
$this->history_attrs = array_merge($this->history_attrs, array_slice(array_splice($this->attrs, $this->y + 1), 0, $this->old_y));
|
|
||||||
$this->attrs = array_merge($this->attrs, array_fill($this->y, $this->max_y, $this->attr_row));
|
|
||||||
|
|
||||||
if (count($this->history) == $this->max_history) {
|
|
||||||
array_shift($this->history);
|
|
||||||
array_shift($this->history_attrs);
|
|
||||||
}
|
|
||||||
case "\x1B[K": // Clear screen from cursor right
|
|
||||||
$this->screen[$this->y] = substr($this->screen[$this->y], 0, $this->x);
|
|
||||||
|
|
||||||
array_splice($this->attrs[$this->y], $this->x + 1, $this->max_x - $this->x, array_fill($this->x, $this->max_x - ($this->x - 1), $this->base_attr_cell));
|
|
||||||
break;
|
|
||||||
case "\x1B[2K": // Clear entire line
|
|
||||||
$this->screen[$this->y] = str_repeat(' ', $this->x);
|
|
||||||
$this->attrs[$this->y] = $this->attr_row;
|
|
||||||
break;
|
|
||||||
case "\x1B[?1h": // set cursor key to application
|
|
||||||
case "\x1B[?25h": // show the cursor
|
|
||||||
case "\x1B(B": // set united states g0 character set
|
|
||||||
break;
|
|
||||||
case "\x1BE": // Move to next line
|
|
||||||
$this->_newLine();
|
|
||||||
$this->x = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
switch (true) {
|
|
||||||
case preg_match('#\x1B\[(\d+)B#', $this->ansi, $match): // Move cursor down n lines
|
|
||||||
$this->old_y = $this->y;
|
|
||||||
$this->y+= $match[1];
|
|
||||||
break;
|
|
||||||
case preg_match('#\x1B\[(\d+);(\d+)H#', $this->ansi, $match): // Move cursor to screen location v,h
|
|
||||||
$this->old_x = $this->x;
|
|
||||||
$this->old_y = $this->y;
|
|
||||||
$this->x = $match[2] - 1;
|
|
||||||
$this->y = $match[1] - 1;
|
|
||||||
break;
|
|
||||||
case preg_match('#\x1B\[(\d+)C#', $this->ansi, $match): // Move cursor right n lines
|
|
||||||
$this->old_x = $this->x;
|
|
||||||
$this->x+= $match[1];
|
|
||||||
break;
|
|
||||||
case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): // Move cursor left n lines
|
|
||||||
$this->old_x = $this->x;
|
|
||||||
$this->x-= $match[1];
|
|
||||||
if ($this->x < 0) {
|
|
||||||
$this->x = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window
|
|
||||||
break;
|
|
||||||
case preg_match('#\x1B\[(\d*(?:;\d*)*)m#', $this->ansi, $match): // character attributes
|
|
||||||
$attr_cell = &$this->attr_cell;
|
|
||||||
$mods = explode(';', $match[1]);
|
|
||||||
foreach ($mods as $mod) {
|
|
||||||
switch ($mod) {
|
|
||||||
case '':
|
|
||||||
case '0': // Turn off character attributes
|
|
||||||
$attr_cell = clone $this->base_attr_cell;
|
|
||||||
break;
|
|
||||||
case '1': // Turn bold mode on
|
|
||||||
$attr_cell->bold = true;
|
|
||||||
break;
|
|
||||||
case '4': // Turn underline mode on
|
|
||||||
$attr_cell->underline = true;
|
|
||||||
break;
|
|
||||||
case '5': // Turn blinking mode on
|
|
||||||
$attr_cell->blink = true;
|
|
||||||
break;
|
|
||||||
case '7': // Turn reverse video on
|
|
||||||
$attr_cell->reverse = !$attr_cell->reverse;
|
|
||||||
$temp = $attr_cell->background;
|
|
||||||
$attr_cell->background = $attr_cell->foreground;
|
|
||||||
$attr_cell->foreground = $temp;
|
|
||||||
break;
|
|
||||||
default: // set colors
|
|
||||||
//$front = $attr_cell->reverse ? &$attr_cell->background : &$attr_cell->foreground;
|
|
||||||
$front = &$attr_cell->{ $attr_cell->reverse ? 'background' : 'foreground' };
|
|
||||||
//$back = $attr_cell->reverse ? &$attr_cell->foreground : &$attr_cell->background;
|
|
||||||
$back = &$attr_cell->{ $attr_cell->reverse ? 'foreground' : 'background' };
|
|
||||||
switch ($mod) {
|
|
||||||
// @codingStandardsIgnoreStart
|
|
||||||
case '30': $front = 'black'; break;
|
|
||||||
case '31': $front = 'red'; break;
|
|
||||||
case '32': $front = 'green'; break;
|
|
||||||
case '33': $front = 'yellow'; break;
|
|
||||||
case '34': $front = 'blue'; break;
|
|
||||||
case '35': $front = 'magenta'; break;
|
|
||||||
case '36': $front = 'cyan'; break;
|
|
||||||
case '37': $front = 'white'; break;
|
|
||||||
|
|
||||||
case '40': $back = 'black'; break;
|
|
||||||
case '41': $back = 'red'; break;
|
|
||||||
case '42': $back = 'green'; break;
|
|
||||||
case '43': $back = 'yellow'; break;
|
|
||||||
case '44': $back = 'blue'; break;
|
|
||||||
case '45': $back = 'magenta'; break;
|
|
||||||
case '46': $back = 'cyan'; break;
|
|
||||||
case '47': $back = 'white'; break;
|
|
||||||
// @codingStandardsIgnoreEnd
|
|
||||||
|
|
||||||
default:
|
|
||||||
//user_error('Unsupported attribute: ' . $mod);
|
|
||||||
$this->ansi = '';
|
|
||||||
break 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
//user_error("{$this->ansi} is unsupported\r\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->ansi = '';
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->tokenization[count($this->tokenization) - 1].= $source[$i];
|
|
||||||
switch ($source[$i]) {
|
|
||||||
case "\r":
|
|
||||||
$this->x = 0;
|
|
||||||
break;
|
|
||||||
case "\n":
|
|
||||||
$this->_newLine();
|
|
||||||
break;
|
|
||||||
case "\x08": // backspace
|
|
||||||
if ($this->x) {
|
|
||||||
$this->x--;
|
|
||||||
$this->attrs[$this->y][$this->x] = clone $this->base_attr_cell;
|
|
||||||
$this->screen[$this->y] = substr_replace(
|
|
||||||
$this->screen[$this->y],
|
|
||||||
$source[$i],
|
|
||||||
$this->x,
|
|
||||||
1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "\x0F": // shift
|
|
||||||
break;
|
|
||||||
case "\x1B": // start ANSI escape code
|
|
||||||
$this->tokenization[count($this->tokenization) - 1] = substr($this->tokenization[count($this->tokenization) - 1], 0, -1);
|
|
||||||
//if (!strlen($this->tokenization[count($this->tokenization) - 1])) {
|
|
||||||
// array_pop($this->tokenization);
|
|
||||||
//}
|
|
||||||
$this->ansi.= "\x1B";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$this->attrs[$this->y][$this->x] = clone $this->attr_cell;
|
|
||||||
if ($this->x > strlen($this->screen[$this->y])) {
|
|
||||||
$this->screen[$this->y] = str_repeat(' ', $this->x);
|
|
||||||
}
|
|
||||||
$this->screen[$this->y] = substr_replace(
|
|
||||||
$this->screen[$this->y],
|
|
||||||
$source[$i],
|
|
||||||
$this->x,
|
|
||||||
1
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($this->x > $this->max_x) {
|
|
||||||
$this->x = 0;
|
|
||||||
$this->_newLine();
|
|
||||||
} else {
|
|
||||||
$this->x++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a new line
|
|
||||||
*
|
|
||||||
* Also update the $this->screen and $this->history buffers
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
function _newLine()
|
|
||||||
{
|
|
||||||
//if ($this->y < $this->max_y) {
|
|
||||||
// $this->y++;
|
|
||||||
//}
|
|
||||||
|
|
||||||
while ($this->y >= $this->max_y) {
|
|
||||||
$this->history = array_merge($this->history, array(array_shift($this->screen)));
|
|
||||||
$this->screen[] = '';
|
|
||||||
|
|
||||||
$this->history_attrs = array_merge($this->history_attrs, array(array_shift($this->attrs)));
|
|
||||||
$this->attrs[] = $this->attr_row;
|
|
||||||
|
|
||||||
if (count($this->history) >= $this->max_history) {
|
|
||||||
array_shift($this->history);
|
|
||||||
array_shift($this->history_attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->y--;
|
|
||||||
}
|
|
||||||
$this->y++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current coordinate without preformating
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function _processCoordinate($last_attr, $cur_attr, $char)
|
|
||||||
{
|
|
||||||
$output = '';
|
|
||||||
|
|
||||||
if ($last_attr != $cur_attr) {
|
|
||||||
$close = $open = '';
|
|
||||||
if ($last_attr->foreground != $cur_attr->foreground) {
|
|
||||||
if ($cur_attr->foreground != 'white') {
|
|
||||||
$open.= '<span style="color: ' . $cur_attr->foreground . '">';
|
|
||||||
}
|
|
||||||
if ($last_attr->foreground != 'white') {
|
|
||||||
$close = '</span>' . $close;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($last_attr->background != $cur_attr->background) {
|
|
||||||
if ($cur_attr->background != 'black') {
|
|
||||||
$open.= '<span style="background: ' . $cur_attr->background . '">';
|
|
||||||
}
|
|
||||||
if ($last_attr->background != 'black') {
|
|
||||||
$close = '</span>' . $close;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($last_attr->bold != $cur_attr->bold) {
|
|
||||||
if ($cur_attr->bold) {
|
|
||||||
$open.= '<b>';
|
|
||||||
} else {
|
|
||||||
$close = '</b>' . $close;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($last_attr->underline != $cur_attr->underline) {
|
|
||||||
if ($cur_attr->underline) {
|
|
||||||
$open.= '<u>';
|
|
||||||
} else {
|
|
||||||
$close = '</u>' . $close;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($last_attr->blink != $cur_attr->blink) {
|
|
||||||
if ($cur_attr->blink) {
|
|
||||||
$open.= '<blink>';
|
|
||||||
} else {
|
|
||||||
$close = '</blink>' . $close;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$output.= $close . $open;
|
|
||||||
}
|
|
||||||
|
|
||||||
$output.= htmlspecialchars($char);
|
|
||||||
|
|
||||||
return $output;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current screen without preformating
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function _getScreen()
|
|
||||||
{
|
|
||||||
$output = '';
|
|
||||||
$last_attr = $this->base_attr_cell;
|
|
||||||
for ($i = 0; $i <= $this->max_y; $i++) {
|
|
||||||
for ($j = 0; $j <= $this->max_x; $j++) {
|
|
||||||
$cur_attr = $this->attrs[$i][$j];
|
|
||||||
$output.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->screen[$i][$j]) ? $this->screen[$i][$j] : '');
|
|
||||||
$last_attr = $this->attrs[$i][$j];
|
|
||||||
}
|
|
||||||
$output.= "\r\n";
|
|
||||||
}
|
|
||||||
$output = substr($output, 0, -2);
|
|
||||||
// close any remaining open tags
|
|
||||||
$output.= $this->_processCoordinate($last_attr, $this->base_attr_cell, '');
|
|
||||||
return rtrim($output);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current screen
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function getScreen()
|
|
||||||
{
|
|
||||||
return '<pre width="' . ($this->max_x + 1) . '" style="color: white; background: black">' . $this->_getScreen() . '</pre>';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current screen and the x previous lines
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
function getHistory()
|
|
||||||
{
|
|
||||||
$scrollback = '';
|
|
||||||
$last_attr = $this->base_attr_cell;
|
|
||||||
for ($i = 0; $i < count($this->history); $i++) {
|
|
||||||
for ($j = 0; $j <= $this->max_x + 1; $j++) {
|
|
||||||
$cur_attr = $this->history_attrs[$i][$j];
|
|
||||||
$scrollback.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->history[$i][$j]) ? $this->history[$i][$j] : '');
|
|
||||||
$last_attr = $this->history_attrs[$i][$j];
|
|
||||||
}
|
|
||||||
$scrollback.= "\r\n";
|
|
||||||
}
|
|
||||||
$base_attr_cell = $this->base_attr_cell;
|
|
||||||
$this->base_attr_cell = $last_attr;
|
|
||||||
$scrollback.= $this->_getScreen();
|
|
||||||
$this->base_attr_cell = $base_attr_cell;
|
|
||||||
|
|
||||||
return '<pre width="' . ($this->max_x + 1) . '" style="color: white; background: black">' . $scrollback . '</span></pre>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,47 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Pure-PHP ASN.1 Parser
|
|
||||||
*
|
|
||||||
* PHP version 5
|
|
||||||
*
|
|
||||||
* @category File
|
|
||||||
* @package ASN1
|
|
||||||
* @author Jim Wigginton <terrafrost@php.net>
|
|
||||||
* @copyright 2012 Jim Wigginton
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
|
||||||
* @link http://phpseclib.sourceforge.net
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpseclib\File\ASN1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ASN.1 Element
|
|
||||||
*
|
|
||||||
* Bypass normal encoding rules in phpseclib\File\ASN1::encodeDER()
|
|
||||||
*
|
|
||||||
* @package ASN1
|
|
||||||
* @author Jim Wigginton <terrafrost@php.net>
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
class Element
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Raw element value
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $element;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param string $encoded
|
|
||||||
* @return \phpseclib\File\ASN1\Element
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function __construct($encoded)
|
|
||||||
{
|
|
||||||
$this->element = $encoded;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
122
build/plugin.xml
Normal file
122
build/plugin.xml
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
<project name="Build" basedir=".">
|
||||||
|
|
||||||
|
<property name="INDEX_ROOT" value=".." />
|
||||||
|
<property name="DIST_PATH" value="dist" />
|
||||||
|
<property name="DIST_PLUGINS_PATH" value="dist/plugins" />
|
||||||
|
<property name="GUID" value="tmp" />
|
||||||
|
|
||||||
|
<target name="_pre_">
|
||||||
|
<mkdir dir="${DIST_PATH}" />
|
||||||
|
<mkdir dir="${DIST_PLUGINS_PATH}" />
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="_build_plugin_" depends="_pre_">
|
||||||
|
|
||||||
|
<loadfile property="plugin-version" srcfile="${INDEX_ROOT}/plugins/${plugin-name}/VERSION" />
|
||||||
|
|
||||||
|
<mkdir dir="${DIST_PLUGINS_PATH}/${plugin-name}-${plugin-version}-${GUID}" />
|
||||||
|
<mkdir dir="${DIST_PLUGINS_PATH}/${plugin-name}-${plugin-version}-${GUID}/${plugin-name}" />
|
||||||
|
|
||||||
|
<copy todir="${DIST_PLUGINS_PATH}/${plugin-name}-${plugin-version}-${GUID}/${plugin-name}">
|
||||||
|
<fileset dir="${INDEX_ROOT}/plugins/${plugin-name}" />
|
||||||
|
</copy>
|
||||||
|
|
||||||
|
<zip destfile="${DIST_PLUGINS_PATH}/${plugin-name}-${plugin-version}.zip"
|
||||||
|
basedir="${DIST_PLUGINS_PATH}/${plugin-name}-${plugin-version}-${GUID}" update="true" />
|
||||||
|
|
||||||
|
<delete dir="${DIST_PLUGINS_PATH}/${plugin-name}-${plugin-version}-${GUID}" />
|
||||||
|
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="add-x-originating-ip-header">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="add-x-originating-ip-header"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="google-analytics">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="google-analytics"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="piwik-analytics">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="piwik-analytics"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="convert-headers-styles">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="convert-headers-styles"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="recaptcha">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="recaptcha"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="cpanel-change-password">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="cpanel-change-password"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="directadmin-change-password">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="directadmin-change-password"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="ispconfig-change-password">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="ispconfig-change-password"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="poppassd-change-password">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="poppassd-change-password"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="hmailserver-change-password">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="hmailserver-change-password"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="ldap-change-password">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="ldap-change-password"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="ldap-contacts-suggestions">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="ldap-contacts-suggestions"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="snowfall-on-login-screen">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="snowfall-on-login-screen"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="black-list">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="black-list"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="white-list">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="white-list"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="override-smtp-credentials">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="override-smtp-credentials"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="custom-login-mapping">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="custom-login-mapping"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
<target name="video-on-login-screen">
|
||||||
|
<antcall target="_build_plugin_">
|
||||||
|
<param name="plugin-name" value="video-on-login-screen"/>
|
||||||
|
</antcall>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -1,144 +0,0 @@
|
||||||
<?php
|
|
||||||
define('PLUGINS_DEST_DIR', __DIR__ . '/dist/releases/plugins');
|
|
||||||
|
|
||||||
is_dir(PLUGINS_DEST_DIR) || mkdir(PLUGINS_DEST_DIR, 0777, true);
|
|
||||||
$files = new RecursiveIteratorIterator(
|
|
||||||
new RecursiveDirectoryIterator(PLUGINS_DEST_DIR, RecursiveDirectoryIterator::SKIP_DOTS),
|
|
||||||
RecursiveIteratorIterator::CHILD_FIRST
|
|
||||||
);
|
|
||||||
foreach ($files as $fileinfo) {
|
|
||||||
$fileinfo->isDir() || unlink($fileinfo->getRealPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
$terser = ROOT_DIR . '/node_modules/terser/bin/terser';
|
|
||||||
|
|
||||||
$manifest = [];
|
|
||||||
require ROOT_DIR . '/snappymail/v/0.0.0/app/libraries/RainLoop/Plugins/AbstractPlugin.php';
|
|
||||||
$keys = [
|
|
||||||
'author',
|
|
||||||
'category',
|
|
||||||
'description',
|
|
||||||
'file',
|
|
||||||
'id',
|
|
||||||
'license',
|
|
||||||
'name',
|
|
||||||
'release',
|
|
||||||
'required',
|
|
||||||
'type',
|
|
||||||
'url',
|
|
||||||
'version'
|
|
||||||
];
|
|
||||||
/*
|
|
||||||
$released = [
|
|
||||||
'add-x-originating-ip-header',
|
|
||||||
'avatars',
|
|
||||||
'backup',
|
|
||||||
'black-list',
|
|
||||||
'change-password',
|
|
||||||
'change-password-froxlor',
|
|
||||||
'change-password-hestia',
|
|
||||||
'change-password-hmailserver',
|
|
||||||
'change-password-ispconfig',
|
|
||||||
'change-password-poppassd',
|
|
||||||
'custom-login-mapping',
|
|
||||||
'imap-contacts-suggestions',
|
|
||||||
'kolab',
|
|
||||||
'ldap-contacts-suggestions',
|
|
||||||
'ldap-identities',
|
|
||||||
'ldap-login-mapping',
|
|
||||||
'ldap-mail-accounts',
|
|
||||||
'login-external',
|
|
||||||
'login-external-sso',
|
|
||||||
'login-override',
|
|
||||||
'login-register',
|
|
||||||
'login-remote',
|
|
||||||
'mailbox-detect',
|
|
||||||
'nextcloud',
|
|
||||||
'override-smtp-credentials',
|
|
||||||
'set-remote-addr',
|
|
||||||
'smtp-use-from-adr-account',
|
|
||||||
'snowfall-on-login-screen',
|
|
||||||
'two-factor-auth',
|
|
||||||
'view-ics',
|
|
||||||
'white-list'
|
|
||||||
];
|
|
||||||
*/
|
|
||||||
foreach (glob(ROOT_DIR . '/plugins/*', GLOB_NOSORT | GLOB_ONLYDIR) as $dir) {
|
|
||||||
if (is_file("{$dir}/index.php") && !strpos($dir, '.bak')) {
|
|
||||||
require "{$dir}/index.php";
|
|
||||||
$name = basename($dir);
|
|
||||||
// if (!in_array($name, $released)) continue;
|
|
||||||
$class = new ReflectionClass(str_replace('-', '', $name) . 'Plugin');
|
|
||||||
$manifest_item = [];
|
|
||||||
foreach ($class->getConstants() as $key => $value) {
|
|
||||||
$key = \strtolower($key);
|
|
||||||
if (in_array($key, $keys)) {
|
|
||||||
$manifest_item[$key] = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$version = $manifest_item['version'];
|
|
||||||
if (0 < floatval($version)) {
|
|
||||||
echo "+ {$name} {$version}\n";
|
|
||||||
|
|
||||||
// Minify JavaScript
|
|
||||||
foreach (glob("{$dir}/*.js") as $file) {
|
|
||||||
if (!strpos($file,'.min')) {
|
|
||||||
$mfile = str_replace('.js', '.min.js', $file);
|
|
||||||
passthru("{$terser} {$file} --output {$mfile} --compress 'drop_console' --ecma 6 --mangle");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach (glob("{$dir}/js/*.js") as $file) {
|
|
||||||
if (!strpos($file,'.min')) {
|
|
||||||
$mfile = str_replace('.js', '.min.js', $file);
|
|
||||||
passthru("{$terser} {$file} --output {$mfile} --compress 'drop_console' --ecma 6 --mangle");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$manifest_item['type'] = 'plugin';
|
|
||||||
$manifest_item['id'] = $name;
|
|
||||||
$manifest_item['file'] = "plugins/{$name}-{$version}.tgz";
|
|
||||||
$tar_destination = PLUGINS_DEST_DIR . "/{$name}-{$version}.tar";
|
|
||||||
$tgz_destination = PLUGINS_DEST_DIR . "/{$name}-{$version}.tgz";
|
|
||||||
@unlink($tgz_destination);
|
|
||||||
@unlink("{$tar_destination}.gz");
|
|
||||||
$tar = new PharData($tar_destination);
|
|
||||||
$tar->buildFromDirectory('./plugins/', '/' . \preg_quote("./plugins/{$name}/", '/') . '((?!\.bak).)*$/');
|
|
||||||
$tar->compress(Phar::GZ);
|
|
||||||
unlink($tar_destination);
|
|
||||||
rename("{$tar_destination}.gz", $tgz_destination);
|
|
||||||
/*
|
|
||||||
if (Phar::canWrite()) {
|
|
||||||
$phar_destination = PLUGINS_DEST_DIR . "/{$name}.phar";
|
|
||||||
@unlink($phar_destination);
|
|
||||||
$tar = new Phar($phar_destination);
|
|
||||||
$tar->buildFromDirectory("./plugins/{$name}/", '/^((?!\.bak).)*$/');
|
|
||||||
$tar->compress(Phar::GZ);
|
|
||||||
unlink($phar_destination);
|
|
||||||
rename("{$phar_destination}.gz", $phar_destination);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if (isset($options['sign'])) {
|
|
||||||
passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign '.escapeshellarg($tgz_destination), $return_var);
|
|
||||||
$manifest_item['pgp_sig'] = trim(preg_replace('/-----(BEGIN|END) PGP SIGNATURE-----/', '', file_get_contents($tgz_destination.'.asc')));
|
|
||||||
}
|
|
||||||
ksort($manifest_item);
|
|
||||||
$manifest[$name] = $manifest_item;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
echo "- {$name} {$version}\n";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
echo "- {$name}\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ksort($manifest);
|
|
||||||
$manifest = json_encode(array_values($manifest));
|
|
||||||
$manifest = str_replace('{"', "\n\t{\n\t\t\"", $manifest);
|
|
||||||
$manifest = str_replace('"}', "\"\n\t}", $manifest);
|
|
||||||
$manifest = str_replace('}]', "}\n]", $manifest);
|
|
||||||
$manifest = str_replace('","', "\",\n\t\t\"", $manifest);
|
|
||||||
$manifest = str_replace('\/', '/', $manifest);
|
|
||||||
file_put_contents(dirname(PLUGINS_DEST_DIR) . "/packages.json", $manifest);
|
|
||||||
|
|
||||||
exit;
|
|
||||||
13
build/rsa.php
Normal file
13
build/rsa.php
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
if (!\class_exists('Crypt_RSA'))
|
||||||
|
{
|
||||||
|
\set_include_path(\get_include_path().PATH_SEPARATOR.\dirname(__FILE__).'/../rainloop/v/0.0.0/app/libraries/phpseclib');
|
||||||
|
include_once 'Crypt/RSA.php';
|
||||||
|
\defined('CRYPT_RSA_MODE') || \define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rsa = new Crypt_RSA();
|
||||||
|
$key = $rsa->createKey(1024);
|
||||||
|
|
||||||
|
var_dump($key);
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
// simple connection test
|
|
||||||
|
|
||||||
$host = 'imap.gmail.com';
|
|
||||||
$port = 993;
|
|
||||||
|
|
||||||
echo $host.':'.$port;
|
|
||||||
|
|
||||||
$streamContextSettings = array(
|
|
||||||
'ssl' => array(
|
|
||||||
'verify_host' => true,
|
|
||||||
'verify_peer' => true,
|
|
||||||
'verify_peer_name' => true,
|
|
||||||
'allow_self_signed' => false
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$streamContext = stream_context_create($streamContextSettings);
|
|
||||||
|
|
||||||
$errorStr = '';
|
|
||||||
$errorNo = 0;
|
|
||||||
|
|
||||||
$connection = stream_socket_client($host.':'.$port, $errorNo, $errorStr, 5, STREAM_CLIENT_CONNECT, $streamContext);
|
|
||||||
if (is_resource($connection)) {
|
|
||||||
echo ' = OK';
|
|
||||||
fclose($connection);
|
|
||||||
} else {
|
|
||||||
echo ' = ERROR ([#'.$errorNo.'] '.$errorStr.')';
|
|
||||||
}
|
|
||||||
280
cli/release.php
280
cli/release.php
|
|
@ -1,280 +0,0 @@
|
||||||
#!/usr/bin/php
|
|
||||||
<?php
|
|
||||||
define('ROOT_DIR', dirname(__DIR__));
|
|
||||||
chdir(ROOT_DIR);
|
|
||||||
|
|
||||||
$options = getopt('', ['aur','docker','plugins','skip-gulp','debian','nextcloud','owncloud','cpanel','sign']);
|
|
||||||
|
|
||||||
if (isset($options['plugins'])) {
|
|
||||||
require(ROOT_DIR . '/build/plugins.php');
|
|
||||||
}
|
|
||||||
|
|
||||||
$gulp = trim(shell_exec('which gulp'));
|
|
||||||
if (!$gulp) {
|
|
||||||
exit('gulp not installed, run as root: npm install --global gulp-cli');
|
|
||||||
}
|
|
||||||
|
|
||||||
$package = json_decode(file_get_contents('package.json'));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update files that contain version
|
|
||||||
*/
|
|
||||||
// cloudron
|
|
||||||
$file = ROOT_DIR . '/integrations/cloudron/Dockerfile';
|
|
||||||
file_put_contents($file, preg_replace('/VERSION=[0-9.]+/', "VERSION={$package->version}", file_get_contents($file)));
|
|
||||||
$file = ROOT_DIR . '/integrations/cloudron/DESCRIPTION.md';
|
|
||||||
file_put_contents($file, preg_replace('/<upstream>[^<]*</', "<upstream>{$package->version}<", file_get_contents($file)));
|
|
||||||
// virtualmin
|
|
||||||
$file = ROOT_DIR . '/integrations/virtualmin/snappymail.pl';
|
|
||||||
file_put_contents($file, preg_replace('/return \\( "[0-9]+\\.[0-9]+\\.[0-9]+" \\)/', "return ( \"{$package->version}\" )", file_get_contents($file)));
|
|
||||||
|
|
||||||
// Arch User Repository
|
|
||||||
// https://aur.archlinux.org/packages/snappymail/
|
|
||||||
$options['aur'] = isset($options['aur']);
|
|
||||||
|
|
||||||
// Docker build
|
|
||||||
$options['docker'] = isset($options['docker']);
|
|
||||||
if ($options['docker'] && !($docker = trim(shell_exec('which docker')))) {
|
|
||||||
exit('docker not found');
|
|
||||||
}
|
|
||||||
if ($options['docker'] && $options['aur']) {
|
|
||||||
exit('Conflict between docker and aur');
|
|
||||||
}
|
|
||||||
|
|
||||||
$destPath = "build/dist/releases/webmail/{$package->version}/";
|
|
||||||
is_dir($destPath) || mkdir($destPath, 0777, true);
|
|
||||||
|
|
||||||
$zip_destination = "{$destPath}snappymail-{$package->version}.zip";
|
|
||||||
$tar_destination = "{$destPath}snappymail-{$package->version}.tar";
|
|
||||||
|
|
||||||
@unlink($zip_destination);
|
|
||||||
@unlink($tar_destination);
|
|
||||||
@unlink("{$tar_destination}.gz");
|
|
||||||
|
|
||||||
if (!isset($options['skip-gulp'])) {
|
|
||||||
echo "\x1b[33;1m === Gulp === \x1b[0m\n";
|
|
||||||
passthru($gulp, $return_var);
|
|
||||||
if ($return_var) {
|
|
||||||
exit("gulp failed with error code {$return_var}\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
$cmddir = escapeshellcmd(ROOT_DIR) . '/snappymail/v/0.0.0/static';
|
|
||||||
|
|
||||||
if ($gzip = trim(shell_exec('which gzip'))) {
|
|
||||||
echo "\x1b[33;1m === Gzip *.js and *.css === \x1b[0m\n";
|
|
||||||
passthru("{$gzip} -k --best {$cmddir}/js/*.js");
|
|
||||||
passthru("{$gzip} -k --best {$cmddir}/js/min/*.js");
|
|
||||||
passthru("{$gzip} -k --best {$cmddir}/css/admin*.css");
|
|
||||||
passthru("{$gzip} -k --best {$cmddir}/css/app*.css");
|
|
||||||
unlink(ROOT_DIR . '/snappymail/v/0.0.0/static/js/boot.js.gz');
|
|
||||||
unlink(ROOT_DIR . '/snappymail/v/0.0.0/static/js/min/boot.min.js.gz');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($brotli = trim(shell_exec('which brotli'))) {
|
|
||||||
echo "\x1b[33;1m === Brotli *.js and *.css === \x1b[0m\n";
|
|
||||||
passthru("{$brotli} -k --best {$cmddir}/js/*.js");
|
|
||||||
passthru("{$brotli} -k --best {$cmddir}/js/min/*.js");
|
|
||||||
passthru("{$brotli} -k --best {$cmddir}/css/admin*.css");
|
|
||||||
passthru("{$brotli} -k --best {$cmddir}/css/app*.css");
|
|
||||||
unlink(ROOT_DIR . '/snappymail/v/0.0.0/static/js/boot.js.br');
|
|
||||||
unlink(ROOT_DIR . '/snappymail/v/0.0.0/static/js/min/boot.min.js.br');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporary rename folder to speed up PharData
|
|
||||||
//if (!rename('snappymail/v/0.0.0', "snappymail/v/{$package->version}")){
|
|
||||||
if (!rename('snappymail/v/0.0.0', "snappymail/v/{$package->version}")) {
|
|
||||||
exit('Failed to temporary rename snappymail/v/0.0.0');
|
|
||||||
}
|
|
||||||
register_shutdown_function(function(){
|
|
||||||
// Rename folder back to original
|
|
||||||
@rename("snappymail/v/{$GLOBALS['package']->version}", 'snappymail/v/0.0.0');
|
|
||||||
});
|
|
||||||
|
|
||||||
echo "\x1b[33;1m === Zip/Tar === \x1b[0m\n";
|
|
||||||
|
|
||||||
$zip = new ZipArchive();
|
|
||||||
if (!$zip->open($zip_destination, ZIPARCHIVE::CREATE)) {
|
|
||||||
exit("Failed to create {$zip_destination}");
|
|
||||||
}
|
|
||||||
|
|
||||||
$tar = new PharData($tar_destination);
|
|
||||||
|
|
||||||
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('snappymail/v'), RecursiveIteratorIterator::SELF_FIRST);
|
|
||||||
foreach ($files as $file) {
|
|
||||||
$file = str_replace('\\', '/', $file);
|
|
||||||
//echo "{$file}\n";
|
|
||||||
// Ignore "." and ".." folders
|
|
||||||
if (!in_array(substr($file, strrpos($file, '/')+1), array('.', '..'))) {
|
|
||||||
if (is_dir($file)) {
|
|
||||||
$zip->addEmptyDir($file);
|
|
||||||
} else if (is_file($file)) {
|
|
||||||
$zip->addFile($file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($options['docker']) {
|
|
||||||
$tar->buildFromDirectory('./snappymail/', "@v/{$package->version}@");
|
|
||||||
} else {
|
|
||||||
$tar->buildFromDirectory('./', "@snappymail/v/{$package->version}@");
|
|
||||||
}
|
|
||||||
|
|
||||||
//$zip->addFile('cli/upgrade.sh');
|
|
||||||
//$tar->addFile('cli/upgrade.sh');
|
|
||||||
|
|
||||||
$zip->addFile('data/.htaccess');
|
|
||||||
$tar->addFile('data/.htaccess');
|
|
||||||
|
|
||||||
$zip->addFromString('data/VERSION', $package->version);
|
|
||||||
$tar->addFromString('data/VERSION', $package->version);
|
|
||||||
|
|
||||||
$zip->addFile('data/README.md');
|
|
||||||
$tar->addFile('data/README.md');
|
|
||||||
|
|
||||||
if ($options['aur']) {
|
|
||||||
$data = '<?php
|
|
||||||
function __get_custom_data_full_path()
|
|
||||||
{
|
|
||||||
return \'/var/lib/snappymail\';
|
|
||||||
}
|
|
||||||
';
|
|
||||||
$zip->addFromString('include.php', $data);
|
|
||||||
$tar->addFromString('include.php', $data);
|
|
||||||
} else {
|
|
||||||
$zip->addFile('_include.php');
|
|
||||||
$tar->addFile('_include.php');
|
|
||||||
}
|
|
||||||
|
|
||||||
$zip->addFile('.htaccess');
|
|
||||||
$tar->addFile('.htaccess');
|
|
||||||
|
|
||||||
$index = file_get_contents('index.php');
|
|
||||||
$index = str_replace('0.0.0', $package->version, $index);
|
|
||||||
$zip->addFromString('index.php', $index);
|
|
||||||
$tar->addFromString('index.php', $index);
|
|
||||||
|
|
||||||
$zip->addFile('README.md');
|
|
||||||
$tar->addFile('README.md');
|
|
||||||
|
|
||||||
$zip->close();
|
|
||||||
|
|
||||||
$tar->compress(Phar::GZ);
|
|
||||||
unlink($tar_destination);
|
|
||||||
$tar_destination .= '.gz';
|
|
||||||
|
|
||||||
echo "{$zip_destination} created\n{$tar_destination} created\n";
|
|
||||||
|
|
||||||
if (isset($options['nextcloud'])) {
|
|
||||||
require(ROOT_DIR . '/build/nextcloud.php');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($options['owncloud'])) {
|
|
||||||
require(ROOT_DIR . '/build/owncloud.php');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($options['cpanel'])) {
|
|
||||||
require(ROOT_DIR . '/build/cpanel.php');
|
|
||||||
}
|
|
||||||
|
|
||||||
rename("snappymail/v/{$package->version}", 'snappymail/v/0.0.0');
|
|
||||||
|
|
||||||
file_put_contents("{$destPath}core.json", '{
|
|
||||||
"version": "'.$package->version.'",
|
|
||||||
"file": "../latest.tar.gz",
|
|
||||||
"warnings": []
|
|
||||||
}');
|
|
||||||
|
|
||||||
// Arch User Repository
|
|
||||||
if ($options['aur']) {
|
|
||||||
// extension_loaded('blake2')
|
|
||||||
if (!function_exists('b2sum') && $b2sum = trim(shell_exec('which b2sum'))) {
|
|
||||||
function b2sum($file) {
|
|
||||||
$file = escapeshellarg($file);
|
|
||||||
exec("b2sum --binary {$file} 2>&1", $output, $exitcode);
|
|
||||||
$output = explode(' ', implode("\n", $output));
|
|
||||||
return $output[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$b2sums = function_exists('b2sum') ? [
|
|
||||||
b2sum($tar_destination),
|
|
||||||
b2sum(ROOT_DIR . '/build/arch/snappymail.sysusers'),
|
|
||||||
b2sum(ROOT_DIR . '/build/arch/snappymail.tmpfiles')
|
|
||||||
] : [];
|
|
||||||
|
|
||||||
file_put_contents('build/arch/.SRCINFO', 'pkgbase = snappymail
|
|
||||||
pkgdesc = modern PHP webmail client
|
|
||||||
pkgver = '.$package->version.'
|
|
||||||
pkgrel = 1
|
|
||||||
url = https://github.com/the-djmaze/snappymail
|
|
||||||
arch = any
|
|
||||||
license = AGPL3
|
|
||||||
makedepends = php
|
|
||||||
makedepends = nodejs
|
|
||||||
makedepends = yarn
|
|
||||||
makedepends = gulp
|
|
||||||
depends = php-fpm
|
|
||||||
optdepends = mariadb: storage backend for contacts
|
|
||||||
optdepends = php-pgsql: storage backend for contacts
|
|
||||||
optdepends = php-sqlite: storage backend for contacts
|
|
||||||
source = snappymail-'.$package->version.'.tar.gz::https://github.com/the-djmaze/snappymail/archive/v'.$package->version.'.tar.gz
|
|
||||||
source = snappymail.sysusers
|
|
||||||
source = snappymail.tmpfiles
|
|
||||||
b2sums = '.implode("\n b2sums = ", $b2sums).'
|
|
||||||
|
|
||||||
pkgname = snappymail
|
|
||||||
');
|
|
||||||
|
|
||||||
$file = ROOT_DIR . '/build/arch/PKGBUILD';
|
|
||||||
if (is_file($file)) {
|
|
||||||
$PKGBUILD = file_get_contents($file);
|
|
||||||
$PKGBUILD = preg_replace('/pkgver=[0-9.]+/', "pkgver={$package->version}", $PKGBUILD);
|
|
||||||
$PKGBUILD = preg_replace('/b2sums=\\([^)]+\\)/s', "b2sums=('".implode("'\n '", $b2sums)."')", $PKGBUILD);
|
|
||||||
file_put_contents($file, $PKGBUILD);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Debian Repository
|
|
||||||
else if (isset($options['debian'])) {
|
|
||||||
require(ROOT_DIR . '/build/deb.php');
|
|
||||||
}
|
|
||||||
// Docker build
|
|
||||||
else if ($options['docker']) {
|
|
||||||
echo "\x1b[33;1m === Docker === \x1b[0m\n";
|
|
||||||
$zip_filename = "snappymail-{$package->version}.zip";
|
|
||||||
copy($zip_destination, "./.docker/release/{$zip_filename}");
|
|
||||||
if ($docker) {
|
|
||||||
passthru("{$docker} build --pull " . ROOT_DIR . "/.docker/release/ --build-arg FILES_ZIP={$zip_filename} -t snappymail:{$package->version}");
|
|
||||||
} else {
|
|
||||||
echo "Docker not installed!\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($options['sign'])) {
|
|
||||||
echo "\x1b[33;1m === PGP Sign === \x1b[0m\n";
|
|
||||||
passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign '.escapeshellarg($tar_destination), $return_var);
|
|
||||||
passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign '.escapeshellarg($zip_destination), $return_var);
|
|
||||||
if (isset($options['nextcloud'])) {
|
|
||||||
passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign '
|
|
||||||
.escapeshellarg("{$destPath}snappymail-{$package->version}-nextcloud.tar.gz"), $return_var);
|
|
||||||
}
|
|
||||||
if (isset($options['owncloud'])) {
|
|
||||||
passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign '
|
|
||||||
.escapeshellarg("{$destPath}snappymail-{$package->version}-owncloud.tar.gz"), $return_var);
|
|
||||||
}
|
|
||||||
if (isset($options['cpanel'])) {
|
|
||||||
passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign '
|
|
||||||
.escapeshellarg("{$destPath}snappymail-{$package->version}-cpanel.tar.gz"), $return_var);
|
|
||||||
}
|
|
||||||
if (isset($options['debian'])) {
|
|
||||||
passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --armor --detach-sign '
|
|
||||||
. escapeshellarg(ROOT_DIR . "/build/dist/releases/webmail/{$package->version}/" . basename(DEB_DEST_DIR.'.deb')), $return_var);
|
|
||||||
// https://github.com/the-djmaze/snappymail/issues/185#issuecomment-1059420588
|
|
||||||
passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --digest-algo SHA512 --clearsign --output '
|
|
||||||
. escapeshellarg(ROOT_DIR . "/build/dist/releases/webmail/{$package->version}/InRelease") . ' '
|
|
||||||
. escapeshellarg(ROOT_DIR . "/build/dist/releases/webmail/{$package->version}/Release"), $return_var);
|
|
||||||
passthru('gpg --local-user 1016E47079145542F8BA133548208BA13290F3EB --digest-algo SHA512 -abs --output '
|
|
||||||
. escapeshellarg(ROOT_DIR . "/build/dist/releases/webmail/{$package->version}/Release.gpg") . ' '
|
|
||||||
. escapeshellarg(ROOT_DIR . "/build/dist/releases/webmail/{$package->version}/Release"), $return_var);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# UPGRADING Snappymail
|
|
||||||
#
|
|
||||||
# I've found this to be working fine when upgrading my Snappymail. The
|
|
||||||
# commands should be used at your own risk. I take no responsibility.
|
|
||||||
#
|
|
||||||
# The script can only be run from the SnappyMail install directory!
|
|
||||||
#
|
|
||||||
# By Uwe Bieling <pychi@gmx.de>
|
|
||||||
# and Jordan S (https://github.com/jas8522)
|
|
||||||
|
|
||||||
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
|
||||||
LATEST_URL="https://snappymail.eu/repository/latest.tar.gz"
|
|
||||||
cd "${SCRIPT_DIR}"
|
|
||||||
OWNERGROUP=`stat -c "%U:%G" snappymail`
|
|
||||||
OLD_VERSION=`grep "define('APP_VERSION" index.php | awk -F\' '{print $4}'`
|
|
||||||
|
|
||||||
# check if we are root
|
|
||||||
if [[ $(id -u) -ne 0 ]] ; then echo -e "\033[1;31mPlease run as root\033[0m" ; exit 1 ; fi
|
|
||||||
|
|
||||||
# check if we are in the correct dir
|
|
||||||
if [ ! -d snappymail/v ] ; then echo -e "\033[1;31mThis script can only be run from the SnappyMail install directory\033[0m" ; exit 1 ; fi
|
|
||||||
|
|
||||||
# Download last release to /tmp
|
|
||||||
echo -e "\033[1;33mDownloading last release\033[0m"
|
|
||||||
wget $LATEST_URL -O /tmp/snappymail_latest.tar.gz >/dev/null 2>&1
|
|
||||||
NEW_VERSION=`tar -tf /tmp/snappymail_latest.tar.gz | grep "snappymail/v/.*/index.php" | awk -F/ '{print $3}'`
|
|
||||||
|
|
||||||
if [ $OLD_VERSION != $NEW_VERSION ] ;
|
|
||||||
then
|
|
||||||
# Safety First ... make a backup
|
|
||||||
DSTAMP=`date +%Y-%m-%d`
|
|
||||||
echo -e "\033[1;33mBacking up snappymail $OLD_VERSION to ../backup_snappymail_${DSTAMP}.tar.gz\033[0m"
|
|
||||||
tar -czf ../backup_snappymail_${DSTAMP}.tar.gz .
|
|
||||||
|
|
||||||
# installing
|
|
||||||
echo -e "\033[1;33mInstalling $NEW_VERSION\033[0m";
|
|
||||||
tar -xzf /tmp/snappymail_latest.tar.gz;
|
|
||||||
|
|
||||||
# set permissions
|
|
||||||
echo -e "\033[1;33mSet permissions\033[0m"
|
|
||||||
find . -type d -exec chmod 755 {} \;
|
|
||||||
find . -type f -exec chmod 644 {} \;
|
|
||||||
chmod u+x "${SCRIPT_DIR}/upgrade.sh"
|
|
||||||
chown -R $OWNERGROUP $SCRIPT_DIR
|
|
||||||
echo -e "\033[1;32mFinished with snappymail upgrade from $OLD_VERSION to $NEW_VERSION... \033[0m"
|
|
||||||
|
|
||||||
else
|
|
||||||
echo -e "\033[1;31msame Version skipping.. \033[0m";
|
|
||||||
fi
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Require all denied
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
You can move this folder outside public http access for security.
|
|
||||||
Edit and rename /_include.php to set the new location.
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
import ko from 'ko';
|
|
||||||
|
|
||||||
import { logoutLink } from 'Common/Links';
|
|
||||||
import { i18nToNodes, initOnStartOrLangChange } from 'Common/Translator';
|
|
||||||
|
|
||||||
import { arePopupsVisible } from 'Knoin/Knoin';
|
|
||||||
|
|
||||||
import { LanguageStore } from 'Stores/Language';
|
|
||||||
import { initThemes } from 'Stores/Theme';
|
|
||||||
|
|
||||||
import { SelectComponent } from 'Component/Select';
|
|
||||||
import { CheckboxComponent } from 'Component/Checkbox';
|
|
||||||
|
|
||||||
export class AbstractApp {
|
|
||||||
/**
|
|
||||||
* @param {RemoteStorage|AdminRemoteStorage} Remote
|
|
||||||
*/
|
|
||||||
constructor(Remote) {
|
|
||||||
this.Remote = Remote;
|
|
||||||
}
|
|
||||||
|
|
||||||
logoutReload(url) {
|
|
||||||
arePopupsVisible(false);
|
|
||||||
url = url || logoutLink();
|
|
||||||
if (location.href !== url) {
|
|
||||||
setTimeout(() => location.href = url, 100);
|
|
||||||
} else {
|
|
||||||
rl.route.reload();
|
|
||||||
}
|
|
||||||
// this does not work due to ViewModelClass.__builded = true;
|
|
||||||
// rl.settings.set('Auth', false);
|
|
||||||
// rl.app.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
bootstart() {
|
|
||||||
const register = (name, ClassObject) => ko.components.register(name, {
|
|
||||||
template: { element: ClassObject.name },
|
|
||||||
viewModel: {
|
|
||||||
createViewModel: (params, componentInfo) => {
|
|
||||||
params = params || {};
|
|
||||||
i18nToNodes(componentInfo.element);
|
|
||||||
return new ClassObject(params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
register('Select', SelectComponent);
|
|
||||||
register('Checkbox', CheckboxComponent);
|
|
||||||
|
|
||||||
initOnStartOrLangChange();
|
|
||||||
|
|
||||||
LanguageStore.populate();
|
|
||||||
initThemes();
|
|
||||||
|
|
||||||
this.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
397
dev/App/Abstract.jsx
Normal file
397
dev/App/Abstract.jsx
Normal file
|
|
@ -0,0 +1,397 @@
|
||||||
|
|
||||||
|
import {window, _, $, key} from 'common';
|
||||||
|
|
||||||
|
import {
|
||||||
|
$win, $html, $doc,
|
||||||
|
startMicrotime, leftPanelDisabled, leftPanelType,
|
||||||
|
sUserAgent, bMobileDevice, bAnimationSupported
|
||||||
|
} from 'Common/Globals';
|
||||||
|
|
||||||
|
import {KeyState} from 'Common/Enums';
|
||||||
|
import {noop, isNormal, pString, inArray, microtime, timestamp, detectDropdownVisibility, windowResizeCallback} from 'Common/Utils';
|
||||||
|
import * as Links from 'Common/Links';
|
||||||
|
import * as Settings from 'Storage/Settings';
|
||||||
|
import * as Events from 'Common/Events';
|
||||||
|
import {initOnStartOrLangChange, initNotificationLanguage} from 'Common/Translator';
|
||||||
|
|
||||||
|
import {AbstractBoot} from 'Knoin/AbstractBoot';
|
||||||
|
|
||||||
|
class AbstractApp extends AbstractBoot
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
* @param {RemoteStorage|AdminRemoteStorage} Remote
|
||||||
|
*/
|
||||||
|
constructor(Remote)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.googlePreviewSupportedCache = null;
|
||||||
|
this.isLocalAutocomplete = true;
|
||||||
|
this.iframe = null;
|
||||||
|
this.lastErrorTime = 0;
|
||||||
|
|
||||||
|
this.iframe = $('<iframe class="internal-hiddden" />').appendTo('body');
|
||||||
|
|
||||||
|
$win.on('error', (event) => {
|
||||||
|
if (event && event.originalEvent && event.originalEvent.message &&
|
||||||
|
-1 === inArray(event.originalEvent.message, [
|
||||||
|
'Script error.', 'Uncaught Error: Error calling method on NPObject.'
|
||||||
|
]))
|
||||||
|
{
|
||||||
|
const time = timestamp();
|
||||||
|
if (this.lastErrorTime >= time)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastErrorTime = time;
|
||||||
|
|
||||||
|
Remote.jsError(
|
||||||
|
noop,
|
||||||
|
event.originalEvent.message,
|
||||||
|
event.originalEvent.filename,
|
||||||
|
event.originalEvent.lineno,
|
||||||
|
window.location && window.location.toString ? window.location.toString() : '',
|
||||||
|
$html.attr('class'),
|
||||||
|
microtime() - startMicrotime
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$win.on('resize', function() {
|
||||||
|
Events.pub('window.resize');
|
||||||
|
});
|
||||||
|
|
||||||
|
Events.sub('window.resize', _.throttle(function() {
|
||||||
|
|
||||||
|
var
|
||||||
|
iH = $win.height(),
|
||||||
|
iW = $win.height();
|
||||||
|
|
||||||
|
if ($win.__sizes[0] !== iH || $win.__sizes[1] !== iW)
|
||||||
|
{
|
||||||
|
$win.__sizes[0] = iH;
|
||||||
|
$win.__sizes[1] = iW;
|
||||||
|
|
||||||
|
Events.pub('window.resize.real');
|
||||||
|
}
|
||||||
|
|
||||||
|
}, 50));
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
// Events.sub({
|
||||||
|
// 'window.resize': function() {
|
||||||
|
// window.console.log('window.resize');
|
||||||
|
// },
|
||||||
|
// 'window.resize.real': function() {
|
||||||
|
// window.console.log('window.resize.real');
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
$doc.on('keydown', function(oEvent) {
|
||||||
|
if (oEvent && oEvent.ctrlKey)
|
||||||
|
{
|
||||||
|
$html.addClass('rl-ctrl-key-pressed');
|
||||||
|
}
|
||||||
|
}).on('keyup', function(oEvent) {
|
||||||
|
if (oEvent && !oEvent.ctrlKey)
|
||||||
|
{
|
||||||
|
$html.removeClass('rl-ctrl-key-pressed');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$doc.on('mousemove keypress click', _.debounce(function() {
|
||||||
|
Events.pub('rl.auto-logout-refresh');
|
||||||
|
}, 5000));
|
||||||
|
|
||||||
|
key('esc, enter', KeyState.All, _.bind(function() {
|
||||||
|
detectDropdownVisibility();
|
||||||
|
}, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
remote() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getApplicationConfiguration(name, default_) {
|
||||||
|
return this.applicationConfiguration[name] || default_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} link
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
download(link) {
|
||||||
|
|
||||||
|
if (sUserAgent && (-1 < sUserAgent.indexOf('chrome') || -1 < sUserAgent.indexOf('chrome')))
|
||||||
|
{
|
||||||
|
const oLink = window.document.createElement('a');
|
||||||
|
oLink.href = link;
|
||||||
|
|
||||||
|
if (window.document && window.document.createEvent)
|
||||||
|
{
|
||||||
|
const oE = window.document.createEvent.MouseEvents;
|
||||||
|
if (oE && oE.initEvent && oLink.dispatchEvent)
|
||||||
|
{
|
||||||
|
oE.initEvent('click', true, true);
|
||||||
|
oLink.dispatchEvent(oE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bMobileDevice)
|
||||||
|
{
|
||||||
|
window.open(link, '_self');
|
||||||
|
window.focus();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.iframe.attr('src', link);
|
||||||
|
// window.document.location.href = link;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
googlePreviewSupported() {
|
||||||
|
if (null === this.googlePreviewSupportedCache)
|
||||||
|
{
|
||||||
|
this.googlePreviewSupportedCache = !!Settings.settingsGet('AllowGoogleSocial') &&
|
||||||
|
!!Settings.settingsGet('AllowGoogleSocialPreview');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.googlePreviewSupportedCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} title
|
||||||
|
*/
|
||||||
|
setWindowTitle(title) {
|
||||||
|
title = ((isNormal(title) && 0 < title.length) ? '' + title : '');
|
||||||
|
if (Settings.settingsGet('Title'))
|
||||||
|
{
|
||||||
|
title += (title ? ' - ' : '') + Settings.settingsGet('Title');
|
||||||
|
}
|
||||||
|
|
||||||
|
window.document.title = title + ' ...';
|
||||||
|
window.document.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectToAdminPanel() {
|
||||||
|
_.delay(() => window.location.href = Links.rootAdmin(), 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearClientSideToken() {
|
||||||
|
if (window.__rlah_clear)
|
||||||
|
{
|
||||||
|
window.__rlah_clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} token
|
||||||
|
*/
|
||||||
|
setClientSideToken(token) {
|
||||||
|
if (window.__rlah_set)
|
||||||
|
{
|
||||||
|
window.__rlah_set(token);
|
||||||
|
|
||||||
|
require('Storage/Settings').settingsSet('AuthAccountHash', token);
|
||||||
|
require('Common/Links').populateAuthSuffix();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean=} admin = false
|
||||||
|
* @param {boolean=} logout = false
|
||||||
|
* @param {boolean=} close = false
|
||||||
|
*/
|
||||||
|
loginAndLogoutReload(admin = false, logout = false, close = false) {
|
||||||
|
|
||||||
|
const
|
||||||
|
kn = require('Knoin/Knoin'),
|
||||||
|
mobile = Settings.appSettingsGet('mobile'),
|
||||||
|
inIframe = !!Settings.appSettingsGet('inIframe');
|
||||||
|
|
||||||
|
let customLogoutLink = pString(Settings.appSettingsGet('customLogoutLink'));
|
||||||
|
|
||||||
|
if (logout)
|
||||||
|
{
|
||||||
|
this.clearClientSideToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logout && close && window.close)
|
||||||
|
{
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
customLogoutLink = customLogoutLink || (admin ? Links.rootAdmin(mobile) : Links.rootUser(mobile));
|
||||||
|
|
||||||
|
if (logout && window.location.href !== customLogoutLink)
|
||||||
|
{
|
||||||
|
_.delay(() => {
|
||||||
|
if (inIframe && window.parent)
|
||||||
|
{
|
||||||
|
window.parent.location.href = customLogoutLink;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
window.location.href = customLogoutLink;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
kn.routeOff();
|
||||||
|
kn.setHash(Links.root(), true);
|
||||||
|
kn.routeOff();
|
||||||
|
|
||||||
|
_.delay(() => {
|
||||||
|
if (inIframe && window.parent)
|
||||||
|
{
|
||||||
|
window.parent.location.reload();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
historyBack() {
|
||||||
|
window.history.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
bootstart() {
|
||||||
|
|
||||||
|
// log('Ps' + 'ss, hac' + 'kers! The' + 're\'s not' + 'hing inte' + 'resting :' + ')');
|
||||||
|
|
||||||
|
Events.pub('rl.bootstart');
|
||||||
|
|
||||||
|
const
|
||||||
|
mobile = Settings.appSettingsGet('mobile'),
|
||||||
|
ssm = require('ssm'),
|
||||||
|
ko = require('ko');
|
||||||
|
|
||||||
|
ko.components.register('SaveTrigger', require('Component/SaveTrigger'));
|
||||||
|
ko.components.register('Input', require('Component/Input'));
|
||||||
|
ko.components.register('Select', require('Component/Select'));
|
||||||
|
ko.components.register('Radio', require('Component/Radio'));
|
||||||
|
ko.components.register('TextArea', require('Component/TextArea'));
|
||||||
|
ko.components.register('Date', require('Component/Date'));
|
||||||
|
|
||||||
|
ko.components.register('x-script', require('Component/Script'));
|
||||||
|
// ko.components.register('svg-icon', require('Component/SvgIcon'));
|
||||||
|
|
||||||
|
if (Settings.appSettingsGet('materialDesign') && bAnimationSupported)
|
||||||
|
{
|
||||||
|
ko.components.register('Checkbox', require('Component/MaterialDesign/Checkbox'));
|
||||||
|
ko.components.register('CheckboxSimple', require('Component/Checkbox'));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ko.components.register('Checkbox', require('Component/Classic/Checkbox'));
|
||||||
|
// ko.components.register('CheckboxSimple', require('Component/Classic/Checkbox'));
|
||||||
|
ko.components.register('Checkbox', require('Component/Checkbox'));
|
||||||
|
ko.components.register('CheckboxSimple', require('Component/Checkbox'));
|
||||||
|
}
|
||||||
|
|
||||||
|
initOnStartOrLangChange(initNotificationLanguage);
|
||||||
|
|
||||||
|
_.delay(windowResizeCallback, 1000);
|
||||||
|
|
||||||
|
Events.sub('ssm.mobile-enter', () => {
|
||||||
|
leftPanelDisabled(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
Events.sub('ssm.mobile-leave', () => {
|
||||||
|
leftPanelDisabled(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!mobile)
|
||||||
|
{
|
||||||
|
ssm.addState({
|
||||||
|
id: 'mobile',
|
||||||
|
maxWidth: 767,
|
||||||
|
onEnter: () => {
|
||||||
|
$html.addClass('ssm-state-mobile');
|
||||||
|
Events.pub('ssm.mobile-enter');
|
||||||
|
},
|
||||||
|
onLeave: () => {
|
||||||
|
$html.removeClass('ssm-state-mobile');
|
||||||
|
Events.pub('ssm.mobile-leave');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ssm.addState({
|
||||||
|
id: 'tablet',
|
||||||
|
minWidth: 768,
|
||||||
|
maxWidth: 999,
|
||||||
|
onEnter: function() {
|
||||||
|
$html.addClass('ssm-state-tablet');
|
||||||
|
},
|
||||||
|
onLeave: function() {
|
||||||
|
$html.removeClass('ssm-state-tablet');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ssm.addState({
|
||||||
|
id: 'desktop',
|
||||||
|
minWidth: 1000,
|
||||||
|
maxWidth: 1400,
|
||||||
|
onEnter: () => {
|
||||||
|
$html.addClass('ssm-state-desktop');
|
||||||
|
},
|
||||||
|
onLeave: () => {
|
||||||
|
$html.removeClass('ssm-state-desktop');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ssm.addState({
|
||||||
|
id: 'desktop-large',
|
||||||
|
minWidth: 1400,
|
||||||
|
onEnter: () => {
|
||||||
|
$html.addClass('ssm-state-desktop-large');
|
||||||
|
},
|
||||||
|
onLeave: () => {
|
||||||
|
$html.removeClass('ssm-state-desktop-large');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$html.addClass('ssm-state-mobile').addClass('rl-mobile');
|
||||||
|
Events.pub('ssm.mobile-enter');
|
||||||
|
}
|
||||||
|
|
||||||
|
leftPanelDisabled.subscribe((bValue) => {
|
||||||
|
$html.toggleClass('rl-left-panel-disabled', bValue);
|
||||||
|
$html.toggleClass('rl-left-panel-enabled', !bValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
leftPanelType.subscribe((sValue) => {
|
||||||
|
$html.toggleClass('rl-left-panel-none', 'none' === sValue);
|
||||||
|
$html.toggleClass('rl-left-panel-short', 'short' === sValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
leftPanelDisabled.valueHasMutated();
|
||||||
|
|
||||||
|
ssm.ready();
|
||||||
|
|
||||||
|
require('Stores/Language').populate();
|
||||||
|
require('Stores/Theme').populate();
|
||||||
|
require('Stores/Social').populate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {AbstractApp, AbstractApp as default};
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
import 'External/ko';
|
|
||||||
|
|
||||||
import { SettingsGet, SettingsAdmin } from 'Common/Globals';
|
|
||||||
import { initThemes } from 'Stores/Theme';
|
|
||||||
|
|
||||||
import Remote from 'Remote/Admin/Fetch';
|
|
||||||
|
|
||||||
import { SettingsAdminScreen } from 'Screen/Admin/Settings';
|
|
||||||
import { LoginAdminScreen } from 'Screen/Admin/Login';
|
|
||||||
|
|
||||||
import { startScreens } from 'Knoin/Knoin';
|
|
||||||
import { AbstractApp } from 'App/Abstract';
|
|
||||||
|
|
||||||
import { AskPopupView } from 'View/Popup/Ask';
|
|
||||||
|
|
||||||
export class AdminApp extends AbstractApp {
|
|
||||||
constructor() {
|
|
||||||
super(Remote);
|
|
||||||
this.weakPassword = ko.observable(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh() {
|
|
||||||
initThemes();
|
|
||||||
this.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
// if (!Settings.app('adminAllowed')) {
|
|
||||||
if (!SettingsAdmin('allowed')) {
|
|
||||||
rl.route.root();
|
|
||||||
setTimeout(() => location.href = '/', 1);
|
|
||||||
} else if (SettingsGet('Auth')) {
|
|
||||||
this.weakPassword(SettingsGet('weakPassword'));
|
|
||||||
startScreens([SettingsAdminScreen]);
|
|
||||||
} else {
|
|
||||||
startScreens([LoginAdminScreen]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AskPopupView.credentials = function(sAskDesc, btnText) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
this.showModal([
|
|
||||||
sAskDesc,
|
|
||||||
view => resolve({username:view.username(), password:view.passphrase()}),
|
|
||||||
() => resolve(null),
|
|
||||||
true,
|
|
||||||
3,
|
|
||||||
btnText
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
257
dev/App/Admin.jsx
Normal file
257
dev/App/Admin.jsx
Normal file
|
|
@ -0,0 +1,257 @@
|
||||||
|
|
||||||
|
import {window, _} from 'common';
|
||||||
|
import ko from 'ko';
|
||||||
|
import progressJs from 'progressJs';
|
||||||
|
|
||||||
|
import * as Links from 'Common/Links';
|
||||||
|
import {getNotification} from 'Common/Translator';
|
||||||
|
import {StorageResultType, Notification} from 'Common/Enums';
|
||||||
|
import {pInt, isNormal, isArray, inArray, isUnd} from 'Common/Utils';
|
||||||
|
|
||||||
|
import * as Settings from 'Storage/Settings';
|
||||||
|
|
||||||
|
import AppStore from 'Stores/Admin/App';
|
||||||
|
import DomainStore from 'Stores/Admin/Domain';
|
||||||
|
import PluginStore from 'Stores/Admin/Plugin';
|
||||||
|
import LicenseStore from 'Stores/Admin/License';
|
||||||
|
import PackageStore from 'Stores/Admin/Package';
|
||||||
|
import CoreStore from 'Stores/Admin/Core';
|
||||||
|
import Remote from 'Remote/Admin/Ajax';
|
||||||
|
|
||||||
|
import kn from 'Knoin/Knoin';
|
||||||
|
|
||||||
|
import {AbstractApp} from 'App/Abstract';
|
||||||
|
|
||||||
|
class AdminApp extends AbstractApp
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super(Remote);
|
||||||
|
}
|
||||||
|
|
||||||
|
remote() {
|
||||||
|
return Remote;
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadDomainList() {
|
||||||
|
DomainStore.domains.loading(true);
|
||||||
|
Remote.domainList((result, data) => {
|
||||||
|
DomainStore.domains.loading(false);
|
||||||
|
if (StorageResultType.Success === result && data && data.Result)
|
||||||
|
{
|
||||||
|
DomainStore.domains(_.map(data.Result, ([enabled, alias], name) => {
|
||||||
|
return {
|
||||||
|
name: name,
|
||||||
|
disabled: ko.observable(!enabled),
|
||||||
|
alias: alias,
|
||||||
|
deleteAccess: ko.observable(false)
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadPluginList() {
|
||||||
|
PluginStore.plugins.loading(true);
|
||||||
|
Remote.pluginList((result, data) => {
|
||||||
|
PluginStore.plugins.loading(false);
|
||||||
|
if (StorageResultType.Success === result && data && data.Result)
|
||||||
|
{
|
||||||
|
PluginStore.plugins(_.map(data.Result, (item) => {
|
||||||
|
return {
|
||||||
|
name: item.Name,
|
||||||
|
disabled: ko.observable(!item.Enabled),
|
||||||
|
configured: ko.observable(!!item.Configured)
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadPackagesList() {
|
||||||
|
PackageStore.packages.loading(true);
|
||||||
|
PackageStore.packagesReal(true);
|
||||||
|
Remote.packagesList((result, data) => {
|
||||||
|
PackageStore.packages.loading(false);
|
||||||
|
if (StorageResultType.Success === result && data && data.Result)
|
||||||
|
{
|
||||||
|
PackageStore.packagesReal(!!data.Result.Real);
|
||||||
|
PackageStore.packagesMainUpdatable(!!data.Result.MainUpdatable);
|
||||||
|
|
||||||
|
let list = [];
|
||||||
|
const loading = {};
|
||||||
|
|
||||||
|
_.each(PackageStore.packages(), (item) => {
|
||||||
|
if (item && item.loading())
|
||||||
|
{
|
||||||
|
loading[item.file] = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isArray(data.Result.List))
|
||||||
|
{
|
||||||
|
list = _.compact(_.map(data.Result.List, (item) => {
|
||||||
|
if (item)
|
||||||
|
{
|
||||||
|
item.loading = ko.observable(!isUnd(loading[item.file]));
|
||||||
|
return 'core' === item.type && !item.canBeInstalled ? null : item;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
PackageStore.packages(list);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PackageStore.packagesReal(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCoreData() {
|
||||||
|
CoreStore.coreUpdating(true);
|
||||||
|
Remote.updateCoreData((result, data) => {
|
||||||
|
CoreStore.coreUpdating(false);
|
||||||
|
CoreStore.coreVersion('');
|
||||||
|
CoreStore.coreRemoteVersion('');
|
||||||
|
CoreStore.coreRemoteRelease('');
|
||||||
|
CoreStore.coreVersionCompare(-2);
|
||||||
|
if (StorageResultType.Success === result && data && data.Result)
|
||||||
|
{
|
||||||
|
CoreStore.coreReal(true);
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CoreStore.coreReal(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadCoreData() {
|
||||||
|
CoreStore.coreChecking(true);
|
||||||
|
CoreStore.coreReal(true);
|
||||||
|
Remote.coreData((result, data) => {
|
||||||
|
CoreStore.coreChecking(false);
|
||||||
|
if (StorageResultType.Success === result && data && data.Result)
|
||||||
|
{
|
||||||
|
CoreStore.coreReal(!!data.Result.Real);
|
||||||
|
CoreStore.coreChannel(data.Result.Channel || 'stable');
|
||||||
|
CoreStore.coreType(data.Result.Type || 'stable');
|
||||||
|
CoreStore.coreUpdatable(!!data.Result.Updatable);
|
||||||
|
CoreStore.coreAccess(!!data.Result.Access);
|
||||||
|
CoreStore.coreWarning(!!data.Result.Warning);
|
||||||
|
CoreStore.coreVersion(data.Result.Version || '');
|
||||||
|
CoreStore.coreRemoteVersion(data.Result.RemoteVersion || '');
|
||||||
|
CoreStore.coreRemoteRelease(data.Result.RemoteRelease || '');
|
||||||
|
CoreStore.coreVersionCompare(pInt(data.Result.VersionCompare));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CoreStore.coreReal(false);
|
||||||
|
CoreStore.coreChannel('stable');
|
||||||
|
CoreStore.coreType('stable');
|
||||||
|
CoreStore.coreWarning(false);
|
||||||
|
CoreStore.coreVersion('');
|
||||||
|
CoreStore.coreRemoteVersion('');
|
||||||
|
CoreStore.coreRemoteRelease('');
|
||||||
|
CoreStore.coreVersionCompare(-2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean=} force = false
|
||||||
|
*/
|
||||||
|
reloadLicensing(force = false) {
|
||||||
|
LicenseStore.licensingProcess(true);
|
||||||
|
LicenseStore.licenseError('');
|
||||||
|
Remote.licensing((result, data) => {
|
||||||
|
LicenseStore.licensingProcess(false);
|
||||||
|
if (StorageResultType.Success === result && data && data.Result && isNormal(data.Result.Expired))
|
||||||
|
{
|
||||||
|
LicenseStore.licenseValid(true);
|
||||||
|
LicenseStore.licenseExpired(pInt(data.Result.Expired));
|
||||||
|
LicenseStore.licenseError('');
|
||||||
|
LicenseStore.licensing(true);
|
||||||
|
AppStore.prem(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (data && data.ErrorCode && -1 < inArray(pInt(data.ErrorCode), [
|
||||||
|
Notification.LicensingServerIsUnavailable,
|
||||||
|
Notification.LicensingExpired
|
||||||
|
]))
|
||||||
|
{
|
||||||
|
LicenseStore.licenseError(getNotification(pInt(data.ErrorCode)));
|
||||||
|
LicenseStore.licensing(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (StorageResultType.Abort === result)
|
||||||
|
{
|
||||||
|
LicenseStore.licenseError(getNotification(Notification.LicensingServerIsUnavailable));
|
||||||
|
LicenseStore.licensing(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LicenseStore.licensing(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, force);
|
||||||
|
}
|
||||||
|
|
||||||
|
bootend(callback = null) {
|
||||||
|
if (progressJs)
|
||||||
|
{
|
||||||
|
progressJs.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callback)
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bootstart() {
|
||||||
|
|
||||||
|
super.bootstart();
|
||||||
|
|
||||||
|
require('Stores/Admin/App').populate();
|
||||||
|
require('Stores/Admin/Capa').populate();
|
||||||
|
|
||||||
|
kn.hideLoading();
|
||||||
|
|
||||||
|
if (!Settings.appSettingsGet('allowAdminPanel'))
|
||||||
|
{
|
||||||
|
kn.routeOff();
|
||||||
|
kn.setHash(Links.root(), true);
|
||||||
|
kn.routeOff();
|
||||||
|
|
||||||
|
_.defer(() => {
|
||||||
|
window.location.href = '/';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Settings.settingsGet('Auth'))
|
||||||
|
{
|
||||||
|
kn.startScreens([
|
||||||
|
require('Screen/Admin/Settings')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
kn.startScreens([
|
||||||
|
require('Screen/Admin/Login')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bootend();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new AdminApp();
|
||||||
302
dev/App/User.js
302
dev/App/User.js
|
|
@ -1,302 +0,0 @@
|
||||||
import 'External/User/ko';
|
|
||||||
|
|
||||||
import { SMAudio } from 'Common/Audio';
|
|
||||||
import { arrayLength } from 'Common/Utils';
|
|
||||||
import { mailToHelper, setLayoutResizer, dropdownsDetectVisibility, loadAccountsAndIdentities } from 'Common/UtilsUser';
|
|
||||||
|
|
||||||
import {
|
|
||||||
FolderType,
|
|
||||||
ClientSideKeyNameFolderListSize
|
|
||||||
} from 'Common/EnumsUser';
|
|
||||||
|
|
||||||
import {
|
|
||||||
doc,
|
|
||||||
elementById,
|
|
||||||
$htmlCL,
|
|
||||||
Settings,
|
|
||||||
SettingsGet,
|
|
||||||
leftPanelDisabled,
|
|
||||||
addEventsListener,
|
|
||||||
addShortcut
|
|
||||||
} from 'Common/Globals';
|
|
||||||
|
|
||||||
import { UNUSED_OPTION_VALUE } from 'Common/Consts';
|
|
||||||
|
|
||||||
import {
|
|
||||||
getFolderInboxName,
|
|
||||||
getFolderFromCacheList
|
|
||||||
} from 'Common/Cache';
|
|
||||||
|
|
||||||
import { i18n, reloadTime, getErrorMessage } from 'Common/Translator';
|
|
||||||
|
|
||||||
import { SettingsUserStore } from 'Stores/User/Settings';
|
|
||||||
import { NotificationUserStore } from 'Stores/User/Notification';
|
|
||||||
import { AccountUserStore } from 'Stores/User/Account';
|
|
||||||
import { ContactUserStore } from 'Stores/User/Contact';
|
|
||||||
import { FolderUserStore } from 'Stores/User/Folder';
|
|
||||||
import { PgpUserStore } from 'Stores/User/Pgp';
|
|
||||||
import { SMimeUserStore } from 'Stores/User/SMime';
|
|
||||||
import { MessagelistUserStore } from 'Stores/User/Messagelist';
|
|
||||||
import { ThemeStore, initThemes } from 'Stores/Theme';
|
|
||||||
import { LanguageStore } from 'Stores/Language';
|
|
||||||
import { MessageUserStore } from 'Stores/User/Message';
|
|
||||||
|
|
||||||
import Remote from 'Remote/User/Fetch';
|
|
||||||
|
|
||||||
import { LoginUserScreen } from 'Screen/User/Login';
|
|
||||||
import { MailBoxUserScreen } from 'Screen/User/MailBox';
|
|
||||||
import { SettingsUserScreen } from 'Screen/User/Settings';
|
|
||||||
|
|
||||||
import { startScreens, showScreenPopup, arePopupsVisible } from 'Knoin/Knoin';
|
|
||||||
|
|
||||||
import { AbstractApp } from 'App/Abstract';
|
|
||||||
|
|
||||||
import { ComposePopupView } from 'View/Popup/Compose';
|
|
||||||
import { FolderSystemPopupView } from 'View/Popup/FolderSystem';
|
|
||||||
import { AskPopupView } from 'View/Popup/Ask';
|
|
||||||
|
|
||||||
import {
|
|
||||||
folderInformation,
|
|
||||||
folderInformationMultiply,
|
|
||||||
setRefreshFoldersInterval
|
|
||||||
} from 'Common/Folders';
|
|
||||||
import { loadFolders } from 'Model/FolderCollection';
|
|
||||||
|
|
||||||
export class AppUser extends AbstractApp {
|
|
||||||
constructor() {
|
|
||||||
super(Remote);
|
|
||||||
|
|
||||||
// wakeUp
|
|
||||||
const interval = 3600000; // 60m
|
|
||||||
let lastTime = Date.now();
|
|
||||||
setInterval(() => {
|
|
||||||
const currentTime = Date.now();
|
|
||||||
(currentTime > (lastTime + interval + 1000))
|
|
||||||
&& Remote.request('Version',
|
|
||||||
iError => (100 < iError) && location.reload(),
|
|
||||||
{ version: Settings.app('version') }
|
|
||||||
);
|
|
||||||
lastTime = currentTime;
|
|
||||||
}, interval);
|
|
||||||
|
|
||||||
addEventsListener(doc, ['keydown','keyup'], (ev=>$htmlCL.toggle('rl-ctrl-key-pressed', ev.ctrlKey)).debounce(500));
|
|
||||||
|
|
||||||
addShortcut('escape,enter', '', dropdownsDetectVisibility);
|
|
||||||
addEventListener('click', dropdownsDetectVisibility);
|
|
||||||
|
|
||||||
this.folderList = FolderUserStore.folderList;
|
|
||||||
this.messageList = MessagelistUserStore;
|
|
||||||
|
|
||||||
this.ask = AskPopupView;
|
|
||||||
|
|
||||||
this.loadAccountsAndIdentities = loadAccountsAndIdentities;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {number} iFolderType
|
|
||||||
* @param {string} sFromFolderFullName
|
|
||||||
* @param {Set} oUids
|
|
||||||
* @param {boolean=} bDelete = false
|
|
||||||
*/
|
|
||||||
moveMessagesToFolderType(iFolderType, sFromFolderFullName, oUids, bDelete) {
|
|
||||||
let oMoveFolder = null,
|
|
||||||
nSetSystemFoldersNotification = 0;
|
|
||||||
|
|
||||||
switch (iFolderType) {
|
|
||||||
case FolderType.Junk:
|
|
||||||
oMoveFolder = getFolderFromCacheList(FolderUserStore.spamFolder());
|
|
||||||
nSetSystemFoldersNotification = iFolderType;
|
|
||||||
bDelete = bDelete || UNUSED_OPTION_VALUE === FolderUserStore.spamFolder();
|
|
||||||
break;
|
|
||||||
case FolderType.Inbox:
|
|
||||||
oMoveFolder = getFolderFromCacheList(getFolderInboxName());
|
|
||||||
break;
|
|
||||||
case FolderType.Trash:
|
|
||||||
oMoveFolder = getFolderFromCacheList(FolderUserStore.trashFolder());
|
|
||||||
nSetSystemFoldersNotification = iFolderType;
|
|
||||||
bDelete = bDelete/* || UNUSED_OPTION_VALUE === FolderUserStore.trashFolder()*/
|
|
||||||
|| sFromFolderFullName === FolderUserStore.spamFolder()
|
|
||||||
|| sFromFolderFullName === FolderUserStore.trashFolder();
|
|
||||||
break;
|
|
||||||
case FolderType.Archive:
|
|
||||||
oMoveFolder = getFolderFromCacheList(FolderUserStore.archiveFolder());
|
|
||||||
nSetSystemFoldersNotification = iFolderType;
|
|
||||||
bDelete = bDelete || UNUSED_OPTION_VALUE === FolderUserStore.archiveFolder();
|
|
||||||
break;
|
|
||||||
// no default
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bDelete) {
|
|
||||||
showScreenPopup(AskPopupView, [
|
|
||||||
i18n('POPUPS_ASK/DESC_WANT_DELETE_MESSAGES'),
|
|
||||||
() => {
|
|
||||||
MessagelistUserStore.moveMessages(sFromFolderFullName, oUids);
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
} else if (oMoveFolder) {
|
|
||||||
MessagelistUserStore.moveMessages(sFromFolderFullName, oUids, oMoveFolder.fullName);
|
|
||||||
} else {
|
|
||||||
showScreenPopup(FolderSystemPopupView, [nSetSystemFoldersNotification]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} folder
|
|
||||||
* @param {Array=} list = []
|
|
||||||
*/
|
|
||||||
folderInformation(folder, list) {
|
|
||||||
folderInformation(folder, list);
|
|
||||||
}
|
|
||||||
|
|
||||||
logout() {
|
|
||||||
Remote.request('Logout', (iError, data) =>
|
|
||||||
iError ? alert('Logout error: ' + getErrorMessage(iError, data))
|
|
||||||
: rl.logoutReload(Settings.app('customLogoutLink'))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bootstart() {
|
|
||||||
super.bootstart();
|
|
||||||
|
|
||||||
addEventListener('beforeunload', event => {
|
|
||||||
if (arePopupsVisible() || (!SettingsUserStore.usePreviewPane() && MessageUserStore.message())) {
|
|
||||||
event.preventDefault();
|
|
||||||
return event.returnValue = i18n('POPUPS_ASK/EXIT_ARE_YOU_SURE');
|
|
||||||
}
|
|
||||||
}, {capture: true});
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh() {
|
|
||||||
initThemes();
|
|
||||||
LanguageStore.language(SettingsGet('language'));
|
|
||||||
this.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
if (SettingsGet('Auth')) {
|
|
||||||
rl.setTitle(i18n('GLOBAL/LOADING'));
|
|
||||||
|
|
||||||
SMAudio.notifications(!!SettingsGet('SoundNotification'));
|
|
||||||
NotificationUserStore.enabled(!!SettingsGet('DesktopNotifications'));
|
|
||||||
|
|
||||||
AccountUserStore.email(SettingsGet('Email'));
|
|
||||||
|
|
||||||
SettingsUserStore.init();
|
|
||||||
ContactUserStore.init();
|
|
||||||
|
|
||||||
loadFolders((success, error) => {
|
|
||||||
try {
|
|
||||||
if (success) {
|
|
||||||
startScreens([
|
|
||||||
MailBoxUserScreen,
|
|
||||||
SettingsUserScreen
|
|
||||||
]);
|
|
||||||
|
|
||||||
setRefreshFoldersInterval(SettingsGet('CheckMailInterval'));
|
|
||||||
|
|
||||||
loadAccountsAndIdentities();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
const cF = FolderUserStore.currentFolderFullName();
|
|
||||||
getFolderInboxName() === cF || folderInformation(cF);
|
|
||||||
FolderUserStore.hasCapability('LIST-STATUS') || folderInformationMultiply(true);
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
setTimeout(() => Remote.request('AppDelayStart'), 35000);
|
|
||||||
|
|
||||||
// add pointermove ?
|
|
||||||
addEventsListener(doc, ['touchstart','mousemove','keydown'], SettingsUserStore.delayLogout, {passive:true});
|
|
||||||
SettingsUserStore.delayLogout();
|
|
||||||
|
|
||||||
// initLeftSideLayoutResizer
|
|
||||||
setTimeout(() => {
|
|
||||||
const left = elementById('rl-left'),
|
|
||||||
fToggle = () =>
|
|
||||||
setLayoutResizer(left, ClientSideKeyNameFolderListSize,
|
|
||||||
(ThemeStore.isMobile() || leftPanelDisabled()) ? 0 : 'Width');
|
|
||||||
if (left) {
|
|
||||||
fToggle();
|
|
||||||
leftPanelDisabled.subscribe(fToggle);
|
|
||||||
}
|
|
||||||
}, 1);
|
|
||||||
|
|
||||||
setInterval(reloadTime, 60000);
|
|
||||||
|
|
||||||
PgpUserStore.init();
|
|
||||||
SMimeUserStore.loadCertificates();
|
|
||||||
|
|
||||||
setTimeout(() => mailToHelper(SettingsGet('mailToEmail')), 500);
|
|
||||||
} else {
|
|
||||||
this.logout();
|
|
||||||
alert('Folders error: ' + getErrorMessage(0, error))
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
startScreens([LoginUserScreen]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showMessageComposer(params = [])
|
|
||||||
{
|
|
||||||
let msg = params[1];
|
|
||||||
if (1 == arrayLength(msg)) {
|
|
||||||
msg = msg[0];
|
|
||||||
}
|
|
||||||
if (msg) {
|
|
||||||
msg.decrypt().then((/*success*/)=>showScreenPopup(ComposePopupView, params));
|
|
||||||
} else {
|
|
||||||
showScreenPopup(ComposePopupView, params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AskPopupView.password = function(sAskDesc, btnText, ask) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
this.showModal([
|
|
||||||
sAskDesc,
|
|
||||||
view => resolve({
|
|
||||||
password:view.passphrase(),
|
|
||||||
username:/*ask & 2 ? */view.username(),
|
|
||||||
remember:/*ask & 4 ? */view.remember(),
|
|
||||||
rememberPermanent: view.rememberPermanent(),
|
|
||||||
}),
|
|
||||||
() => resolve(null),
|
|
||||||
true,
|
|
||||||
ask || 1,
|
|
||||||
btnText
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
AskPopupView.cryptkey = () => new Promise(resolve => {
|
|
||||||
const fn = () => AskPopupView.showModal([
|
|
||||||
i18n('CRYPTO/ASK_CRYPTKEY_PASS'),
|
|
||||||
view => {
|
|
||||||
let pass = view.passphrase();
|
|
||||||
if (pass) {
|
|
||||||
Remote.post('ResealCryptKey', null, {
|
|
||||||
passphrase: pass
|
|
||||||
}).then(response => {
|
|
||||||
resolve(response?.Result);
|
|
||||||
}).catch(e => {
|
|
||||||
if (111 === e.code) {
|
|
||||||
fn();
|
|
||||||
} else {
|
|
||||||
console.error(e);
|
|
||||||
resolve(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
resolve(null);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
() => resolve(null),
|
|
||||||
true,
|
|
||||||
1,
|
|
||||||
i18n('CRYPTO/DECRYPT')
|
|
||||||
]);
|
|
||||||
fn();
|
|
||||||
});
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue