mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-10-25 14:45:10 +03:00
Compare commits
No commits in common. "master" and "v1.10.3.150-beta.1" have entirely different histories.
master
...
v1.10.3.15
4137 changed files with 330673 additions and 323123 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
|
||||
329
.eslintrc.js
329
.eslintrc.js
|
|
@ -1,64 +1,281 @@
|
|||
|
||||
module.exports = {
|
||||
parser: 'babel-eslint',
|
||||
// extends: ['eslint:recommended', 'plugin:prettier/recommended'],
|
||||
extends: ['eslint:recommended'],
|
||||
parserOptions: {
|
||||
ecmaVersion: 11,
|
||||
sourceType: 'module'
|
||||
'extends': 'eslint:recommended',
|
||||
'ecmaFeatures': {
|
||||
'modules': true
|
||||
},
|
||||
env: {
|
||||
node: true,
|
||||
browser: true,
|
||||
es2020: true
|
||||
'parserOptions': {
|
||||
'ecmaVersion': 6,
|
||||
'sourceType': 'module'
|
||||
},
|
||||
'env': {
|
||||
'node': true,
|
||||
'commonjs': true,
|
||||
'es6': true,
|
||||
'browser': true
|
||||
},
|
||||
globals: {
|
||||
// SnappyMail
|
||||
'rainloopI18N': "readonly",
|
||||
'rainloopTEMPLATES': "readonly",
|
||||
'rl': "readonly",
|
||||
'shortcuts': "readonly",
|
||||
// '__APP_BOOT': "readonly",
|
||||
// deb/boot.js
|
||||
'progressJs': "readonly",
|
||||
// others
|
||||
'openpgp': "readonly",
|
||||
'CKEDITOR': "readonly",
|
||||
'Squire': "readonly",
|
||||
'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"
|
||||
'globals': {
|
||||
'RL_COMMUNITY': true
|
||||
},
|
||||
// http://eslint.org/docs/rules/
|
||||
rules: {
|
||||
'no-cond-assign': 0,
|
||||
// plugins
|
||||
'no-mixed-spaces-and-tabs': 'off',
|
||||
'max-len': [
|
||||
'error',
|
||||
120,
|
||||
2,
|
||||
{
|
||||
ignoreComments: true,
|
||||
ignoreUrls: true,
|
||||
ignoreTrailingComments: true,
|
||||
ignorePattern: '(^\\s*(const|let|var)\\s.+=\\s*require\\s*\\(|^import\\s.+\\sfrom\\s.+;$)'
|
||||
'rules': {
|
||||
|
||||
// errors
|
||||
'no-cond-assign': [2, 'always'],
|
||||
'no-console': 2,
|
||||
'no-constant-condition': 2,
|
||||
'no-control-regex': 2,
|
||||
'no-debugger': 2,
|
||||
'no-dupe-args': 2,
|
||||
'no-dupe-keys': 2,
|
||||
'no-duplicate-case': 2,
|
||||
'no-empty': 2,
|
||||
'no-empty-character-class': 2,
|
||||
'no-ex-assign': 2,
|
||||
'no-extra-boolean-cast': 2,
|
||||
// 'no-extra-parens': 2,
|
||||
'no-extra-semi': 2,
|
||||
'no-func-assign': 2,
|
||||
'no-inner-declarations': 2,
|
||||
'no-invalid-regexp': 2,
|
||||
'no-irregular-whitespace': 2,
|
||||
'no-negated-in-lhs': 2,
|
||||
'no-obj-calls': 2,
|
||||
'no-prototype-builtins': 2,
|
||||
'no-regex-spaces': 2,
|
||||
'no-sparse-arrays': 2,
|
||||
'no-unexpected-multiline': 2,
|
||||
'no-unreachable': 2,
|
||||
'no-unsafe-finally': 2,
|
||||
'use-isnan': 2,
|
||||
// 'valid-jsdoc': [2, {
|
||||
// 'requireParamDescription': false,
|
||||
// 'requireReturnDescription': false
|
||||
// }],
|
||||
'valid-typeof': 2,
|
||||
|
||||
// best practices
|
||||
'accessor-pairs': 2,
|
||||
'array-callback-return': 2,
|
||||
'block-scoped-var': 2,
|
||||
// 'complexity': 2,
|
||||
'consistent-return': 2,
|
||||
'curly': 2,
|
||||
'default-case': 2,
|
||||
'dot-location': [2, 'property'],
|
||||
'dot-notation': 2,
|
||||
'eqeqeq': 2,
|
||||
'guard-for-in': 2,
|
||||
'no-alert': 2,
|
||||
'no-caller': 2,
|
||||
'no-case-declarations': 2,
|
||||
'no-div-regex': 2,
|
||||
'no-else-return': 2,
|
||||
'no-empty-function': 2,
|
||||
'no-empty-pattern': 2,
|
||||
'no-eq-null': 2,
|
||||
'no-eval': 2,
|
||||
'no-extend-native': 2,
|
||||
'no-extra-bind': 2,
|
||||
'no-extra-label': 2,
|
||||
'no-fallthrough': 2,
|
||||
'no-floating-decimal': 2,
|
||||
'no-implicit-coercion': [2, {'allow': ['!!', '+']}],
|
||||
'no-implicit-globals': 2,
|
||||
'no-implied-eval': 2,
|
||||
// 'no-invalid-this': 2,
|
||||
'no-iterator': 2,
|
||||
'no-labels': 2,
|
||||
'no-lone-blocks': 2,
|
||||
'no-loop-func': 2,
|
||||
// 'no-magic-numbers': [2, {
|
||||
// 'ignore': [-1, 0, 1],
|
||||
// 'ignoreArrayIndexes': true
|
||||
// }],
|
||||
'no-multi-spaces': 2,
|
||||
'no-multi-str': 2,
|
||||
'no-native-reassign': 2,
|
||||
'no-new': 2,
|
||||
'no-new-func': 2,
|
||||
'no-new-wrappers': 2,
|
||||
'no-octal': 2,
|
||||
'no-octal-escape': 2,
|
||||
// 'no-param-reassign': 2,
|
||||
'no-proto': 2,
|
||||
'no-redeclare': 2,
|
||||
'no-return-assign': 2,
|
||||
'no-script-url': 2,
|
||||
'no-self-assign': 2,
|
||||
'no-self-compare': 2,
|
||||
'no-sequences': 2,
|
||||
'no-throw-literal': 2,
|
||||
'no-unmodified-loop-condition': 2,
|
||||
'no-unused-expressions': 2,
|
||||
'no-unused-labels': 2,
|
||||
'no-useless-call': 2,
|
||||
'no-useless-concat': 2,
|
||||
'no-useless-escape': 2,
|
||||
'no-void': 2,
|
||||
'no-warning-comments': 2,
|
||||
'no-with': 2,
|
||||
'radix': 2,
|
||||
// 'vars-on-top': 2,
|
||||
'wrap-iife': 2,
|
||||
'yoda': [2, 'always'],
|
||||
|
||||
// strict mode
|
||||
'strict': 2,
|
||||
|
||||
// variables
|
||||
'init-declarations': 2,
|
||||
'no-catch-shadow': 2,
|
||||
'no-delete-var': 2,
|
||||
'no-label-var': 2,
|
||||
'no-restricted-globals': 2,
|
||||
'no-shadow': 2,
|
||||
'no-shadow-restricted-names': 2,
|
||||
'no-undef': 2,
|
||||
'no-undef-init': 2,
|
||||
'no-undefined': 2,
|
||||
'no-unused-vars': 2,
|
||||
'no-use-before-define': 2,
|
||||
|
||||
// node.js and commonjs
|
||||
'callback-return': 2,
|
||||
// 'global-require': 2,
|
||||
'handle-callback-err': 2,
|
||||
// 'no-mixed-requires': 2,
|
||||
'no-new-require': 2,
|
||||
'no-path-concat': 2,
|
||||
'no-process-env': 2,
|
||||
'no-process-exit': 2,
|
||||
'no-restricted-modules': 2,
|
||||
// 'no-sync': 2,
|
||||
|
||||
// stylistic issues
|
||||
'array-bracket-spacing': 2,
|
||||
'block-spacing': [2, 'never'],
|
||||
// 'brace-style': [2, 'allman'],
|
||||
'camelcase': 2,
|
||||
'comma-dangle': [2, 'never'],
|
||||
'comma-spacing': 2,
|
||||
'comma-style': 2,
|
||||
'computed-property-spacing': 2,
|
||||
'consistent-this': [2, 'self'],
|
||||
'eol-last': 2,
|
||||
'func-names': [2, 'never'],
|
||||
// 'func-style': 2,
|
||||
'id-blacklist': [2, 'x'],
|
||||
'id-length': [2, {'min': 1, 'max': 50}],
|
||||
'id-match': 2,
|
||||
'indent': [2, 'tab', {
|
||||
'SwitchCase': 1,
|
||||
'VariableDeclarator': {
|
||||
'var': 1,
|
||||
'let': 1,
|
||||
'const': 1
|
||||
}
|
||||
],
|
||||
'no-constant-condition': ["error", { "checkLoops": false }]
|
||||
}],
|
||||
'jsx-quotes': 2,
|
||||
'key-spacing': 2,
|
||||
'keyword-spacing': 2,
|
||||
'linebreak-style': [2, 'unix'],
|
||||
// 'lines-around-comment': 2,
|
||||
'max-depth': [2, 10],
|
||||
'max-len': [2, 200],
|
||||
// 'max-lines': 2,
|
||||
'max-nested-callbacks': [2, 5],
|
||||
// 'max-params': 2,
|
||||
// 'max-statements': [2, {'max': 10}, {'ignoreTopLevelFunctions': true}],
|
||||
'max-statements-per-line': 2,
|
||||
'new-cap': 2,
|
||||
'new-parens': 2,
|
||||
// 'newline-after-var': 2,
|
||||
// 'newline-before-return': 2,
|
||||
// 'newline-per-chained-call': 2,
|
||||
'no-array-constructor': 2,
|
||||
'no-bitwise': 2,
|
||||
'no-continue': 2,
|
||||
// 'no-inline-comments': 2,
|
||||
// 'no-lonely-if': 2,
|
||||
// 'no-mixed-operators': 2,
|
||||
'no-mixed-spaces-and-tabs': 2,
|
||||
'no-multiple-empty-lines': [2, {
|
||||
"max": 1,
|
||||
"maxEOF": 1,
|
||||
"maxBOF": 1
|
||||
}],
|
||||
// 'no-negated-condition': 2,
|
||||
// 'no-nested-ternary': 2,
|
||||
'no-new-object': 2,
|
||||
'no-plusplus': [2, {
|
||||
'allowForLoopAfterthoughts': true
|
||||
}],
|
||||
'no-restricted-syntax': 2,
|
||||
'no-spaced-func': 2,
|
||||
'no-ternary': 0,
|
||||
'no-trailing-spaces': 2, // disallow trailing whitespace at the end of lines
|
||||
// 'no-underscore-dangle': 2, // disallow dangling underscores in identifiers
|
||||
'no-unneeded-ternary': 2, // disallow ternary operators when simpler alternatives exist
|
||||
'no-whitespace-before-property': 2,
|
||||
// 'object-curly-newline': 2,
|
||||
'object-curly-spacing': [2, 'never'],
|
||||
'object-property-newline': [2, {'allowMultiplePropertiesPerLine': true}],
|
||||
// 'one-var': [2, {
|
||||
// 'var': 'always',
|
||||
// 'let': 'always',
|
||||
// 'const': 'always'
|
||||
// }],
|
||||
'one-var-declaration-per-line': [2, 'always'],
|
||||
'operator-assignment': 2,
|
||||
'operator-linebreak': [2, 'after'],
|
||||
// 'padded-blocks': [2, 'never'],
|
||||
// 'quote-props': [2, 'as-needed'],
|
||||
'quotes': [2, 'single'],
|
||||
'require-jsdoc': 2,
|
||||
'semi': [2, 'always'],
|
||||
'semi-spacing': 2,
|
||||
// 'sort-vars': 2,
|
||||
'space-before-blocks': 2,
|
||||
'space-before-function-paren': [2, 'never'],
|
||||
'space-in-parens': 2,
|
||||
'space-infix-ops': 2,
|
||||
'space-unary-ops': 2,
|
||||
'spaced-comment': 2,
|
||||
'unicode-bom': [2, 'never'],
|
||||
'wrap-regex': 2,
|
||||
|
||||
// es6
|
||||
'arrow-body-style': [2, 'as-needed'],
|
||||
'arrow-parens': 2,
|
||||
'arrow-spacing': 2,
|
||||
'constructor-super': 2,
|
||||
'generator-star-spacing': 2,
|
||||
'no-class-assign': 2,
|
||||
'no-confusing-arrow': [2, {'allowParens': true}],
|
||||
'no-const-assign': 2,
|
||||
'no-dupe-class-members': 2,
|
||||
'no-duplicate-imports': 2,
|
||||
'no-new-symbol': 2,
|
||||
'no-restricted-imports': 2,
|
||||
'no-this-before-super': 2,
|
||||
'no-useless-computed-key': 2,
|
||||
'no-useless-constructor': 2,
|
||||
'no-useless-rename': 2,
|
||||
// 'no-var': 2,
|
||||
// 'object-shorthand': 2,
|
||||
// 'prefer-arrow-callback': 2,
|
||||
'prefer-const': 2,
|
||||
// 'prefer-reflect': 2,
|
||||
|
||||
'prefer-rest-params': 2,
|
||||
'prefer-spread': 2,
|
||||
|
||||
// 'prefer-template': 2,
|
||||
'require-yield': 2,
|
||||
'rest-spread-spacing': 2,
|
||||
'sort-imports': 0, // off
|
||||
'template-curly-spacing': 2,
|
||||
'yield-star-spacing': 2
|
||||
}
|
||||
};
|
||||
|
|
|
|||
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
|
||||
42
.gitignore
vendored
42
.gitignore
vendored
|
|
@ -1,32 +1,24 @@
|
|||
*.bak
|
||||
*.old
|
||||
/.idea
|
||||
/.vscode
|
||||
/api.php
|
||||
/*.log
|
||||
/snappymail/v/0.0.0/static/*
|
||||
/error.log
|
||||
/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
|
||||
/build/local
|
||||
/build/dist
|
||||
/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
|
||||
.DS_Store
|
||||
/tests/fix.php
|
||||
/MULTIPLY
|
||||
/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
|
||||
/include.php
|
||||
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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
13
.travis.yml
Normal file
13
.travis.yml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
language: php
|
||||
php:
|
||||
- '5.3'
|
||||
- '5.4'
|
||||
- '5.5'
|
||||
- '5.6'
|
||||
- '7.0'
|
||||
before_script:
|
||||
- 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 eslint
|
||||
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!**
|
||||
|
||||
1. Fork the repo, do work in a feature branch.
|
||||
2. Issue a pull request.
|
||||
|
||||
---
|
||||
|
||||
**Getting started**
|
||||
|
||||
1. Install PHP
|
||||
2. Install node.js - `https://nodejs.org/download/`
|
||||
3. Install yarn - `https://yarnpkg.com/en/docs/install`
|
||||
4. Install gulp - `npm install gulp -g`
|
||||
5. Fork snappymail from https://github.com/the-djmaze/snappymail
|
||||
6. Clone snappymail - `git clone git@github.com:USERNAME/snappymail.git snappymail`
|
||||
7. `cd snappymail`
|
||||
8. Install all dependencies - `yarn install`
|
||||
9. Run gulp - `gulp`
|
||||
|
||||
---
|
||||
|
||||
**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.
|
||||
**Thanks for contributing to RainLoop Webmail!**
|
||||
|
||||
1. Sign the CLA (individual or corporate) at <http://www.rainloop.net/contribute/>.
|
||||
2. Fork the repo, do work in a feature branch.
|
||||
3. Issue a pull request.
|
||||
|
||||
---
|
||||
|
||||
**Getting started**
|
||||
|
||||
1. Install node.js - `https://nodejs.org/download/`
|
||||
2. Install gulp - `npm install gulp -g`
|
||||
3. Fork rainloop - `https://github.com/RainLoop/rainloop-webmail/issues/new#fork-destination-box`
|
||||
4. Clone rainloop - `git clone git@github.com:USERNAME/rainloop-webmail.git rainloop`
|
||||
5. `cd rainloop`
|
||||
6. Install gulp add gulp plugins into project directory - `npm install`
|
||||
7. Run gulp - `gulp`
|
||||
|
||||
---
|
||||
|
||||
If you have any questions, open an issue or email support@rainloop.net.
|
||||
|
|
|
|||
2
LICENSE
2
LICENSE
|
|
@ -658,4 +658,4 @@ specific requirements.
|
|||
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.
|
||||
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">
|
||||
<a href="https://github.com/the-djmaze/snappymail">
|
||||
<img src="https://snappymail.eu/static/img/logo-256x256-white.png">
|
||||
</a>
|
||||
<br>
|
||||
<h1>SnappyMail</h1>
|
||||
<br>
|
||||
|
||||
[](https://github.com/the-djmaze/snappymail/actions/workflows/docker.yml)
|
||||
[](https://hub.docker.com/r/djmaze/snappymail/tags)
|
||||
|
||||
<p>
|
||||
Simple, modern, lightweight & fast web-based email client.
|
||||
</p>
|
||||
<p>
|
||||
The drastically upgraded & secured fork of <a href="https://github.com/RainLoop/rainloop-webmail">RainLoop Webmail Community edition</a>.
|
||||
</p>
|
||||
<p>
|
||||
We thank the RainLoop Team for making a great PHP 5 product that was good in the past.
|
||||
</p>
|
||||
<p>
|
||||
Up to date system requirements, snappy performance, simple installation and upgrade, no database required
|
||||
- all these make SnappyMail a good choice.
|
||||
</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).
|
||||
RainLoop Webmail [](https://travis-ci.org/RainLoop/rainloop-webmail)
|
||||
==================
|
||||
|
||||
Simple, modern & fast web-based email client.
|
||||
|
||||
Modest system requirements, decent performance, simple installation and upgrade, no database required - all these make RainLoop Webmail a perfect choice for your email solution.
|
||||
|
||||
For more information about the product, check [rainloop.net](http://www.rainloop.net/).
|
||||
|
||||
Information about installing the product, check the [documentation page](http://www.rainloop.net/docs/installation/).
|
||||
|
||||
## Attention
|
||||
|
||||
This is **NOT** a stable version of RainLoop Webmail.
|
||||
It's not recommended to use in production environment.
|
||||
|
||||
## License
|
||||
|
||||
**RainLoop Webmail (Community edition)** is released under
|
||||
**GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (AGPL)**.
|
||||
http://www.gnu.org/licenses/agpl-3.0.html
|
||||
|
||||
Copyright (c) 2016 Rainloop Team
|
||||
|
|
|
|||
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
|
||||
|
||||
// Name this file as "include.php" to enable it.
|
||||
|
||||
//header('Strict-Transport-Security: max-age=31536000');
|
||||
// Rename this file to "include.php" to enable it.
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Custom 'data' folder path
|
||||
*/
|
||||
//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);
|
||||
function __get_additional_configuration_name()
|
||||
{
|
||||
return '';
|
||||
return defined('APP_SITE') && 0 < strlen(APP_SITE) ? APP_SITE.'.ini' : ''; // additional configuration file name
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
@ -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'
|
||||
));
|
||||
|
||||
18
build/owncloud/rainloop-app/appinfo/info.xml
Normal file
18
build/owncloud/rainloop-app/appinfo/info.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?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"/>
|
||||
<owncloud min-version="6"/>
|
||||
</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();
|
||||
}
|
||||
}
|
||||
401
dev/App/Abstract.jsx
Normal file
401
dev/App/Abstract.jsx
Normal file
|
|
@ -0,0 +1,401 @@
|
|||
|
||||
import window from 'window';
|
||||
import $ from '$';
|
||||
import _ from '_';
|
||||
import key from 'key';
|
||||
|
||||
import {
|
||||
$win, $html, $doc,
|
||||
startMicrotime, leftPanelDisabled, leftPanelType,
|
||||
sUserAgent, bMobileDevice, bAnimationSupported
|
||||
} from 'Common/Globals';
|
||||
|
||||
import {
|
||||
noop, isNormal, pString, inArray, microtime, timestamp,
|
||||
detectDropdownVisibility, windowResizeCallback
|
||||
} from 'Common/Utils';
|
||||
|
||||
import {KeyState} from 'Common/Enums';
|
||||
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 {toggle as toggleCmd} from 'Common/Cmd';
|
||||
|
||||
import {routeOff, setHash} from 'Knoin/Knoin';
|
||||
import {AbstractBoot} from 'Knoin/AbstractBoot';
|
||||
|
||||
class AbstractApp extends AbstractBoot
|
||||
{
|
||||
/**
|
||||
* @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', () => {
|
||||
Events.pub('window.resize');
|
||||
});
|
||||
|
||||
Events.sub('window.resize', _.throttle(() => {
|
||||
const
|
||||
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', (event) => {
|
||||
if (event && event.ctrlKey)
|
||||
{
|
||||
$html.addClass('rl-ctrl-key-pressed');
|
||||
}
|
||||
}).on('keyup', (event) => {
|
||||
if (event && !event.ctrlKey)
|
||||
{
|
||||
$html.removeClass('rl-ctrl-key-pressed');
|
||||
}
|
||||
});
|
||||
|
||||
$doc.on('mousemove keypress click', _.debounce(() => {
|
||||
Events.pub('rl.auto-logout-refresh');
|
||||
}, 5000));
|
||||
|
||||
key('esc, enter', KeyState.All, () => {
|
||||
detectDropdownVisibility();
|
||||
});
|
||||
|
||||
key('ctrl+shift+`', KeyState.All, () => {
|
||||
toggleCmd();
|
||||
});
|
||||
}
|
||||
|
||||
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 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() : Links.rootUser());
|
||||
|
||||
if (logout && window.location.href !== customLogoutLink)
|
||||
{
|
||||
_.delay(() => {
|
||||
if (inIframe && window.parent)
|
||||
{
|
||||
window.parent.location.href = customLogoutLink;
|
||||
}
|
||||
else
|
||||
{
|
||||
window.location.href = customLogoutLink;
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
else
|
||||
{
|
||||
routeOff();
|
||||
setHash(Links.root(), true);
|
||||
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',
|
||||
query: '(max-width: 767px)',
|
||||
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',
|
||||
query: '(min-width: 768px) and (max-width: 999px)',
|
||||
onEnter: function() {
|
||||
$html.addClass('ssm-state-tablet');
|
||||
},
|
||||
onLeave: function() {
|
||||
$html.removeClass('ssm-state-tablet');
|
||||
}
|
||||
});
|
||||
|
||||
ssm.addState({
|
||||
id: 'desktop',
|
||||
query: '(min-width: 1000px) and (max-width: 1400px)',
|
||||
onEnter: () => {
|
||||
$html.addClass('ssm-state-desktop');
|
||||
},
|
||||
onLeave: () => {
|
||||
$html.removeClass('ssm-state-desktop');
|
||||
}
|
||||
});
|
||||
|
||||
ssm.addState({
|
||||
id: 'desktop-large',
|
||||
query: '(min-width: 1401px)',
|
||||
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();
|
||||
|
||||
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
|
||||
]);
|
||||
});
|
||||
};
|
||||
255
dev/App/Admin.jsx
Normal file
255
dev/App/Admin.jsx
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
|
||||
import window from 'window';
|
||||
import _ from '_';
|
||||
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 {SettingsAdminScreen} from 'Screen/Admin/Settings';
|
||||
import {LoginAdminScreen} from 'Screen/Admin/Login';
|
||||
|
||||
import {hideLoading, routeOff, setHash, startScreens} 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) => ({
|
||||
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) => ({
|
||||
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(bootendCallback = null) {
|
||||
if (progressJs)
|
||||
{
|
||||
progressJs.end();
|
||||
}
|
||||
|
||||
if (bootendCallback)
|
||||
{
|
||||
bootendCallback();
|
||||
}
|
||||
}
|
||||
|
||||
bootstart() {
|
||||
|
||||
super.bootstart();
|
||||
|
||||
require('Stores/Admin/App').populate();
|
||||
require('Stores/Admin/Capa').populate();
|
||||
|
||||
hideLoading();
|
||||
|
||||
if (!Settings.appSettingsGet('allowAdminPanel'))
|
||||
{
|
||||
routeOff();
|
||||
setHash(Links.root(), true);
|
||||
routeOff();
|
||||
|
||||
_.defer(() => {
|
||||
window.location.href = '/';
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Settings.settingsGet('Auth'))
|
||||
{
|
||||
startScreens([
|
||||
SettingsAdminScreen
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
startScreens([
|
||||
LoginAdminScreen
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
});
|
||||
1485
dev/App/User.jsx
Normal file
1485
dev/App/User.jsx
Normal file
File diff suppressed because it is too large
Load diff
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